package org.jedit.keymap; import java.lang.Character; import java.lang.StringBuffer; import org.gjt.sp.jedit.Buffer; import org.gjt.sp.jedit.EditAction; import org.gjt.sp.jedit.Registers; import org.gjt.sp.jedit.gui.HistoryModel; import org.gjt.sp.jedit.gui.InputHandler; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.textarea.Selection; import org.gjt.sp.jedit.textarea.TextArea; /** Emacs Macro utility functions. These functions are based on EmacsUtil.bsh from the Emacs macros by Brian M. Clapper. Rewritten in Java by Alan Ezust in 2013. */ public class EmacsUtil { Buffer buffer; TextArea textArea; public EmacsUtil() { buffer = jEdit.getActiveView().getBuffer(); textArea = jEdit.getActiveView().getTextArea(); } public void emacsKillLine() { boolean lastActionWasThis = repeatingSameMacro ("Emacs/Emacs_Kill_Line"); int caret = textArea.getCaretPosition(); int caretLine = textArea.getCaretLine(); int lineStart = textArea.getLineStartOffset (caretLine); int lineEnd = textArea.getLineEndOffset (caretLine); // If we're at the end of line (ignoring any trailing white space), // then kill the newline, too. int caret2 = caret + 1; while (caret2 < lineEnd) { char ch = charAt (caret2); if (! Character.isWhitespace (ch)) break; caret2++; } String deletedText = null; Selection selection = null; if (caret2 == lineEnd) { // We're at the end of the line. Join this line and the next line--but // do it with a true delete, not with textArea.joinLines(), to // emulate emacs better. if (caretLine != textArea.getLastPhysicalLine()) selection = new Selection.Range (caret, caret2); } else { // Simple delete to end of line. selection = new Selection.Range (caret, lineEnd - 1); //textArea.deleteToEndOfLine(); } if (selection != null) { textArea.setSelection (selection); deletedText = textArea.getSelectedText(); textArea.replaceSelection (""); textArea.removeFromSelection (selection); if (lastActionWasThis) { String clipboard = getClipboard(); if (clipboard == null) clipboard = ""; setClipboard (clipboard + deletedText); } else { setClipboard (deletedText); } } } public boolean repeatingSameMacro (String macroName) { InputHandler ih = jEdit.getActiveView().getInputHandler(); EditAction lastAction = ih.getLastAction(); int lastActionCount = ih.getLastActionCount(); // When called from within a macro, the last action will be that macro. // But, if the last action count is greater than 1, then it's a repeat. boolean repeat = false; if ( (lastAction.getName().equals (macroName)) && (lastActionCount > 1) ) repeat = true; return repeat; } public String lineAt (int i) { StringBuilder sb = new StringBuilder(); while (! atEndOfBuffer (i)) { char c = charAt (i); sb.append(c); if (c == '\n') break; } return sb.toString(); } public char charAt (int i) { if (i >= buffer.getLength()) return 0; return buffer.getText (i, 1).charAt (0); } public char charAtCaret() { int caret = textArea.getCaretPosition(); return (atEndOfBuffer() ? '\0' : buffer.getText (caret, 1).charAt (0)); } public boolean atEndOfBuffer() { TextArea textArea = jEdit.getActiveView().getTextArea(); return atEndOfBuffer (textArea.getCaretPosition()); } public boolean atEndOfBuffer (int caret) { return (caret >= buffer.getLength()); } public int eatNonAlphanums() { boolean eat = true; while (eat) { char ch = charAtCaret(); if (ch == '\n') { textArea.goToNextLine (false); textArea.goToStartOfLine (false); } else { if (Character.isLetterOrDigit (ch)) eat = false; else textArea.goToNextCharacter (false); } } return textArea.getCaretPosition(); } public int eatWhitespace() { boolean eat = true; while (eat) { char ch = charAtCaret(); if (ch == '\n') { textArea.goToNextLine (false); textArea.goToStartOfLine (false); } else if (Character.isWhitespace (ch)) { textArea.goToNextCharacter (false); } else { eat = false; } } return textArea.getCaretPosition(); } public int getCardinalProperty (String name, int defaultValue) { int result = jEdit.getIntegerProperty (name, defaultValue); if (result <= 0) result = defaultValue; return result; } public String makeBufferPropertyName (String prefix) { return makeBufferPropertyName (buffer, prefix); } public String makeBufferPropertyName (Buffer theBuffer, String prefix) { StringBuilder propName = new StringBuilder(prefix); // Convert any Windows-style file separators to Unix ones, since // backslashes are special characters in properties files. String fileSep = System.getProperty ("file.separator"); String bufName; if (! fileSep.equals ("/")) { // Backslash is also special in regular expressions. Since, in theory, // the file separator could be *anything*, we check explicitly for // backslash here. if (fileSep.equals ("\\")) fileSep = fileSep + "\\"; bufName = theBuffer.getPath().replaceAll (fileSep, "/"); } else { bufName = theBuffer.getPath(); } propName.append (bufName); return propName.toString(); } public int getDefaultWrap() { return getCardinalProperty ("buffer.maxLineLen", 79); } public int getMark (Buffer buffer) { String propName = makeBufferPropertyName ("emacs.mark"); int mark = getCardinalProperty (propName, -1); if (mark != -1) { if (mark >= buffer.getLength()) mark = buffer.getLength() - 1; } return mark; } public void setMark (Buffer buffer, int pos) { String propName = makeBufferPropertyName (buffer, "emacs.mark"); jEdit.setTemporaryProperty (propName, String.valueOf (pos)); } public void beep() { jEdit.getActiveView().getToolkit().beep(); } public Selection getKillRegion() { // If there's a selection, use it instead. int caret = textArea.getCaretPosition(); Selection selection = textArea.getSelectionAtOffset (caret); if (selection == null) { int mark = getMark (buffer); if (mark == -1) { beep(); return null; } selection = new Selection.Range (Math.min (caret, mark), Math.max (caret, mark)); textArea.setSelection (selection); } return selection; } public String getClipboard() { return Registers.getRegister ('$').toString(); } public void setClipboard (String string) { Registers.setRegister ('$', string); } public void setClipboard (Selection selection) { TextArea textArea = jEdit.getActiveView().getTextArea(); setClipboard (textArea.getSelectedText (selection)); } public void addToClipboardAndHistory (String string) { // The special register '$' is the clipboard. setClipboard (string); // Save the text in the history, too. HistoryModel.getModel ("clipboard").addItem (string); } public void addToClipboardAndHistory (Selection selection) { addToClipboardAndHistory (textArea.getSelectedText (selection)); } public int findEndOfSentence() { int caret = textArea.getCaretPosition(); for (;;) { if (atEndOfBuffer (caret)) break; char ch = charAt (caret); if (ch == '.') { if (Character.isWhitespace (charAt (caret + 1))) { caret++; break; } } caret++; } return caret; } public int findBeginningOfSentence() { int caret = textArea.getCaretPosition() - 1; if (charAt (caret) == '.') caret--; for (;;) { if (caret <= 0) break; char ch = charAt (caret); if (ch == '.') { if (Character.isWhitespace (charAt (caret + 1))) { caret++; break; } } else if (Character.isUpperCase (ch)) { caret--; if (caret <= 0) break; if (Character.isWhitespace (charAt (caret))) break; } caret--; } return caret; } }