From b0ec6af72115872909e0b74e125b126f60ec20f4 Mon Sep 17 00:00:00 2001 From: Wakeful Cloud Date: Fri, 3 Feb 2023 00:50:25 -0700 Subject: [PATCH] Improved indentation system --- .../jeditsyntax/DefaultInputHandler.java | 1 + .../editors/jeditsyntax/InputHandler.java | 90 ++++++++++++++++++- .../editors/jeditsyntax/JEditTextArea.java | 6 +- .../editors/jeditsyntax/TextUtilities.java | 32 +++++++ 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/rars/venus/editors/jeditsyntax/DefaultInputHandler.java b/src/rars/venus/editors/jeditsyntax/DefaultInputHandler.java index 5ac1bab1..18b24573 100644 --- a/src/rars/venus/editors/jeditsyntax/DefaultInputHandler.java +++ b/src/rars/venus/editors/jeditsyntax/DefaultInputHandler.java @@ -43,6 +43,7 @@ public void addDefaultKeyBindings() { addKeyBinding("ENTER", INSERT_BREAK); addKeyBinding("TAB", INSERT_TAB); + addKeyBinding("S+TAB", DELETE_TAB); addKeyBinding("INSERT", OVERWRITE); addKeyBinding("C+\\", TOGGLE_RECT); diff --git a/src/rars/venus/editors/jeditsyntax/InputHandler.java b/src/rars/venus/editors/jeditsyntax/InputHandler.java index e4a97386..dd27c509 100644 --- a/src/rars/venus/editors/jeditsyntax/InputHandler.java +++ b/src/rars/venus/editors/jeditsyntax/InputHandler.java @@ -54,6 +54,7 @@ public abstract class InputHandler extends KeyAdapter { public static final ActionListener SELECT_DOC_END = new document_end(true); public static final ActionListener INSERT_BREAK = new insert_break(); public static final ActionListener INSERT_TAB = new insert_tab(); + public static final ActionListener DELETE_TAB = new delete_tab(); public static final ActionListener HOME = new home(false); public static final ActionListener DOCUMENT_HOME = new document_home(false); public static final ActionListener SELECT_HOME = new home(true); @@ -100,6 +101,7 @@ public abstract class InputHandler extends KeyAdapter { actions.put("select-doc-end", SELECT_DOC_END); actions.put("insert-break", INSERT_BREAK); actions.put("insert-tab", INSERT_TAB); + actions.put("delete-tab", DELETE_TAB); actions.put("home", HOME); actions.put("select-home", SELECT_HOME); actions.put("document-home", DOCUMENT_HOME); @@ -672,7 +674,93 @@ public void actionPerformed(ActionEvent evt) { return; } - textArea.overwriteSetSelectedText("\t"); + int startOffset = textArea.getSelectionStart(); + int startLine = textArea.getSelectionStartLine(); + int startLineOffset = textArea.getLineStartOffset(startLine); + int endOffset = textArea.getSelectionEnd(); + int endLine = textArea.getSelectionEndLine(); + int endLineOffset = textArea.getLineEndOffset(endLine); + + if (startLineOffset != endLineOffset && startLine != endLine) { + String text = textArea.getText(); + String selected = text.substring(startLineOffset, endLineOffset - 1); + String prefixed = TextUtilities.addLinePrefixes(selected, "\t"); + + try { + textArea.document.replace(startLineOffset, endLineOffset - startLineOffset - 1, prefixed, null); + } catch (BadLocationException bl) { + bl.printStackTrace(); + } + textArea.select(startOffset + 1, endOffset + (prefixed.length() - selected.length())); + } else { + textArea.overwriteSetSelectedText("\t"); + } + } + } + + public static class delete_tab implements ActionListener { + public void actionPerformed(ActionEvent evt) { + JEditTextArea textArea = getTextArea(evt); + + if (!textArea.isEditable()) { + textArea.getToolkit().beep(); + return; + } + + int startOffset = textArea.getSelectionStart(); + int startLine = textArea.getSelectionStartLine(); + int startLineOffset = textArea.getLineStartOffset(startLine); + int endOffset = textArea.getSelectionEnd(); + int endLine = textArea.getSelectionEndLine(); + int endLineOffset = textArea.getLineEndOffset(endLine); + + if (startOffset != endOffset) { + String text = textArea.getText(); + String selected = text.substring(startLineOffset, endLineOffset - 1); + String stripped = TextUtilities.deleteLinePrefixes(selected, "\t"); + + if (selected.equals(stripped)) { + textArea.getToolkit().beep(); + return; + } + + try { + textArea.document.replace(startLineOffset, endLineOffset - startLineOffset - 1, stripped, null); + } catch (BadLocationException bl) { + bl.printStackTrace(); + } + + textArea.select(startOffset - 1 < 0 ? 0 : startOffset - 1, endOffset + (stripped.length() - selected.length())); + } else { + int caretOffset = textArea.getCaretPosition(); + int caretLine = textArea.getCaretLine(); + int caretLineOffset = textArea.getLineStartOffset(caretLine); + + if (caretOffset == 0) { + textArea.getToolkit().beep(); + return; + } + try { + String lineText = textArea.getLineText(caretLine); + int tabLineIndex = -1; + for (int i = 0; i < lineText.length(); i++) { + if (lineText.charAt(i) != '\t') { + break; + } + tabLineIndex = i; + } + + if (tabLineIndex == -1) { + textArea.getToolkit().beep(); + return; + } + + int tabIndex = caretLineOffset + tabLineIndex; + textArea.getDocument().remove(tabIndex, 1); + } catch (BadLocationException bl) { + bl.printStackTrace(); + } + } } } diff --git a/src/rars/venus/editors/jeditsyntax/JEditTextArea.java b/src/rars/venus/editors/jeditsyntax/JEditTextArea.java index 0bf967a1..7c942a08 100644 --- a/src/rars/venus/editors/jeditsyntax/JEditTextArea.java +++ b/src/rars/venus/editors/jeditsyntax/JEditTextArea.java @@ -141,7 +141,7 @@ public JEditTextArea(TextAreaDefaults defaults, JComponent lineNumbers) { caretTimer.setDelay(caretBlinkRate); // Intercept keystrokes before focus manager gets them. If in editing window, - // pass TAB keystrokes on to the key processor instead of letting focus + // pass (SHIFT) TAB keystrokes on to the key processor instead of letting focus // manager use them for focus traversal. // One can also accomplish this using: setFocusTraversalKeysEnabled(false); // but that seems heavy-handed. @@ -149,7 +149,9 @@ public JEditTextArea(TextAreaDefaults defaults, JComponent lineNumbers) { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher( new KeyEventDispatcher() { public boolean dispatchKeyEvent(KeyEvent e) { - if (JEditTextArea.this.isFocusOwner() && e.getKeyCode() == KeyEvent.VK_TAB && e.getModifiers() == 0) { + int modifiers = e.getModifiers(); + if (JEditTextArea.this.isFocusOwner() && e.getKeyCode() == KeyEvent.VK_TAB + && (modifiers == 0 || (modifiers & InputEvent.SHIFT_MASK) != 0)) { processKeyEvent(e); return true; } else { diff --git a/src/rars/venus/editors/jeditsyntax/TextUtilities.java b/src/rars/venus/editors/jeditsyntax/TextUtilities.java index a50d0613..5997fedc 100644 --- a/src/rars/venus/editors/jeditsyntax/TextUtilities.java +++ b/src/rars/venus/editors/jeditsyntax/TextUtilities.java @@ -189,4 +189,36 @@ public static int findWordEnd(String line, int pos, String noWordSep) { } return wordEnd; } + + /** + * Prefix all lines with the specified prefix. + * + * @param text The text + * @param prefix The prefix + */ + public static String addLinePrefixes(String text, String prefix) { + String[] lines = text.split("\n", -1); + + for (int i = 0; i < lines.length; i++) { + lines[i] = prefix + lines[i]; + } + + return String.join("\n", lines); + } + + /** + * Delete all lines of the specified prefix. + * + * @param text The text + * @param prefix The prefix + */ + public static String deleteLinePrefixes(String text, String prefix) { + String[] lines = text.split("\n", -1); + + for (int i = 0; i < lines.length; i++) { + lines[i] = lines[i].replaceFirst(prefix, ""); + } + + return String.join("\n", lines); + } }