From 3ae8a656ed6bbf5977e4a0acd24bd56f80a22e63 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sun, 20 Mar 2022 12:42:18 +0530 Subject: [PATCH] Validate range before selecting in editor --- .../main/assets/data/common/gradle-plugin.zip | Bin 37269 -> 37270 bytes .../com/itsaky/androidide/EditorActivity.java | 17 ++--- .../adapters/DiagnosticsAdapter.java | 9 +-- .../androidide/lsp/IDELanguageClientImpl.java | 16 ++--- .../views/editor/CodeEditorView.java | 26 ++++--- .../androidide/views/editor/IDEEditor.java | 67 ++++++++++++++++-- 6 files changed, 94 insertions(+), 41 deletions(-) diff --git a/app/src/main/assets/data/common/gradle-plugin.zip b/app/src/main/assets/data/common/gradle-plugin.zip index 6a3cc3af72361de798967960da77567aa2eeaa46..54985b81f8178275eeda8ef6aeb2ca3b0a36a793 100644 GIT binary patch delta 3480 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 y2j{ZP;XuJ$GoIxEPhkFV%=Uy&yu=8N*G diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index fdaba73e6..d754c54d5 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, @@ -696,11 +701,7 @@ public class EditorActivity extends StudioActivity editor.setSelection( range.getStart().getLine(), range.getEnd().getColumn()); } else { - editor.setSelectionRegion( - range.getStart().getLine(), - range.getStart().getColumn(), - range.getEnd().getLine(), - range.getEnd().getColumn()); + editor.setSelection(range); } }); } diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java index 683194653..81a605593 100755 --- a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java @@ -1,8 +1,6 @@ -/************************************************************************************ +/* * 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 @@ -16,20 +14,23 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . * - **************************************************************************************/ + */ package com.itsaky.androidide.adapters; import android.graphics.PorterDuff; import android.view.LayoutInflater; import android.view.ViewGroup; + import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.itsaky.androidide.R; import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding; import com.itsaky.androidide.interfaces.DiagnosticClickListener; import com.itsaky.androidide.models.DiagnosticGroup; + import java.util.List; public class DiagnosticsAdapter extends RecyclerView.Adapter { 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 c2ce268ac..9f55e9191 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; @@ -47,7 +48,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,15 +56,15 @@ 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 { protected static final Gson gson = new Gson(); protected static final Logger LOG = Logger.instance("AbstractLanguageClient"); - - private final Map> diagnostics = new HashMap<>(); private static IDELanguageClientImpl mInstance; - + private final Map> diagnostics = new HashMap<>(); protected EditorActivityProvider activityProvider; private IDELanguageClientImpl(EditorActivityProvider provider) { @@ -366,12 +367,7 @@ public class IDELanguageClientImpl implements ILanguageClient { .setSelection( range.getStart().getLine(), range.getStart().getColumn()); } else { - frag.getEditor() - .setSelectionRegion( - range.getStart().getLine(), - range.getStart().getColumn(), - range.getEnd().getLine(), - range.getEnd().getColumn()); + frag.getEditor().setSelection(range); } } else { activity().openFileAndSelect(file, range); 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 ecfe5aec4..7c5a90ca2 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. @@ -78,14 +83,12 @@ import org.antlr.v4.runtime.Token; @SuppressLint("ViewConstructor") // This view is always dynamically created. public class CodeEditorView extends FrameLayout { + private static final Logger LOG = Logger.instance("CodeEditorView"); private final File file; private final LayoutCodeEditorBinding binding; - private boolean isModified; private boolean isFirstCreate; - private static final Logger LOG = Logger.instance("CodeEditorView"); - public CodeEditorView( @NonNull Context context, @NonNull File file, final @NonNull Range selection) { super(context); @@ -122,12 +125,7 @@ public class CodeEditorView extends FrameLayout { selection.getStart().getLine(), selection.getStart().getColumn()); } else { - getEditor() - .setSelectionRegion( - selection.getStart().getLine(), - selection.getStart().getColumn(), - selection.getEnd().getLine(), - selection.getEnd().getColumn()); + getEditor().setSelection(selection); } }); }); 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 b841d49a3..6615f022a 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 @@ -126,17 +126,74 @@ public class IDEEditor extends CodeEditor { } } + /** + * Checks if the given range is valid for this editor's text. + * + * @param range The range to check. + * @return true if valid, false otherwise. + */ + public boolean isValidRange(final Range range) { + if (range == null) { + return false; + } + + final var start = range.getStart(); + final var end = range.getEnd(); + + return isValidPosition(start) + && isValidPosition(end) + && start.compareTo(end) < 0; // make sure start position is before end position + } + + /** + * Checks if the given position is valid for this editor's text. + * + * @param position The position to check. + * @return true if valid, false otherwise. + */ + public boolean isValidPosition(final Position position) { + if (position == null) { + return false; + } + + return isValidLine(position.getLine()) + && isValidColumn(position.getLine(), position.getColumn()); + } + + /** + * Checks if the given line is valid for this editor's text. + * + * @param line The line to check. + * @return true if valid, false otherwise. + */ + public boolean isValidLine(int line) { + return line >= 0 && line < getText().getLineCount(); + } + + /** + * Checks if the given column is valid for this editor's text. + * + * @param line The line of the column to check. + * @param column The column to check. + * @return true if valid, false otherwise. + */ + public boolean isValidColumn(int line, int column) { + return column >= 0 && column < getText().getColumnCount(line); + } + /** * Set selection to the given range. * * @param range The range to select. */ public void setSelection(@NonNull Range range) { - setSelectionRegion( - range.getStart().getLine(), - range.getStart().getColumn(), - range.getEnd().getLine(), - range.getEnd().getColumn()); + if (isValidRange(range)) { + setSelectionRegion( + range.getStart().getLine(), + range.getStart().getColumn(), + range.getEnd().getLine(), + range.getEnd().getColumn()); + } } /**