diff --git a/CHANGELOG.md b/CHANGELOG.md
index d037f025b..0b54d476b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,8 +15,7 @@ This is the changelog of all the releases of AndroidIDE.
- Code formatter for Java source code.
- Support for
- [`GestureOverlayView`](https://developer.android.com/reference/android/gesture/GestureOverlayView)
- ,
+ [`GestureOverlayView`](https://developer.android.com/reference/android/gesture/GestureOverlayView),
[`ToggleButton`](https://developer.android.com/reference/android/widget/ToggleButton),
[`Switch`](https://developer.android.com/reference/android/widget/Switch),
[`GridLayout`](https://developer.android.com/reference/android/widget/GridLayout) in layout
diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
index d1aab8308..94e98b694 100755
--- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
@@ -35,6 +35,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.LinearLayout;
+
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -50,6 +51,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.transition.Slide;
import androidx.transition.TransitionManager;
+
import com.blankj.utilcode.util.ClipboardUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.IntentUtils;
@@ -115,8 +117,9 @@ import com.itsaky.lsp.models.InitializeParams;
import com.itsaky.lsp.models.Range;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
-import io.github.rosemoe.sora.event.ContentChangeEvent;
-import io.github.rosemoe.sora.event.Unsubscribe;
+
+import org.jetbrains.annotations.Contract;
+
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -131,9 +134,11 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+
+import io.github.rosemoe.sora.event.ContentChangeEvent;
+import io.github.rosemoe.sora.event.Unsubscribe;
import me.piruin.quickaction.ActionItem;
import me.piruin.quickaction.QuickAction;
-import org.jetbrains.annotations.Contract;
public class EditorActivity extends StudioActivity
implements FileTreeFragment.FileActionListener,
@@ -708,7 +713,10 @@ public class EditorActivity extends StudioActivity
mBinding.editorContainer.setDisplayedChild(position);
final var editorView = getEditorAtIndex(position);
- mViewModel.setCurrentFile(position, Objects.requireNonNull(editorView).getFile());
+ Objects.requireNonNull(editorView);
+
+ editorView.onEditorSelected();
+ mViewModel.setCurrentFile(position, editorView.getFile());
refreshSymbolInput(editorView);
invalidateOptionsMenu();
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java b/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java
new file mode 100644
index 000000000..e6186d481
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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 .
+ */
+
+package com.itsaky.androidide.language;
+
+import androidx.annotation.NonNull;
+
+import com.itsaky.lsp.models.DiagnosticItem;
+
+import java.util.List;
+
+/**
+ * Base class for analyze managers.
+ *
+ * @author Akash Yadav
+ */
+public interface IAnalyzeManager {
+
+ /**
+ * Update the diagnostic items.
+ *
+ * @param diagnostics The new list of diagnostics.
+ */
+ default void updateDiagnostics(@NonNull List diagnostics) {}
+
+ /**
+ * Get the diagnostic items.
+ *
+ * @return The diagnostic items. Must not be null.
+ */
+ @NonNull
+ List getDiagnostics();
+}
diff --git a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
index 70c529a02..e68608b3e 100644
--- a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
@@ -17,13 +17,17 @@
package com.itsaky.androidide.language;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.lsp.api.ILanguageServer;
import com.itsaky.lsp.models.DiagnosticItem;
-import io.github.rosemoe.sora.lang.Language;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import io.github.rosemoe.sora.lang.Language;
+
/**
* Base class for language implementations in the IDE.
*
@@ -50,6 +54,18 @@ public abstract class IDELanguage implements Language {
return Collections.emptyList();
}
+ public void setDiagnostics(List diagnostics) {
+ if (diagnostics == null) {
+ diagnostics = new ArrayList<>(0);
+ }
+
+ final var analyzer = getAnalyzeManager();
+ if (analyzer instanceof IAnalyzeManager) {
+ ((IAnalyzeManager) analyzer).updateDiagnostics(diagnostics);
+ analyzer.rerun();
+ }
+ }
+
@Override
public CharSequence format(CharSequence text) {
final var server = getLanguageServer();
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
index ca081fd45..2c101c6f6 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
@@ -28,50 +28,51 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.withoutCompletion;
import androidx.annotation.NonNull;
+
+import com.itsaky.androidide.language.IAnalyzeManager;
import com.itsaky.androidide.lexers.java.JavaLexer;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.Logger;
-import com.itsaky.androidide.views.editor.IDEEditor;
-import com.itsaky.lsp.api.ILanguageServer;
import com.itsaky.lsp.models.DiagnosticItem;
-import com.itsaky.lsp.models.DiagnosticResult;
import com.itsaky.lsp.models.DiagnosticSeverity;
import com.itsaky.lsp.models.Position;
import com.itsaky.lsp.models.Range;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CodePointCharStream;
+import org.antlr.v4.runtime.Token;
+import org.jetbrains.annotations.Contract;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.styling.CodeBlock;
import io.github.rosemoe.sora.lang.styling.MappedSpans;
import io.github.rosemoe.sora.lang.styling.Span;
import io.github.rosemoe.sora.lang.styling.Styles;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-import java.util.concurrent.CompletableFuture;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.CodePointCharStream;
-import org.antlr.v4.runtime.Token;
-import org.jetbrains.annotations.Contract;
/**
* Code analyzer for the Java Language
*
* @author Akash Yadav
*/
-public class JavaAnalyzer extends SimpleAnalyzeManager {
+public class JavaAnalyzer extends SimpleAnalyzeManager implements IAnalyzeManager {
private static final Logger LOG = Logger.instance("JavaAnalyzer");
private final List ideDiagnostics = new ArrayList<>();
- private final ILanguageServer languageServer;
private List diagnostics = new ArrayList<>();
- public JavaAnalyzer(ILanguageServer languageServer) {
- this.languageServer = languageServer;
+ @Override
+ public void updateDiagnostics(@NonNull List diagnostics) {
+ this.diagnostics = diagnostics;
}
@NonNull
+ @Override
public List getDiagnostics() {
final var result = new ArrayList<>(ideDiagnostics);
if (diagnostics != null && !diagnostics.isEmpty()) {
@@ -83,23 +84,6 @@ public class JavaAnalyzer extends SimpleAnalyzeManager {
@Override
protected Styles analyze(StringBuilder text, Delegate delegate) {
- final var args = getExtraArguments();
- if (args.containsKey(IDEEditor.KEY_FILE)) {
- final var file = new File(args.getString(IDEEditor.KEY_FILE));
- if (languageServer != null && file.exists()) {
- CompletableFuture.runAsync(
- () -> {
- diagnostics = languageServer.analyze(file.toPath());
- if (languageServer.getClient() != null) {
- languageServer
- .getClient()
- .publishDiagnostics(
- new DiagnosticResult(file.toPath(), diagnostics));
- }
- });
- }
- }
-
final CodePointCharStream stream;
final var styles = new Styles();
final var colors = new MappedSpans.Builder();
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
index 0357f42ff..d659b0066 100755
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
@@ -54,7 +54,7 @@ public class JavaLanguage extends IDELanguage {
public JavaLanguage() {
final var server = getLanguageServer();
- this.analyzer = new JavaAnalyzer(server);
+ this.analyzer = new JavaAnalyzer();
this.completer = new CommonCompletionProvider(server);
this.newlineHandlers = new NewlineHandler[1];
diff --git a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java
index 900669f9e..2fdba17fa 100755
--- a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java
+++ b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java
@@ -20,6 +20,7 @@
package com.itsaky.androidide.lsp;
import android.view.View;
+
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -31,6 +32,7 @@ import com.itsaky.androidide.adapters.SearchListAdapter;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.interfaces.EditorActivityProvider;
+import com.itsaky.androidide.language.IDELanguage;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.tasks.TaskExecutor;
@@ -47,7 +49,7 @@ import com.itsaky.lsp.models.Location;
import com.itsaky.lsp.models.Range;
import com.itsaky.lsp.models.TextEdit;
import com.itsaky.toaster.Toaster;
-import io.github.rosemoe.sora.text.Content;
+
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@@ -55,6 +57,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
+import io.github.rosemoe.sora.text.Content;
+
/** AndroidIDE specific implementation of the LanguageClient */
public class IDELanguageClientImpl implements ILanguageClient {
@@ -142,6 +146,17 @@ public class IDELanguageClientImpl implements ILanguageClient {
File file = result.getFile().toFile();
if (!file.exists() || !file.isFile()) return;
+ final var editorView = activity().getEditorForFile(file);
+ if (editorView != null) {
+ final var editor = editorView.getEditor();
+ if (editor != null) {
+ final var editorLanguage = editor.getEditorLanguage();
+ if (editorLanguage instanceof IDELanguage) {
+ ((IDELanguage) editorLanguage).setDiagnostics(result.getDiagnostics());
+ }
+ }
+ }
+
diagnostics.put(file, result.getDiagnostics());
activity().setDiagnosticsAdapter(newDiagnosticsAdapter());
}
diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java
index 67b32422b..adfd501fb 100644
--- a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java
+++ b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java
@@ -38,8 +38,10 @@ import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.SizeUtils;
import com.itsaky.androidide.adapters.CompletionListAdapter;
@@ -57,9 +59,10 @@ import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.inflater.values.ValuesTableFactory;
import com.itsaky.lsp.models.Range;
-import io.github.rosemoe.sora.event.ContentChangeEvent;
-import io.github.rosemoe.sora.lang.EmptyLanguage;
-import io.github.rosemoe.sora.text.Content;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.Token;
+
import java.io.File;
import java.io.StringReader;
import java.nio.file.Files;
@@ -67,8 +70,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.Token;
+
+import io.github.rosemoe.sora.event.ContentChangeEvent;
+import io.github.rosemoe.sora.lang.EmptyLanguage;
+import io.github.rosemoe.sora.text.Content;
/**
* A view that handles opened code editors.
@@ -231,6 +236,13 @@ public class CodeEditorView extends FrameLayout {
configureEditorIfNeeded();
}
+ public void onEditorSelected() {
+ final var editor = getEditor();
+ if (editor != null) {
+ editor.onEditorSelected();
+ }
+ }
+
public void undo() {
if (binding.editor.canUndo()) {
binding.editor.undo();
diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java
index c7aa3c413..3d6da6384 100644
--- a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java
+++ b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java
@@ -35,6 +35,7 @@ import androidx.core.content.ContextCompat;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
+import com.itsaky.androidide.language.IAnalyzeManager;
import com.itsaky.androidide.language.IDELanguage;
import com.itsaky.androidide.lsp.IDELanguageClientImpl;
import com.itsaky.androidide.managers.PreferenceManager;
@@ -238,6 +239,21 @@ public class IDEEditor extends CodeEditor {
final var event = new DocumentOpenEvent(file.toPath(), text, mFileVersion = 0);
documentHandler.onFileOpened(event);
}
+
+ // request diagnostics
+ if (getFile() != null && getEditorLanguage() instanceof IDELanguage) {
+ CompletableFuture.supplyAsync(() -> mLanguageServer.analyze(getFile().toPath()))
+ .whenComplete(
+ (diagnostics, throwable) -> {
+ final var lang = getEditorLanguage();
+ final var analyzeManager = lang.getAnalyzeManager();
+ if (analyzeManager instanceof IAnalyzeManager) {
+ ((IAnalyzeManager) analyzeManager)
+ .updateDiagnostics(diagnostics);
+ analyzeManager.rerun();
+ }
+ });
+ }
}
if (file != null) {
@@ -604,6 +620,17 @@ public class IDEEditor extends CodeEditor {
ensureWindowsDismissed();
}
+ public void onEditorSelected() {
+ if (getFile() == null) {
+ return;
+ }
+
+ final var path = getFile().toPath();
+ if (mLanguageServer != null) {
+ mLanguageServer.getDocumentHandler().onFileSelected(path);
+ }
+ }
+
/**
* Tells the language client to perform the given code action item.
*
diff --git a/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java b/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java
index c2456ffad..79e4071bc 100644
--- a/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java
+++ b/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java
@@ -17,10 +17,13 @@
package com.itsaky.lsp.api;
+import androidx.annotation.NonNull;
+
import com.itsaky.lsp.models.DocumentChangeEvent;
import com.itsaky.lsp.models.DocumentCloseEvent;
import com.itsaky.lsp.models.DocumentOpenEvent;
import com.itsaky.lsp.models.DocumentSaveEvent;
+
import java.nio.file.Path;
/**
@@ -71,4 +74,12 @@ public interface IDocumentHandler {
* @param event The document close event data.
*/
void onFileClosed(DocumentCloseEvent event);
+
+ /**
+ * Notify the document handler that the user has selected the given file for editing. Usually,
+ * the given file is visible to user.
+ *
+ * @param path The file that was selected.
+ */
+ void onFileSelected(@NonNull Path path);
}
diff --git a/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java b/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java
index b083d1faf..ba1e854cc 100644
--- a/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java
+++ b/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java
@@ -17,11 +17,14 @@
package com.itsaky.lsp.util;
+import androidx.annotation.NonNull;
+
import com.itsaky.lsp.api.IDocumentHandler;
import com.itsaky.lsp.models.DocumentChangeEvent;
import com.itsaky.lsp.models.DocumentCloseEvent;
import com.itsaky.lsp.models.DocumentOpenEvent;
import com.itsaky.lsp.models.DocumentSaveEvent;
+
import java.nio.file.Path;
/**
@@ -45,4 +48,7 @@ public class NoDocumentHandler implements IDocumentHandler {
@Override
public void onFileClosed(DocumentCloseEvent event) {}
+
+ @Override
+ public void onFileSelected(@NonNull Path path) {}
}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java
index 9a6df117f..e74c94c0b 100644
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java
@@ -19,6 +19,7 @@ package com.itsaky.lsp.java;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.api.ICompletionProvider;
import com.itsaky.lsp.api.IDocumentHandler;
@@ -36,11 +37,13 @@ import com.itsaky.lsp.java.providers.JavaDiagnosticProvider;
import com.itsaky.lsp.java.providers.JavaSelectionProvider;
import com.itsaky.lsp.java.providers.ReferenceProvider;
import com.itsaky.lsp.java.providers.SignatureProvider;
+import com.itsaky.lsp.java.utils.AnalyzeTimer;
import com.itsaky.lsp.models.CodeActionParams;
import com.itsaky.lsp.models.CodeActionResult;
import com.itsaky.lsp.models.DefinitionParams;
import com.itsaky.lsp.models.DefinitionResult;
import com.itsaky.lsp.models.DiagnosticItem;
+import com.itsaky.lsp.models.DiagnosticResult;
import com.itsaky.lsp.models.DocumentChangeEvent;
import com.itsaky.lsp.models.DocumentCloseEvent;
import com.itsaky.lsp.models.DocumentOpenEvent;
@@ -54,13 +57,16 @@ import com.itsaky.lsp.models.ServerCapabilities;
import com.itsaky.lsp.models.SignatureHelp;
import com.itsaky.lsp.models.SignatureHelpParams;
import com.itsaky.lsp.util.NoCompletionsProvider;
+
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
private static final Logger LOG = Logger.instance("JavaLanguageServer");
+ private final AnalyzeTimer analyzeTimer;
private ILanguageClient client;
private IServerSettings settings;
private JavaCompilerService compiler;
@@ -68,11 +74,13 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
private boolean initialized;
private boolean createCompiler;
private ServerCapabilities capabilities;
+ private Path selectedFile;
public JavaLanguageServer() {
this.initialized = false;
this.createCompiler = true;
this.configuration = new JavaServerConfiguration();
+ this.analyzeTimer = new AnalyzeTimer(this::analyzeSelected);
applySettings(getSettings());
}
@@ -87,6 +95,19 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
return compiler;
}
+ private void analyzeSelected() {
+ if (this.selectedFile == null) {
+ return;
+ }
+
+ final List diagnostics = analyze(selectedFile);
+ if (client != null) {
+ client.publishDiagnostics(new DiagnosticResult(this.selectedFile, diagnostics));
+ }
+
+ this.analyzeTimer.restart();
+ }
+
public IServerSettings getSettings() {
if (settings == null) {
settings = JavaServerSettings.getInstance();
@@ -136,6 +157,7 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
}
FileStore.shutdown();
+ this.analyzeTimer.shutdown();
initialized = false;
}
@@ -265,7 +287,11 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
}
@Override
- public void onContentChange(DocumentChangeEvent event) {
+ public void onContentChange(@NonNull DocumentChangeEvent event) {
+ // If a file's content is changed, it is definitely visible to user.
+ onFileSelected(event.getChangedFile());
+
+ this.analyzeTimer.restart();
FileStore.change(event);
}
@@ -278,4 +304,13 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler {
public void onFileClosed(DocumentCloseEvent event) {
FileStore.close(event);
}
+
+ @Override
+ public void onFileSelected(@NonNull Path path) {
+ if (Objects.equals(this.selectedFile, path)) {
+ return;
+ }
+
+ this.selectedFile = path;
+ }
}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java
new file mode 100644
index 000000000..6a912165d
--- /dev/null
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java
@@ -0,0 +1,88 @@
+/*
+ * 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 .
+ */
+
+package com.itsaky.lsp.java.utils;
+
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A timer which helps the language server to run a lint check after a specified interval.
+ *
+ * @author Akash Yadav
+ */
+public class AnalyzeTimer {
+
+ public static final long DEFAULT_INTERVAL = 400;
+ @NonNull private final Handler timerHandler;
+ @NonNull private final Runnable timerCallback;
+ private long interval;
+
+ /**
+ * Creates a new AnalyzeTimer instance.
+ *
+ * @param timerCallback The callback that will be invoked after the timer has ended.
+ */
+ public AnalyzeTimer(@NonNull Runnable timerCallback) {
+ this.timerHandler = new Handler();
+ this.interval = DEFAULT_INTERVAL;
+ this.timerCallback = timerCallback;
+
+ Objects.requireNonNull(this.timerCallback, "Callback cannot be null");
+ }
+
+ /**
+ * Get the interval set to this timer.
+ *
+ * @return The interval.
+ */
+ public long getInterval() {
+ return interval;
+ }
+
+ /**
+ * Set the interval for the lint timer.
+ *
+ * @param interval The interval after which timerCallback will be called.
+ */
+ public void setInterval(long interval) {
+ this.interval = interval;
+
+ if (this.interval <= 0) {
+ throw new IllegalArgumentException("Invalid interval specified for timer.");
+ }
+ }
+
+ /** Starts the timer. */
+ public void start() {
+ restart();
+ }
+
+ /** Restarts the timer. */
+ public void restart() {
+ timerHandler.removeCallbacks(timerCallback);
+ timerHandler.postDelayed(timerCallback, interval);
+ }
+
+ /** Shutdown the timer. Cancels any running timers. */
+ public void shutdown() {
+ timerHandler.removeCallbacks(timerCallback);
+ }
+}