/**
* 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 static com.mulgasoft.emacsplus.minibuffer.SearchMinibuffer.REGEX_BOL;
import static com.mulgasoft.emacsplus.minibuffer.SearchMinibuffer.REGEX_BOL_HACK;
import static com.mulgasoft.emacsplus.minibuffer.SearchMinibuffer.REGEX_EOL;
import static com.mulgasoft.emacsplus.minibuffer.SearchMinibuffer.REGEX_EOL_HACK;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.texteditor.ITextEditor;
import com.mulgasoft.emacsplus.EmacsPlusActivator;
import com.mulgasoft.emacsplus.minibuffer.ReadRegexpMinibuffer;
/**
* Implements: count-matches
*
* Print and return number of matches following point for REGEXP
*
* As in Emacs, this handler starts looking for the next match from the end of the previous match;
* therefore, it ignores matches that overlap a previously found match.
*
* @author Mark Feber - initial API and implementation
*/
public class CountMatchesHandler extends MinibufferExecHandler implements INonEditingCommand {
private final static String OCCURRENCE = "Count_Match_Occurrence"; //$NON-NLS-1$
private final static String OCCURRENCES = EmacsPlusActivator.getResourceString("Count_Match_Occurrences"); //$NON-NLS-1$
/**
* @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#transform(ITextEditor, IDocument, ITextSelection, ExecutionEvent)
*/
@Override
protected int transform(ITextEditor editor, IDocument document, ITextSelection currentSelection, ExecutionEvent event)
throws BadLocationException {
return bufferTransform(new ReadRegexpMinibuffer(this), editor, event);
}
/**
* @see com.mulgasoft.emacsplus.commands.MinibufferExecHandler#doExecuteResult(org.eclipse.ui.texteditor.ITextEditor, java.lang.Object)
*/
@Override
protected boolean doExecuteResult(ITextEditor editor, Object minibufferResult) {
String msg = null;
boolean isError = false;
if (minibufferResult == null) {
msg = String.format(msg, 0);
} else {
try {
int count = 1; // cursor is already at the end of the first find
int begin = getCursorOffset(editor);
String searchStr = getSearchStr(minibufferResult);
FindReplaceDocumentAdapter fda = new FindReplaceDocumentAdapter(getThisDocument(editor));
IRegion found = null;
while ((found = getNextMatch(fda,begin,searchStr)) != null) {
++count;
int tmp = found.getOffset() + found.getLength();
if (tmp != begin) {
begin = tmp;
} else {
// offset should always move after match, but just in case
msg = "Infinite loop on = " + searchStr; //$NON-NLS-1$
isError = true;
break;
}
}
if (msg == null) {
msg = ((count == 1) ? OCCURRENCE : String.format(OCCURRENCES, count));
}
} catch (BadLocationException e) {
// shouldn't happen, but alert user if it does
msg = BAD_LOCATION_ERROR;
isError = true;
}
}
asyncShowMessage(editor, msg, isError);
return true;
}
private String getSearchStr(Object minibufferResult) {
String result = (String)minibufferResult;
// workaround Eclipse bug in FindReplaceDocumentAdapter
if (REGEX_BOL.equals(result)) {
result = REGEX_BOL_HACK;
} else if (REGEX_EOL.equals(result)) {
result = REGEX_EOL_HACK;
}
return result;
}
/**
* Find the next match in the document and return its region
*
* @param documentAdapter
* @param begin the search here
* @param regExp
* @return the found region or null
* @throws BadLocationException
*/
private IRegion getNextMatch(FindReplaceDocumentAdapter fda, int begin, String regExp) throws BadLocationException {
return fda.find(begin, regExp, true, false, false, true);
}
}