/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.internal.text.completion; import com.google.dart.tools.core.completion.CompletionProposal; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.internal.text.dart.DartTextMessages; import com.google.dart.tools.ui.internal.text.editor.DartEditor; import com.google.dart.tools.ui.internal.text.editor.EditorHighlightingSynchronizer; import com.google.dart.tools.ui.text.dart.DartContentAssistInvocationContext; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; 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.LinkedPositionGroup; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; import java.util.ArrayList; import java.util.List; /** * A method proposal with filled in argument names. */ public final class FilledArgumentNamesMethodProposal extends DartMethodCompletionProposal { private IRegion fSelectedRegion; // initialized by apply() private int[] fArgumentOffsets; private int[] fArgumentLengths; public FilledArgumentNamesMethodProposal(CompletionProposal proposal, DartContentAssistInvocationContext context) { super(proposal, context); } @Override public void apply(final IDocument document, char trigger, int offset) { super.apply(document, trigger, offset); int baseOffset = getReplacementOffset(); String replacement = getReplacementString(); ITextViewer textViewer = getTextViewer(); if (fArgumentOffsets != null && fArgumentOffsets.length != 0 && textViewer != null) { try { OptionalArgumentModel model = new OptionalArgumentModel(); buildLinkedModeModel(model, document, baseOffset); model.forceInstall(); DartEditor editor = getDartEditor(); if (editor != null) { model.addLinkingListener(new EditorHighlightingSynchronizer(editor)); } LinkedModeUI ui = new EditorLinkedModeUI(model, textViewer); ui.setExitPosition(textViewer, baseOffset + replacement.length(), 0, Integer.MAX_VALUE); model.exitPositionUpdater(ui, textViewer, baseOffset + replacement.length()); ui.setExitPolicy(new ExitPolicy(')', document) { @Override public ExitFlags doExit(LinkedModeModel environment, VerifyEvent event, int offset, int length) { maybeRewriteCommaWithTab(document, event, offset); return super.doExit(environment, event, offset, length); } }); ui.setDoContextInfo(true); ui.setCyclingMode(LinkedModeUI.CYCLE_WHEN_NO_PARENT); ui.enter(); fSelectedRegion = ui.getSelectedRegion(); } catch (BadLocationException e) { DartToolsPlugin.log(e); openErrorDialog(e); } } else { fSelectedRegion = new Region(baseOffset + replacement.length(), 0); } } @Override public Point getSelection(IDocument document) { if (fSelectedRegion == null) { return new Point(getReplacementOffset(), 0); } return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength()); } protected void buildLinkedModeModel(OptionalArgumentModel model, IDocument document, int baseOffset) throws BadLocationException { List<OptionalArgumentPosition> positions = new ArrayList<OptionalArgumentPosition>(); boolean hasNamed = getProposal().hasNamedParameters(); int positionalCount = getProposal().getPositionalParameterCount(); for (int i = 0; i != fArgumentOffsets.length; i++) { LinkedPositionGroup group = new LinkedPositionGroup(); OptionalArgumentPosition pos; pos = new OptionalArgumentPosition( document, baseOffset + fArgumentOffsets[i], fArgumentLengths[i], LinkedPositionGroup.NO_STOP); boolean isRequired = i < positionalCount; pos.setIsRequired(isRequired); if (!isRequired && hasNamed) { pos.resetNameStart(); } positions.add(pos); group.addPosition(pos); model.setHasNamed(hasNamed); model.addGroup(group); } model.setPositions(positions, document); } @Override protected String computeReplacementString() { if (!hasParameters() || !hasArgumentList()) { return super.computeReplacementString(); } StringBuffer buffer = new StringBuffer(); if (fProposal.getKind() != CompletionProposal.ARGUMENT_LIST) { appendMethodNameReplacement(buffer); } int positionalCount = fProposal.getPositionalParameterCount(); boolean hasNamed = fProposal.hasNamedParameters(); char[][] parameterNames = fProposal.findParameterNames(null); int count = parameterNames.length; if (fProposal.getKind() != CompletionProposal.ARGUMENT_LIST) { count = positionalCount; } fArgumentOffsets = new int[count]; fArgumentLengths = new int[count]; FormatterPrefs prefs = getFormatterPrefs(); setCursorPosition(buffer.length()); if (prefs.afterOpeningParen) { buffer.append(SPACE); } for (int i = 0; i != count; i++) { if (i != 0) { if (prefs.beforeComma) { buffer.append(SPACE); } buffer.append(COMMA); if (prefs.afterComma) { buffer.append(SPACE); } } fArgumentOffsets[i] = buffer.length(); buffer.append(parameterNames[i]); fArgumentLengths[i] = parameterNames[i].length; if (hasNamed && i >= positionalCount) { buffer.append(": "); fArgumentLengths[i] += 2; } } // create an artificial position for optional arguments if (count == 0) { fArgumentOffsets = new int[] {buffer.length()}; fArgumentLengths = new int[] {0}; } if (prefs.beforeClosingParen) { buffer.append(SPACE); } if (fProposal.getKind() != CompletionProposal.ARGUMENT_LIST) { buffer.append(RPAREN); } return buffer.toString(); } @Override protected boolean needsLinkedMode() { return false; // we handle it ourselves } /** * Returns the currently active editor, or <code>null</code> if it cannot be determined. * * @return the currently active editor, or <code>null</code> */ private DartEditor getDartEditor() { IEditorPart part = DartToolsPlugin.getActivePage().getActiveEditor(); if (part instanceof DartEditor) { return (DartEditor) part; } else { return null; } } /** * We call this hackish method from {@link ExitPolicy} to check if <code>','</code> is pressed at * place where it could be used as <code>TAB</code>, i.e. just before <code>','</code>. * <p> * https://code.google.com/p/dart/issues/detail?id=15798 */ private void maybeRewriteCommaWithTab(IDocument document, VerifyEvent event, int offset) { if (event.character == ',') { try { if (document.get(offset, 1).equals(",")) { event.character = '\t'; } } catch (Throwable e) { } } } private void openErrorDialog(BadLocationException e) { Shell shell = getTextViewer().getTextWidget().getShell(); MessageDialog.openError( shell, DartTextMessages.FilledArgumentNamesMethodProposal_error_msg, e.getMessage()); } }