package bluej.editor.moe;

import bluej.Config;
import bluej.debugger.gentype.JavaType;
import bluej.editor.moe.MoeIndent.AutoIndentInformation;
import bluej.editor.moe.MoeSyntaxDocument.Element;
import bluej.parser.entity.JavaEntity;
import bluej.parser.nodes.CommentNode;
import bluej.parser.nodes.MethodNode;
import bluej.parser.nodes.NodeTree.NodeAndPosition;
import bluej.parser.nodes.ParsedNode;
import bluej.prefmgr.PrefMgr;
import bluej.utility.Debug;
import bluej.utility.Utility;
import bluej.utility.javafx.FXAbstractAction;
import bluej.utility.javafx.FXPlatformConsumer;
import bluej.utility.javafx.FXPlatformFunction;
import bluej.utility.javafx.FXPlatformRunnable;
import bluej.utility.javafx.JavaFXUtil;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.scene.input.DataFormat;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyCombination.Modifier;
import javafx.scene.input.KeyCombination.ModifierValue;
import org.fxmisc.richtext.NavigationActions.SelectionPolicy;
import org.fxmisc.richtext.model.TwoDimensional.Bias;
import org.fxmisc.wellbehaved.event.EventPattern;
import org.fxmisc.wellbehaved.event.InputMap;
import org.fxmisc.wellbehaved.event.Nodes;
import threadchecker.OnThread;
import threadchecker.Tag;

import javax.swing.KeyStroke;
import javax.swing.text.DefaultEditorKit;
import java.awt.Event;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;


