/* * 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 org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.link.ILinkedModeListener; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedModeUI; import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.MultiTextEdit; import java.util.List; /** * Adds support to the linked mode editor that removes unneeded optional arguments from a method * invocation completion proposal after it has been inserted and edited. The superclass is marked * * @noextend, but we need to ignore that. */ class OptionalArgumentModel extends LinkedModeModel { private boolean hasNamed; private LinkedModeUI ui; private ITextViewer viewer; private int exitLocation; private int delta; private List<OptionalArgumentPosition> positions; private IDocument document; private boolean isStopped; public void exitPositionUpdater(LinkedModeUI ui, ITextViewer viewer, int loc) { this.ui = ui; this.viewer = viewer; this.exitLocation = loc; this.isStopped = false; } public void setHasNamed(boolean hasNamed) { this.hasNamed = hasNamed; } public void setPositions(List<OptionalArgumentPosition> positions, IDocument document) { this.positions = positions; this.document = document; } @Override public void stopForwarding(int flags) { try { if (isStopped) { // This is a hack. Don't do it twice, if the framework somehow re-enters this method. return; } isStopped = true; if ((flags & ILinkedModeListener.EXTERNAL_MODIFICATION) != 0) { if ((flags & (ILinkedModeListener.EXIT_ALL | ILinkedModeListener.EXIT_ALL)) == 0) { return; } } // Clean up unnecessary optional arguments. MultiTextEdit textEdit = updateUneditedPositions(); if (textEdit.getChildrenSize() == 0) { return; } positions.clear(); // Move the exit position so the text cursor appears in the right place. ui.setExitPosition(viewer, exitLocation - delta, 0, Integer.MAX_VALUE); // NOW we can update the document. This allows each argument to be restored via undo. textEdit.apply(document); } catch (BadLocationException ex) { // ignore it } finally { super.stopForwarding(flags); } } private void deleteText(OptionalArgumentPosition pos, MultiTextEdit textEdit, boolean preserveContent) throws BadLocationException { IDocument doc = pos.getDocument(); int len = doc.getLength(); int offset = pos.getNameOffset(); int end = pos.getNameLength() + offset; char ch = doc.getChar(end); if (ch == ',') { ch = doc.getChar(++end); while (Character.isWhitespace(ch)) { if (++end == len) { return; } ch = doc.getChar(end); } } if (preserveContent) { offset = pos.getNameLength() + offset; } len = end - offset; delta += len; textEdit.addChild(new DeleteEdit(offset, len)); } private MultiTextEdit updateUneditedPositions() throws BadLocationException { if (!hasNamed) { // Preserve unedited optional arguments that appear to the left of an edited optional. boolean required = false; for (int i = positions.size() - 1; i >= 0; i--) { OptionalArgumentPosition pos = positions.get(i); if (pos.isModified()) { required = true; } else { pos.setIsRequired(required); } } } MultiTextEdit textEdit = new MultiTextEdit(); for (OptionalArgumentPosition pos : positions) { if (!pos.isModified()) { // Remove unedited argument. deleteText(pos, textEdit, false); } } for (int i = positions.size() - 1; i >= 0; i--) { OptionalArgumentPosition pos = positions.get(i); if (pos.isModified()) { // Remove comma after last argument, if any. deleteText(pos, textEdit, true); break; } } return textEdit; } }