/**
* 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.core.commands.ExecutionException;
import org.eclipse.core.commands.common.CommandException;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Position;
import org.eclipse.swt.SWT;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditor;
import com.mulgasoft.emacsplus.EmacsPlusUtils;
import com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds;
import com.mulgasoft.emacsplus.execute.ColumnSupport;
/**
* Implement a generic indent handler: indent-for-tab
* Looks for an indent command bound to ^I and invokes it.
* If no command found or if called with ^U rigidly indent with tab (or tabWidth spaces)
*
* @author Mark Feber - initial API and implementation
*/
public class IndentForTabHandler extends EmacsPlusCmdHandler {
// TODO: this is the usual case for an emacs binding, but could be different
// so potentially set these up as preferences
private static char indentKey = 'i';
private static int indentMode = (EmacsPlusUtils.isMac() ? SWT.COMMAND : SWT.CTRL);
private static String indentExp = ".*[Ii]ndent.*"; //$NON-NLS-1$
@Override
protected boolean isLooping() {
return false;
}
@Override
protected boolean undoProtect() {
// force undo wrapping when transform is called
return true;
}
/**
* @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#execute(org.eclipse.core.commands.ExecutionEvent)
*/
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Object result = null;
ITextEditor editor = getTextEditor(event);
extractUniversalCount(event); // side effect sets up isUniversalPresent
Binding indent = null;
// ^U or no binding results in transform behavior
if (!isUniversalPresent() && (indent = getBinding(editor,indentKey,indentMode)) != null) {
String id = indent.getParameterizedCommand().getId();
if (id != null && id.matches(indentExp) && !id.equals(IEmacsPlusCommandDefinitionIds.INDENT_FOR_TAB)) {
try {
result = executeCommand(id, null, editor);
} catch (CommandException e) {
}
} else {
super.execute(event);
}
} else {
super.execute(event);
}
return result;
}
/**
* Insert tab/spaces at selection offset and each subsequent line origin
*
* @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 {
// if we're here, either ^U or no relevant ^I
ColumnSupport cs = new ColumnSupport(document,editor);
String tab = cs.getSpaces(0,cs.getTabWidth());
int off = currentSelection.getOffset();
Position coff = new Position(getCursorOffset(editor,currentSelection),0);
try {
document.addPosition(coff);
int begin = document.getLineOfOffset(off);
int end = document.getLineOfOffset(off+ currentSelection.getLength());
if (begin != end) {
while (++begin <= end) {
document.replace(document.getLineOffset(begin), 0, tab);
}
}
document.replace(off, 0, tab);
} finally {
document.removePosition(coff);
}
return coff.offset;
}
/**
* Return the exact binding if it exists and is enabled, else null
*
* @param editor
* @param keyCode
* @param mode
* @return binding if it exists and is enabled, else null
*/
private Binding getBinding(ITextEditor editor, char keyCode, int mode) {
Binding result = null;
// ensure key is upper case
IBindingService bindingService = (IBindingService) editor.getSite().getService(IBindingService.class);
KeyStroke key = KeyStroke.getInstance(mode, Character.toUpperCase(keyCode));
result = bindingService.getPerfectMatch(KeySequence.getInstance(key));
if (result != null && !result.getParameterizedCommand().getCommand().isEnabled()) {
result = null;
}
return result;
}
}