mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Extracted document related APIs to FileManager
This commit is contained in:
Generated
+1
-1
@@ -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) {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-215
@@ -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")
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-97
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-9
@@ -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);
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
+2
-1
@@ -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.
Reference in New Issue
Block a user