| A set of actions supported by the Moe editor. This has a single instance | per editor. | | Actions are stored both in a hash-map and in an array. The hash-map is used | for fast lookup by name, whereas the array is needed to support complete, | ordered access. | | @author Michael Kolling | @author Bruce Quig | public final class MoeActions { private static final String KEYS_FILE = "editor.keys"; private static final String KEYS_FILE_FX = "editor_fx.keys"; private static final int tabSize = Config.getPropInteger("bluej.editor.tabsize", 4); private static final String spaces = " "; private static final char TAB_CHAR = '\t'; private static Modifier SHORTCUT_MASK = KeyCombination.SHORTCUT_DOWN; private static Modifier[] SHIFT_SHORTCUT_MASK = { KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN }; private static final IdentityHashMap<MoeEditor, MoeActions> moeActions = new IdentityHashMap<>(); private final MoeEditor editor; public MoeAbstractAction compileOrNextErrorAction; public MoeAbstractAction contentAssistAction; private HashMap<String, MoeAbstractAction> actions; private final ObservableMap<KeyCodeCombination, MoeAbstractAction> builtInKeymap = FXCollections.observableHashMap(); private final ObservableMap<KeyCodeCombination, MoeAbstractAction> keymap = FXCollections.observableMap(new LinkedHashMap<>()); private org.fxmisc.wellbehaved.event.InputMap<javafx.scene.input.KeyEvent> curKeymap; private boolean lastActionWasCut; private MoeAbstractAction[] overrideActions; private MoeActions(MoeEditor editor) { this.editor = editor; createActionTable(); if (!load()) setDefaultKeyBindings(); lastActionWasCut = false; if (Config.isMacOS()) { builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.ALT_DOWN), actions.get(DefaultEditorKit.previousWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.ALT_DOWN), actions.get(DefaultEditorKit.nextWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.ALT_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionPreviousWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.ALT_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionNextWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.META_DOWN), actions.get(DefaultEditorKit.beginLineAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.META_DOWN), actions.get(DefaultEditorKit.endLineAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.META_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionBeginLineAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.META_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionEndLineAction)); } else { builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.CONTROL_DOWN), actions.get(DefaultEditorKit.previousWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.CONTROL_DOWN), actions.get(DefaultEditorKit.nextWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.LEFT, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionPreviousWordAction)); builtInKeymap.put(new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), actions.get(DefaultEditorKit.selectionNextWordAction)); } builtInKeymap.put(new KeyCodeCombination(KeyCode.X, KeyCombination.SHORTCUT_DOWN), null); builtInKeymap.put(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN), null); builtInKeymap.put(new KeyCodeCombination(KeyCode.V, KeyCombination.SHORTCUT_DOWN), null); builtInKeymap.put(new KeyCodeCombination(KeyCode.Z, KeyCombination.SHORTCUT_DOWN), null); builtInKeymap.put(new KeyCodeCombination(KeyCode.Y, KeyCombination.SHORTCUT_DOWN), null); updateKeymap(); } void updateKeymap() { if (getTextComponent() != null) { if (curKeymap != null) Nodes.removeInputMap(getTextComponent(), curKeymap); Stream<Entry<KeyCodeCombination, MoeAbstractAction>> joinedKeyMapStream = Stream.concat( builtInKeymap.entrySet().stream(), keymap.entrySet().stream() .filter(e -> !e.getValue().hasMenuItemWithAccelerator(e.getKey())) ); HashMap<KeyCodeCombination, MoeAbstractAction> all = new HashMap<>(); joinedKeyMapStream.forEach(kv -> all.put(kv.getKey(), kv.getValue())); joinedKeyMapStream = all.entrySet().stream(); curKeymap = InputMap.sequence(joinedKeyMapStream.map(e -> { if (e.getValue() == null) return InputMap.ignore(EventPattern.keyPressed(e.getKey())); else { return InputMap.consume(EventPattern.keyPressed(e.getKey()), ev -> e.getValue().actionPerformed(false)); } }).toArray(InputMap[]::new)); Nodes.addInputMap(getTextComponent(), curKeymap); } }
| Get the actions object for the given editor. | public static MoeActions getActions(MoeEditor editor) { return moeActions.computeIfAbsent(editor, MoeActions::new); } private static int findWordLimit(MoeEditorPane c, int pos, boolean forwards) { int maxLen = c.getDocument().length(); if (forwards && pos >= maxLen) return maxLen; if (! forwards && pos <= 0) return 0; char curChar = c.getText(pos, pos + 1).charAt(0); if (Character.isWhitespace(curChar)) { while (Character.isWhitespace(curChar)){ if (forwards) pos++; else{ pos--; } if (pos == maxLen) return pos; if (pos == 0) return 0; curChar = c.getText(pos, pos + 1).charAt(0); } return forwards ? pos : pos + 1; } else if (Character.isJavaIdentifierPart(curChar)) { while (Character.isJavaIdentifierPart(curChar)){ if (forwards) pos++; else{ pos--; } if (pos == maxLen) return pos; if (pos == 0) return 0; curChar = c.getText(pos, pos + 1).charAt(0); } return forwards ? pos : pos + 1; } else { return forwards ? pos + 1 : pos; } }
| Check whether any text is currently selected. | @return True, if a selection is active. | private static boolean haveSelection(MoeEditor ed) { MoeEditorPane textPane = ed.getSourcePane(); return textPane.getCaretMark() != textPane.getCaretDot(); }
| Return the current column number. | private int getCurrentColumn() { int pos = Math.min(editor.getSourcePane().getCaretMark(), editor.getSourcePane().getCaretDot()); return editor.getSourcePane().offsetToPosition(pos, Bias.Forward).getMinor(); }
| Find and return a line by line number | private Element getLine(int lineNo) { return editor.getSourceDocument().getDefaultRootElement().getElement(lineNo); }
| Return the number of the current line. | private int getCurrentLineIndex() { MoeSyntaxDocument document = editor.getSourceDocument(); return document.getDefaultRootElement().getElementIndex(editor.getSourcePane().getCaretPosition()); }
| Check whether the indentation s opens a new multi-line comment | @param lineStart The position in the document of the (newly-added) line start | private static boolean isNewCommentStart(String s, MoeSyntaxDocument doc, int lineStart) { s = s.trim(); if (s.endsWith("/**") || s.endsWith("/*")) { NodeAndPosition<ParsedNode> curNode = doc.getParser().findNodeAt(lineStart, 0); while (curNode != null && !(curNode.getNode() instanceof CommentNode)) { curNode = curNode.getNode().findNodeAt(lineStart, curNode.getPosition()); } if (curNode == null) { return true; } String comment = getNodeContents(doc, curNode); comment = comment.substring(2); return comment.contains("/*"); } return false; }
| Insert text to complete a new, started block comment and place the cursor | appropriately. | | The indentString passed in always ends with "/*". |*/ private static void completeNewCommentBlock(MoeEditorPane textPane, String indentString) { String nextIndent = indentString.substring(0, indentString.length() - 2); textPane.replaceSelection(nextIndent + " * "); int pos = textPane.getCaretPosition(); textPane.replaceSelection("\n"); textPane.replaceSelection(nextIndent + " */"); textPane.setCaretPosition(pos); } /** * Check whether the given line ends with an opening brace. */ private static boolean isOpenBrace(String s) { int index = s.lastIndexOf('{'); | |if (index == -1) { | |return false; | |} | |return s.indexOf('}', index + 1) == -1; | |} | |/** | Transform indentation string to ensure: | <ul> | <li>after " / *" follows " *" |* <li>after " / * *" follows " *" * <li>after " * /" follows "" * </ul> */ private static String nextIndent(String s, boolean openBrace, boolean commentEndOnly) { // after an opening brace, add some spaces to the indentation if (openBrace) { | |return s + spaces.substring(0, tabSize); | |} | |if (commentEndOnly) { | |return s.substring(0, s.length() - 1); | |} | |if (s.endsWith("/*")) { return s.substring(0, s.length() - 2) + " * "; } return s; } /** * Insert a spaced tab at the current caret position in to the textPane. */ private void insertSpacedTab() { int numSpaces = tabSize - (getCurrentColumn() % tabSize); | |editor.getSourcePane().replaceSelection(spaces.substring(0, numSpaces)); | |} | |/** | Remove characters before the current caret position to take the | caret back to the previous TAB position. No check is made what kind | of characters those are - the caller should make sure they can be | removed (usually they should be whitespace). | private void removeTab() { int col = getCurrentColumn(); if (col > 0) { int remove = col % tabSize; if (remove == 0) { remove = tabSize; } int pos = editor.getSourcePane().getCaretPosition(); editor.getSourcePane().replaceText(pos-remove, pos, ""); editor.getSourcePane().setCaretPosition(pos-remove); } } private static String expandTab(String s, int idx) { int numSpaces = tabSize - (idx % tabSize); return s.substring(0, idx) + spaces.substring(0, numSpaces) + s.substring(idx + 1); }
| Insert text from a named template into the editor at the current cursor | position. Every line in the template will be indented to the current | cursor position (in addition to possible indentation in the template | itself), and TAB characters at beginnings of lines in the template will | be converted to a spaced tab according to the current tabsize. | | @param templateName | The name of the template (without path or suffix) | private void insertTemplate(String templateName) { try { MoeEditorPane textPane = getTextComponent(); File template = Config.getTemplateFile(templateName); InputStream fileStream = new FileInputStream(template); BufferedReader in = new BufferedReader(new InputStreamReader(fileStream, "UTF-8")); int addedTextLength = 0; String line = in.readLine(); while (line != null){ while ((line.length() > 0) && (line.charAt(0) == '\t')){ line = line.substring(1); } addedTextLength += line.length() + 1; textPane.replaceSelection(line); textPane.replaceSelection("\n"); line = in.readLine(); } int caretPos = editor.getSourcePane().getCaretPosition(); AutoIndentInformation info = MoeIndent.calculateIndentsAndApply(editor.getSourceDocument(),caretPos - addedTextLength,caretPos+2,caretPos); editor.setCaretPositionForward(info.getNewCaretPosition() - editor.getSourcePane().getCaretPosition()); in.close(); } catch (IOException exc) { Debug.reportError("Could not read method template."); Debug.reportError("Exception: " + exc); } }
| Perform an action on all selected lines in the source document. | private static void blockAction(MoeEditor editor, LineAction lineAction) { int selectionStart = editor.getSourcePane().getCaretMark(); int selectionEnd = editor.getSourcePane().getCaretDot(); if (selectionStart > selectionEnd) { int tmp = selectionStart; selectionStart = selectionEnd; selectionEnd = tmp; } if (selectionStart != selectionEnd) selectionEnd = selectionEnd - 1; MoeSyntaxDocument doc = editor.getSourceDocument(); Element text = doc.getDefaultRootElement(); int firstLineIndex = editor.getLineColumnFromOffset(selectionStart).getLine() - 1; int lastLineIndex = editor.getLineColumnFromOffset(selectionEnd).getLine(); for (int i = firstLineIndex; i < lastLineIndex; i++) { Element line = text.getElement(i); lineAction.apply(line, doc); } if (selectionStart == selectionEnd) { Element line = text.getElement(selectionStart); editor.getSourcePane().deselect(); } } private static String getNodeContents(MoeSyntaxDocument doc, NodeAndPosition<ParsedNode> nap) { return doc.getText(nap.getPosition(), nap.getSize()); } | Add a new key binding into the action table. | public static void addKeyCombinationForActionToAllEditors(KeyCodeCombination key, String actionName) { moeActions.values().forEach(moeAction -> moeAction.addKeyCombinationForAction(key, actionName)); }
| Add a new key binding into the action table. | private void addKeyCombinationForAction(KeyCodeCombination key, String actionName) { MoeAbstractAction action = actions.get(actionName); if (action != null) { keymap.put(key, action); updateKeymap(); } }
| Return an action with a given name. | public MoeAbstractAction getActionByName(String name) { return actions.get(name); } | Remove a key binding from the action table. | public void removeKeyStrokeBinding(KeyCombination key) { keymap.remove(key); } | Save the key bindings. Return true if successful. | public boolean save() { try { File file = Config.getUserConfigFile(KEYS_FILE_FX); ArrayList<String> lines = new ArrayList<>(); lines.add("version " + MoeEditor.version); lines.add("# ALT CTRL META SHIFT SHORT KEYCODE ACTION"); for (Entry<KeyCodeCombination, MoeAbstractAction> binding : keymap.entrySet()) { KeyCodeCombination k = binding.getKey(); lines.add(k.getAlt().name() + " " + k.getControl().name() + " " + k.getMeta() + " " + k.getShift() + " " + k.getShortcut() + " " + k.getCode().name() + " " + binding.getValue().getName()); } Files.write(file.toPath(), lines, Charset.forName("UTF-8")); return true; } catch (Exception exc) { Debug.message("Cannot save key bindings: " + exc); return false; } } | Load the key bindings. Return true if successful. | public boolean load() { try { File file = Config.getUserConfigFile(KEYS_FILE_FX); if (file.exists()) { List<String> lines = Files.readAllLines(file.toPath(), Charset.forName("UTF-8")).stream() .filter(l -> !l.startsWith("#") && !l.trim().isEmpty()).collect(Collectors.toList()); if (lines.isEmpty() || !lines.get(0).startsWith("version")) return false; try { for (int i = 1; i < lines.size() ; i++) { String line = lines.get(i); String[] split = line.split(" +"); addKeyCombinationForAction(new KeyCodeCombination( KeyCode.valueOf(split[5]), ModifierValue.valueOf(split[3]), ModifierValue.valueOf(split[1]), ModifierValue.valueOf(split[0]), ModifierValue.valueOf(split[2]), ModifierValue.valueOf(split[4]) ), split[6]); } return true; } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) { Debug.reportError(e); return false; } } else { file = Config.getUserConfigFile(KEYS_FILE); FileInputStream istream = new FileInputStream(file); ObjectInputStream stream = new ObjectInputStream(istream); int version = 0; int count = stream.readInt(); if (count > 100) { version = count; count = stream.readInt(); } if (Config.isMacOS() && (version < 140)) { istream.close(); return false; } for (int i = 0; i < count; i++) { Object keyBinding = stream.readObject(); KeyCodeCombination keyCombination = null; if (keyBinding instanceof KeyStroke) { keyCombination = convertSwingBindingToFX((KeyStroke) keyBinding); } String actionName = (String) stream.readObject(); if (actionName != null && keyCombination != null) { addKeyCombinationForAction(keyCombination, actionName); } } istream.close(); if (version < 252) { addKeyCombinationForAction(new KeyCodeCombination(KeyCode.EQUALS, KeyCombination.SHORTCUT_DOWN), "increase-font"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.MINUS, KeyCombination.SHORTCUT_DOWN), "decrease-font"); } if (version < 300) { addKeyCombinationForAction(new KeyCodeCombination(KeyCode.SPACE, KeyCombination.CONTROL_DOWN), "code-completion"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.I, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN), "autoindent"); } if (version < 320) { addKeyCombinationForAction(new KeyCodeCombination(KeyCode.K, KeyCombination.SHORTCUT_DOWN), "compile"); } if (version < 330) { addKeyCombinationForAction(new KeyCodeCombination(KeyCode.COMMA, KeyCombination.SHORTCUT_DOWN), "preferences"); } if (version < 400) { addKeyCombinationForAction(new KeyCodeCombination(KeyCode.DIGIT0, KeyCombination.SHORTCUT_DOWN), "reset-font"); } return true; } } catch (IOException | ClassNotFoundException exc) { return false; } } @SuppressWarnings("deprecation") private static KeyCodeCombination convertSwingBindingToFX(KeyStroke swing) { List<Modifier> modifiers = new ArrayList<>(); if ((swing.getModifiers() & Event.CTRL_MASK) != 0) modifiers.add(KeyCombination.CONTROL_DOWN); if ((swing.getModifiers() & Event.SHIFT_MASK) != 0) modifiers.add(KeyCombination.SHIFT_DOWN); if ((swing.getModifiers() & Event.META_MASK) != 0) modifiers.add(KeyCombination.META_DOWN); if ((swing.getModifiers() & Event.ALT_MASK) != 0) modifiers.add(KeyCombination.ALT_DOWN); KeyCode code = JavaFXUtil.awtKeyCodeToFX(swing.getKeyCode()); if (code != null) { return new KeyCodeCombination(code, modifiers.toArray(new Modifier[0])); } else { return null; } } | Called to inform that any one of the user actions (text edit or caret | move) was executed. | public void userAction() { lastActionWasCut = false; } | We just typed a closing brace character - indent appropriately. | public void closingBrace(int offset) { int lineIndex = getCurrentLineIndex(); Element line = getLine(lineIndex); int lineStart = line.getStartOffset(); String prefix = editor.getSourcePane().getText(lineStart, offset); if (prefix.trim().length() == 0) { editor.getSourcePane().setCaretPosition(lineStart); doIndent(true); editor.getSourcePane().setCaretPosition(editor.getSourcePane().getCaretPosition() + 1); } } | Add the current selection of the text component to the clipboard. | public static void addSelectionToClipboard(MoeEditor ed) { javafx.scene.input.Clipboard clipboard = javafx.scene.input.Clipboard.getSystemClipboard(); String clipContent = clipboard.getString(); if (clipContent == null) clipContent = ""; clipboard.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, clipContent + ed.getSourcePane().getSelectedText())); } | Do some semi-intelligent indentation. That is: indent the current line to | the same depth, using the same characters (TABs or spaces) as the line | immediately above. | | @param isNewLine true if the action was to insert a line or closing brace; | false if the action was to tab/indent | private void doIndent(boolean isNewLine) { MoeEditorPane textPane = editor.getSourcePane(); int lineIndex = getCurrentLineIndex(); if (lineIndex == 0) { if (!isNewLine) { insertSpacedTab(); } return; } MoeSyntaxDocument doc = editor.getSourceDocument(); Element line = getLine(lineIndex); int lineStart = line.getStartOffset(); int pos = textPane.getCaretPosition(); boolean isOpenBrace = false; boolean isCommentEnd = false, isCommentEndOnly = false; String prefix = doc.getText(lineStart, pos - lineStart); if (prefix.trim().length() > 0) { insertSpacedTab(); return; } boolean foundLine = false; int lineOffset = 1; String prevLineText = ""; while ((lineIndex - lineOffset >= 0) && !foundLine){ Element prevline = getLine(lineIndex - lineOffset); int prevLineStart = prevline.getStartOffset(); int prevLineEnd = prevline.getEndOffset(); prevLineText = doc.getText(prevLineStart, prevLineEnd - prevLineStart); if (!MoeIndent.isWhiteSpaceOnly(prevLineText)) { foundLine = true; } else { lineOffset++; } } if (!foundLine) { if (!isNewLine) insertSpacedTab(); return; } if (isOpenBrace(prevLineText)) { isOpenBrace = true; } else { isCommentEnd = prevLineText.trim().endsWith("*/"); isCommentEndOnly = prevLineText.trim().equals("*/"); } int indentPos = MoeIndent.findFirstNonIndentChar(prevLineText, isCommentEnd); String indent = prevLineText.substring(0, indentPos); if (isOpenBrace) { indentPos += tabSize; } int caretColumn = getCurrentColumn(); if (caretColumn >= indentPos) { if (!isNewLine) { insertSpacedTab(); } return; } if (isNewLine && isNewCommentStart(indent, doc, lineStart)) { completeNewCommentBlock(textPane, indent); return; } int lineEnd = line.getEndOffset(); String lineText = doc.getText(lineStart, lineEnd - lineStart); indentPos = MoeIndent.findFirstNonIndentChar(lineText, true); char firstChar = lineText.isEmpty() ? '\u0000' : lineText.charAt(indentPos); doc.remove(lineStart, indentPos); String newIndent = nextIndent(indent, isOpenBrace, isCommentEndOnly); if (firstChar == '*') { newIndent = newIndent.replace('*', ' '); } textPane.replaceText(lineStart, lineStart, newIndent); if (firstChar == '}') { removeTab(); } } | Do some semi-intelligent de-indentation. That is: indent the current line | one indentation level less that the line above, or less than it currently | is. | private void doDeIndent(MoeEditorPane textPane) { int lineIndex = getCurrentLineIndex(); MoeSyntaxDocument doc = editor.getSourceDocument(); Element line = getLine(lineIndex); int lineStart = line.getStartOffset(); int lineEnd = line.getEndOffset(); String lineText = doc.getText(lineStart, lineEnd - lineStart); int currentIndentPos = MoeIndent.findFirstNonIndentChar(lineText, true); char firstChar = lineText.charAt(currentIndentPos); textPane.setCaretPosition(lineStart + currentIndentPos); if (lineIndex == 0) { removeTab(); return; } Element prevline = getLine(lineIndex - 1); int prevLineStart = prevline.getStartOffset(); int prevLineEnd = prevline.getEndOffset(); String prevLineText = doc.getText(prevLineStart, prevLineEnd - prevLineStart); int targetIndentPos = MoeIndent.findFirstNonIndentChar(prevLineText, true); if (currentIndentPos > targetIndentPos) { String indent = prevLineText.substring(0, targetIndentPos); doc.remove(lineStart, currentIndentPos); doc.insertString(lineStart, indent); if (firstChar == '}') removeTab(); } else { removeTab(); } } | Indent a block of lines (defined by the current selection) by one | additional level. | private void doBlockIndent(MoeEditor editor) { editor.undoManager.compoundEdit(() -> blockAction(editor, new IndentLineAction())); } | De-indent a block of lines (defined by the current selection) by one | level. | private void doBlockDeIndent(MoeEditor editor) { editor.undoManager.compoundEdit(() -> blockAction(editor, new DeindentLineAction())); } | Convert all tabs in this text to spaces, maintaining the current | indentation. | | @param editor Reference to the editor | @return The number of tab characters converted | private static int convertTabsToSpaces(MoeEditor editor) { int count = 0; int lineNo = 0; MoeSyntaxDocument doc = editor.getSourceDocument(); Element root = doc.getDefaultRootElement(); Element line = root.getElement(lineNo); while (line != null){ int start = line.getStartOffset(); int length = line.getEndOffset() - start; String text = doc.getText(start, length); int startCount = count; int tabIndex = text.indexOf('\t'); while (tabIndex != -1){ text = expandTab(text, tabIndex); count++; tabIndex = text.indexOf('\t'); } if (count != startCount) { doc.remove(start, length); doc.insertString(start, text); } lineNo++; line = root.getElement(lineNo); } return count; } | Create the table of action supported by this editor | private void createActionTable() { compileOrNextErrorAction = compileOrNextErrorAction(); overrideActions = new MoeAbstractAction[]{ new NextWordAction(false), new NextWordAction(true), new PrevWordAction(false), new PrevWordAction(true), new EndWordAction(false), new EndWordAction(true), new BeginWordAction(false), new BeginWordAction(true), new EndLineAction(false), new EndLineAction(true), new BeginLineAction(false), new BeginLineAction(true), deleteWordAction(), selectWordAction() }; MoeAbstractAction[] myActions = { saveAction(), printAction(), closeAction(), undoAction(), redoAction(), commentBlockAction(), uncommentBlockAction(), autoIndentAction(), indentBlockAction(), deindentBlockAction(), insertMethodAction(), addJavadocAction(), indentAction(), deIndentAction(), newLineAction(), cutAction(), copyAction(), pasteAction(), copyLineAction(), cutLineAction(), cutEndOfLineAction(), cutWordAction(), cutEndOfWordAction(), findAction(), findNextAction(), findPrevAction(), replaceAction(), compileOrNextErrorAction, goToLineAction(), toggleInterfaceAction(), toggleBreakPointAction(), keyBindingsAction(), preferencesAction(), increaseFontAction(), decreaseFontAction(), resetFontAction(), contentAssistAction() }; actions = new LinkedHashMap<>(); for (MoeAbstractAction action : overrideActions) { actions.put(action.getName(), action); } for (MoeAbstractAction action : myActions) { actions.put(action.getName(), action); } } public List getAllActions() { return new ArrayList<>(actions.values()); } public List getKeyStrokesForAction(String actionName) { return keymap.entrySet().stream().filter(e -> Objects.equals(e.getValue().getName(), actionName)).map(e -> e.getKey()).collect(Collectors.toList()); }
| Makes all actions unavailable (i.e. force disable) except the given actions | public void makeAllUnavailableExcept(String... actionNames) { Set<String> keepEnabled = new HashSet<>(Arrays.asList(actionNames)); for (Entry<String, MoeAbstractAction> action : this.actions.entrySet()) { action.getValue().setAvailable(keepEnabled.contains(action.getKey())); } } public void makeAllAvailable() { for (Entry<String, MoeAbstractAction> action : this.actions.entrySet()) { action.getValue().setAvailable(true); } } public static enum Category { EDIT("editor.functions.editFunctions"), MOVE_SCROLL("editor.functions.moveScroll"), CLASS("editor.functions.classFunctions"), MISC("editor.functions.misc"); private final String label; private Category(String labelKey) { this.label = Config.getString(labelKey); } @OnThread(Tag.Any) @Override public String toString() { return label; } } | Set up the default key bindings. Used for initial setup, or restoring the | default later on. | public void setDefaultKeyBindings() { keymap.clear(); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.S, SHORTCUT_MASK), "save"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.P, SHORTCUT_MASK), "print"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.W, SHORTCUT_MASK), "close"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.Z, SHORTCUT_MASK), "undo"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.Y, SHORTCUT_MASK), "redo"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F8), "comment-block"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F7), "uncomment-block"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F6), "indent-block"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F5), "deindent-block"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.M, SHORTCUT_MASK), "insert-method"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.TAB), "indent"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.TAB, KeyCombination.SHIFT_DOWN), "de-indent"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.I, SHORTCUT_MASK), "insert-tab"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.ENTER), "new-line"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.ENTER, KeyCombination.SHIFT_DOWN), "insert-break"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F, SHORTCUT_MASK), "find"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.G, SHORTCUT_MASK), "find-next"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.G, SHORTCUT_MASK, KeyCombination.SHIFT_DOWN), "find-next-backward"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.R, SHORTCUT_MASK), "replace"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.L, SHORTCUT_MASK), "go-to-line"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.K, SHORTCUT_MASK), "compile"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.J, SHORTCUT_MASK), "toggle-interface-view"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.B, SHORTCUT_MASK), "toggle-breakpoint"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.COMMA, SHORTCUT_MASK), "preferences"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.D, SHORTCUT_MASK), "describe-key"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.C, SHORTCUT_MASK), DefaultEditorKit.copyAction); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.X, SHORTCUT_MASK), DefaultEditorKit.cutAction); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.V, SHORTCUT_MASK), DefaultEditorKit.pasteAction); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F2), "copy-line"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F3), DefaultEditorKit.pasteAction); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.F4), "cut-line"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.EQUALS, SHORTCUT_MASK), "increase-font"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.MINUS, SHORTCUT_MASK), "decrease-font"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.DIGIT0, SHORTCUT_MASK), "reset-font"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.SPACE, KeyCombination.CONTROL_DOWN), "code-completion"); addKeyCombinationForAction(new KeyCodeCombination(KeyCode.I, SHIFT_SHORTCUT_MASK), "autoindent"); } private MoeAbstractAction action(String name, Category category, FXPlatformRunnable action) { return new MoeAbstractAction(name, category) { @Override public @OnThread(value = Tag.FXPlatform) void actionPerformed(boolean viaContextMenu) { action.run(); } }; }
| Creates an action that can act differently if it | is called via a context menu. | | @param name The action name | @param category The category for the preferences | @param action Called with true if called via a context menu, false otherwise. | @return | private MoeAbstractAction contextSensitiveAction(String name, Category category, FXPlatformConsumer<Boolean> action) { return new MoeAbstractAction(name, category) { @Override public @OnThread(value = Tag.FXPlatform) void actionPerformed(boolean viaContextMenu) { action.accept(viaContextMenu); } }; } | Interface LineAction - a superclass for all line actions. Line actions | manipulate a single line of text and are used by the blockAction method. | The blockAction applies a LineAction to each line in a block of text. | interface LineAction {
| Apply some action to a line in the document. | public void apply(Element line, MoeSyntaxDocument doc); } @OnThread(Tag.FXPlatform) abstract class MoeAbstractAction extends FXAbstractAction { private final Category category; public MoeAbstractAction(String name, Category category) { super(name); this.accelerator.bind(Bindings.createObjectBinding(() -> { return keymap.entrySet().stream().filter(e -> e.getValue().equals(this)).map(e -> e.getKey()).findFirst().orElse(null); }, keymap)); this.category = category; } public Category getCategory() { return category; } }
| retained side effect: clears message in editor! | private final MoeEditor getEditor() { editor.clearMessage(); return editor; } private MoeAbstractAction saveAction() { return action("save", Category.CLASS, () -> getEditor().userSave()); } private MoeAbstractAction printAction() { return action("print", Category.CLASS, () -> getEditor().print()); } private MoeAbstractAction closeAction() { return action("close", Category.CLASS, () -> getEditor().close()); } private MoeAbstractAction undoAction() { MoeAbstractAction action = action("undo", Category.MISC, () -> { MoeEditor editor = getEditor(); editor.undoManager.undo(); }); action.bindEnabled(editor == null ? null : editor.undoManager.canUndo()); return action; } private MoeAbstractAction redoAction() { MoeAbstractAction action = action("redo", Category.MISC, () -> { MoeEditor editor = getEditor(); editor.undoManager.redo(); }); action.bindEnabled(editor == null ? null : editor.undoManager.canRedo()); return action; } private MoeAbstractAction commentBlockAction() { return action("comment-block", Category.EDIT, () -> { MoeEditor editor = getEditor(); editor.undoManager.compoundEdit(() -> blockAction(editor, new CommentLineAction())); }); } private MoeAbstractAction uncommentBlockAction() { return action("uncomment-block", Category.EDIT, () -> { MoeEditor editor = getEditor(); editor.undoManager.compoundEdit(() -> blockAction(editor, new UncommentLineAction())); }); } private MoeAbstractAction indentBlockAction() { return action("indent-block", Category.EDIT, () -> doBlockIndent(getEditor())); } private MoeAbstractAction deindentBlockAction() { return action("deindent-block", Category.EDIT, () -> doBlockDeIndent(getEditor())); } private MoeAbstractAction autoIndentAction() { return action("autoindent", Category.EDIT, () -> { MoeEditor editor = getEditor(); MoeSyntaxDocument doc = editor.getSourceDocument(); if (doc.getParsedNode() == null) { return; } int prevCaretPos = editor.getSourcePane().getCaretPosition(); editor.undoManager.compoundEdit(() -> { AutoIndentInformation info = MoeIndent.calculateIndentsAndApply(doc, prevCaretPos); editor.setCaretPositionForward(info.getNewCaretPosition() - prevCaretPos); if (info.isPerfect()) { editor.writeMessage(Config.getString("editor.info.perfectIndent")); } }); }); } private MoeAbstractAction insertMethodAction() { return action("insert-method", Category.EDIT, () -> { MoeEditor editor = getEditor(); if (!editor.containsSourceCode()){ return; } editor.undoManager.compoundEdit(() -> insertTemplate("method")); }); } private MoeAbstractAction addJavadocAction() { return action("add-javadoc", Category.EDIT, () -> { MoeEditor editor = getEditor(); if (!editor.containsSourceCode()) { return; } int caretPos = editor.getCurrentTextPane().getCaretPosition(); NodeAndPosition<ParsedNode> node = editor.getParsedNode().findNodeAt(caretPos, 0); while (node != null && node.getNode().getNodeType() != ParsedNode.NODETYPE_METHODDEF){ node = node.getNode().findNodeAt(caretPos, node.getPosition()); } if (node == null || !(node.getNode() instanceof MethodNode)) { editor.writeMessage(Config.getString("editor.addjavadoc.notAMethod")); } else { MethodNode methodNode = ((MethodNode)node.getNode()); boolean hasJavadocComment = false; Iterator<NodeAndPosition<ParsedNode>> it = methodNode.getChildren(node.getPosition()); while (it.hasNext()){ ParsedNode subNode = it.next().getNode(); if (subNode instanceof CommentNode) { hasJavadocComment = hasJavadocComment || ((CommentNode)subNode).isJavadocComment(); } } if (hasJavadocComment) { editor.writeMessage(Config.getString("editor.addjavadoc.hasJavadoc")); } else { StringBuilder indent = new StringBuilder(); int column = editor.getLineColumnFromOffset(node.getPosition()).getColumn(); for (int i = 0;i < column-1;i++) indent.append(' '); StringBuilder newComment = new StringBuilder(); newComment.append("/**\n"); JavaEntity retTypeEntity = methodNode.getReturnType(); if (retTypeEntity == null) { newComment.append(indent).append(" * ").append(methodNode.getName()).append(" "); newComment.append(Config.getString("editor.addjavadoc.constructor")).append("\n"); } else { newComment.append(indent).append(" * ").append(Config.getString("editor.addjavadoc.method")); newComment.append(" ").append(methodNode.getName()).append("\n"); } newComment.append(indent).append(" *\n"); for (String s: methodNode.getParamNames()) { newComment.append(indent).append(" * @param ").append(s).append(" "); newComment.append(Config.getString("editor.addjavadoc.parameter")).append("\n"); } if (retTypeEntity != null) { JavaType retType = retTypeEntity.resolveAsType().getType(); if (retType != null && !retType.isVoid()) { newComment.append(indent).append(" * @return "); newComment.append(Config.getString("editor.addjavadoc.returnValue")).append("\n"); } } newComment.append(indent).append(" */\n").append(indent); NodeAndPosition<ParsedNode> nodeFinal = node; editor.undoManager.compoundEdit(() -> { editor.getCurrentTextPane().setCaretPosition(nodeFinal.getPosition()); editor.getCurrentTextPane().replaceSelection(newComment.toString()); editor.getCurrentTextPane().setCaretPosition((caretPos + newComment.length())); }); } } }); } private MoeAbstractAction indentAction() { return action("indent", Category.EDIT, () -> { MoeEditor ed = getEditor(); if (haveSelection(ed)) { doBlockIndent(ed); } else { int converted = 0; if (ed.checkExpandTabs()) { converted = convertTabsToSpaces(ed); } if (PrefMgr.getFlag(PrefMgr.AUTO_INDENT)) { doIndent(false); } else { insertSpacedTab(); } if (converted > 0) { ed.writeMessage(Config.getString("editor.info.tabsExpanded")); } } }); } private MoeAbstractAction deIndentAction() { return action("de-indent", Category.EDIT, () -> { MoeEditor ed = getEditor(); if (haveSelection(ed)) { doBlockDeIndent(ed); } else { if (ed.checkExpandTabs()) { int converted = convertTabsToSpaces(ed); if (converted > 0) ed.writeMessage(Config.getString("editor.info.tabsExpanded")); } doDeIndent(ed.getCurrentTextPane()); } }); } private MoeEditorPane getTextComponent() { return editor == null ? null : editor.getSourcePane(); } private MoeAbstractAction newLineAction() { return action("new-line", Category.EDIT, () -> { editor.getSourcePane().replaceSelection("\n"); editor.getSourcePane().requestFollowCaret(); editor.getSourcePane().layout(); if (PrefMgr.getFlag(PrefMgr.AUTO_INDENT)) { doIndent(true); } editor.undoManager.breakEdit(); }); } private MoeAbstractAction cutAction() { return contextSensitiveAction("cut-to-clipboard", Category.EDIT, viaContextMenu -> { if (viaContextMenu || editor.getSourcePane().isFocused()) { editor.getSourcePane().cut(); if (viaContextMenu) { editor.getSourcePane().requestFocus(); } } }); } private MoeAbstractAction copyAction() { return contextSensitiveAction("copy-to-clipboard", Category.EDIT, viaContextMenu -> { if (viaContextMenu || editor.getSourcePane().isFocused()) { editor.getSourcePane().copy(); if (viaContextMenu) { editor.getSourcePane().requestFocus(); } } }); } private MoeAbstractAction pasteAction() { return contextSensitiveAction("paste-from-clipboard", Category.EDIT, viaContextMenu -> { if (viaContextMenu || editor.getSourcePane().isFocused()) { editor.getSourcePane().paste(); if (viaContextMenu) { editor.getSourcePane().requestFocus(); } editor.getSourcePane().requestFollowCaret(); } }); } private MoeAbstractAction copyLineAction() { return action("copy-line", Category.EDIT, () -> { boolean addToClipboard = lastActionWasCut; editor.getSourcePane().lineStart(SelectionPolicy.CLEAR); editor.getSourcePane().lineEnd(SelectionPolicy.EXTEND); editor.getSourcePane().nextChar(SelectionPolicy.EXTEND); if (addToClipboard) { addSelectionToClipboard(editor); } else { editor.getSourcePane().copy(); } editor.getSourcePane().setCaretPosition(editor.getSourcePane().getSelection().getEnd()); lastActionWasCut = true; }); } private MoeAbstractAction cutLineAction() { return action("cut-line", Category.EDIT, () -> { boolean addToClipboard = lastActionWasCut; editor.getSourcePane().lineStart(SelectionPolicy.CLEAR); editor.getSourcePane().lineEnd(SelectionPolicy.EXTEND); editor.getSourcePane().nextChar(SelectionPolicy.EXTEND); if (addToClipboard) { addSelectionToClipboard(editor); editor.getSourcePane().replaceSelection(""); } else { editor.getSourcePane().cut(); } lastActionWasCut = true; }); } private MoeAbstractAction increaseFontAction() { return action("increase-font", Category.MISC, () -> { Utility.increaseFontSize(PrefMgr.getEditorFontSize()); }); } private MoeAbstractAction decreaseFontAction() { return action("decrease-font", Category.MISC, () -> { Utility.decreaseFontSize(PrefMgr.getEditorFontSize()); }); } private MoeAbstractAction resetFontAction() { return action("reset-font", Category.MISC, () -> { PrefMgr.getEditorFontSize().set(PrefMgr.DEFAULT_JAVA_FONT_SIZE); }); } private MoeAbstractAction cutEndOfLineAction() { return action("cut-end-of-line", Category.EDIT, () -> { boolean addToClipboard = lastActionWasCut; MoeEditorPane textComponent = getTextComponent(); textComponent.paragraphEnd(SelectionPolicy.ADJUST); if (addToClipboard) { addSelectionToClipboard(editor); textComponent.replaceSelection(""); } else { textComponent.cut(); } lastActionWasCut = true; }); } private MoeAbstractAction cutWordAction() { return action("cut-word", Category.EDIT, () -> { boolean addToClipboard = lastActionWasCut; getActionByName("caret-previous-word").actionPerformed(false); getActionByName("selection-next-word").actionPerformed(false); if (addToClipboard) { addSelectionToClipboard(editor); getActionByName("delete-previous").actionPerformed(false); } else { getActionByName("cut-to-clipboard").actionPerformed(false); } lastActionWasCut = true; }); } private MoeAbstractAction contentAssistAction() { return action("code-completion", Category.MISC, () -> { MoeEditor editor = getEditor(); if (Config.getPropBoolean("bluej.editor.codecompletion", true)){ editor.createContentAssist(); } }); } private MoeAbstractAction cutEndOfWordAction() { return action("cut-end-of-word", Category.EDIT, () -> { boolean addToClipboard = lastActionWasCut; getActionByName("selection-next-word").actionPerformed(false); if (addToClipboard) { addSelectionToClipboard(editor); getActionByName("delete-previous").actionPerformed(false); } else { getActionByName("cut-to-clipboard").actionPerformed(false); } lastActionWasCut = true; }); } private abstract class MoeActionWithOrWithoutSelection extends MoeAbstractAction { protected final boolean withSelection; protected MoeActionWithOrWithoutSelection(String actionName, Category category, boolean withSelection) { super(actionName, category); this.withSelection = withSelection; } protected void moveCaret(MoeEditorPane c, int pos) { if (withSelection) { c.moveCaretPosition(pos); } else { c.setCaretPosition(pos); } } } class NextWordAction extends MoeActionWithOrWithoutSelection { public NextWordAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionNextWordAction : DefaultEditorKit.nextWordAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { MoeEditorPane c = getTextComponent(); int origPos = c.getCaretDot(); int end = findWordLimit(c, origPos, true); if (Character.isWhitespace(c.getText(end, end + 1).charAt(0))) { int endOfWS = findWordLimit(c, end, true); moveCaret(c, endOfWS); } else { moveCaret(c, end); } } } class PrevWordAction extends MoeActionWithOrWithoutSelection { public PrevWordAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionPreviousWordAction : DefaultEditorKit.previousWordAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { MoeEditorPane c = getTextComponent(); int origPos = c.getCaretDot(); if (origPos == 0) return; if (Character.isWhitespace(c.getText(origPos - 1, origPos).charAt(0))) { int startOfWS = findWordLimit(c, origPos - 1, false); int startOfPrevWord = findWordLimit(c, startOfWS - 1, false); moveCaret(c, startOfPrevWord); } else { int startOfWord = findWordLimit(c, origPos - 1, false); moveCaret(c, startOfWord); } } } class EndWordAction extends MoeActionWithOrWithoutSelection { public EndWordAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionEndWordAction : DefaultEditorKit.endWordAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { MoeEditorPane c = getTextComponent(); int origPos = c.getCaretDot(); int end = findWordLimit(c, origPos, true); moveCaret(c, end); } } class BeginWordAction extends MoeActionWithOrWithoutSelection { public BeginWordAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionBeginWordAction : DefaultEditorKit.beginWordAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { MoeEditorPane c = getTextComponent(); int origPos = c.getCaretDot(); int start = findWordLimit(c, origPos, false); moveCaret(c, start); } } private class BeginLineAction extends MoeActionWithOrWithoutSelection { public BeginLineAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionBeginLineAction : DefaultEditorKit.beginLineAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { MoeEditorPane ed = getTextComponent(); if (ed.getCaretColumn() > 1) { ed.lineStart(withSelection ? SelectionPolicy.EXTEND : SelectionPolicy.CLEAR); } else { int line = ed.getCurrentParagraph(); int oldPos = ed.getCaretPosition(); ed.wordBreaksForwards(1, withSelection ? SelectionPolicy.EXTEND : SelectionPolicy.CLEAR); if (ed.getCurrentParagraph() != line) { ed.setCaretPosition(oldPos); } } } } private class EndLineAction extends MoeActionWithOrWithoutSelection { public EndLineAction(boolean withSelection) { super(withSelection ? DefaultEditorKit.selectionEndLineAction : DefaultEditorKit.endLineAction, Category.MOVE_SCROLL, withSelection); } @Override public void actionPerformed(boolean viaContextMenu) { getTextComponent().lineEnd(withSelection ? SelectionPolicy.EXTEND : SelectionPolicy.CLEAR); } } private MoeAbstractAction deleteWordAction() { return action("delete-previous-word", Category.EDIT, () -> { MoeEditorPane c = getTextComponent(); MoeAbstractAction prevWordAct = actions.get(DefaultEditorKit.previousWordAction); int end = c.getCaretDot(); prevWordAct.actionPerformed(false); int begin = c.getCaretDot(); c.replaceText(begin, end, ""); }); } private MoeAbstractAction selectWordAction() { return action(DefaultEditorKit.selectWordAction, Category.MOVE_SCROLL, () -> { MoeEditorPane c = getTextComponent(); int origPos = c.getCaretDot(); int newStart = findWordLimit(c, origPos, false); int newEnd = findWordLimit(c, origPos, true); c.setCaretPosition(newStart); c.moveCaretPosition(newEnd); }); } private MoeAbstractAction findAction() { return action("find", Category.MISC, () -> { MoeEditor editor=getEditor(); editor.initFindPanel(); }); } private MoeAbstractAction findNextAction() { return action("find-next", Category.MISC, () -> { getEditor().findNext(false); }); } private MoeAbstractAction findPrevAction() { return action("find-next-backward", Category.MISC, () -> { getEditor().findNext(true); }); } private MoeAbstractAction replaceAction() { return action("replace", Category.MISC, () -> { MoeEditor editor = getEditor(); editor.setFindPanelVisible(); editor.showReplacePanel(); if (editor.getSourcePane().getSelectedText() != null) { editor.setFindTextfield(editor.getSourcePane().getSelectedText()); } }); } private MoeAbstractAction compileOrNextErrorAction() { return action("compile", Category.MISC, () -> getEditor().compileOrShowNextError()); } private MoeAbstractAction toggleInterfaceAction() { return action("toggle-interface-view", Category.MISC, () -> { getEditor().toggleInterface(); }); } private MoeAbstractAction toggleBreakPointAction() { return action("toggle-breakpoint", Category.MISC, () -> getEditor().toggleBreakpoint()); } private MoeAbstractAction keyBindingsAction() { return action("key-bindings", Category.MISC, () -> editor.showPreferences(1)); } private MoeAbstractAction preferencesAction() { return action("preferences", Category.MISC, () -> editor.showPreferences(0)); } private MoeAbstractAction goToLineAction() { return action("go-to-line", Category.MISC, () -> getEditor().goToLine()); }
| Class CommentLineAction - add a comment symbol to the given line. | class CommentLineAction implements LineAction {
| Comment the given line | @Override public void apply(Element line, MoeSyntaxDocument doc) { int lineStart = line.getStartOffset(); int lineEnd = line.getEndOffset(); String lineText = doc.getText(lineStart, lineEnd - lineStart); if (lineText.trim().length() > 0) { int textStart = MoeIndent.findFirstNonIndentChar(lineText, true); doc.insertString(lineStart+textStart, "// "); } } }
| Class UncommentLineAction - remove the comment symbol (if any) from the | given line. | class UncommentLineAction implements LineAction { @Override public void apply(Element line, MoeSyntaxDocument doc) { int lineStart = line.getStartOffset(); int lineEnd = line.getEndOffset(); try { String lineText = doc.getText(lineStart, lineEnd - lineStart); if (lineText.trim().startsWith("//")) { int cnt = 0; while (lineText.charAt(cnt) != '/'){ cnt++; } if (lineText.charAt(cnt + 2) == ' ') { doc.remove(lineStart+cnt, 3); } else { doc.remove(lineStart+cnt, 2); } } } catch (Exception exc) { } } }
| Class IndentLineAction - add one level of indentation to the given line. | class IndentLineAction implements LineAction { @Override public void apply(Element line, MoeSyntaxDocument doc) { int lineStart = line.getStartOffset(); doc.insertString(lineStart, spaces.substring(0, tabSize)); } }
| Class DeindentLineAction - remove one indentation level from the given | line. | class DeindentLineAction implements LineAction { @Override public void apply(Element line, MoeSyntaxDocument doc) { int lineStart = line.getStartOffset(); int lineEnd = line.getEndOffset(); try { String lineText = doc.getText(lineStart, lineEnd - lineStart); String spacedTab = spaces.substring(0, tabSize); if (lineText.startsWith(spacedTab)) { doc.remove(lineStart, tabSize); } else if (lineText.charAt(0) == TAB_CHAR) { doc.remove(lineStart, 1); } else { int cnt = 0; while (lineText.charAt(cnt) == ' '){ cnt++; } doc.remove(lineStart, cnt); } } catch (Exception exc) { } } } }

