Extracted document related APIs to FileManager

This commit is contained in:
Akash Yadav
2022-07-12 12:14:17 +05:30
parent d48b2d37d5
commit dec0c5d84f
16 changed files with 280 additions and 414 deletions
+1 -1
View File
@@ -41,7 +41,7 @@
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
@@ -102,6 +102,7 @@ import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.models.SaveResult;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.api.Project;
import com.itsaky.androidide.services.GradleBuildService;
@@ -109,10 +110,12 @@ import com.itsaky.androidide.services.LogReceiver;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData;
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult;
import com.itsaky.androidide.utils.BootClasspathProvider;
import com.itsaky.androidide.utils.CharSequenceInputStream;
import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.EditorActivityActions;
import com.itsaky.androidide.utils.EditorBottomSheetBehavior;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.LSPUtils;
import com.itsaky.androidide.utils.RecursiveFileSearcher;
@@ -161,6 +164,7 @@ public class EditorActivity extends StudioActivity
private static final int ACTION_ID_ALL = 102;
private static final ILogger LOG = ILogger.newInstance("EditorActivity");
private final EditorEventListener mBuildEventListener = new EditorEventListener();
private final FileTreeActionHandler mFileActionsHandler = new FileTreeActionHandler();
private ActivityEditorBinding mBinding;
private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding;
private EditorBottomSheetTabAdapter bottomSheetTabAdapter;
@@ -174,9 +178,6 @@ public class EditorActivity extends StudioActivity
private ActivityResultLauncher<Intent> mUIDesignerLauncher;
private EditorBottomSheetBehavior<? extends View> mEditorBottomSheet;
private EditorViewModel mViewModel;
private final FileTreeActionHandler mFileActionsHandler = new FileTreeActionHandler();
private GradleBuildService mBuildService;
private final ServiceConnection mGradleServiceConnection =
new ServiceConnection() {
@@ -196,21 +197,6 @@ public class EditorActivity extends StudioActivity
}
};
@Override
public void onBackPressed() {
if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
mBinding.getRoot().closeDrawer(GravityCompat.END);
} else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
mBinding.getRoot().closeDrawer(GravityCompat.START);
} else if (getDaemonStatusFragment().isShowing()) {
getDaemonStatusFragment().dismiss();
} else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) {
mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
confirmProjectClose();
}
}
@SuppressLint("RestrictedApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -1046,10 +1032,6 @@ public class EditorActivity extends StudioActivity
execTasks(null, "lintRelease");
}
/////////////////////////////////////////////////
////////////// PRIVATE APIS /////////////////////
/////////////////////////////////////////////////
public void cleanAndRebuild() {
execTasks(null, "clean", "build");
}
@@ -1141,26 +1123,6 @@ public class EditorActivity extends StudioActivity
}
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString(KEY_PROJECT_PATH, ProjectManager.INSTANCE.getProjectDirPath());
super.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
super.onStart();
mFileActionsHandler.register();
ProjectManager.INSTANCE.register();
}
@Override
protected void onStop() {
super.onStop();
mFileActionsHandler.unregister();
ProjectManager.INSTANCE.unregister();
}
// TODO Replace with events
private void dispatchOnResumeToEditors() {
CompletableFuture.runAsync(
@@ -1186,6 +1148,48 @@ public class EditorActivity extends StudioActivity
});
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString(KEY_PROJECT_PATH, ProjectManager.INSTANCE.getProjectDirPath());
super.onSaveInstanceState(outState);
}
@Override
public void onBackPressed() {
if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
mBinding.getRoot().closeDrawer(GravityCompat.END);
} else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
mBinding.getRoot().closeDrawer(GravityCompat.START);
} else if (getDaemonStatusFragment().isShowing()) {
getDaemonStatusFragment().dismiss();
} else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) {
mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
confirmProjectClose();
}
}
@Override
protected void onStart() {
super.onStart();
// Make sure we list and store the bootstrap classes
CompletableFuture.runAsync(
() ->
BootClasspathProvider.update(
Collections.singleton(Environment.ANDROID_JAR.getAbsolutePath())));
mFileActionsHandler.register();
FileManager.INSTANCE.register();
}
@Override
protected void onStop() {
super.onStop();
mFileActionsHandler.unregister();
FileManager.INSTANCE.unregister();
}
@Override
protected void onDestroy() {
closeProject(false);
@@ -1,6 +1,5 @@
/**
* ********************************************************************************** This file is
* part of AndroidIDE.
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
@@ -0,0 +1,170 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.projects
import com.itsaky.androidide.eventbus.events.EventReceiver
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.models.Range
import com.itsaky.androidide.progress.ProcessCancelledException
import com.itsaky.androidide.progress.ProgressManager
import com.itsaky.androidide.projects.models.ActiveDocument
import com.itsaky.androidide.utils.ILogger
import java.io.BufferedReader
import java.io.InputStream
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Path
import java.time.Instant
import org.apache.commons.io.FileUtils
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode.BACKGROUND
/**
* Manages active documents.
*
* @author Akash Yadav
*/
object FileManager : EventReceiver {
private val log = ILogger.newInstance(javaClass.simpleName)
private val activeDocuments: MutableMap<Path, ActiveDocument> = mutableMapOf()
fun isActive(file: Path): Boolean {
return this.activeDocuments.containsKey(file.normalize())
}
fun getActiveDocument(file: Path): ActiveDocument? {
return this.activeDocuments[file.normalize()]
}
fun getDocumentContents(file: Path): String {
val document = getActiveDocument(file)
if (document != null) {
return document.content
}
return getFileContents(file)
}
fun getLastModified(file: Path): Instant {
val document = getActiveDocument(file)
if (document != null) {
return document.modified
}
return getLastModifiedFromDisk(file)
}
fun getReader(file: Path): BufferedReader {
val document = getActiveDocument(file)
if (document != null) {
return document.content.reader().buffered()
}
return createFileReader(file)
}
fun getInputStream(file: Path): InputStream {
val document = getActiveDocument(file)
if (document != null) {
return document.content.byteInputStream().buffered()
}
return createFileInputStream(file)
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentOpen(event: DocumentOpenEvent) {
activeDocuments[event.openedFile.normalize()] = createDocument(event)
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentContentChange(event: DocumentChangeEvent) {
activeDocuments[event.changedFile.normalize()] = createDocument(event)
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentClose(event: DocumentCloseEvent) {
activeDocuments.remove(event.closedFile.normalize())
}
private fun createDocument(event: DocumentOpenEvent): ActiveDocument {
return ActiveDocument(
file = event.openedFile,
content = event.text,
changeRange = Range.NONE,
version = event.version,
changDelta = 0,
modified = Instant.now()
)
}
private fun createDocument(event: DocumentChangeEvent): ActiveDocument {
return ActiveDocument(
file = event.changedFile,
content = event.newText,
changeRange = event.changeRange,
version = event.version,
changDelta = event.changeDelta,
modified = Instant.now()
)
}
private fun createFileReader(file: Path): BufferedReader {
return try {
Files.newBufferedReader(file)
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
"".reader().buffered()
} catch (cancelled: ProcessCancelledException) {
"".reader().buffered()
}
}
private fun createFileInputStream(file: Path): InputStream {
return try {
Files.newInputStream(file)
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
"".byteInputStream()
} catch (cancelled: ProcessCancelledException) {
"".byteInputStream()
}
}
private fun getLastModifiedFromDisk(file: Path): Instant {
return Files.getLastModifiedTime(file).toInstant()
}
private fun getFileContents(file: Path): String {
return try {
ProgressManager.abortIfCancelled()
FileUtils.readFileToString(file.toFile(), Charset.defaultCharset())
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
""
} catch (cancelled: ProcessCancelledException) {
""
}
}
}
@@ -16,45 +16,24 @@
*/
package com.itsaky.androidide.projects
import com.itsaky.androidide.eventbus.events.EventReceiver
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.eventbus.events.project.ProjectInitializedEvent
import com.itsaky.androidide.progress.ProcessCancelledException
import com.itsaky.androidide.progress.ProgressManager.Companion.abortIfCancelled
import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.projects.api.ModuleProject
import com.itsaky.androidide.projects.api.Project
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.projects.models.ActiveDocument
import com.itsaky.androidide.projects.util.ProjectTransformer
import com.itsaky.androidide.tooling.api.IProject
import com.itsaky.androidide.utils.BootClasspathProvider
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.StringReader
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Path
import java.time.Instant
import java.util.*
import java.util.concurrent.*
import org.apache.commons.io.FileUtils
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode.BACKGROUND
/**
* Manages projects in AndroidIDE.
*
* @author Akash Yadav
*/
object ProjectManager : EventReceiver {
object ProjectManager {
private val log = ILogger.newInstance(javaClass.simpleName)
lateinit var projectPath: String
@@ -146,24 +125,6 @@ object ProjectManager : EventReceiver {
return findModuleForFile(file.toFile())
}
fun isDocumentActive(file: Path): Boolean {
if (!checkInit()) {
return false
}
for (module in this.rootProject!!.subModules) {
if (module !is ModuleProject) {
continue
}
if (module.isActive(file)) {
return true
}
}
return false
}
fun containsSourceFile(file: Path): Boolean {
if (!checkInit()) {
return false
@@ -183,144 +144,10 @@ object ProjectManager : EventReceiver {
return false
}
fun getActiveDocument(file: Path): ActiveDocument? {
if (!checkInit()) {
return null
}
for (module in this.rootProject!!.subModules) {
if (module !is ModuleProject) {
continue
}
val document = module.getActiveDocument(file)
if (document != null) {
return document
}
}
return null
}
fun getLastModified(file: Path): Instant {
if (!checkInit()) {
return getLastModifiedFromDisk(file)
}
val document = getActiveDocument(file)
if (document != null) {
return document.modified
}
return getLastModifiedFromDisk(file)
}
fun getDocumentContents(file: Path): String {
if (!checkInit()) {
return getFileContents(file)
}
val document = getActiveDocument(file)
if (document != null) {
return document.content
}
return getFileContents(file)
}
fun getReader(file: Path): BufferedReader {
if (!checkInit()) {
return createFileReader(file)
}
val document = getActiveDocument(file)
if (document != null) {
return BufferedReader(StringReader(document.content))
}
return createFileReader(file)
}
fun getInputStream(file: Path): InputStream {
if (!checkInit()) {
return createFileInputStream(file)
}
val document = getActiveDocument(file)
if (document != null) {
BufferedInputStream(document.content.byteInputStream())
}
return createFileInputStream(file)
}
//////////////////////////////////////////////////////////////////////////////////////////
////// TODO Subscribe to file creation/deletion/rename events and update source map //////
//////////////////////////////////////////////////////////////////////////////////////////
override fun register() {
super.register()
// Make sure we list and store the bootstrap classes
CompletableFuture.runAsync {
BootClasspathProvider.update(Collections.singleton(Environment.ANDROID_JAR.absolutePath))
}
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentOpen(event: DocumentOpenEvent) {
if (!checkInit()) {
return
}
for (subModule in this.rootProject!!.subModules) {
if (subModule !is ModuleProject) {
continue
}
if (subModule.onDocumentOpen(event)) {
break
}
}
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentClose(event: DocumentCloseEvent) {
if (!checkInit()) {
return
}
for (subModule in this.rootProject!!.subModules) {
if (subModule !is ModuleProject) {
continue
}
if (subModule.onDocumentClose(event)) {
break
}
}
}
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")
fun onDocumentContentChange(event: DocumentChangeEvent) {
if (!checkInit()) {
return
}
for (subModule in this.rootProject!!.subModules) {
if (subModule !is ModuleProject) {
continue
}
if (subModule.onDocumentChanged(event)) {
break
}
}
}
private fun isInitialized() = rootProject != null
private fun checkInit(): Boolean {
@@ -331,45 +158,4 @@ object ProjectManager : EventReceiver {
log.warn("Project is not initialized yet!")
return false
}
private fun createFileReader(file: Path): BufferedReader {
return try {
Files.newBufferedReader(file)
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
"".reader().buffered()
} catch (cancelled: ProcessCancelledException) {
log.debug("createFileReader(): cancelled")
"".reader().buffered()
}
}
private fun createFileInputStream(file: Path): InputStream {
return try {
Files.newInputStream(file)
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
"".byteInputStream()
} catch (cancelled: ProcessCancelledException) {
log.debug("createFileInputStream(): cancelled")
"".byteInputStream()
}
}
private fun getLastModifiedFromDisk(file: Path): Instant {
return Files.getLastModifiedTime(file).toInstant()
}
private fun getFileContents(file: Path): String {
return try {
abortIfCancelled()
FileUtils.readFileToString(file.toFile(), Charset.defaultCharset())
} catch (noFile: java.nio.file.NoSuchFileException) {
log.warn("No such file", noFile)
""
} catch (cancelled: ProcessCancelledException) {
log.debug("getFileContents(): cancelled")
""
}
}
}
@@ -18,22 +18,15 @@
package com.itsaky.androidide.projects.api
import android.text.TextUtils
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.models.Range
import com.itsaky.androidide.projects.models.ActiveDocument
import com.itsaky.androidide.tooling.api.model.GradleTask
import com.itsaky.androidide.utils.ClassTrie
import com.itsaky.androidide.utils.ClasspathReader
import com.itsaky.androidide.utils.DocumentUtils
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.SourceClassTrie
import com.itsaky.androidide.utils.SourceClassTrie.SourceNode
import com.itsaky.androidide.tooling.api.model.GradleTask
import com.itsaky.androidide.utils.ClasspathReader
import com.itsaky.androidide.utils.ILogger
import java.io.File
import java.nio.file.Path
import java.time.Instant
import java.util.concurrent.*
/**
* A module project. Base class for [AndroidModule] and [JavaModule].
@@ -63,12 +56,6 @@ abstract class ModuleProject(
@JvmField val compileJavaSourceClasses = SourceClassTrie()
@JvmField val compileClasspathClasses = ClassTrie()
/**
* Map of documents that are open in the editor. Keys here are the canonical paths of the
* documents.
*/
val activeDocuments: MutableMap<Path, ActiveDocument> = ConcurrentHashMap<Path, ActiveDocument>()
/**
* Get the source directories of this module (non-transitive i.e for this module only).
*
@@ -105,59 +92,6 @@ abstract class ModuleProject(
*/
abstract fun getCompileModuleProjects(): List<ModuleProject>
/**
* Called by [com.itsaky.androidide.projects.ProjectManager] when a document is opened.
*
* @param event The event descriptor.
* @return `true` if this module consumed the event. `false` otherwise.
*/
fun onDocumentOpen(event: DocumentOpenEvent): Boolean {
if (!isCacheable(event.openedFile) || !isFromThisModule(event.openedFile)) {
return false
}
activeDocuments[event.openedFile.normalize()] = createDocument(event)
return true
}
/**
* Called by [com.itsaky.androidide.projects.ProjectManager] when a document is changed.
*
* @param event The event descriptor.
* @return `true` if this module consumed the event. `false` otherwise.
*/
fun onDocumentChanged(event: DocumentChangeEvent): Boolean {
if (!isCacheable(event.changedFile) || !isFromThisModule(event.changedFile)) {
return false
}
activeDocuments[event.changedFile.normalize()] = createDocument(event)
return true
}
/**
* Called by [com.itsaky.androidide.projects.ProjectManager] when a document is closed.
*
* @param event The event descriptor.
* @return `true` if this module consumed the event. `false` otherwise.
*/
fun onDocumentClose(event: DocumentCloseEvent): Boolean {
if (!isCacheable(event.closedFile) || !isFromThisModule(event.closedFile)) {
return false
}
activeDocuments.remove(event.closedFile.normalize())
return true
}
fun isActive(file: Path): Boolean {
return this.activeDocuments.containsKey(file.normalize())
}
fun getActiveDocument(file: Path): ActiveDocument? {
return this.activeDocuments[file.normalize()]
}
/** Finds the source files and classes from source directories and classpaths and indexes them. */
@Suppress("UnstableApiUsage")
fun indexSourcesAndClasspaths() {
@@ -229,35 +163,8 @@ abstract class ModuleProject(
.filterIsInstance(SourceNode::class.java)
}
protected open fun isCacheable(file: Path): Boolean {
// For now, we only cache Java files
return DocumentUtils.isJavaFile(file)
}
protected open fun isFromThisModule(file: Path): Boolean {
open fun isFromThisModule(file: Path): Boolean {
// TODO This can be probably improved
return file.startsWith(this.projectDir.toPath())
}
protected open fun createDocument(event: DocumentOpenEvent): ActiveDocument {
return ActiveDocument(
file = event.openedFile,
content = event.text,
changeRange = Range.NONE,
version = event.version,
changDelta = 0,
modified = Instant.now()
)
}
protected open fun createDocument(event: DocumentChangeEvent): ActiveDocument {
return ActiveDocument(
file = event.changedFile,
content = event.newText,
changeRange = event.changeRange,
version = event.version,
changDelta = event.changeDelta,
modified = Instant.now()
)
}
}
@@ -17,7 +17,7 @@
package com.itsaky.androidide.projects.util;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.projects.models.ActiveDocument;
import com.itsaky.androidide.utils.Cache;
@@ -201,13 +201,8 @@ public class StringSearch {
private static final ByteBuffer SEARCH_BUFFER = ByteBuffer.allocateDirect(1024 * 1024);
private static String tryGetActiveDocContent(Path file) {
if (ProjectManager.INSTANCE.isDocumentActive(file)) {
final ActiveDocument document = ProjectManager.INSTANCE.getActiveDocument(file);
if (document == null) {
return null;
}
final ActiveDocument document = FileManager.INSTANCE.getActiveDocument(file);
if (document != null) {
return document.getContent();
}
@@ -378,7 +373,7 @@ public class StringSearch {
public static String packageName(Path file) {
Pattern packagePattern = Pattern.compile("^package +(.*);");
Pattern startOfClass = Pattern.compile("^[\\w ]*class +\\w+");
try (BufferedReader lines = ProjectManager.INSTANCE.getReader(file)) {
try (BufferedReader lines = FileManager.INSTANCE.getReader(file)) {
for (String line = lines.readLine(); line != null; line = lines.readLine()) {
if (startOfClass.matcher(line).find()) return "";
Matcher matchPackage = packagePattern.matcher(line);
@@ -30,6 +30,9 @@ import java.util.Objects;
public class Cache<K, V> {
private static final ILogger LOG = ILogger.newInstance("Cache");
// Cannot access FileManager from this module
private static Object FileManager_INSTANCE;
private static Method FileManager_getLastModified;
private final Map<Key<K>, Value> map = new HashMap<>();
public boolean has(Path file, K k) {
@@ -54,6 +57,39 @@ public class Cache<K, V> {
return value.created.isBefore(modified);
}
private static Instant getLastModified(Path file) {
initProjectManagerInstance();
try {
return (Instant) FileManager_getLastModified.invoke(FileManager_INSTANCE, file);
} catch (Throwable err) {
LOG.error("Cannot get last modified from ProjectManager", err);
return Instant.now();
}
}
private static void initProjectManagerInstance() {
if (FileManager_INSTANCE == null) {
try {
final var klass = Class.forName("com.itsaky.androidide.projects.FileManager");
final var field = klass.getDeclaredField("INSTANCE");
if (!field.isAccessible()) {
field.setAccessible(true);
}
FileManager_INSTANCE = field.get(null);
FileManager_getLastModified = klass.getDeclaredMethod("getLastModified", Path.class);
if (!FileManager_getLastModified.isAccessible()) {
FileManager_getLastModified.setAccessible(true);
}
} catch (Throwable err) {
LOG.error("Cannot reflect ProjectManager INSTANCE", err);
throw new RuntimeException(err);
}
}
}
public void load(Path file, K k, V v) {
// TODO limit total size of cache
Key<K> key = new Key<>(file, k);
@@ -102,41 +138,4 @@ public class Cache<K, V> {
this.value = value;
}
}
// Cannot access ProjectManager from this module
private static Object ProjectManager_INSTANCE;
private static Method ProjectManager_getLastModified;
private static Instant getLastModified(Path file) {
initProjectManagerInstance();
try {
return (Instant) ProjectManager_getLastModified.invoke(ProjectManager_INSTANCE, file);
} catch (Throwable err) {
LOG.error("Cannot get last modified from ProjectManager", err);
return Instant.now();
}
}
private static void initProjectManagerInstance() {
if (ProjectManager_INSTANCE == null) {
try {
final var klass = Class.forName("com.itsaky.androidide.projects.ProjectManager");
final var field = klass.getDeclaredField("INSTANCE");
if (!field.isAccessible()) {
field.setAccessible(true);
}
ProjectManager_INSTANCE = field.get(null);
ProjectManager_getLastModified = klass.getDeclaredMethod("getLastModified", Path.class);
if (!ProjectManager_getLastModified.isAccessible()) {
ProjectManager_getLastModified.setAccessible(true);
}
} catch (Throwable err) {
LOG.error("Cannot reflect ProjectManager INSTANCE", err);
throw new RuntimeException(err);
}
}
}
}
@@ -29,6 +29,7 @@ import com.itsaky.androidide.javac.services.partial.DiagnosticListenerImpl;
import com.itsaky.androidide.lsp.java.parser.Parser;
import com.itsaky.androidide.lsp.java.visitors.MethodRangeScanner;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.api.AndroidModule;
import com.itsaky.androidide.projects.api.ModuleProject;
@@ -246,7 +247,7 @@ public class CompileBatch implements AutoCloseable {
private String errorText(javax.tools.Diagnostic<? extends javax.tools.JavaFileObject> err) {
Path file = Paths.get(err.getSource().toUri());
CharSequence contents = ProjectManager.INSTANCE.getDocumentContents(file);
CharSequence contents = FileManager.INSTANCE.getDocumentContents(file);
int begin = (int) err.getStartPosition();
int end = (int) err.getEndPosition();
return substring(contents, begin, end);
@@ -33,6 +33,7 @@ import com.itsaky.androidide.lsp.java.parser.Parser;
import com.itsaky.androidide.lsp.java.utils.Extractors;
import com.itsaky.androidide.lsp.java.visitors.FindTypeDeclarations;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.api.ModuleProject;
import com.itsaky.androidide.projects.util.StringSearch;
@@ -131,7 +132,7 @@ public class JavaCompilerService implements CompilerProvider {
List<String> list = new ArrayList<>();
Pattern importClass = Pattern.compile("^import +([\\w.]+\\.\\w+);");
Pattern importStar = Pattern.compile("^import +([\\w.]+\\.\\*);");
try (BufferedReader lines = ProjectManager.INSTANCE.getReader(file)) {
try (BufferedReader lines = FileManager.INSTANCE.getReader(file)) {
for (String line = lines.readLine(); line != null; line = lines.readLine()) {
// If we reach a class declaration, stop looking for imports
// TODO This could be a little more specific
@@ -18,7 +18,7 @@
package com.itsaky.androidide.lsp.java.compiler;
import com.itsaky.androidide.lsp.util.PathUtils;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.utils.DocumentUtils;
import java.io.ByteArrayInputStream;
@@ -117,7 +117,7 @@ public class SourceFileObject implements JavaFileObject {
byte[] bytes = contents.getBytes();
return new ByteArrayInputStream(bytes);
}
return ProjectManager.INSTANCE.getInputStream(path);
return FileManager.INSTANCE.getInputStream(path);
}
@Override
@@ -130,7 +130,7 @@ public class SourceFileObject implements JavaFileObject {
if (contents != null) {
return new StringReader(contents);
}
return ProjectManager.INSTANCE.getReader(path);
return FileManager.INSTANCE.getReader(path);
}
@Override
@@ -138,7 +138,7 @@ public class SourceFileObject implements JavaFileObject {
if (contents != null) {
return contents;
}
return ProjectManager.INSTANCE.getDocumentContents(this.path);
return FileManager.INSTANCE.getDocumentContents(this.path);
}
@Override
@@ -151,7 +151,7 @@ public class SourceFileObject implements JavaFileObject {
if (contents != null) {
return modified.toEpochMilli();
}
return ProjectManager.INSTANCE.getLastModified(this.path).toEpochMilli();
return FileManager.INSTANCE.getLastModified(this.path).toEpochMilli();
}
@Override
@@ -26,6 +26,7 @@ import com.itsaky.androidide.lsp.models.DiagnosticSeverity
import com.itsaky.androidide.lsp.models.DiagnosticSeverity.WARNING
import com.itsaky.androidide.lsp.util.PathUtils.Companion.isSameFile
import com.itsaky.androidide.models.Range
import com.itsaky.androidide.projects.FileManager
import com.itsaky.androidide.projects.ProjectManager
import com.sun.source.tree.BlockTree
import com.sun.source.tree.ClassTree
@@ -192,7 +193,7 @@ object DiagnosticsProvider {
}
}
val file = Paths.get(root.sourceFile.toUri())
val contents = ProjectManager.getDocumentContents(file)
val contents = FileManager.getDocumentContents(file)
var name = unusedEl.simpleName
if (name.contentEquals("<init>")) {
name = unusedEl.enclosingElement.simpleName
@@ -20,6 +20,7 @@ import com.itsaky.androidide.lsp.java.compiler.CompileTask
import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService
import com.itsaky.androidide.lsp.java.providers.DiagnosticsProvider.findDiagnostics
import com.itsaky.androidide.lsp.java.utils.CancelChecker
import com.itsaky.androidide.projects.FileManager
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.utils.ILogger
import java.nio.file.Path
@@ -51,7 +52,7 @@ class JavaDiagnosticProvider(private val completionChecker: Supplier<Boolean>) {
return com.itsaky.androidide.lsp.models.DiagnosticResult.NO_UPDATE
}
val modifiedAt = ProjectManager.getLastModified(file)
val modifiedAt = FileManager.getLastModified(file)
val analyzedAt = analyzeTimestamps[file]
if (analyzedAt?.isAfter(modifiedAt) == true) {
@@ -34,6 +34,7 @@ import com.itsaky.androidide.lsp.models.CompletionResult
import com.itsaky.androidide.lsp.models.InsertTextFormat.SNIPPET
import com.itsaky.androidide.lsp.models.MatchLevel.NO_MATCH
import com.itsaky.androidide.progress.ProgressManager.Companion.abortIfCancelled
import com.itsaky.androidide.projects.FileManager
import com.itsaky.androidide.projects.ProjectManager
import com.squareup.javapoet.MethodSpec.Builder
import com.sun.source.tree.ClassTree
@@ -159,7 +160,7 @@ class ScopeCompletionProvider(
// Print the method details and the annotations
// Print the method details and the annotations
val indent =
EditHelper.indent(ProjectManager.getDocumentContents(completingFile), cursor.toInt())
EditHelper.indent(FileManager.getDocumentContents(completingFile), cursor.toInt())
val builder: Builder
try {
builder = buildMethod(method, types, type)
@@ -26,6 +26,7 @@ import com.itsaky.androidide.lsp.models.HighlightToken;
import com.itsaky.androidide.lsp.models.HighlightTokenKind;
import com.itsaky.androidide.models.Position;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.utils.ILogger;
import com.sun.source.tree.ClassTree;
@@ -213,7 +214,7 @@ public class SemanticHighlighter extends TreePathScanner<Void, List<HighlightTok
}
// Find name inside expression
Path file = Paths.get(root.getSourceFile().toUri());
CharSequence contents = ProjectManager.INSTANCE.getDocumentContents(file);
CharSequence contents = FileManager.INSTANCE.getDocumentContents(file);
String region = substring(contents, start, end);
start += region.indexOf(name.toString());
end = start + name.length();
Binary file not shown.