blob: 9ae77171464a18ac950be39498258a276762c6e7 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting.assist;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.statushandlers.StatusManager;
public abstract class CompletionProposalWithOverwrite
implements IAssistCompletionProposal, ICompletionProposalExtension4 {
private final AssistInvocationContext context;
/** The replacement offset. */
private final int replacementOffset;
// private StyleRange rememberedStyleRange;
private Annotation rememberedOverwriteAnnotation;
protected CompletionProposalWithOverwrite(final AssistInvocationContext context, final int startOffset) {
this.context= context;
this.replacementOffset= startOffset;
}
public AssistInvocationContext getInvocationContext() {
return this.context;
}
public final int getReplacementOffset() {
return this.replacementOffset;
}
protected int computeReplacementLength(final int replacementOffset, final Point selection, final int caretOffset, final boolean overwrite) throws BadLocationException {
final int end= Math.max(caretOffset, selection.x + selection.y);
return (end - replacementOffset);
}
private boolean isInOverwriteMode(final boolean toggle) {
return toggle;
}
protected abstract String getPluginId();
@Override
public final void selected(final ITextViewer viewer, final boolean smartToggle) {
if (isInOverwriteMode(smartToggle)) {
addOverwriteStyle();
} else {
repairPresentation();
}
}
@Override
public final void unselected(final ITextViewer viewer) {
repairPresentation();
}
/**
* not supported, use {@link #apply(ITextViewer, char, int, int)}
*/
@Override
public void apply(final IDocument document) {
}
/**
* {@inheritDoc}
*/
@Override
public final void apply(final ITextViewer viewer, final char trigger, final int stateMask, final int offset) {
assert (this.context.getSourceViewer() == viewer);
final boolean smartToggle= (stateMask & SWT.CTRL) != 0;
try {
final int replacementOffset= getReplacementOffset();
final int replacementLength= computeReplacementLength(replacementOffset, viewer.getSelectedRange(), offset, isInOverwriteMode(smartToggle));
if (validate(viewer.getDocument(), offset, null)) {
doApply(trigger, stateMask, offset, replacementOffset, replacementLength);
return;
}
}
catch (final BadLocationException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, getPluginId(), "Failed to apply completion proposal.", e));
}
Display.getCurrent().beep();
}
protected abstract void doApply(final char trigger, final int stateMask,
final int caretOffset, final int replacementOffset, final int replacementLength) throws BadLocationException;
protected void reinvokeAssist(final ITextViewer viewer) {
if (viewer instanceof ITextOperationTarget) {
final ITextOperationTarget target= (ITextOperationTarget) viewer;
Display.getCurrent().asyncExec(new Runnable() {
@Override
public void run() {
if (target.canDoOperation(ISourceViewer.CONTENTASSIST_PROPOSALS)) {
target.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
}
}
});
}
}
private void addOverwriteStyle() {
final SourceViewer viewer= this.context.getSourceViewer();
final StyledText text= viewer.getTextWidget();
if (text == null || text.isDisposed()) {
return;
}
final int widgetCaret= viewer.getTextWidget().getCaretOffset();
final int modelCaret= viewer.widgetOffset2ModelOffset(widgetCaret);
final int replacementOffset= getReplacementOffset();
int replacementLength;
try {
replacementLength= computeReplacementLength(replacementOffset, viewer.getSelectedRange(), modelCaret, true);
}
catch (final BadLocationException e) {
replacementLength= -1;
}
if (replacementLength < 0 || modelCaret >= replacementOffset + replacementLength) {
repairPresentation();
return;
}
final int offset= widgetCaret;
final int length= replacementOffset + replacementLength - modelCaret;
repairPresentation();
this.rememberedOverwriteAnnotation= new Annotation("org.eclipse.statet.ecommons.text.editorAnnotations.ContentAssistOverwrite", false, "");
viewer.getAnnotationModel().addAnnotation(this.rememberedOverwriteAnnotation, new Position(offset, length));
// final Color background= getOverwriteBackgroundColor();
// final Color foreground= getOverwriteForegroundColor();
// if (foreground == null && background == null) {
// repairPresentation();
// return;
// }
//
// final StyleRange currentStyle= text.getStyleRangeAtOffset(offset);
//
// final StyleRange style= new StyleRange(offset, length, foreground, background);
// if (currentStyle != null) {
// style.fontStyle= currentStyle.fontStyle;
// style.strikeout= currentStyle.strikeout;
// style.underline= currentStyle.underline;
// }
//
// repairPresentation();
// // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
// try {
// text.setStyleRange(style);
// this.rememberedStyleRange= style;
// }
// catch (final IllegalArgumentException x) {
// }
}
private void repairPresentation() {
final SourceViewer viewer= this.context.getSourceViewer();
if (this.rememberedOverwriteAnnotation != null) {
viewer.getAnnotationModel().removeAnnotation(this.rememberedOverwriteAnnotation);
this.rememberedOverwriteAnnotation= null;
}
// if (this.rememberedStyleRange != null) {
// final IRegion modelRange= viewer.widgetRange2ModelRange(new Region(this.rememberedStyleRange.start, this.rememberedStyleRange.length));
// if (modelRange != null) {
// viewer.invalidateTextPresentation(modelRange.getOffset(), modelRange.getLength());
// }
// this.rememberedStyleRange= null;
// }
}
}