/*******************************************************************************
* Copyright (c) 2011 Sebastian Benz.
* 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
*
* Contributors:
* Sebastian Benz - initial API and implementation
******************************************************************************/
package de.sebastianbenz.task.ui.editor;
import static de.sebastianbenz.task.ui.editor.TaskTokenTypeToPartitionTypeMapper.CODE_PARTITION;
import static org.eclipse.jface.text.IDocument.DEFAULT_CONTENT_TYPE;
import static org.eclipse.xtext.ui.editor.model.TerminalsTokenTypeToPartitionMapper.STRING_LITERAL_PARTITION;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IRegion;
import org.eclipse.xtext.ui.editor.autoedit.AbstractEditStrategy;
import org.eclipse.xtext.ui.editor.autoedit.DefaultAutoEditStrategyProvider;
import org.eclipse.xtext.ui.editor.autoedit.ShortCutEditStrategy;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class AutoEditStrategyProvider extends DefaultAutoEditStrategyProvider {
protected static class LineBreakInserter extends AbstractEditStrategy {
private final IAutoEditStrategy defaultStrategy;
protected LineBreakInserter(IAutoEditStrategy defaultStrategy) {
this.defaultStrategy = defaultStrategy;
}
@Override
protected void internalCustomizeDocumentCommand(IDocument document,
DocumentCommand command) throws BadLocationException {
if (!isLineBreak(document, command)) {
return;
}
String line = currentLine(document, command);
if (isEmpty(line)) {
return;
}
defaultStrategy.customizeDocumentCommand(document, command);
if(!atEndOfLineInput(document, command.offset)){
return;
}else if(isEmptyTask(line)){
removeSingleHyphen(document, command, line);
}else if (isNewProject(line)) {
command.text = command.text + '\t';
} else if (isTask(line)) {
command.text = command.text + "- ";
}
}
private void removeSingleHyphen(IDocument document,
DocumentCommand command, String line)
throws BadLocationException {
int lineNr = document.getLineOfOffset(command.offset);
int lineOffset = document.getLineOffset(lineNr);
int index = line.indexOf("-");
int hyphenPosition = index + lineOffset;
int toDelete = line.length() - index;
document.replace(hyphenPosition, toDelete, "");
command.offset = command.offset - toDelete;
}
protected boolean atEndOfLineInput(IDocument document, int offset) throws BadLocationException {
IRegion line = document.getLineInformation(document.getLineOfOffset(offset));
return document.get(offset, line.getOffset() + line.getLength() - offset).trim().length() == 0;
}
private boolean isEmptyTask(String line) {
return "-".equals(line.trim());
}
private boolean isEmpty(String line) {
return line.length() == 0;
}
private boolean isTask(String line) {
return line.trim().startsWith("- ");
}
private boolean isNewProject(String line) {
return line.endsWith(":");
}
private String currentLine(IDocument document, DocumentCommand command)
throws BadLocationException {
IRegion region = activeRegion(document, command);
String line = document.get(region.getOffset(), region.getLength());
return line;
}
private IRegion activeRegion(IDocument document, DocumentCommand command)
throws BadLocationException {
return document
.getLineInformationOfOffset(command.offset);
}
private boolean isLineBreak(IDocument document, DocumentCommand command) {
return command.text.equals(((IDocumentExtension4) document)
.getDefaultLineDelimiter()) && command.length == 0;
}
}
@Inject
protected Provider<ShortCutEditStrategy> shortCut;
private String[] defaultContentTypes = {DEFAULT_CONTENT_TYPE, CODE_PARTITION};
@Override
protected void configure(IEditStrategyAcceptor acceptor) {
super.configure(acceptor);
configureAngleBracket(acceptor);
acceptor.accept(partitionInsert.newInstance("'''", "'''"),DEFAULT_CONTENT_TYPE);
}
private void configureAngleBracket(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(singleLineTerminals.newInstance("<", ">"),contentType);
}
}
@Override
protected void configureIndentationEditStrategy(
IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(
new LineBreakInserter(defaultIndentLineAutoEditStrategy.get()),
contentType);
}
}
protected void configureCompoundBracesBlocks(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(compoundMultiLineTerminals.newInstanceFor("{", "}").and("[", "]").and("(", ")"), contentType);
}
}
protected void configureMultilineComments(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(singleLineTerminals.newInstance("/*", " */"), contentType);
acceptor.accept(multiLineTerminals.newInstance("/*"," * ", " */"), contentType);
}
}
protected void configureCurlyBracesBlock(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(singleLineTerminals.newInstance("{", "}"),contentType);
}
}
protected void configureSquareBrackets(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(singleLineTerminals.newInstance("[", "]"),contentType);
}
}
protected void configureParenthesis(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
acceptor.accept(singleLineTerminals.newInstance("(", ")"),contentType);
}
}
protected void configureStringLiteral(IEditStrategyAcceptor acceptor) {
for(String contentType : defaultContentTypes ){
// The following two are registered for the default content type, because on deletion
// the command.offset is cursor-1, which is outside the partition of terminals.length = 1.
// How crude is that?
// Note that in case you have two string literals following each other directly, the deletion strategy wouldn't apply.
// One could add the same strategy for the STRING partition in addition to solve this
acceptor.accept(partitionDeletion.newInstance("\"","\""),contentType);
acceptor.accept(partitionDeletion.newInstance("'","'"),contentType);
}
acceptor.accept(partitionInsert.newInstance("\"","\""),STRING_LITERAL_PARTITION);
acceptor.accept(partitionInsert.newInstance("'","'"),STRING_LITERAL_PARTITION);
acceptor.accept(partitionEndSkippingEditStrategy.get(),STRING_LITERAL_PARTITION);
}
}