diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index d99926a12..833d139bd 100644 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -193,8 +193,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F mBinding.tabs.setupWithViewPager(mBinding.editorViewPager); mBinding.tabs.setOnTabSelectedListener(this); mBinding.fabView.setOnClickListener(v -> showViewOptions()); -// mBinding.diagnosticTextContainer.setVisibility(View.GONE); -// mBinding.diagnosticText.setClickable(false); createQuickActions(); @@ -705,6 +703,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F startActivity(IntentUtils.getShareTextIntent(getString(R.string.msg_share_app))); } else if(id == R.id.editornav_close_project) { confirmProjectClose(); + } else if (id == R.id.editornav_terminal) { + openTerminal(); } mBinding.getRoot().closeDrawer(GravityCompat.START); return false; @@ -1004,6 +1004,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F ////////////// PRIVATE APIS ///////////////////// ///////////////////////////////////////////////// + private void openTerminal () { + final Intent intent = new Intent(this, TerminalActivity.class); + intent.putExtra(TerminalActivity.KEY_WORKING_DIRECTORY, mProject.getProjectPath()); + startActivity(intent); + } + private void startLanguageServers() { LSP.setActivityProvider(this); LSP.Java.start(() -> { diff --git a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java index 7bd1563ad..b80730f6b 100644 --- a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java +++ b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java @@ -28,6 +28,7 @@ import com.itsaky.terminal.view.TerminalView; import com.itsaky.terminal.view.TerminalViewClient; import java.util.Map; import org.json.JSONException; +import android.os.Bundle; public class TerminalActivity extends StudioActivity { @@ -42,12 +43,14 @@ public class TerminalActivity extends StudioActivity { private final Client client = new Client(); private static final Logger LOG = Logger.instance("TerminalActivity"); + public static final String KEY_WORKING_DIRECTORY = "terminal_workingDirectory"; + @Override protected View bindLayout() { binding = ActivityTerminalBinding.inflate(getLayoutInflater()); terminal = new TerminalView(this, null); terminal.setTerminalViewClient(client); - terminal.attachSession(createSession()); + terminal.attachSession(createSession(getWorkingDirectory())); terminal.setKeepScreenOn(true); terminal.setTextSize(currentTextSize = SizeUtils.dp2px(10)); terminal.setTypeface(TypefaceUtils.jetbrainsMono()); @@ -65,6 +68,18 @@ public class TerminalActivity extends StudioActivity { return binding.getRoot(); } + + private String getWorkingDirectory() { + final Bundle extras = getIntent().getExtras(); + if(extras != null && extras.containsKey(KEY_WORKING_DIRECTORY)) { + String directory = extras.getString(KEY_WORKING_DIRECTORY, null); + if(directory == null || directory.trim().length() <= 0) { + directory = Environment.HOME.getAbsolutePath(); + } + return directory; + } + return Environment.HOME.getAbsolutePath(); + } private KeyListener getKeyListener() { return listener == null ? listener = new KeyListener(terminal) : listener; @@ -99,7 +114,7 @@ public class TerminalActivity extends StudioActivity { } } - private TerminalSession createSession() { + private TerminalSession createSession(final String workingDirectory) { final Map envs = Environment.getEnvironment(true); final String[] env = new String[envs.size()]; int i=0; @@ -110,7 +125,7 @@ public class TerminalActivity extends StudioActivity { session = new TerminalSession( Environment.SHELL.getAbsolutePath(), // Shell command - Environment.HOME.getAbsolutePath(), // Working directory + workingDirectory, // Working directory new String[]{}, // Arguments env, // Environment variables TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS, // Transcript rows diff --git a/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java index 5117d5e2a..389673701 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java @@ -62,17 +62,11 @@ public class CompletionListAdapter extends EditorCompletionAdapter { showApiInfoIfNeeded(item, binding.completionApiInfo); - final ViewTreeObserver.OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener(){ - - @Override - public void onGlobalLayout() { - binding.getRoot().getViewTreeObserver().removeOnGlobalLayoutListener(this); - ViewGroup.LayoutParams p = binding.completionIconText.getLayoutParams(); - p.height = binding.getRoot().getHeight(); - binding.completionIconText.setLayoutParams(p); - } - }; - binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(listener); + binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(() -> { + ViewGroup.LayoutParams p = binding.completionIconText.getLayoutParams(); + p.height = binding.getRoot().getHeight(); + binding.completionIconText.setLayoutParams(p); + }); return binding.getRoot(); } diff --git a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java index 6c47be48c..65f4b92c2 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java @@ -1,6 +1,6 @@ package com.itsaky.androidide.handlers; -import com.itsaky.androidide.EditorActivity; +import android.view.View; import com.itsaky.androidide.R; import com.itsaky.androidide.managers.PreferenceManager; import com.itsaky.androidide.models.AndroidProject; @@ -106,6 +106,7 @@ public class BuildServiceHandler extends IDEHandler implements IDEService.BuildL activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); activity().invalidateOptionsMenu(); + activity().getBinding().buildProgressIndicator.setVisibility(View.GONE); } @Override @@ -117,6 +118,7 @@ public class BuildServiceHandler extends IDEHandler implements IDEService.BuildL activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); activity().invalidateOptionsMenu(); + activity().getBinding().buildProgressIndicator.setVisibility(View.GONE); } @Override @@ -136,12 +138,13 @@ public class BuildServiceHandler extends IDEHandler implements IDEService.BuildL } @Override - public void prepare() { + public void prepareBuild() { boolean isFirstBuild = activity().getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true); activity().setStatus(activity().getString(isFirstBuild ? R.string.preparing_first : R.string.preparing)); if(isFirstBuild) { activity().showFirstBuildNotice(); } + activity().getBinding().buildProgressIndicator.setVisibility(View.VISIBLE); } private boolean isClasspathValid(String path) { diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java index 2c373aab0..4ded86d40 100644 --- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java +++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java @@ -3,6 +3,7 @@ package com.itsaky.androidide.language.groovy; import com.itsaky.androidide.syntax.lexer.impls.groovy.GroovyLexerImpl; import com.itsaky.lsp.SemanticHighlight; import com.itsaky.lsp.services.IDELanguageServer; +import io.github.rosemoe.editor.interfaces.EditorLanguage; import io.github.rosemoe.editor.text.Content; import io.github.rosemoe.editor.text.TextAnalyzeResult; import io.github.rosemoe.editor.text.TextAnalyzer; @@ -11,11 +12,17 @@ import java.util.Map; import org.eclipse.lsp4j.Diagnostic; public class GroovyAnalyzer extends io.github.rosemoe.editor.langs.AbstractCodeAnalyzer { - + + private final EditorLanguage language; + public GroovyAnalyzer (EditorLanguage language) { + this.language = language; + } + + @Override public void analyze(IDELanguageServer server, File file, CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) { try { - GroovyLexerImpl lexer = new GroovyLexerImpl((Content) content, colors); + GroovyLexerImpl lexer = new GroovyLexerImpl(this.language, (Content) content, colors); lexer.init(); // Adding spans, hex colors, block lines and all is handled by JavaLexerImpl diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java index 2fbf3a912..e36bf7edc 100644 --- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java @@ -8,6 +8,7 @@ import com.itsaky.lsp.services.IDELanguageServer; import io.github.rosemoe.editor.interfaces.AutoCompleteProvider; import io.github.rosemoe.editor.interfaces.CodeAnalyzer; import io.github.rosemoe.editor.interfaces.NewlineHandler; +import io.github.rosemoe.editor.text.CharPosition; import io.github.rosemoe.editor.text.TextUtils; import io.github.rosemoe.editor.widget.SymbolPairMatch; import java.io.File; @@ -26,7 +27,7 @@ public class GroovyLanguage extends BaseLanguage { public GroovyLanguage(File file) { super(file); - analyzer = new GroovyAnalyzer(); + analyzer = new GroovyAnalyzer(this); completor = new GroovyAutoComplete(); } @@ -107,7 +108,7 @@ public class GroovyLanguage extends BaseLanguage { class BraceHandler implements NewlineHandler { @Override - public boolean matchesRequirement(String beforeText, String afterText) { + public boolean matchesRequirement(String beforeText, String afterText, CharPosition cursor) { beforeText = beforeText.trim(); afterText = afterText.trim(); return beforeText.endsWith("{") && afterText.startsWith("}"); 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 df8534d1a..6d7ce67ac 100644 --- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java @@ -10,6 +10,7 @@ import com.itsaky.lsp.services.IDELanguageServer; import io.github.rosemoe.editor.interfaces.AutoCompleteProvider; import io.github.rosemoe.editor.interfaces.CodeAnalyzer; import io.github.rosemoe.editor.interfaces.NewlineHandler; +import io.github.rosemoe.editor.text.CharPosition; import io.github.rosemoe.editor.text.TextUtils; import io.github.rosemoe.editor.widget.SymbolPairMatch; import java.io.File; @@ -22,14 +23,20 @@ public class JavaLanguage extends BaseLanguage { private final JavaLexerImpl lexer; private final JavaAutoComplete complete; + private final NewlineHandler[] newlineHandlers; + public JavaLanguage() { this(null); } public JavaLanguage(File file) { super(file); - this.lexer = new JavaLexerImpl(); + this.lexer = new JavaLexerImpl(this); this.complete = new JavaAutoComplete(); + + this.newlineHandlers = new NewlineHandler[2]; + this.newlineHandlers[0] = new BraceHandler(); + this.newlineHandlers[1] = this.lexer.stringHandler; } @Override @@ -92,10 +99,8 @@ public class JavaLanguage extends BaseLanguage { @Override public CharSequence format(CharSequence content) { return content; - } - - private NewlineHandler[] newlineHandlers = new NewlineHandler[] { new BraceHandler() }; - + } + @Override public NewlineHandler[] getNewlineHandlers() { return newlineHandlers; @@ -104,7 +109,7 @@ public class JavaLanguage extends BaseLanguage { class BraceHandler implements NewlineHandler { @Override - public boolean matchesRequirement(String beforeText, String afterText) { + public boolean matchesRequirement(String beforeText, String afterText, CharPosition cursor) { return beforeText.endsWith("{") && afterText.startsWith("}"); } diff --git a/app/src/main/java/com/itsaky/androidide/services/IDEService.java b/app/src/main/java/com/itsaky/androidide/services/IDEService.java index 2d4c6e1f8..306e91d7a 100644 --- a/app/src/main/java/com/itsaky/androidide/services/IDEService.java +++ b/app/src/main/java/com/itsaky/androidide/services/IDEService.java @@ -32,6 +32,7 @@ import java.util.Optional; import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.eclipse.lsp4j.FileChangeType; import org.eclipse.lsp4j.FileEvent; +import com.blankj.utilcode.util.ThreadUtils; public class IDEService implements ShellServer.Callback { @@ -77,8 +78,8 @@ public class IDEService implements ShellServer.Callback { this.isRunning = true; } - public IDEService setListener(BuildListener listener) { - this.listener = listener; + public IDEService setListener(final BuildListener listener) { + this.listener = listener == null ? null : new MainThreadBuildListener(listener); return this; } @@ -285,7 +286,7 @@ public class IDEService implements ShellServer.Callback { shell.bgAppend(String.format("cd \"%s\"", new File(projectRoot, "app").getAbsolutePath())); shell.bgAppend(getArguments(task.getTasks())); isBuilding = true; - listener.prepare(); + listener.prepareBuild(); } } @@ -385,6 +386,78 @@ public class IDEService implements ShellServer.Callback { return isBuilding; } + private class MainThreadBuildListener implements BuildListener { + + private final BuildListener listener; + + public MainThreadBuildListener(BuildListener listener) { + this.listener = listener; + } + + @Override + public void onBuildModified() { + ThreadUtils.runOnUiThread(() -> { + listener.onBuildModified(); + }); + } + + @Override + public void onProjectLoaded(IDEProject project, Optional appModule) { + ThreadUtils.runOnUiThread(() -> { + listener.onProjectLoaded(project, appModule); + }); + } + + @Override + public void onStartingGradleDaemon(GradleTask task) { + ThreadUtils.runOnUiThread(() -> { + listener.onStartingGradleDaemon(task); + }); + } + + @Override + public void onRunTask(GradleTask task, String name) { + ThreadUtils.runOnUiThread(() -> { + listener.onRunTask(task, name); + }); + } + + @Override + public void onBuildSuccessful(GradleTask task, String msg) { + ThreadUtils.runOnUiThread(() -> { + listener.onBuildSuccessful(task, msg); + }); + } + + @Override + public void onBuildFailed(GradleTask task, String msg) { + ThreadUtils.runOnUiThread(() -> { + listener.onBuildFailed(task, msg); + }); + } + + @Override + public void saveFiles() { + ThreadUtils.runOnUiThread(() -> { + listener.saveFiles(); + }); + } + + @Override + public void appendOutput(GradleTask task, CharSequence text) { + ThreadUtils.runOnUiThread(() -> { + listener.appendOutput(task, text); + }); + } + + @Override + public void prepareBuild() { + ThreadUtils.runOnUiThread(() -> { + listener.prepareBuild(); + }); + } + } + /** * A client should implement this interface to get callbacks about a build */ @@ -449,7 +522,7 @@ public class IDEService implements ShellServer.Callback { /** * Called to notify the client to prepare for a build */ - void prepare(); + void prepareBuild(); } /** diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java index 112b87c41..a05b5b1e5 100644 --- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java +++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java @@ -17,6 +17,7 @@ import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.blankj.utilcode.util.ThreadUtils; public class ShellServer extends Thread { diff --git a/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/groovy/GroovyLexerImpl.java b/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/groovy/GroovyLexerImpl.java index bd5f6cd26..0b8edcba8 100644 --- a/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/groovy/GroovyLexerImpl.java +++ b/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/groovy/GroovyLexerImpl.java @@ -5,18 +5,32 @@ import com.itsaky.androidide.syntax.lexer.Lexer; import com.itsaky.androidide.syntax.lexer.impls.BaseJavaLexer; import com.itsaky.androidide.syntax.lexer.tokens.Token; import com.itsaky.androidide.syntax.lexer.tokens.TokenType; +import com.itsaky.androidide.utils.LSPUtils; +import io.github.rosemoe.editor.interfaces.NewlineHandler; import io.github.rosemoe.editor.struct.BlockLine; +import io.github.rosemoe.editor.text.CharPosition; import io.github.rosemoe.editor.text.Content; import io.github.rosemoe.editor.text.TextAnalyzeResult; +import io.github.rosemoe.editor.text.TextUtils; import io.github.rosemoe.editor.widget.EditorColorScheme; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.antlr.v4.runtime.CharStreams; +import org.eclipse.lsp4j.Range; +import io.github.rosemoe.editor.interfaces.EditorLanguage; public class GroovyLexerImpl extends BaseJavaLexer implements Lexer { - - public GroovyLexerImpl(Content content, TextAnalyzeResult colors) { + + private final EditorLanguage language; + private final Map> stringMap = new HashMap<>(); + public final MultilineStringHandler stringHandler = new MultilineStringHandler(); + + public GroovyLexerImpl(EditorLanguage language, Content content, TextAnalyzeResult colors) { + this.language = language; this.content = content; this.colors = colors; this.maxSwitch = 0; @@ -283,4 +297,46 @@ public class GroovyLexerImpl extends BaseJavaLexer implements Lexer { return type; } + + private class MultilineStringHandler implements NewlineHandler { + + @Override + public boolean matchesRequirement(String beforeText, String afterText, CharPosition cursor) { + if(language == null || stringMap == null || stringMap.isEmpty()) { + return false; + } + + final List ranges = stringMap.get(cursor.line); + if(ranges == null || ranges.isEmpty()) { + return false; + } + + return isInRanges(ranges, cursor.line, cursor.column); + } + + @Override + public NewlineHandler.HandleResult handleNewline(String beforeText, String afterText, int tabSize) { + int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize); + int advance = language.getIndentAdvance(beforeText) + 1; + final StringBuilder sb = new StringBuilder("\\n\" + \n") // Appends: \n" + + .append(TextUtils.createIndent(count + advance, tabSize, language.useTab())) + .append("\""); + return new HandleResult(sb, 0); + } + + private boolean isInRanges (List ranges, int line, int column) { + + if(ranges == null || ranges.isEmpty()) { + return false; + } + + for (Range range : ranges) { + if (LSPUtils.isInRange(range, line, column)) { + return true; + } + } + + return false; + } + } } diff --git a/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/java/JavaLexerImpl.java b/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/java/JavaLexerImpl.java index 86776e3ff..57aebc379 100644 --- a/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/java/JavaLexerImpl.java +++ b/app/src/main/java/com/itsaky/androidide/syntax/lexer/impls/java/JavaLexerImpl.java @@ -10,9 +10,8 @@ import com.itsaky.androidide.syntax.lexer.tokens.TokenType; import com.itsaky.androidide.utils.LSPUtils; import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.SemanticHighlight; -import com.itsaky.lsp.SemanticHighlightsParams; import com.itsaky.lsp.services.IDELanguageServer; -import com.itsaky.lsp.services.IDETextDocumentService; +import io.github.rosemoe.editor.interfaces.NewlineHandler; import io.github.rosemoe.editor.struct.BlockLine; import io.github.rosemoe.editor.struct.Span; import io.github.rosemoe.editor.text.CharPosition; @@ -20,28 +19,34 @@ import io.github.rosemoe.editor.text.Content; import io.github.rosemoe.editor.text.Indexer; import io.github.rosemoe.editor.text.TextAnalyzeResult; import io.github.rosemoe.editor.text.TextAnalyzer; +import io.github.rosemoe.editor.text.TextUtils; import io.github.rosemoe.editor.widget.EditorColorScheme; import java.io.File; -import java.io.IOException; -import java.io.StringReader; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.antlr.v4.runtime.CharStreams; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.TextDocumentIdentifier; +import io.github.rosemoe.editor.interfaces.EditorLanguage; public class JavaLexerImpl extends io.github.rosemoe.editor.langs.AbstractCodeAnalyzer { + private final EditorLanguage language; private HighlightRangeHelper helper; private Map> diagnostics = new HashMap<>(); + private final Map> stringMap = new HashMap<>(); + + public final MultilineStringHandler stringHandler = new MultilineStringHandler(); + + public JavaLexerImpl(EditorLanguage language) { + this.language = language; + } @Override public void setSemanticHighlights(SemanticHighlight highlights) { @@ -59,6 +64,9 @@ public class JavaLexerImpl extends io.github.rosemoe.editor.langs.AbstractCodeAn } catch (Throwable th) { return; } + + stringMap.clear(); + while (delegate.shouldAnalyze()) { // null = EOF if(lexer.nextToken() == null) @@ -120,6 +128,48 @@ public class JavaLexerImpl extends io.github.rosemoe.editor.langs.AbstractCodeAn return diagnostics.get(line); } + private class MultilineStringHandler implements NewlineHandler { + + @Override + public boolean matchesRequirement(String beforeText, String afterText, CharPosition cursor) { + if(language == null || stringMap == null || stringMap.isEmpty()) { + return false; + } + + final List ranges = stringMap.get(cursor.line); + if(ranges == null || ranges.isEmpty()) { + return false; + } + + return isInRanges(ranges, cursor.line, cursor.column); + } + + @Override + public NewlineHandler.HandleResult handleNewline(String beforeText, String afterText, int tabSize) { + int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize); + int advance = language.getIndentAdvance(beforeText) + 1; + final StringBuilder sb = new StringBuilder("\\n\" + \n") // Appends: \n" + + .append(TextUtils.createIndent(count + advance, tabSize, language.useTab())) + .append("\""); + return new HandleResult(sb, 0); + } + + private boolean isInRanges (List ranges, int line, int column) { + + if(ranges == null || ranges.isEmpty()) { + return false; + } + + for (Range range : ranges) { + if (LSPUtils.isInRange(range, line, column)) { + return true; + } + } + + return false; + } + } + public class JavaLexerAnalyzer extends BaseJavaLexer implements Lexer { private final HighlightRangeHelper helper; @@ -250,7 +300,16 @@ public class JavaLexerImpl extends io.github.rosemoe.editor.langs.AbstractCodeAn Position start = new Position(line, column); Position end = new Position(line, column + currentToken.getText().length()); colors.addStringRange(line, new Range(start, end)); - + + { + List ranges = stringMap.get(line); + if(ranges == null) { + ranges = new ArrayList<>(); + } + ranges.add(new Range(start, end)); + stringMap.put(line, ranges); + } + addHexColorIfPresent(); break; case JavaLexer.LPAREN : diff --git a/app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java index d3023cfa9..f950b9556 100644 --- a/app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java +++ b/app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java @@ -14,6 +14,7 @@ * limitations under the License. */ package io.github.rosemoe.editor.interfaces; +import io.github.rosemoe.editor.text.CharPosition; /** * Perform text processing when user enters '\n' and selection size is 0 @@ -27,7 +28,7 @@ public interface NewlineHandler { * @param afterText Text of line after cursor * @return Whether this handler should be called */ - boolean matchesRequirement(String beforeText, String afterText); + boolean matchesRequirement(String beforeText, String afterText, CharPosition cursor); /** * Handle newline and return processed content to insert diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java index 4a45640e1..eb16c3e45 100644 --- a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java +++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java @@ -4334,7 +4334,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca boolean consumed = false; for (NewlineHandler handler : handlers) { if (handler != null) { - if (handler.matchesRequirement(beforeText, afterText)) { + if (handler.matchesRequirement(beforeText, afterText, getCursor().left())) { try { NewlineHandler.HandleResult result = handler.handleNewline(beforeText, afterText, getTabWidth()); if (result != null) { diff --git a/app/src/main/res/layout/activity_editor.xml b/app/src/main/res/layout/activity_editor.xml index f6ee1ea13..cdaaa86e4 100644 --- a/app/src/main/res/layout/activity_editor.xml +++ b/app/src/main/res/layout/activity_editor.xml @@ -28,6 +28,14 @@ android:background="@color/primaryDarkColor" android:id="@+id/editor_toolbar"/> + + +