.   - MoeActions
.   MoeActions
.   updateKeymap
.   getActions
.   findWordLimit
.   haveSelection
.   getCurrentColumn
.   getLine
.   getCurrentLineIndex
.   isNewCommentStart
.   removeTab
.   expandTab
.   insertTemplate
.   blockAction
.   getNodeContents
.   addKeyCombinationForActionToAllEditors
.   addKeyCombinationForAction
.   getActionByName
.   removeKeyStrokeBinding
.   save
.   load
.   convertSwingBindingToFX
.   userAction
.   closingBrace
.   addSelectionToClipboard
.   doIndent
.   doDeIndent
.   doBlockIndent
.   doBlockDeIndent
.   convertTabsToSpaces
.   createActionTable
.   getAllActions
.   getKeyStrokesForAction
.   makeAllUnavailableExcept
.   makeAllAvailable
.   Category
.   toString
.   setDefaultKeyBindings
.   action
.   actionPerformed
.   contextSensitiveAction
.   actionPerformed
.   - LineAction
.   apply

top, use, map, abstract class MoeAbstractAction

.   MoeAbstractAction
.   getCategory
.   getEditor
.   saveAction
.   printAction
.   closeAction
.   undoAction
.   redoAction
.   commentBlockAction
.   uncommentBlockAction
.   indentBlockAction
.   deindentBlockAction
.   autoIndentAction
.   insertMethodAction
.   addJavadocAction
.   indentAction
.   deIndentAction
.   getTextComponent
.   newLineAction
.   cutAction
.   copyAction
.   pasteAction
.   copyLineAction
.   cutLineAction
.   increaseFontAction
.   decreaseFontAction
.   resetFontAction
.   cutEndOfLineAction
.   cutWordAction
.   contentAssistAction
.   cutEndOfWordAction

top, use, map, abstract class MoeActionWithOrWithoutSelection

.   MoeActionWithOrWithoutSelection
.   moveCaret

top, use, map, abstract class NextWordAction

.   NextWordAction
.   actionPerformed

top, use, map, abstract class PrevWordAction

.   PrevWordAction
.   actionPerformed

top, use, map, abstract class EndWordAction

.   EndWordAction
.   actionPerformed

top, use, map, abstract class BeginWordAction

.   BeginWordAction
.   actionPerformed

top, use, map, class BeginLineAction

.   BeginLineAction
.   actionPerformed

top, use, map, class EndLineAction

.   EndLineAction
.   actionPerformed
.   deleteWordAction
.   selectWordAction
.   findAction
.   findNextAction
.   findPrevAction
.   replaceAction
.   compileOrNextErrorAction
.   toggleInterfaceAction
.   toggleBreakPointAction
.   keyBindingsAction
.   preferencesAction
.   goToLineAction

top, use, map, class CommentLineAction

.   apply

top, use, map, class UncommentLineAction

.   apply

top, use, map, class IndentLineAction

.   apply

top, use, map, class DeindentLineAction

.   apply




2271 neLoCode + 96 LoComm