/** * 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 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.swt.widgets.Control; import org.eclipse.ui.texteditor.ITextEditor; import com.mulgasoft.emacsplus.EmacsPlusActivator; import com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds; import com.mulgasoft.emacsplus.KillRing; import com.mulgasoft.emacsplus.MarkUtils; import com.mulgasoft.emacsplus.minibuffer.ZapMinibuffer; /** * Implement: zap-to-char * * The command `M-z' (`zap-to-char') combines killing with searching: * it reads a character and kills from point up to (and including) the * next occurrence of that character in the buffer. A numeric argument * acts as a repeat count; a negative argument means to search backward * and kill text before point. * * @author Mark Feber - initial API and implementation */ public class ZapToCharHandler extends MinibufferExecHandler { private static final String ZAP_PREFIX = EmacsPlusActivator.getResourceString("Zap_ToChar"); //$NON-NLS-1$ // Zap to char: private static final String ZAP_FAIL= EmacsPlusActivator.getResourceString("Zap_Fail"); //$NON-NLS-1$ // Zap to char: private static final String ZAP_FAIL_COUNT= EmacsPlusActivator.getResourceString("Zap_FailCount"); //$NON-NLS-1$ // Zap to char: /** * @see com.mulgasoft.emacsplus.commands.MinibufferHandler#getMinibufferPrefix() */ public String getMinibufferPrefix() { return ZAP_PREFIX; } /** * Instead of looping entire command, just jump the appropriate number of searches * * @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#isLooping() */ @Override protected boolean isLooping() { return false; } /** * @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#transform(org.eclipse.ui.texteditor.ITextEditor, org.eclipse.jface.text.IDocument, org.eclipse.jface.text.ITextSelection, org.eclipse.core.commands.ExecutionEvent) */ @Override protected int transform(ITextEditor editor, IDocument document, ITextSelection currentSelection, ExecutionEvent event) throws BadLocationException { return bufferTransform(new ZapMinibuffer(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 searchStr = (String)minibufferResult; int count = getUniversalCount(); if (searchStr.length() > 0) { int modelOffset = MarkUtils.getCursorOffset(editor); boolean forward = (count < 0 ? false : true); int searchOffset = modelOffset + (forward ? 0 : -1); // separate search from result offset because of a bug in FRDA. int resultOffset = modelOffset; // Also forces an extra bounds check here. See comment below if (!(searchOffset <0)) { // use widget to avoid unpleasant scrolling side effects of IRewriteTarget Control widget = MarkUtils.getTextWidget(editor); try { widget.setRedraw(false); // flag for kill ring KillRing.getInstance().setKill(IEmacsPlusCommandDefinitionIds.ZAP_TO_CHAR,forward); IDocument doc = getThisDocument(editor); FindReplaceDocumentAdapter frda = new FindReplaceDocumentAdapter(doc); int counter = Math.abs(count); for (int i = 0; i < counter; i++) { IRegion reg = frda.find(searchOffset, searchStr, forward, true, false, false); if (reg != null) { resultOffset = reg.getOffset() + (forward ? reg.getLength() : 0); // There's a ?bug? in org.eclipse.jface.text.FindReplaceDocumentAdapter that causes improper overlap on reverse search: // while (found && fFindReplaceMatcher.start() + fFindReplaceMatcher.group().length() <= fFindReplaceMatchOffset + 1) searchOffset = (forward ? resultOffset : resultOffset -1); } else { // back to original cursor offset on failure selectAndReveal(editor, modelOffset, modelOffset); fail(editor, searchStr, ++i, counter); return true; } } int length = Math.abs(resultOffset - modelOffset); doc.replace((forward ? modelOffset : resultOffset), length, EMPTY_STR); } catch (BadLocationException e) { e.printStackTrace(); } finally { KillRing.getInstance().setKill(null,false); widget.setRedraw(true); } } } else { fail(editor,searchStr); } return true; } private void fail(ITextEditor editor, String message, int index, int count) { this.showResultMessage(editor, String.format(ZAP_FAIL_COUNT, message, index, count), true); } private void fail(ITextEditor editor, String message) { this.showResultMessage(editor, String.format(ZAP_FAIL, message), true); } }