/*=============================================================================# # Copyright (c) 2008-2016 Stephan Wahlbrink (WalWare.de) and others. # 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.ecommons.ltk.ui.sourceediting.assist; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedModeUI; import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy; import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.link.LinkedPositionGroup; import org.eclipse.swt.SWT; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; import de.walware.ecommons.text.ui.DefaultBrowserInformationInput; import de.walware.ecommons.ltk.internal.ui.LTKUIPlugin; import de.walware.ecommons.ltk.ui.LTKUI; import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditor; public abstract class LinkedNamesAssistProposal implements IAssistCompletionProposal, ICompletionProposalExtension5 { /** * An exit policy that skips Backspace and Delete at the beginning and at the end * of a linked position, respectively. */ public static class DeleteBlockingExitPolicy implements IExitPolicy { private final IDocument document; public DeleteBlockingExitPolicy(final IDocument document) { this.document= document; } @Override public ExitFlags doExit(final LinkedModeModel model, final VerifyEvent event, final int offset, final int length) { switch (event.character) { case SWT.BS: { //skip backspace at beginning of linked position final LinkedPosition position= model.findPosition(new LinkedPosition( this.document, offset, 0, LinkedPositionGroup.NO_STOP)); if (position != null && offset <= position.getOffset() && length == 0) { event.doit= false; } return null; } case SWT.DEL: { //skip delete at end of linked position final LinkedPosition position= model.findPosition(new LinkedPosition( this.document, offset, 0, LinkedPositionGroup.NO_STOP)); if (position != null && offset >= position.getOffset()+position.getLength() && length == 0) { event.doit= false; } return null; } } return null; } } private final AssistInvocationContext context; private String label; private String description; private int relevance; private String valueSuggestion; public LinkedNamesAssistProposal(final AssistInvocationContext invocationContext) { this.context= invocationContext; } protected void init(final String label, final String description, final int relevance) { this.label= label; this.description= description; this.relevance= relevance; } /** * {@inheritDoc} */ @Override public void selected(final ITextViewer textViewer, final boolean smartToggle) { } /** * {@inheritDoc} */ @Override public void unselected(final ITextViewer textViewer) { } /** * {@inheritDoc} */ @Override public boolean validate(final IDocument document, final int offset, final DocumentEvent event) { return false; } /** * {@inheritDoc} */ @Override public void apply(final ITextViewer viewer, final char trigger, final int stateMask, final int offset) { try { Point seletion= viewer.getSelectedRange(); final IDocument document= viewer.getDocument(); final LinkedModeModel model= new LinkedModeModel(); final LinkedPositionGroup group= new LinkedPositionGroup(); collectPositions(document, group); if (group.isEmpty()) { return; } model.addGroup(group); model.forceInstall(); { final ISourceEditor editor= this.context.getEditor(); if (editor != null && editor.getTextEditToolSynchronizer() != null) { editor.getTextEditToolSynchronizer().install(model); } } final LinkedModeUI ui= new EditorLinkedModeUI(model, viewer); ui.setExitPolicy(new DeleteBlockingExitPolicy(document)); ui.setExitPosition(viewer, offset, 0, LinkedPositionGroup.NO_STOP); ui.enter(); if (this.valueSuggestion != null) { final Position position= group.getPositions()[0]; document.replace(position.getOffset(), position.getLength(), this.valueSuggestion); seletion= new Point(position.getOffset(), this.valueSuggestion.length()); } viewer.setSelectedRange(seletion.x, seletion.y); // by default full word is selected, restore original selection } catch (final BadLocationException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, LTKUIPlugin.PLUGIN_ID, -1, "Error initializing linked rename.", e)); //$NON-NLS-1$ } } protected abstract void collectPositions(final IDocument document, final LinkedPositionGroup group) throws BadLocationException; protected int addPosition(final LinkedPositionGroup group, final IDocument document, final Position position, final int idx) throws BadLocationException { if (position != null) { group.addPosition(new LinkedPosition(document, position.getOffset(), position.getLength(), idx)); return idx+1; } return idx; } @Override public void apply(final IDocument document) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public Point getSelection(final IDocument document) { return null; } @Override public int getRelevance() { return this.relevance; } @Override public String getSortingString() { return this.label; } /** * {@inheritDoc} */ @Override public String getDisplayString() { return this.label; } /** * {@inheritDoc} */ @Override public Image getImage() { return LTKUI.getImages().get(LTKUI.OBJ_TEXT_LINKEDRENAME_IMAGE_ID); } /** * {@inheritDoc} */ @Override public String getAdditionalProposalInfo() { return this.description; } /** * {@inheritDoc} */ @Override public Object getAdditionalProposalInfo(final IProgressMonitor monitor) { return new DefaultBrowserInformationInput(null, getDisplayString(), this.description, DefaultBrowserInformationInput.FORMAT_TEXT_INPUT); } /** * {@inheritDoc} */ @Override public IContextInformation getContextInformation() { return null; } }