This is an automated email from the git hooks/post-receive script. ben pushed a commit to branch master in repository autocomplete.
commit ff7db970571a3b5f306efd94bea5092198da5aeb Author: bobbylight <[email protected]> Date: Sun Jun 17 17:33:03 2012 +0000 Starting work on TemplateCompletions - allows completions for parameterized boilerplate code - for-loops, etc. --- .../fife/ui/autocomplete/FunctionCompletion.java | 15 +- .../ui/autocomplete/ParameterizedCompletion.java | 37 +- .../ParameterizedCompletionChoicesWindow.java | 4 + .../ParameterizedCompletionDescriptionToolTip.java | 376 ++++++++++++++++---- .../ParameterizedCompletionInsertionInfo.java | 67 +++- src/org/fife/ui/autocomplete/SizeGrip.java | 3 - .../fife/ui/autocomplete/TemplateCompletion.java | 213 ++++++++--- src/org/fife/ui/autocomplete/TemplatePiece.java | 63 ++++ 8 files changed, 647 insertions(+), 131 deletions(-) diff --git a/src/org/fife/ui/autocomplete/FunctionCompletion.java b/src/org/fife/ui/autocomplete/FunctionCompletion.java index 9f9b13e..427d146 100644 --- a/src/org/fife/ui/autocomplete/FunctionCompletion.java +++ b/src/org/fife/ui/autocomplete/FunctionCompletion.java @@ -147,7 +147,8 @@ public class FunctionCompletion extends VariableCompletion public ParameterizedCompletionInsertionInfo getInsertionInfo( - JTextComponent tc, boolean addParamStartList) { + JTextComponent tc, boolean addParamStartList, + boolean replaceTabsWithSpaces) { ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo(); @@ -163,8 +164,10 @@ public class FunctionCompletion extends VariableCompletion // this tool tip. int minPos = dot; Position maxPos = null; + int defaultEndOffs = -1; try { - maxPos = tc.getDocument().createPosition(dot-sb.length()); + maxPos = tc.getDocument().createPosition(dot-sb.length()+1); + defaultEndOffs = dot-sb.length(); } catch (BadLocationException ble) { ble.printStackTrace(); // Never happens } @@ -192,7 +195,13 @@ public class FunctionCompletion extends VariableCompletion } } sb.append(getProvider().getParameterListEnd()); - + int endOffs = dot + sb.length(); + if (addParamStartList) { + endOffs -= 1;//getProvider().getParameterListStart().length(); + } + info.addReplacementLocation(endOffs, endOffs); // offset after function + info.setDefaultEndOffs(endOffs); + int selectionEnd = paramCount>0 ? (dot+firstParamLen) : dot; info.setInitialSelection(dot, selectionEnd); info.setTextToInsert(sb.toString()); diff --git a/src/org/fife/ui/autocomplete/ParameterizedCompletion.java b/src/org/fife/ui/autocomplete/ParameterizedCompletion.java index 1d995f0..acd674f 100644 --- a/src/org/fife/ui/autocomplete/ParameterizedCompletion.java +++ b/src/org/fife/ui/autocomplete/ParameterizedCompletion.java @@ -50,7 +50,8 @@ public interface ParameterizedCompletion extends Completion { public ParameterizedCompletionInsertionInfo getInsertionInfo( - JTextComponent tc, boolean addParamStartList); + JTextComponent tc, boolean addParamStartList, + boolean replaceTabsWithSpaces); /** @@ -64,6 +65,7 @@ public interface ParameterizedCompletion extends Completion { private String name; private Object type; private String desc; + private boolean isEndParam; /** * Constructor. @@ -77,8 +79,30 @@ public interface ParameterizedCompletion extends Completion { * @param name The name of the parameter. */ public Parameter(Object type, String name) { + this(type, name, false); + } + + /** + * Constructor. + * + * @param type The type of this parameter. This may be + * <code>null</code> for languages without specific types, + * dynamic typing, etc. Usually you'll pass a String for this + * value, but you may pass any object representing a type in + * your language, as long as its <code>toString()</code> method + * returns a string representation of the type. + * @param name The name of the parameter. + * @param endParam Whether this parameter is an "ending parameter;" + * that is, whether this parameter is at a logical "ending + * point" in the completion text. If the user types in a + * parameter that is an ending point, parameter completion mode + * terminates. Set this to <code>true</code> for a trailing + * parameter after a function call's closing ')', for example. + */ + public Parameter(Object type, String name, boolean endParam) { this.name = name; this.type = type; + this.isEndParam = endParam; } public String getDescription() { @@ -107,6 +131,17 @@ public interface ParameterizedCompletion extends Completion { return type; } + /** + * @return Whether this parameter is an "ending parameter;" + * that is, whether this parameter is at a logical "ending + * point" in the completion text. If the user types in a + * parameter that is an ending point, parameter completion mode + * terminates. + */ + public boolean isEndParam() { + return isEndParam; + } + public void setDescription(String desc) { this.desc = desc; } diff --git a/src/org/fife/ui/autocomplete/ParameterizedCompletionChoicesWindow.java b/src/org/fife/ui/autocomplete/ParameterizedCompletionChoicesWindow.java index cd553cf..31f21e8 100644 --- a/src/org/fife/ui/autocomplete/ParameterizedCompletionChoicesWindow.java +++ b/src/org/fife/ui/autocomplete/ParameterizedCompletionChoicesWindow.java @@ -280,6 +280,10 @@ public class ParameterizedCompletionChoicesWindow extends JWindow { } + else { + setVisible(false); + } + } diff --git a/src/org/fife/ui/autocomplete/ParameterizedCompletionDescriptionToolTip.java b/src/org/fife/ui/autocomplete/ParameterizedCompletionDescriptionToolTip.java index 677c2f7..47a1e48 100644 --- a/src/org/fife/ui/autocomplete/ParameterizedCompletionDescriptionToolTip.java +++ b/src/org/fife/ui/autocomplete/ParameterizedCompletionDescriptionToolTip.java @@ -20,6 +20,7 @@ import java.awt.event.FocusListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; @@ -34,6 +35,9 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Highlighter; @@ -41,7 +45,13 @@ import javax.swing.text.JTextComponent; import javax.swing.text.Position; import javax.swing.text.Highlighter.Highlight; +import org.fife.ui.autocomplete.ParameterizedCompletion.Parameter; +import org.fife.ui.autocomplete.ParameterizedCompletionInsertionInfo.ReplacementCopy; +import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; +import org.fife.ui.rtextarea.ChangeableHighlightPainter; /** @@ -63,11 +73,17 @@ class ParameterizedCompletionDescriptionToolTip { */ Highlighter.HighlightPainter p; + Highlighter.HighlightPainter paramCopyP; + /** * The tags for the highlights around parameters. */ List tags; + private List paramCopyInfos; + + private transient boolean ignoringDocumentEvents; + /** * The parent AutoCompletion instance. */ @@ -100,6 +116,8 @@ class ParameterizedCompletionDescriptionToolTip { */ private Position maxPos; // Moves with text inserted. + private Position defaultEndOffs; + /** * A small popup window giving likely choices for parameterized completions. */ @@ -158,7 +176,7 @@ class ParameterizedCompletionDescriptionToolTip { descLabel = new JLabel(); descLabel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(Color.BLACK), + TipUtil.getToolTipBorder(), BorderFactory.createEmptyBorder(2, 5, 2, 5))); descLabel.setOpaque(true); descLabel.setBackground(TipUtil.getToolTipBackground()); @@ -185,7 +203,9 @@ class ParameterizedCompletionDescriptionToolTip { listener = new Listener(); p = new OutlineHighlightPainter(Color.GRAY); + paramCopyP = new ChangeableHighlightPainter(new Color(255, 224, 224)); tags = new ArrayList(1); // Usually small + paramCopyInfos = new ArrayList(1); paramChoicesWindow = createParamChoicesWindow(); @@ -206,6 +226,31 @@ class ParameterizedCompletionDescriptionToolTip { } + public String getArgumentText(int offs) { + List paramHighlights = getParameterHighlights(); + if (paramHighlights==null || paramHighlights.size()==0) { + return null; + } + for (int i=0; i<paramHighlights.size(); i++) { + Highlight h = (Highlight)paramHighlights.get(i); + if (offs>=h.getStartOffset() && offs<=h.getEndOffset()) { + int start = h.getStartOffset() + 1; + int len = h.getEndOffset() - start; + JTextComponent tc = ac.getTextComponent(); + Document doc = tc.getDocument(); + try { + return doc.getText(start, len); + } catch (BadLocationException ble) { + UIManager.getLookAndFeel().provideErrorFeedback(tc); + ble.printStackTrace(); + return null; + } + } + } + return null; + } + + /** * Returns the highlight of the current parameter. * @@ -234,6 +279,27 @@ class ParameterizedCompletionDescriptionToolTip { } + private int getCurrentParameterIndex() { + + JTextComponent tc = ac.getTextComponent(); + int dot = tc.getCaretPosition(); + if (dot>0) { + dot--; // Workaround for Java Highlight issues + } + + List paramHighlights = getParameterHighlights(); + for (int i=0; i<paramHighlights.size(); i++) { + Highlight h = (Highlight)paramHighlights.get(i); + if (dot>=h.getStartOffset() && dot<h.getEndOffset()) { + return i; + } + } + + return -1; + + } + + /** * Returns the starting offset of the current parameter. * @@ -247,6 +313,54 @@ class ParameterizedCompletionDescriptionToolTip { } + /** + * Returns the highlight from a list that comes "first" in a list. Even + * though most parameter highlights are ordered, sometimes they aren't + * (e.g. the "cursor" parameter in a template completion is always last, + * even though it can be anywhere in the template). + * + * @param highlights The list of highlights. Assumed to be non-empty. + * @return The highlight that comes first in the document. + * @see #getLastHighlight(List) + */ + private static final int getFirstHighlight(List highlights) { + int first = -1; + Highlight firstH = null; + for (int i=0; i<highlights.size(); i++) { + Highlight h = (Highlight)highlights.get(i); + if (firstH==null || h.getStartOffset()<firstH.getStartOffset()) { + firstH = h; + first = i; + } + } + return first; + } + + + /** + * Returns the highlight from a list that comes "last" in that list. Even + * though most parameter highlights are ordered, sometimes they aren't + * (e.g. the "cursor" parameter in a template completion is always last, + * even though it can be anywhere in the template. + * + * @param highlights The list of highlights. Assumed to be non-empty. + * @return The highlight that comes last in the document. + * @see #getFirstHighlight(List) + */ + private static final int getLastHighlight(List highlights) { + int last = -1; + Highlight lastH = null; + for (int i=highlights.size()-1; i>=0; i--) { + Highlight h = (Highlight)highlights.get(i); + if (lastH==null || h.getStartOffset()>lastH.getStartOffset()) { + lastH = h; + last = i; + } + } + return last; + } + + private List getParameterHighlights() { List paramHighlights = new ArrayList(1); JTextComponent tc = ac.getTextComponent(); @@ -360,7 +474,6 @@ class ParameterizedCompletionDescriptionToolTip { JTextComponent tc = ac.getTextComponent(); int dot = tc.getCaretPosition(); - int tagCount = tags.size(); if (tagCount==0) { tc.setCaretPosition(maxPos.getOffset()); @@ -383,17 +496,18 @@ class ParameterizedCompletionDescriptionToolTip { } } - if (currentNext!=null && dot<=currentNext.getStartOffset()) { - // "+1" is a workaround for Java Highlight issues. - tc.setSelectionStart(currentNext.getStartOffset()+1); - tc.setSelectionEnd(currentNext.getEndOffset()); - updateText(pos); - } - else { - tc.setCaretPosition(maxPos.getOffset()); - setVisible(false, false); + // No params after caret - go to first one + if (currentNext.getStartOffset()+1<=dot) { + int nextIndex = getFirstHighlight(highlights); + currentNext = (Highlight)highlights.get(nextIndex); + pos = 0; } + // "+1" is a workaround for Java Highlight issues. + tc.setSelectionStart(currentNext.getStartOffset()+1); + tc.setSelectionEnd(currentNext.getEndOffset()); + updateText(pos); + } @@ -422,15 +536,18 @@ class ParameterizedCompletionDescriptionToolTip { Highlight h = (Highlight)highlights.get(i); if (currentPrev==null || currentPrev.getStartOffset()>=dot || (h.getStartOffset()<selStart && - h.getStartOffset()>currentPrev.getStartOffset())) { + (h.getStartOffset()>currentPrev.getStartOffset() || + pos==lastSelectedParam))) { currentPrev = h; pos = i; } } // Loop back from param 0 to last param. - if (pos==0 && lastSelectedParam==0 && highlights.size()>1) { - pos = highlights.size() - 1; + int firstIndex = getFirstHighlight(highlights); + //if (pos==0 && lastSelectedParam==0 && highlights.size()>1) { + if (pos==firstIndex && lastSelectedParam==firstIndex && highlights.size()>1) { + pos = getLastHighlight(highlights); currentPrev = (Highlight)highlights.get(pos); // "+1" is a workaround for Java Highlight issues. tc.setSelectionStart(currentPrev.getStartOffset()+1); @@ -451,6 +568,49 @@ class ParameterizedCompletionDescriptionToolTip { } + private void possiblyUpdateParamCopies(Document doc) { + + int index = getCurrentParameterIndex(); + // FunctionCompletions add an extra param at end of inserted text + if (index>-1 && index<pc.getParamCount()) { + + // Typing in an "end parameter" => stop parameter assistance. + Parameter param = pc.getParam(index); + if (param.isEndParam()) { + setVisible(false, false); + return; + } + + // Get the current value of the current parameter. + List paramHighlights = getParameterHighlights(); + Highlight h = (Highlight)paramHighlights.get(index); + int start = h.getStartOffset() + 1; // param offsets are offset (!) by 1 + int len = h.getEndOffset() - start; + String replacement = null; + try { + replacement = doc.getText(start, len); + } catch (BadLocationException ble) { + ble.printStackTrace(); // Never happens + } + + // Replace any param copies tracking this parameter with the + // value of this parameter. + for (Iterator i=paramCopyInfos.iterator(); i.hasNext(); ) { + ParamCopyInfo pci = (ParamCopyInfo)i.next(); + if (pci.paramName.equals(param.getName())) { + pci.h = replaceHighlightedText(doc, pci.h, replacement); + } + } + + } + + else { // Probably the "end parameter" for FunctionCompletions. + setVisible(false, false); + } + + } + + /** * Updates the optional window listing likely completion choices, */ @@ -496,6 +656,52 @@ class ParameterizedCompletionDescriptionToolTip { h.removeHighlight(tags.get(i)); } tags.clear(); + for (int i=0; i<paramCopyInfos.size(); i++) { + ParamCopyInfo pci = (ParamCopyInfo)paramCopyInfos.get(i); + h.removeHighlight(pci.h); + } + paramCopyInfos.clear(); + } + + + /** + * Replaces highlighted text with new text. Takes special care so that + * the highlight stays just around the newly-highlighted text, since + * Swing's <code>Highlight</code> classes are funny about insertions at + * their start offsets. + * + * @param doc The document. + * @param h The highlight whose text to change. + * @param replacement The new text to be in the highlight. + * @return The replacement highlight for <code>h</code>. + */ + private Highlight replaceHighlightedText(Document doc, Highlight h, + String replacement) { + try { + + int start = h.getStartOffset(); + int len = h.getEndOffset() - start; + Highlighter highlighter = ac.getTextComponent().getHighlighter(); + highlighter.removeHighlight(h); + + if (doc instanceof AbstractDocument) { + ((AbstractDocument)doc).replace(start, len, replacement, null); + } + else { + doc.remove(start, len); + doc.insertString(start, replacement, null); + } + + int newEnd = start + replacement.length(); + h = (Highlight)highlighter.addHighlight(start, newEnd, paramCopyP); + return h; + + } catch (BadLocationException ble) { + ble.printStackTrace(); // Never happens + } + + return null; + } @@ -693,28 +899,7 @@ class ParameterizedCompletionDescriptionToolTip { // (such as type parameters in Java). We need to take care to // escape these. String temp = pc.getParam(i).toString(); - int lt = temp.indexOf('<'); - if (lt>-1) { - sb.append(temp.substring(0, lt)); - sb.append("<"); - for (int j=lt+1; j<temp.length(); j++) { - char ch = temp.charAt(j); - switch (ch) { - case '<': - sb.append("<"); - break; - case '>': - sb.append(">"); - break; - default: - sb.append(ch); - break; - } - } - } - else { - sb.append(temp); - } + sb.append(RSyntaxUtilities.escapeForHtml(temp, "<br>", false)); if (i==selectedParam) { sb.append("</b>"); @@ -775,7 +960,7 @@ class ParameterizedCompletionDescriptionToolTip { // Otherwise, just move to the end. JTextComponent tc = ac.getTextComponent(); - tc.setCaretPosition(maxPos.getOffset()); + tc.setCaretPosition(defaultEndOffs.getOffset()); setVisible(false, false); } @@ -799,9 +984,9 @@ class ParameterizedCompletionDescriptionToolTip { char end = pc.getProvider().getParameterListEnd(); // Are they at or past the end of the parameters? - if (dot>=maxPos.getOffset()-1) { // ">=" for overwrite mode + if (dot>=maxPos.getOffset()-2) { // ">=" for overwrite mode - if (dot==maxPos.getOffset()) { // Happens in overwrite mode + if (dot==maxPos.getOffset()-1) { // Happens in overwrite mode tc.replaceSelection(Character.toString(end)); } @@ -818,7 +1003,8 @@ class ParameterizedCompletionDescriptionToolTip { return; } } - tc.setCaretPosition(maxPos.getOffset()); + //tc.setCaretPosition(maxPos.getOffset()); + tc.setCaretPosition(tc.getCaretPosition()+1); } setVisible(false, false); @@ -832,30 +1018,6 @@ class ParameterizedCompletionDescriptionToolTip { } - public String getArgumentText(int offs) { - List paramHighlights = getParameterHighlights(); - if (paramHighlights==null || paramHighlights.size()==0) { - return null; - } - for (int i=0; i<paramHighlights.size(); i++) { - Highlight h = (Highlight)paramHighlights.get(i); - if (offs>=h.getStartOffset() && offs<=h.getEndOffset()) { - int start = h.getStartOffset() + 1; - int len = h.getEndOffset() - start; - JTextComponent tc = ac.getTextComponent(); - Document doc = tc.getDocument(); - try { - return doc.getText(start, len); - } catch (BadLocationException ble) { - UIManager.getLookAndFeel().provideErrorFeedback(tc); - ble.printStackTrace(); - return null; - } - } - } - return null; - } - public int getCount(String text, char ch) { int count = 0; int old = 0; @@ -903,7 +1065,10 @@ class ParameterizedCompletionDescriptionToolTip { * @author Robert Futrell * @version 1.0 */ - private class Listener implements FocusListener, CaretListener { + private class Listener implements FocusListener, CaretListener, + DocumentListener { + + private boolean markOccurrencesEnabled; /** * Called when the text component's caret moves. @@ -917,6 +1082,7 @@ class ParameterizedCompletionDescriptionToolTip { } int dot = e.getDot(); if (dot<minPos || dot>=maxPos.getOffset()) { + System.err.println(">>>>> dot==" + dot + ", " + minPos + ", " + maxPos); setVisible(false, false); return; } @@ -927,6 +1093,10 @@ class ParameterizedCompletionDescriptionToolTip { } + public void changedUpdate(DocumentEvent e) { + } + + /** * Called when the text component gains focus. * @@ -947,6 +1117,24 @@ class ParameterizedCompletionDescriptionToolTip { } + private void handleDocumentEvent(final DocumentEvent e) { + if (!ignoringDocumentEvents) { + ignoringDocumentEvents = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + possiblyUpdateParamCopies(e.getDocument()); + ignoringDocumentEvents = false; + } + }); + } + } + + + public void insertUpdate(DocumentEvent e) { + handleDocumentEvent(e); + } + + /** * Installs this listener onto a text component. * @@ -963,20 +1151,34 @@ class ParameterizedCompletionDescriptionToolTip { tc.addFocusListener(this); installKeyBindings(); + boolean replaceTabs = false; + if (tc instanceof RSyntaxTextArea) { + RSyntaxTextArea textArea = (RSyntaxTextArea)tc; + markOccurrencesEnabled = textArea.getMarkOccurrences(); + textArea.setMarkOccurrences(false); + replaceTabs = textArea.getTabsEmulated(); + } + Highlighter h = tc.getHighlighter(); try { + // Insert the parameter text ParameterizedCompletionInsertionInfo info = - pc.getInsertionInfo(tc, addParamStartList); - - // Insert the parameter text and add highlights around the - // parameters. + pc.getInsertionInfo(tc, addParamStartList, replaceTabs); tc.replaceSelection(info.getTextToInsert()); + + // Add highlights around the parameters. for (int i=0; i<info.getReplacementCount(); i++) { - Point pt = info.getReplacementLocation(i); + DocumentRange dr = info.getReplacementLocation(i); // "-1" is a workaround for Java Highlight issues. - tags.add(h.addHighlight(pt.x-1, pt.y, p)); + tags.add(h.addHighlight(dr.getStartOffset()-1, dr.getEndOffset(), p)); + } + for (int i=0; i<info.getReplacementCopyCount(); i++) { + ReplacementCopy rc = info.getReplacementCopy(i); + paramCopyInfos.add(new ParamCopyInfo(rc.getId(), + (Highlight)h.addHighlight(rc.getStart(), rc.getEnd(), + paramCopyP))); } // Go back and start at the first parameter. @@ -987,6 +1189,15 @@ class ParameterizedCompletionDescriptionToolTip { minPos = info.getMinOffset(); maxPos = info.getMaxOffset(); + try { + defaultEndOffs = tc.getDocument().createPosition( + info.getDefaultEndOffs()); + } catch (BadLocationException ble) { + ble.printStackTrace(); // Never happens + } + + // Listen for document events AFTER we insert + tc.getDocument().addDocumentListener(this); } catch (BadLocationException ble) { ble.printStackTrace(); // Never happens @@ -995,6 +1206,11 @@ class ParameterizedCompletionDescriptionToolTip { } + public void removeUpdate(DocumentEvent e) { + handleDocumentEvent(e); + } + + /** * Uninstalls this listener from the current text component. * @@ -1004,8 +1220,13 @@ class ParameterizedCompletionDescriptionToolTip { JTextComponent tc = ac.getTextComponent(); tc.removeCaretListener(this); tc.removeFocusListener(this); + tc.getDocument().removeDocumentListener(this); uninstallKeyBindings(); + if (markOccurrencesEnabled) { + ((RSyntaxTextArea)tc).setMarkOccurrences(markOccurrencesEnabled); + } + // Remove WeakReferences in javax.swing.text. maxPos = null; minPos = -1; @@ -1064,6 +1285,19 @@ class ParameterizedCompletionDescriptionToolTip { } + private static class ParamCopyInfo { + + private String paramName; + private Highlight h; + + public ParamCopyInfo(String paramName, Highlight h) { + this.paramName = paramName; + this.h = h; + } + + } + + /** * Action performed when the user hits shift+tab. * diff --git a/src/org/fife/ui/autocomplete/ParameterizedCompletionInsertionInfo.java b/src/org/fife/ui/autocomplete/ParameterizedCompletionInsertionInfo.java index b82430d..f7e3a94 100644 --- a/src/org/fife/ui/autocomplete/ParameterizedCompletionInsertionInfo.java +++ b/src/org/fife/ui/autocomplete/ParameterizedCompletionInsertionInfo.java @@ -9,11 +9,12 @@ */ package org.fife.ui.autocomplete; -import java.awt.Point; import java.util.ArrayList; import java.util.List; import javax.swing.text.Position; +import org.fife.ui.rsyntaxtextarea.DocumentRange; + /** * Describes a parameterized completion - what's being inserted, where the @@ -26,13 +27,24 @@ class ParameterizedCompletionInsertionInfo { private int minOffs; private Position maxOffs; + private int defaultEnd; private int selStart; private int selEnd; private String textToInsert; private List replacementLocations; + private List replacementCopies; public ParameterizedCompletionInsertionInfo() { + defaultEnd = -1; + } + + + public void addReplacementCopy(String id, int start, int end) { + if (replacementCopies==null) { + replacementCopies = new ArrayList(1); + } + replacementCopies.add(new ReplacementCopy(id, start, end)); } @@ -49,7 +61,12 @@ class ParameterizedCompletionInsertionInfo { if (replacementLocations==null) { replacementLocations = new ArrayList(1); } - replacementLocations.add(new Point(start, end)); + replacementLocations.add(new DocumentRange(start, end)); + } + + + public int getDefaultEndOffs() { + return defaultEnd>-1 ? defaultEnd : getMaxOffset().getOffset(); } @@ -77,6 +94,11 @@ class ParameterizedCompletionInsertionInfo { } + public int getReplacementCopyCount() { + return replacementCopies==null ? 0 : replacementCopies.size(); + } + + /** * Returns the number of replacements in the completion. * @@ -87,6 +109,11 @@ class ParameterizedCompletionInsertionInfo { } + public ReplacementCopy getReplacementCopy(int index) { + return (ReplacementCopy)replacementCopies.get(index); + } + + /** * Returns the starting- and ending-offsets of the replacement regions * in the completion. @@ -95,8 +122,8 @@ class ParameterizedCompletionInsertionInfo { * @return The range in the document of that replacement region. * @see #getReplacementCount() */ - public Point getReplacementLocation(int index) { - return (Point)replacementLocations.get(index); + public DocumentRange getReplacementLocation(int index) { + return (DocumentRange)replacementLocations.get(index); } @@ -178,6 +205,11 @@ class ParameterizedCompletionInsertionInfo { } + public void setDefaultEndOffs(int end) { + defaultEnd = end; + } + + /** * Sets the text to insert for the completion. * @@ -189,4 +221,31 @@ class ParameterizedCompletionInsertionInfo { } + public static class ReplacementCopy { + + private String id; + private int start; + private int end; + + public ReplacementCopy(String id, int start, int end) { + this.id = id; + this.start = start; + this.end = end; + } + + public int getEnd() { + return end; + } + + public String getId() { + return id; + } + + public int getStart() { + return start; + } + + } + + } \ No newline at end of file diff --git a/src/org/fife/ui/autocomplete/SizeGrip.java b/src/org/fife/ui/autocomplete/SizeGrip.java index e698cc8..3f90b84 100644 --- a/src/org/fife/ui/autocomplete/SizeGrip.java +++ b/src/org/fife/ui/autocomplete/SizeGrip.java @@ -193,9 +193,6 @@ class SizeGrip extends JPanel { /** * Listens for mouse events on this panel and resizes the parent window * appropriately. - * - * @author Robert Futrell - * @version 1.0 */ /* * NOTE: We use SwingUtilities.convertPointToScreen() instead of just using diff --git a/src/org/fife/ui/autocomplete/TemplateCompletion.java b/src/org/fife/ui/autocomplete/TemplateCompletion.java index 19a085c..6e5efad 100644 --- a/src/org/fife/ui/autocomplete/TemplateCompletion.java +++ b/src/org/fife/ui/autocomplete/TemplateCompletion.java @@ -15,8 +15,11 @@ import java.util.List; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; +import javax.swing.text.PlainDocument; import javax.swing.text.Position; +import org.fife.ui.autocomplete.TemplatePiece.Param; +import org.fife.ui.autocomplete.TemplatePiece.ParamCopy; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; @@ -25,7 +28,26 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; * can tab through and fill in. This completion type is useful for inserting * common boilerplate code, such as for-loops.<p> * - * This class is a work in progress and currently should not be used. + * The format of a template is similar to those in Eclipse. The following + * example would be the format for a for-loop template: + * + * <pre> + * for (int ${i} = 0; ${i} < ${array}.length; ${i}++) { + * ${cursor} + * } + * </pre> + * + * In the above example, the first <code>${i}</code> is a parameter for the + * user to type into; all the other <code>${i}</code> instances are + * automatically changed to what the user types in the first one. The parameter + * named <code>${cursor}</code> is the "ending position" of the template. It's + * where the caret moves after it cycles through all other parameters. If the + * user types into it, template mode terminates. If more than one + * <code>${cursor}</code> parameter is specified, behavior is undefined.<p> + * + * Leading whitespace is automatically added to lines if the template spans + * more than one line, and if used with a text component using a + * <code>PlainDocument</code>, tabs will be converted to spaces if requested. * * @author Robert Futrell * @version 1.0 @@ -35,7 +57,7 @@ public class TemplateCompletion extends AbstractCompletion private List pieces; - private String replacementText; + private String inputText; private String definitionString; @@ -45,43 +67,35 @@ public class TemplateCompletion extends AbstractCompletion private List params; - - public TemplateCompletion(CompletionProvider provider, String replacementText, - String definitionString) { + public TemplateCompletion(CompletionProvider provider, + String inputText, String definitionString, String template) { super(provider); - this.replacementText = replacementText; + this.inputText = inputText; this.definitionString = definitionString; pieces = new ArrayList(3); params = new ArrayList(3); + parse(template); } - public void addTemplatePiece(TemplatePiece piece) { + private void addTemplatePiece(TemplatePiece piece) { pieces.add(piece); - if (piece.isParam) { + if (piece instanceof Param && !"cursor".equals(piece.getText())) { final String type = null; // TODO - Parameter param = new Parameter(type, piece.text); + Parameter param = new Parameter(type, piece.getText()); params.add(param); } } + public String getInputText() { + return inputText; + } + + private String getPieceText(int index, String leadingWS) { - String text = null; TemplatePiece piece = (TemplatePiece)pieces.get(index); - if (piece.id!=null) { - final String id = piece.id; - for (int i=0; i<pieces.size(); i++) { - piece = (TemplatePiece)pieces.get(i); - if (id.equals(piece.id)) { - text = piece.text; - break; - } - } - } - else { - text = piece.text; - } + String text = piece.getText(); if (text.indexOf('\n')>-1) { text = text.replaceAll("\n", "\n" + leadingWS); } @@ -90,13 +104,13 @@ public class TemplateCompletion extends AbstractCompletion /** - * For template completions, the "replacement text" is really just the - * first piece of the template, not the entire thing. You should add - * template pieces so that the rest of the template is dynamically - * generated. + * Returns <code>null</code>; template completions insert all of their + * text via <code>getInsertionInfo()</code>. + * + * @return <code>null</code> always. */ public String getReplacementText() { - return replacementText; + return null; } @@ -112,19 +126,20 @@ public class TemplateCompletion extends AbstractCompletion public ParameterizedCompletionInsertionInfo getInsertionInfo( - JTextComponent tc, boolean addParamStartList) { + JTextComponent tc, boolean addParamStartList, + boolean replaceTabsWithSpaces) { ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo(); StringBuffer sb = new StringBuffer(); int dot = tc.getCaretPosition(); - int paramCount = getParamCount(); // Get the range in which the caret can move before we hide // this tool tip. int minPos = dot; Position maxPos = null; + int defaultEndOffs = -1; try { maxPos = tc.getDocument().createPosition(dot); } catch (BadLocationException ble) { @@ -132,6 +147,8 @@ public class TemplateCompletion extends AbstractCompletion } info.setCaretRange(minPos, maxPos); int firstParamLen = 0; + int selStart = dot; // Default value + int selEnd = selStart; Document doc = tc.getDocument(); String leadingWS = null; @@ -148,19 +165,38 @@ public class TemplateCompletion extends AbstractCompletion for (int i=0; i<pieces.size(); i++) { TemplatePiece piece = (TemplatePiece)pieces.get(i); String text = getPieceText(i, leadingWS); - sb.append(text); - int end = start + text.length(); - if (piece.isParam) { - info.addReplacementLocation(start, end); - if (firstParamLen==0) { - firstParamLen = text.length(); + if (piece instanceof Param && "cursor".equals(text)) { + if (replaceTabsWithSpaces) { + start = possiblyReplaceTabsWithSpaces(sb, tc, start); } + defaultEndOffs = start; + } + else { + int end = start + text.length(); + sb.append(text); + if (piece instanceof Param) { + info.addReplacementLocation(start, end); + if (firstParamLen==0) { + firstParamLen = text.length(); + selStart = start; + selEnd = selStart + firstParamLen; + } + } + else if (piece instanceof ParamCopy) { + info.addReplacementCopy(piece.getText(), start, end); + } + start = end; } - start = end; } - int selectionEnd = paramCount>0 ? (dot+firstParamLen) : dot; - info.setInitialSelection(dot, selectionEnd); + + info.setInitialSelection(selStart, selEnd); + if (defaultEndOffs>-1) { + // Keep this location "after" all others when tabbing + info.addReplacementLocation(defaultEndOffs, defaultEndOffs); + } + info.setDefaultEndOffs(defaultEndOffs); info.setTextToInsert(sb.toString()); + return info; } @@ -182,23 +218,102 @@ public class TemplateCompletion extends AbstractCompletion } - public String toString() { - return getDefinitionString(); + /** + * Returns whether a parameter is already defined with a specific name. + * + * @param name The name. + * @return Whether a parameter is defined with that name. + */ + private boolean isParamDefined(String name) { + for (int i=0; i<getParamCount(); i++) { + Parameter param = getParam(i); + if (name.equals(param.getName())) { + return true; + } + } + return false; + } + + + /** + * Parses a template string into logical pieces used by this class. + * + * @param template The template to parse. + */ + private void parse(String template) { + + int offs = 0; + int lastOffs = 0; + + while ((offs=template.indexOf('$', lastOffs))>-1 && offs<template.length()-1) { + + char next = template.charAt(offs+1); + switch (next) { + case '$': // "$$" => escaped single dollar sign + addTemplatePiece(new TemplatePiece.Text( + template.substring(lastOffs, offs+1))); + lastOffs = offs += 2; + break; + case '{': // "${...}" => variable + int closingCurly = template.indexOf('}', offs+2); + if (closingCurly>-1) { + addTemplatePiece(new TemplatePiece.Text( + template.substring(lastOffs, offs))); + String varName = template.substring(offs+2, closingCurly); + if (!"cursor".equals(varName) && isParamDefined(varName)) { + addTemplatePiece(new TemplatePiece.ParamCopy(varName)); + } + else { + addTemplatePiece(new TemplatePiece.Param(varName)); + } + lastOffs = offs = closingCurly + 1; + } + break; + } + + } + + if (lastOffs<template.length()) { + String text = template.substring(lastOffs); + addTemplatePiece(new TemplatePiece.Text(text)); + } + } - public static class TemplatePiece { + private int possiblyReplaceTabsWithSpaces(StringBuffer sb, JTextComponent tc, + int start) { - private String id; - private String text; - private boolean isParam; + int size = 4; + Document doc = tc.getDocument(); + if (doc != null) { + Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute); + if (i != null) { + size = i.intValue(); + } + } + String tab = ""; + for (int i=0; i<size; i++) { + tab += " "; + } - public TemplatePiece(String id, String text, boolean isParam) { - this.id = id; - this.text = text; - this.isParam = isParam; + int lastNewline = sb.lastIndexOf("\n"); + int lineOffs = 0; + for (int j=lastNewline+1; j<sb.length(); j++) { + if (sb.charAt(j)=='\t') { + int count = size - (lineOffs%size); + sb.replace(j, j+1, tab.substring(0, count)); + start += count - 1; + } } + return start; + + } + + + public String toString() { + return getDefinitionString(); } diff --git a/src/org/fife/ui/autocomplete/TemplatePiece.java b/src/org/fife/ui/autocomplete/TemplatePiece.java new file mode 100644 index 0000000..df69343 --- /dev/null +++ b/src/org/fife/ui/autocomplete/TemplatePiece.java @@ -0,0 +1,63 @@ +package org.fife.ui.autocomplete; + + +/** + * A piece of a <code>TemplateCompletion</code>. You add instances of this + * class to template completions to define them. + * + * @author Robert Futrell + * @version 1.0 + * @see TemplateCompletion + */ +interface TemplatePiece { + + + String getText(); + + + public class Text implements TemplatePiece { + + private String text; + + public Text(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + } + + + public class Param implements TemplatePiece { + + String text; + + public Param(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + } + + + public class ParamCopy implements TemplatePiece { + + private String text; + + public ParamCopy(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + } + + +} \ No newline at end of file -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/autocomplete.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

