/**
* Copyright (c) 2009, 2010 Mark Feber, MulgaSoft
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package com.mulgasoft.emacsplus.commands;
import java.text.BreakIterator;
import java.util.regex.Matcher;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Implements: backward-sexp. Move backward one s-expression
*
* @author Mark Feber - initial API and implementation
*/
public abstract class SexpBaseBackwardHandler extends SexpHandler implements IConsoleDispatch {
@Override
protected int getDirection() {
return BACKWARD;
}
/**
* @see com.mulgasoft.emacsplus.commands.EmacsPlusNoEditHandler#transform(ITextEditor, IDocument, ITextSelection, ExecutionEvent)
*/
protected int transform(ITextEditor editor, IDocument document, ITextSelection currentSelection, ExecutionEvent event)
throws BadLocationException {
ITextSelection selection = null;
try {
ITextSelection nSelection = currentSelection;
if (checkMark(editor, currentSelection.getOffset(),currentSelection.getLength())) {
// when expanding mark, always start from the cursor
nSelection = new TextSelection(document, getCursorOffset(editor,currentSelection), 0);
}
selection = getNextSexp(document, nSelection);
if (selection == null && !isUnbalanced()) {
// move to front
selection = new TextSelection(document, 0, 0);
}
} catch (BadLocationException e) {
selection = null;
}
if (selection == null){
unbalanced(editor,true);
throw new BadLocationException();
}
return endTransform(editor,selection.getOffset(),currentSelection,selection);
}
/**
* @see com.mulgasoft.emacsplus.commands.IConsoleDispatch#consoleDispatch(TextConsoleViewer, IConsoleView, ExecutionEvent)
*/
public Object consoleDispatch(TextConsoleViewer viewer, IConsoleView activePart, ExecutionEvent event) {
IDocument document = viewer.getDocument();
ITextSelection currentSelection = (ITextSelection)viewer.getSelectionProvider().getSelection();
ITextSelection selection = null;
try {
selection = getNextSexp(document, currentSelection);
if (selection == null) {
selection = currentSelection;
unbalanced(activePart,true);
return null;
} else {
return endTransform(viewer, selection.getOffset(), currentSelection, selection);
}
} catch (BadLocationException e) {
}
return null;
}
Matcher getDotMatcher() {
return dotBackMatcher;
}
/**
* When moving backwards, modify args to correct for directionality
*
* @see com.mulgasoft.emacsplus.commands.SexpHandler#checkDot(org.eclipse.jface.text.IDocument, int, int)
*/
int checkDot(IDocument doc, int start, int end) {
int result = end;
// reverse position args
if (isDot()) {
int check = super.checkDot(doc, end, start);
if (check != start) {
// position after '.'
result = ++check;
}
}
return result;
}
Matcher getUnderMatcher() {
return (isDot() ? underBackMatcher : underDotBackMatcher);
}
/**
* When moving backward, and we're consuming _'s, see if we're currently positioned by an _
*
* @param doc
* @param iter
* @param pos current word position
* @return new offset if word moves past any _'s, else pos
*/
int checkUnder(IDocument doc, BreakIterator iter, int pos) {
int result = pos;
try {
if (!isUnder()) {
char c = doc.getChar(result);
IRegion lineInfo = doc.getLineInformationOfOffset(pos);
if (c == '_') {
// we've backed over an _
Matcher matcher = getUnderMatcher();
// get text including the trailing _
matcher.reset(doc.get(lineInfo.getOffset(), pos+1 - lineInfo.getOffset()));
if (matcher.find()) {
result = lineInfo.getOffset() + matcher.start(1);
}
} else if (result > lineInfo.getOffset()){
// check preceding character
c = doc.getChar(result-1);
if (c == '_' || (!isDot() && c == '.')) {
// we've been stopped by a preceding _ or .
result = checkUnder(doc,iter,iter.previous());
}
}
}
} catch (BadLocationException e) {
}
return result;
}
/**
* @see com.mulgasoft.emacsplus.commands.SexpHandler#getNextPosition(org.eclipse.jface.text.IDocument, java.text.BreakIterator, int)
*/
@Override
protected int getNextPosition(IDocument document, BreakIterator iter, int pos) {
int result = iter.preceding(pos);
if (result != BreakIterator.DONE) {
result = checkUnder(document,iter,result);
result = checkDot(document,pos,result);
}
return result;
}
/**
* @see com.mulgasoft.emacsplus.commands.SexpHandler#getNextPosition(org.eclipse.jface.text.IDocument, java.text.BreakIterator)
*/
@Override
protected int getNextPosition(IDocument document, BreakIterator iter) {
int pos = iter.current();
int result = iter.previous();
if (result != BreakIterator.DONE) {
result = checkUnder(document,iter,result);
result = checkDot(document,pos,result);
}
return result;
}
/**
* @see com.mulgasoft.emacsplus.commands.SexpHandler#isBracket(char)
*/
@Override
protected boolean isBracket(char c){
return isBracket(c,CLOSE);
}
/**
* @see com.mulgasoft.emacsplus.commands.SexpHandler#isUnbalanced(char, int)
*/
protected boolean isUnbalanced(char c,int updown) {
boolean result = false;
if (GNU_SEXP && updown != UP) {
result = isBracket(c,OPEN);
}
setUnbalanced(result);
return result;
}
@Override
protected int getBracketPosition(IDocument document, int pos) {
int result = -1;
IRegion reg = getBracketMatch(document, pos);
if (reg != null){
result = reg.getOffset();
}
return result;
}
/**
* @see com.mulgasoft.emacsplus.commands.SexpHandler#getTransSexp(org.eclipse.jface.text.IDocument, int, boolean)
*/
@Override
ITextSelection getTransSexp(IDocument document, int pos, boolean wordp)
throws BadLocationException {
ITextSelection result = null;
SexpHandler forsexp = new SexpForwardHandler();
result = getNextSexp(document, new TextSelection(document, pos, 0), wordp);
if (result != null) {
result = new TextSelection(document, result.getOffset(), result.getText().trim().length());
String text;
while ((text = result.getText()).length() == 1 && !Character.isJavaIdentifierPart(text.charAt(0))) {
result = getNextSexp(document, new TextSelection(document, result.getOffset() - 1, 0), wordp);
result = forsexp.getNextSexp(document, result, wordp);
result = new TextSelection(document, result.getOffset(), result.getText().trim().length());
text = result.getText();
}
// transpose around . if it is a break character
if (isDot() && text.charAt(result.getLength()-1) == '.') {
result = new TextSelection(document, result.getOffset(), result.getLength()-1);
}
}
return result;
}
}