Append newline in String literals in Java and Groovy

This commit is contained in:
Akash Yadav
2021-10-27 12:54:45 +05:30
parent 0855ab1918
commit b2db9992fb
17 changed files with 278 additions and 45 deletions
@@ -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(() -> {
@@ -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<String, String> 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
@@ -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();
}
@@ -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) {
@@ -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
@@ -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("}");
@@ -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("}");
}
@@ -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<IDEModule> 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();
}
/**
@@ -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 {
@@ -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<Integer, List<Range>> 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<Range> 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" + <new-line>
.append(TextUtils.createIndent(count + advance, tabSize, language.useTab()))
.append("\"");
return new HandleResult(sb, 0);
}
private boolean isInRanges (List<Range> 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;
}
}
}
@@ -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<Integer, Map<Integer, Diagnostic>> diagnostics = new HashMap<>();
private final Map<Integer, List<Range>> 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<Range> 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" + <new-line>
.append(TextUtils.createIndent(count + advance, tabSize, language.useTab()))
.append("\"");
return new HandleResult(sb, 0);
}
private boolean isInRanges (List<Range> 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<Range> 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 :
@@ -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
@@ -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) {
@@ -28,6 +28,14 @@
android:background="@color/primaryDarkColor"
android:id="@+id/editor_toolbar"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/build_progress_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:indicatorColor="@color/secondaryColor"/>
<com.itsaky.androidide.views.MaterialBanner
android:id="@+id/syncBanner"
android:layout_height="wrap_content"
@@ -5,6 +5,10 @@
<group
android:title="@string/group_project"
android:id="@+id/editornav_group_project">
<item
android:id="@+id/editornav_terminal"
android:icon="@drawable/ic_terminal"
android:title="@string/btn_terminal"/>
<item
android:id="@+id/editornav_close_project"
android:icon="@drawable/ic_close"
Binary file not shown.
Vendored
+1 -1
View File
@@ -72,7 +72,7 @@ case "`uname`" in
Darwin* )
darwin=true
;;
MINGW* )
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )