fix(projects): introduce workspace API and remove 'Project' model

This commit is contained in:
Akash Yadav
2024-07-01 15:58:06 +05:30
parent dba601b71e
commit 1165f12ccb
51 changed files with 674 additions and 538 deletions
@@ -60,8 +60,8 @@ import com.itsaky.androidide.lsp.util.LSPEditorActions
import com.itsaky.androidide.models.Range
import com.itsaky.androidide.projects.FileManager.getActiveDocumentCount
import com.itsaky.androidide.projects.IProjectManager.Companion.getInstance
import com.itsaky.androidide.projects.IWorkspace
import com.itsaky.androidide.projects.ModuleProject
import com.itsaky.androidide.projects.Project
import com.itsaky.androidide.utils.DocumentUtils
import com.itsaky.androidide.utils.VMUtils
import kotlinx.coroutines.CoroutineScope
@@ -131,10 +131,10 @@ class JavaLanguageServer : ILanguageServer {
this._settings = settings
}
override fun setupWithProject(project: Project) {
override fun setupWorkspace(workspace: IWorkspace) {
LSPEditorActions.ensureActionsMenuRegistered(JavaCodeActionsMenu)
// Once we have project initialized
// Once we have workspace initialized
// Destory the NO_MODULE_COMPILER instance
JavaCompilerService.NO_MODULE_COMPILER.destroy()
@@ -144,15 +144,15 @@ class JavaLanguageServer : ILanguageServer {
// Clear cached JAR file system for R.jar
// Using the cached instance will result in completions not being updated for updated resources
// TODO Clearing caches for JAR files ending with '/R.jar' is probably not a good idea
// Maybe this could be improved by using data from the AndroidModule project model
// Maybe this could be improved by using data from the AndroidModule workspace model
clearCachesForPaths { path: String -> path.endsWith("/R.jar") }
// Clear cached module-specific compilers
JavaCompilerProvider.getInstance().destroy()
// Cache classpath locations
for (subModule in project.subProjects) {
if (subModule !is ModuleProject || subModule.path == project.rootProject.path) {
for (subModule in workspace.getSubProjects()) {
if (subModule !is ModuleProject || subModule.path == workspace.getRootProject().path) {
continue
}
SourceFileManager.forModule(subModule)
@@ -173,7 +173,8 @@ class JavaLanguageServer : ILanguageServer {
}
completionProvider.reset(
compiler, settings, cachedCompletion) { cachedCompletion: CachedCompletion ->
compiler, settings, cachedCompletion
) { cachedCompletion: CachedCompletion ->
updateCachedCompletion(cachedCompletion)
}
@@ -239,9 +240,9 @@ class JavaLanguageServer : ILanguageServer {
if (!DocumentUtils.isJavaFile(file)) {
return JavaCompilerService.NO_MODULE_COMPILER
}
val root = getInstance().rootProject
val workspace = getInstance().getWorkspace()
?: return JavaCompilerService.NO_MODULE_COMPILER
val module = root.findModuleForFile(file!!) ?: return JavaCompilerService.NO_MODULE_COMPILER
val module = workspace.findModuleForFile(file!!) ?: return JavaCompilerService.NO_MODULE_COMPILER
return JavaCompilerProvider.get(module)
}
@@ -271,7 +272,7 @@ class JavaLanguageServer : ILanguageServer {
// TODO Find an alternative to efficiently update changeDelta in JavaCompilerService instance
JavaCompilerService.NO_MODULE_COMPILER.onDocumentChange(event)
val module = getInstance()
.findModuleForFile(event.changedFile)
.getWorkspace()?.findModuleForFile(event.changedFile, true)
if (module != null) {
val compiler = JavaCompilerProvider.get(module)
compiler.onDocumentChange(event)
@@ -105,7 +105,8 @@ abstract class BaseJavaCodeAction : EditorActionItem {
}
protected fun ActionData.requireCompiler(): JavaCompilerService {
val module = IProjectManager.getInstance().findModuleForFile(requireFile(), false)
val module =
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(requireFile(), false)
requireNotNull(module) {
"Cannot get compiler instance. Unable to find module for file: ${requireFile().name}"
}
@@ -60,7 +60,7 @@ abstract class FieldBasedAction : BaseJavaCodeAction() {
com.itsaky.androidide.models.Range::class.java,
CodeEditor::class.java
) ||
IProjectManager.getInstance().rootProject == null
IProjectManager.getInstance().getWorkspace() == null
) {
markInvisible()
return
@@ -73,7 +73,8 @@ abstract class FieldBasedAction : BaseJavaCodeAction() {
override suspend fun execAction(data: ActionData): Any {
val range = data[com.itsaky.androidide.models.Range::class.java]!!
val file = data.requirePath()
val module = IProjectManager.getInstance().findModuleForFile(file, false) ?: return Any()
val module =
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false) ?: return Any()
return JavaCompilerProvider.get(module).compile(file).get { task ->
val triple = findFields(task, file, range)
@@ -67,7 +67,7 @@ class AddImportAction : BaseJavaCodeAction() {
val file = data.requireFile()
val module =
IProjectManager.getInstance().findModuleForFile(file, false)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false)
?: run {
markInvisible()
return
@@ -99,7 +99,7 @@ class AddImportAction : BaseJavaCodeAction() {
)!!
val file = data.requireFile()
val module =
IProjectManager.getInstance().findModuleForFile(file, false)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false)
?: run {
markInvisible()
return Any()
@@ -140,7 +140,7 @@ class AddImportAction : BaseJavaCodeAction() {
val file = data.requireFile()
val module =
IProjectManager.getInstance().findModuleForFile(file, false)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false)
?: run {
markInvisible()
return
@@ -64,7 +64,9 @@ class AddThrowsAction : BaseJavaCodeAction() {
val diagnostic = data[DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get { task ->
val needsThrow = CodeActionUtils.findMethod(task, diagnostic.range)
@@ -66,7 +66,9 @@ class CreateMissingMethodAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get {
CreateMissingMethod(file, findPosition(it, diagnostic.range.start))
@@ -67,7 +67,9 @@ class FieldToBlockAction : BaseJavaCodeAction() {
override suspend fun execAction(data: ActionData): Any {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val diagnostic = data[DiagnosticItem::class.java]!!
val file = data.requirePath()
@@ -65,7 +65,9 @@ class RemoveClassAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get {
@@ -65,7 +65,9 @@ class RemoveMethodAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get {
@@ -66,7 +66,9 @@ class RemoveUnusedThrowsAction : BaseJavaCodeAction() {
val d = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get { task ->
val notThrown = CodeActionUtils.extractNotThrownExceptionName(d.message)
@@ -65,7 +65,9 @@ class SuppressUncheckedWarningAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get { task ->
val warnedMethod = CodeActionUtils.findMethod(task, diagnostic.range)
@@ -70,7 +70,9 @@ class VariableToStatementAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val path = data.requirePath()
return compiler.compile(path).get {
@@ -82,7 +82,9 @@ class GenerateConstructorAction : FieldBasedAction() {
private fun generateConstructor(data: ActionData, selected: MutableSet<String>) {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return
)
val range = data[com.itsaky.androidide.models.Range::class.java]!!
val file = data.requirePath()
@@ -65,7 +65,9 @@ class GenerateMissingConstructorAction : BaseJavaCodeAction() {
val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get { task ->
val needsConstructor =
@@ -89,7 +89,9 @@ class GenerateSettersAndGettersAction : FieldBasedAction() {
private fun generateForFields(data: ActionData, names: MutableSet<String>) {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return
)
val range = data[com.itsaky.androidide.models.Range::class.java]!!
val file = data.requirePath()
@@ -84,7 +84,9 @@ class GenerateToStringMethodAction : FieldBasedAction() {
private fun generateToString(data: ActionData, selected: MutableSet<String>) {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return
)
val range = data[com.itsaky.androidide.models.Range::class.java]!!
val file = data.requirePath()
@@ -95,7 +95,9 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() {
val range = data[com.itsaky.androidide.models.Range::class.java]!!
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any())
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return Any()
)
val file = data.requirePath()
return compiler.compile(file).get { task ->
@@ -203,7 +205,9 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() {
private fun overrideMethods(data: ActionData, checkedMethods: MutableList<MethodPtr>) {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return
)
val file = data.requirePath()
compiler.compile(file).run { task ->
@@ -270,7 +274,9 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() {
) {
val compiler =
JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(data.requireFile(), false)
?: return
)
val editor = data[CodeEditor::class.java]!!
val file = data.requirePath()
val text = editor.text
@@ -41,7 +41,8 @@ abstract class AdvancedJavaEditHandler(protected val file: Path) : BaseJavaEditH
index: Int
) {
val compiler = JavaCompilerProvider.get(
IProjectManager.getInstance().findModuleForFile(file, false) ?: return)
IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false) ?: return
)
performEdits(compiler, editor, item)
executeCommand(editor, item.command)
@@ -19,7 +19,7 @@ package com.itsaky.androidide.lsp.java.indexing.internal
import com.google.auto.service.AutoService
import com.itsaky.androidide.indexing.IIndexService
import com.itsaky.androidide.projects.Project
import com.itsaky.androidide.projects.IWorkspace
import java.io.File
/**
@@ -31,12 +31,12 @@ internal class JavaIndexServiceImpl : IIndexService {
override val displayName: String
get() = "Java LSP Indexing Service"
override fun scanFiles(project: Project): Collection<File> {
override fun scanFiles(workspace: IWorkspace): Collection<File> {
return emptySet()
}
override suspend fun indexFiles(
project: Project,
workspace: IWorkspace,
files: Collection<File>
) {
}
@@ -23,12 +23,17 @@ import com.itsaky.androidide.models.Position;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.projects.IProjectManager;
import com.itsaky.androidide.projects.ModuleProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdkx.tools.JavaCompiler;
import jdkx.tools.JavaFileObject;
import openjdk.source.tree.ClassTree;
@@ -42,8 +47,6 @@ import openjdk.source.util.SourcePositions;
import openjdk.source.util.TreePath;
import openjdk.source.util.Trees;
import openjdk.tools.javac.api.JavacTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Parser {
@@ -78,20 +81,23 @@ public class Parser {
* Create a task that compiles a single file
*/
private static JavacTask singleFileTask(JavaFileObject file) {
final ModuleProject module = IProjectManager.getInstance()
.findModuleForFile(Paths.get(file.toUri()));
final var workspace = IProjectManager.getInstance().getWorkspace();
final var module = workspace != null
? workspace.findModuleForFile(Paths.get(file.toUri()), false)
: null;
if (module != null) {
FILE_MANAGER = SourceFileManager.forModule(module);
}
return (JavacTask)
COMPILER.getTask(
null,
FILE_MANAGER,
Parser::ignoreError,
Collections.emptyList(),
Collections.emptyList(),
Collections.singletonList(file));
COMPILER.getTask(
null,
FILE_MANAGER,
Parser::ignoreError,
Collections.emptyList(),
Collections.emptyList(),
Collections.singletonList(file));
}
private static void ignoreError(jdkx.tools.Diagnostic<? extends JavaFileObject> __) {
@@ -215,7 +221,7 @@ public class Parser {
int endCol = (int) lines.getColumnNumber(end);
return new Range(
new Position(startLine - 1, startCol - 1), new Position(endLine - 1, endCol - 1));
new Position(startLine - 1, startCol - 1), new Position(endLine - 1, endCol - 1));
}
private static int indexOf(CharSequence contents, String name, int start) {
@@ -49,7 +49,7 @@ class JavaDiagnosticProvider {
fun analyze(file: Path): DiagnosticResult {
val module = IProjectManager.getInstance().findModuleForFile(file, false)
val module = IProjectManager.getInstance().getWorkspace()?.findModuleForFile(file, false)
?: return DiagnosticResult.NO_UPDATE
val compiler = JavaCompilerService(module)
@@ -39,12 +39,12 @@ class JavaCompilerProviderTest {
@Test
fun `test module specific compilers`() {
val rootProject = IProjectManager.getInstance().rootProject!!
val appModule = rootProject.findByPath(":app")!! as ModuleProject
val androidLib = rootProject.findByPath(":android-library")!! as ModuleProject
val anotherAndroidLib = rootProject.findByPath(":another-android-library")!! as ModuleProject
val javaLib = rootProject.findByPath(":java-library")!! as ModuleProject
val anotherJavaLib = rootProject.findByPath(":another-java-library")!! as ModuleProject
val workspace = IProjectManager.getInstance().getWorkspace()!!
val appModule = workspace.getProject(":app") as ModuleProject
val androidLib = workspace.getProject(":android-library") as ModuleProject
val anotherAndroidLib = workspace.getProject(":another-android-library") as ModuleProject
val javaLib = workspace.getProject(":java-library") as ModuleProject
val anotherJavaLib = workspace.getProject(":another-java-library") as ModuleProject
val compilers = mutableSetOf<JavaCompilerService>()
for (module in listOf(appModule, androidLib, anotherAndroidLib, javaLib, anotherJavaLib)) {