Added source for ByteEdit

This commit is contained in:
SuspiciousActivity committed 2018-04-20 18:08:44 +02:00
1 parent 32dacb30ff
commit d29e5765af
352 files changed
+199016

No files matched your search

@@ -0,0 +1,185 @@
/*
* 12/21/2008
*
* AbstractCompletion.java - Base class for possible completions.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import javax.swing.Icon;
import javax.swing.text.JTextComponent;
/**
* Base class for possible completions. Most, if not all, {@link Completion}
* implementations can extend this class. It remembers the
* <tt>CompletionProvider</tt> that returns this completion, and also implements
* <tt>Comparable</tt>, allowing such completions to be compared
* lexicographically (ignoring case).<p>
*
* This implementation assumes the input text and replacement text are the
* same value. It also returns the input text from its {@link #toString()}
* method (which is what <code>DefaultListCellRenderer</code> uses to render
* objects). Subclasses that wish to override any of this behavior can simply
* override the corresponding method(s) needed to do so.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractCompletion implements Completion {
/**
* The provider that created this completion;
*/
private CompletionProvider provider;
/**
* The icon to use for this completion.
*/
private Icon icon;
/**
* The relevance of this completion. Completion instances with higher
* "relevance" values are inserted higher into the list of possible
* completions than those with lower values. Completion instances with
* equal relevance values are sorted alphabetically.
*/
private int relevance;
/**
* Constructor.
*
* @param provider The provider that created this completion.
*/
protected AbstractCompletion(CompletionProvider provider) {
this.provider = provider;
}
/**
* Constructor.
*
* @param provider The provider that created this completion.
* @param icon The icon for this completion.
*/
protected AbstractCompletion(CompletionProvider provider, Icon icon) {
this(provider);
setIcon(icon);
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(Completion c2) {
if (c2==this) {
return 0;
}
else if (c2!=null) {
return toString().compareToIgnoreCase(c2.toString());
}
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public String getAlreadyEntered(JTextComponent comp) {
return provider.getAlreadyEnteredText(comp);
}
/**
* {@inheritDoc}
*/
@Override
public Icon getIcon() {
return icon;
}
/**
* Returns the text the user has to (start) typing for this completion
* to be offered. The default implementation simply returns
* {@link #getReplacementText()}.
*
* @return The text the user has to (start) typing for this completion.
* @see #getReplacementText()
*/
@Override
public String getInputText() {
return getReplacementText();
}
/**
* {@inheritDoc}
*/
@Override
public CompletionProvider getProvider() {
return provider;
}
/**
* {@inheritDoc}
*/
@Override
public int getRelevance() {
return relevance;
}
/**
* The default implementation returns <code>null</code>. Subclasses
* can override this method.
*
* @return The tool tip text.
*/
@Override
public String getToolTipText() {
return null;
}
/**
* Sets the icon to use for this completion.
*
* @param icon The icon to use.
* @see #getIcon()
*/
public void setIcon(Icon icon) {
this.icon = icon;
}
/**
* Sets the relevance of this completion.
*
* @param relevance The new relevance of this completion.
* @see #getRelevance()
*/
public void setRelevance(int relevance) {
this.relevance = relevance;
}
/**
* Returns a string representation of this completion. The default
* implementation returns {@link #getInputText()}.
*
* @return A string representation of this completion.
*/
@Override
public String toString() {
return getInputText();
}
}
@@ -0,0 +1,249 @@
/*
* 12/21/2008
*
* AbstractCompletionProvider.java - Base class for completion providers.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.text.JTextComponent;
/**
* A base class for completion providers. {@link Completion}s are kept in
* a sorted list. To get the list of completions that match a given input,
* a binary search is done to find the first matching completion, then all
* succeeding completions that also match are also returned.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractCompletionProvider
extends CompletionProviderBase {
/**
* The completions this provider is aware of. Subclasses should ensure
* that this list is sorted alphabetically (case-insensitively).
*/
protected List<Completion> completions;
/**
* Compares a {@link Completion} against a String.
*/
protected CaseInsensitiveComparator comparator;
/**
* Constructor.
*/
public AbstractCompletionProvider() {
comparator = new CaseInsensitiveComparator();
clearParameterizedCompletionParams();
completions = new ArrayList<Completion>();
}
/**
* Adds a single completion to this provider. If you are adding multiple
* completions to this provider, for efficiency reasons please consider
* using {@link #addCompletions(List)} instead.
*
* @param c The completion to add.
* @throws IllegalArgumentException If the completion's provider isn't
* this <tt>CompletionProvider</tt>.
* @see #addCompletions(List)
* @see #removeCompletion(Completion)
* @see #clear()
*/
public void addCompletion(Completion c) {
checkProviderAndAdd(c);
Collections.sort(completions);
}
/**
* Adds {@link Completion}s to this provider.
*
* @param completions The completions to add. This cannot be
* <code>null</code>.
* @throws IllegalArgumentException If a completion's provider isn't
* this <tt>CompletionProvider</tt>.
* @see #addCompletion(Completion)
* @see #removeCompletion(Completion)
* @see #clear()
*/
public void addCompletions(List<Completion> completions) {
//this.completions.addAll(completions);
for (Completion c : completions) {
checkProviderAndAdd(c);
}
Collections.sort(this.completions);
}
/**
* Adds simple completions for a list of words.
*
* @param words The words.
* @see BasicCompletion
*/
protected void addWordCompletions(String[] words) {
int count = words==null ? 0 : words.length;
for (int i=0; i<count; i++) {
completions.add(new BasicCompletion(this, words[i]));
}
Collections.sort(completions);
}
protected void checkProviderAndAdd(Completion c) {
if (c.getProvider()!=this) {
throw new IllegalArgumentException("Invalid CompletionProvider");
}
completions.add(c);
}
/**
* Removes all completions from this provider. This does not affect
* the parent <tt>CompletionProvider</tt>, if there is one.
*
* @see #addCompletion(Completion)
* @see #addCompletions(List)
* @see #removeCompletion(Completion)
*/
public void clear() {
completions.clear();
}
/**
* Returns a list of <tt>Completion</tt>s in this provider with the
* specified input text.
*
* @param inputText The input text to search for.
* @return A list of {@link Completion}s, or <code>null</code> if there
* are no matching <tt>Completion</tt>s.
*/
@SuppressWarnings("unchecked")
public List<Completion> getCompletionByInputText(String inputText) {
// Find any entry that matches this input text (there may be > 1).
int end = Collections.binarySearch(completions, inputText, comparator);
if (end<0) {
return null;
}
// There might be multiple entries with the same input text.
int start = end;
while (start>0 &&
comparator.compare(completions.get(start-1), inputText)==0) {
start--;
}
int count = completions.size();
while (++end<count &&
comparator.compare(completions.get(end), inputText)==0);
return completions.subList(start, end); // (inclusive, exclusive)
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
protected List<Completion> getCompletionsImpl(JTextComponent comp) {
List<Completion> retVal = new ArrayList<Completion>();
String text = getAlreadyEnteredText(comp);
if (text!=null) {
int index = Collections.binarySearch(completions, text, comparator);
if (index<0) { // No exact match
index = -index - 1;
}
else {
// If there are several overloads for the function being
// completed, Collections.binarySearch() will return the index
// of one of those overloads, but we must return all of them,
// so search backward until we find the first one.
int pos = index - 1;
while (pos>0 &&
comparator.compare(completions.get(pos), text)==0) {
retVal.add(completions.get(pos));
pos--;
}
}
while (index<completions.size()) {
Completion c = completions.get(index);
if (Util.startsWithIgnoreCase(c.getInputText(), text)) {
retVal.add(c);
index++;
}
else {
break;
}
}
}
return retVal;
}
/**
* Removes the specified completion from this provider. This method
* will not remove completions from the parent provider, if there is one.
*
* @param c The completion to remove.
* @return <code>true</code> if this provider contained the specified
* completion.
* @see #clear()
* @see #addCompletion(Completion)
* @see #addCompletions(List)
*/
public boolean removeCompletion(Completion c) {
// Don't just call completions.remove(c) as it'll be a linear search.
int index = Collections.binarySearch(completions, c);
if (index<0) {
return false;
}
completions.remove(index);
return true;
}
/**
* A comparator that compares the input text of a {@link Completion}
* against a String lexicographically, ignoring case.
*/
@SuppressWarnings("rawtypes")
public static class CaseInsensitiveComparator implements Comparator,
Serializable {
@Override
public int compare(Object o1, Object o2) {
String s1 = o1 instanceof String ? (String)o1 :
((Completion)o1).getInputText();
String s2 = o2 instanceof String ? (String)o2 :
((Completion)o2).getInputText();
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
}
}
@@ -0,0 +1,597 @@
/*
* 12/21/2008
*
* AutoCompleteDescWindow.java - A window containing a description of the
* currently selected completion.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* The optional "description" window that describes the currently selected
* item in the auto-completion window.
*
* @author Robert Futrell
* @version 1.0
*/
class AutoCompleteDescWindow extends JWindow implements HyperlinkListener,
DescWindowCallback {
/**
* The parent AutoCompletion instance.
*/
private AutoCompletion ac;
/**
* Renders the HTML description.
*/
private JEditorPane descArea;
/**
* The scroll pane that {@link #descArea} is in.
*/
private JScrollPane scrollPane;
/**
* The bottom panel, containing the toolbar and size grip.
*/
private JPanel bottomPanel;
/**
* The toolbar with "back" and "forward" buttons.
*/
private JToolBar descWindowNavBar;
/**
* Action that goes to the previous description displayed.
*/
private Action backAction;
/**
* Action that goes to the next description displayed.
*/
private Action forwardAction;
/**
* History of descriptions displayed.
*/
private List<HistoryEntry> history;
/**
* The current position in {@link #history}.
*/
private int historyPos;
/**
* Provides a slight delay between asking to set a description and actually
* displaying it, so that if the user is scrolling quickly through
* completions, those with slow-to-calculate summaries won't bog down the
* scrolling.
*/
private Timer timer;
/**
* The action that listens for the timer to fire.
*/
private TimerAction timerAction;
/**
* The resource bundle for this window.
*/
private ResourceBundle bundle;
/**
* The amount of time to wait after the user changes the selected
* completion to refresh the description. This delay is in place to help
* performance for {@link Completion}s that may be slow to compute their
* summary text.
*/
private static final int INITIAL_TIMER_DELAY = 120;
/**
* The resource bundle name.
*/
private static final String MSG =
"org.fife.ui.autocomplete.AutoCompleteDescWindow";
/**
* Constructor.
*
* @param owner The parent window.
* @param ac The parent auto-completion.
*/
public AutoCompleteDescWindow(Window owner, AutoCompletion ac) {
super(owner);
this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation();
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(TipUtil.getToolTipBorder());
descArea = new JEditorPane("text/html", null);
TipUtil.tweakTipEditorPane(descArea);
descArea.addHyperlinkListener(this);
scrollPane = new JScrollPane(descArea);
Border b = BorderFactory.createEmptyBorder();
scrollPane.setBorder(b);
scrollPane.setViewportBorder(b);
scrollPane.setBackground(descArea.getBackground());
scrollPane.getViewport().setBackground(descArea.getBackground());
cp.add(scrollPane);
descWindowNavBar = new JToolBar();
backAction = new ToolBarBackAction(o.isLeftToRight());
forwardAction = new ToolBarForwardAction(o.isLeftToRight());
descWindowNavBar.setFloatable(false);
descWindowNavBar.add(new JButton(backAction));
descWindowNavBar.add(new JButton(forwardAction));
bottomPanel = new JPanel(new BorderLayout());
b = new AbstractBorder() {
@Override
public Insets getBorderInsets(Component c) {
return new Insets(1, 0, 0, 0);
}
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
g.setColor(UIManager.getColor("controlDkShadow"));
g.drawLine(x,y, x+w-1,y);
}
};
bottomPanel.setBorder(b);
SizeGrip rp = new SizeGrip();
bottomPanel.add(descWindowNavBar, BorderLayout.LINE_START);
bottomPanel.add(rp, BorderLayout.LINE_END);
cp.add(bottomPanel, BorderLayout.SOUTH);
setContentPane(cp);
applyComponentOrientation(o);
setFocusableWindowState(false);
// Give apps a chance to decorate us with drop shadows, etc.
if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) {
PopupWindowDecorator decorator = PopupWindowDecorator.get();
if (decorator!=null) {
decorator.decorate(this);
}
}
history = new ArrayList<HistoryEntry>(1); // Usually small
historyPos = -1;
timerAction = new TimerAction();
timer = new Timer(INITIAL_TIMER_DELAY, timerAction);
timer.setRepeats(false);
}
/**
* Sets the currently displayed description and updates the history.
*
* @param historyItem The item to add to the history.
*/
private void addToHistory(HistoryEntry historyItem) {
history.add(++historyPos, historyItem);
clearHistoryAfterCurrentPos();
setActionStates();
}
/**
* Clears the history of viewed descriptions.
*/
private void clearHistory() {
history.clear(); // Try to free some memory.
historyPos = -1;
if (descWindowNavBar!=null) {
setActionStates();
}
}
/**
* Makes the current history page the last one in the history.
*/
private void clearHistoryAfterCurrentPos() {
for (int i=history.size()-1; i>historyPos; i--) {
history.remove(i);
}
setActionStates();
}
/**
* Copies from the description text area, if it is visible and there is
* a selection.
*
* @return Whether a copy occurred.
*/
public boolean copy() {
if (isVisible() &&
descArea.getSelectionStart()!=descArea.getSelectionEnd()) {
descArea.copy();
return true;
}
return false;
}
/**
* Returns the localized message for the specified key.
*
* @param key The key.
* @return The localized message.
*/
private String getString(String key) {
if (bundle==null) {
bundle = ResourceBundle.getBundle(MSG);
}
return bundle.getString(key);
}
/**
* Called when a hyperlink is clicked.
*
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
HyperlinkEvent.EventType type = e.getEventType();
if (!type.equals(HyperlinkEvent.EventType.ACTIVATED)) {
return;
}
// Users can redirect URL's, perhaps to a local copy of documentation.
URL url = e.getURL();
if (url!=null) {
LinkRedirector redirector = AutoCompletion.getLinkRedirector();
if (redirector!=null) {
URL newUrl = redirector.possiblyRedirect(url);
if (newUrl!=null && newUrl!=url) {
url = newUrl;
e = new HyperlinkEvent(e.getSource(), e.getEventType(),
newUrl, e.getDescription(), e.getSourceElement());
}
}
}
// Custom hyperlink handler for this completion type
ExternalURLHandler handler = ac.getExternalURLHandler();
if (handler!=null) {
HistoryEntry current = history.get(historyPos);
handler.urlClicked(e, current.completion, this);
return;
}
// No custom handler...
if (url!=null) {
// Try loading in external browser (Java 6+ only).
try {
Util.browse(new URI(url.toString()));
} catch (/*IO*/URISyntaxException ioe) {
UIManager.getLookAndFeel().provideErrorFeedback(descArea);
ioe.printStackTrace();
}
}
else { // Assume simple function name text, like in c.xml
// FIXME: This is really a hack, and we assume we can find the
// linked-to item in the same CompletionProvider.
AutoCompletePopupWindow parent =
(AutoCompletePopupWindow)getParent();
CompletionProvider p = parent.getSelection().getProvider();
if (p instanceof AbstractCompletionProvider) {
String name = e.getDescription();
List<Completion> l = ((AbstractCompletionProvider)p).
getCompletionByInputText(name);
if (l!=null && !l.isEmpty()) {
// Just use the 1st one if there's more than 1
Completion c = l.get(0);
setDescriptionFor(c, true);
}
else {
UIManager.getLookAndFeel().provideErrorFeedback(descArea);
}
}
}
}
/**
* Enables or disables the back and forward actions as appropriate.
*/
private void setActionStates() {
// TODO: Localize this text!
String desc = null;
if (historyPos>0) {
backAction.setEnabled(true);
desc = "Back to " + history.get(historyPos-1);
}
else {
backAction.setEnabled(false);
}
backAction.putValue(Action.SHORT_DESCRIPTION, desc);
if (historyPos>-1 && historyPos<history.size()-1) {
forwardAction.setEnabled(true);
desc = "Forward to " + history.get(historyPos+1);
}
else {
forwardAction.setEnabled(false);
desc = null;
}
forwardAction.putValue(Action.SHORT_DESCRIPTION, desc);
}
/**
* Sets the description displayed in this window. This clears the
* history.
*
* @param item The item whose description you want to display.
*/
public void setDescriptionFor(Completion item) {
setDescriptionFor(item, false);
}
/**
* Sets the description displayed in this window.
*
* @param item The item whose description you want to display.
* @param addToHistory Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, boolean addToHistory) {
setDescriptionFor(item, null, addToHistory);
}
/**
* Sets the description displayed in this window.
*
* @param item The item whose description you want to display.
* @parma anchor The anchor to jump to, or <code>null</code> if none.
* @param addToHistory Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, String anchor,
boolean addToHistory) {
timer.stop();
timerAction.setCompletion(item, anchor, addToHistory);
timer.start();
}
private void setDisplayedDesc(Completion completion, final String anchor,
boolean addToHistory) {
String desc = completion==null ? null : completion.getSummary();
if (desc==null) {
desc = "<html><em>" + getString("NoDescAvailable") + "</em>";
}
descArea.setText(desc);
if (anchor!=null) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
descArea.scrollToReference(anchor);
}
});
}
else {
descArea.setCaretPosition(0); // In case of scrolling
}
if (!addToHistory) {
// Remove everything first if this is going to be the only
// thing in history.
clearHistory();
}
addToHistory(new HistoryEntry(completion, desc, null));
}
/**
* {@inheritDoc}
*/
@Override
public void setVisible(boolean visible) {
if (!visible) {
clearHistory();
}
super.setVisible(visible);
}
/**
* Callback for custom <code>ExternalURLHandler</code>s.
*
* @param completion The completion to display.
* @param anchor The anchor in the HTML to jump to, or <code>null</code>
* if none.
*/
@Override
public void showSummaryFor(Completion completion, String anchor) {
setDescriptionFor(completion, anchor, true);
}
/**
* Called by the parent completion popup window the LookAndFeel is updated.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(this);
// Update editor pane for new font, bg, selection colors, etc.
TipUtil.tweakTipEditorPane(descArea);
scrollPane.setBackground(descArea.getBackground());
scrollPane.getViewport().setBackground(descArea.getBackground());
((JPanel)getContentPane()).setBorder(TipUtil.getToolTipBorder());
}
/**
* A completion and its cached summary text.
*/
private static class HistoryEntry {
public Completion completion;
public String summary;
public String anchor;
public HistoryEntry(Completion completion, String summary,
String anchor) {
this.completion = completion;
this.summary = summary;
this.anchor = anchor;
}
/**
* Overridden to display a short name for the completion, since it's
* used in the tool tips for the "back" and "forward" buttons.
*
* @return A string representation of this history entry.
*/
@Override
public String toString() {
return completion.getInputText();
}
}
/**
* Action that actually updates the summary text displayed.
*/
private class TimerAction extends AbstractAction {
private Completion completion;
private String anchor;
private boolean addToHistory;
/**
* Called when the timer is fired.
*/
@Override
public void actionPerformed(ActionEvent e) {
setDisplayedDesc(completion, anchor, addToHistory);
}
public void setCompletion(Completion c, String anchor,
boolean addToHistory) {
this.completion = c;
this.anchor = anchor;
this.addToHistory = addToHistory;
}
}
/**
* Action that moves to the previous description displayed.
*/
class ToolBarBackAction extends AbstractAction {
public ToolBarBackAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" +
(ltr ? "left.png" : "right.png");
ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon);
}
@Override
public void actionPerformed(ActionEvent e) {
if (historyPos>0) {
HistoryEntry pair = history.get(--historyPos);
descArea.setText(pair.summary);
if (pair.anchor!=null) {
//System.out.println("Scrolling to: " + pair.anchor);
descArea.scrollToReference(pair.anchor);
}
else {
descArea.setCaretPosition(0);
}
setActionStates();
}
}
}
/**
* Action that moves to the previous description displayed.
*/
class ToolBarForwardAction extends AbstractAction {
public ToolBarForwardAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" +
(ltr ? "right.png" : "left.png");
ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon);
}
@Override
public void actionPerformed(ActionEvent e) {
if (history!=null && historyPos<history.size()-1) {
HistoryEntry pair = history.get(++historyPos);
descArea.setText(pair.summary);
if (pair.anchor!=null) {
//System.out.println("Scrolling to: " + pair.anchor);
descArea.scrollToReference(pair.anchor);
}
else {
descArea.setCaretPosition(0);
}
setActionStates();
}
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,71 @@
/*
* 02/08/2014
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.EventObject;
/**
* An event fired by an instance of {@link AutoCompletion}. This can be
* used by applications that wish to be notified of the auto-complete popup
* window showing and hiding.
*
* @author Robert Futrell
* @version 1.0
*/
public class AutoCompletionEvent extends EventObject {
/**
* The type of this event.
*/
private Type type;
/**
* Constructor.
*
* @param source The <code>AutoCompletion</code> instance that fired
* this event.
* @param type The event type.
*/
public AutoCompletionEvent(AutoCompletion source, Type type) {
super(source);
this.type = type;
}
/**
* Returns the source <code>AutoCompletion</code> instance. This is just
* shorthand for <code>return (AutoCompletion)getSource();</code>.
*
* @return The source <code>AutoCompletion</code> instance.
*/
public AutoCompletion getAutoCompletion() {
return (AutoCompletion)getSource();
}
/**
* Returns the type of this event.
*
* @return The type of this event.
*/
public Type getEventType() {
return type;
}
/**
* Enumeration of the various types of this event.
*/
public static enum Type {
POPUP_SHOWN,
POPUP_HIDDEN
}
}
@@ -0,0 +1,31 @@
/*
* 02/08/2014
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.EventListener;
/**
* An interface that allows listening for interesting events from an
* {@link AutoCompletion}.
*
* @author Robert Futrell
* @version 1.0
*/
public interface AutoCompletionListener extends EventListener {
/**
* Callback notified when a change to the <code>AutoCompletion</code>'s
* status occurs.
*
* @param e The event.
*/
void autoCompleteUpdate(AutoCompletionEvent e);
}
@@ -0,0 +1,120 @@
/*
* 06/24/2012
*
* AutoCompletionStyleContext.java - Manages styles related to auto-completion.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
/**
* Manages the colors shared across the library.
*
* @author Robert Futrell
* @version 1.0
*/
public class AutoCompletionStyleContext {
/**
* The color used to denote the ending caret position for parameterized
* completions.
*/
private Color parameterizedCompletionCursorPositionColor;
/**
* The color used to highlight copies of editable parameters in
* parameterized completions.
*/
private Color parameterCopyColor;
/**
* The color of the outline highlight used to denote editable parameters
* in parameterized completions.
*/
private Color parameterOutlineColor;
public AutoCompletionStyleContext() {
setParameterOutlineColor(Color.gray);
setParameterCopyColor(new Color(0xb4d7ff));
setParameterizedCompletionCursorPositionColor(new Color(0x00b400));
}
/**
* Returns the color of the highlight painted on copies of editable
* parameters in parameterized completions.
*
* @return The color used.
* @see #setParameterCopyColor(Color)
*/
public Color getParameterCopyColor() {
return parameterCopyColor;
}
/**
* Returns the color used to denote the ending caret position for
* parameterized completions.
*
* @return The color used.
* @see #setParameterizedCompletionCursorPositionColor(Color)
*/
public Color getParameterizedCompletionCursorPositionColor() {
return parameterizedCompletionCursorPositionColor;
}
/**
* Returns the color of the outline highlight used to denote editable
* parameters in parameterized completions.
*
* @return The color used.
* @see #setParameterOutlineColor(Color)
*/
public Color getParameterOutlineColor() {
return parameterOutlineColor;
}
/**
* Sets the color of the highlight painted on copies of editable
* parameters in parameterized completions.
*
* @param color The color to use.
* @see #setParameterCopyColor(Color)
*/
public void setParameterCopyColor(Color color) {
this.parameterCopyColor = color;
}
/**
* Sets the color used to denote the ending caret position for
* parameterized completions.
*
* @param color The color to use.
* @see #getParameterizedCompletionCursorPositionColor()
*/
public void setParameterizedCompletionCursorPositionColor(Color color) {
this.parameterizedCompletionCursorPositionColor = color;
}
/**
* Sets the color of the outline highlight used to denote editable
* parameters in parameterized completions.
*
* @param color The color to use.
* @see #getParameterOutlineColor()
*/
public void setParameterOutlineColor(Color color) {
this.parameterOutlineColor = color;
}
}
@@ -0,0 +1,146 @@
/*
* 01/03/2009
*
* BasicCompletion.java - A straightforward Completion implementation.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
/**
* A straightforward {@link Completion} implementation. This implementation
* can be used if you have a relatively short number of static completions
* with no (or short) summaries.<p>
*
* This implementation uses the replacement text as the input text. It also
* includes a "short description" field, which (if non-<code>null</code>), is
* used in the completion choices list.
*
* @author Robert Futrell
* @version 1.0
*/
public class BasicCompletion extends AbstractCompletion {
private String replacementText;
private String shortDesc;
private String summary;
/**
* Constructor.
*
* @param provider The parent completion provider.
* @param replacementText The text to replace.
*/
public BasicCompletion(CompletionProvider provider, String replacementText){
this(provider, replacementText, null);
}
/**
* Constructor.
*
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be <code>null</code>.
*/
public BasicCompletion(CompletionProvider provider, String replacementText,
String shortDesc) {
this(provider, replacementText, shortDesc, null);
}
/**
* Constructor.
*
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be <code>null</code>.
* @param summary The summary of this completion. This should be HTML.
* This may be <code>null</code>.
*/
public BasicCompletion(CompletionProvider provider, String replacementText,
String shortDesc, String summary) {
super(provider);
this.replacementText = replacementText;
this.shortDesc = shortDesc;
this.summary = summary;
}
/**
* {@inheritDoc}
*/
@Override
public String getReplacementText() {
return replacementText;
}
/**
* Returns the short description of this completion, usually used in
* the completion choices list.
*
* @return The short description, or <code>null</code> if there is none.
* @see #setShortDescription(String)
*/
public String getShortDescription() {
return shortDesc;
}
/**
* {@inheritDoc}
*/
@Override
public String getSummary() {
return summary;
}
/**
* Sets the short description of this completion.
*
* @param shortDesc The short description of this completion.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
this.shortDesc = shortDesc;
}
/**
* Sets the summary for this completion.
*
* @param summary The summary for this completion.
* @see #getSummary()
*/
public void setSummary(String summary) {
this.summary = summary;
}
/**
* Returns a string representation of this completion. If the short
* description is not <code>null</code>, this method will return:
*
* <code>getInputText() + " - " + shortDesc</code>
*
* otherwise, it will return <tt>getInputText()</tt>.
*
* @return A string representation of this completion.
*/
@Override
public String toString() {
if (shortDesc==null) {
return getInputText();
}
return getInputText() + " - " + shortDesc;
}
}
@@ -0,0 +1,154 @@
/*
* 12/21/2008
*
* Completion.java - Represents a single completion choice.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import javax.swing.Icon;
import javax.swing.text.JTextComponent;
/**
* Represents a completion choice. A {@link CompletionProvider} returns lists
* of objects implementing this interface. A <tt>Completion</tt> contains the
* following information:
*
* <ul>
* <li>The text the user must (begin to) input for this to be a completion
* choice.
* <li>The text that will be filled in if the user chooses this completion.
* Note that often, this is the same as the text the user must (begin to)
* enter, but this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that
* can be displayed in a helper "tooltip"-style window beside the
* completion list. This may be <code>null</code>. It may also be
* lazily generated to cut down on memory usage.
* <li>The <tt>CompletionProvider</tt> that returned this completion.
* <li>Tool tip text that can be displayed when a mouse hovers over this
* completion in a text component.
* </ul>
*
* @author Robert Futrell
* @version 1.0
* @see AbstractCompletion
*/
public interface Completion extends Comparable<Completion> {
/**
* Compares this completion to another one lexicographically, ignoring
* case.
*
* @param other Another completion instance.
* @return How this completion compares to the other one.
*/
@Override
public int compareTo(Completion other);
/**
* Returns the portion of this completion that has already been entered
* into the text component. The match is case-insensitive.<p>
*
* This is a convenience method for:
* <code>getProvider().getAlreadyEnteredText(comp)</code>.
*
* @param comp The text component.
* @return The already-entered portion of this completion.
*/
public String getAlreadyEntered(JTextComponent comp);
/**
* Returns the icon to use for this completion.
*
* @return The icon, or <code>null</code> for none.
*/
public Icon getIcon();
/**
* Returns the text that the user has to (start) typing for this completion
* to be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be
* a way to implement shorthand, for example, "<code>sysout</code>" mapping
* to "<code>System.out.println(</code>").
*
* @return The text the user has to (start) typing for this completion to
* be offered.
* @see #getReplacementText()
*/
public String getInputText();
/**
* Returns the provider that returned this completion.
*
* @return The provider.
*/
public CompletionProvider getProvider();
/**
* Returns the "relevance" of this completion. This is used when sorting
* completions by their relevance. It is an abstract concept that may
* mean different things to different languages, and may depend on the
* context of the completion.<p>
*
* By default, all completions have a relevance of <code>0</code>. The
* higher the value returned by this method, the higher up in the list
* this completion will be; the lower the value returned, the lower it will
* be. <code>Completion</code>s with equal relevance values will be
* sorted alphabetically.
*
* @return The relevance of this completion.
*/
public int getRelevance();
/**
* Returns the text to insert as the result of this auto-completion. This
* is the "complete" text, including any text that replaces what the user
* has already typed.
*
* @return The replacement text.
* @see #getInputText()
*/
public String getReplacementText();
/**
* Returns the description of this auto-complete choice. This can be
* used in a popup "description window."
*
* @return This item's description. This should be HTML. It may be
* <code>null</code> if there is no description for this
* completion.
*/
public String getSummary();
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.<p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
*/
public String getToolTipText();
}
@@ -0,0 +1,697 @@
/*
* 12/23/2008
*
* CompletionCellRenderer.java - Cell renderer that can render the standard
* completion types like Eclipse or NetBeans does.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
/**
* A cell renderer that adds some pizazz when rendering the standard
* {@link Completion} types, like Eclipse and NetBeans do. Specifically,
* this renderer handles:
*
* <ul>
* <li>{@link FunctionCompletion}s</li>
* <li>{@link VariableCompletion}s</li>
* <li>{@link MarkupTagCompletion}s</li>
* <li>{@link ShorthandCompletion}s</li>
* <li>{@link TemplateCompletion}s</li>
* </ul>
*
* @author Robert Futrell
* @version 1.0
*/
public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* The alternating background color, or <code>null</code> if alternating
* row colors should not be used.
*/
private static Color altBG;
/**
* The font to use when rendering items, or <code>null</code> if the
* list's default font should be used.
*/
private Font font;
/**
* Whether to display the types of fields and return types of functions
* in the completion text.
*/
private boolean showTypes;
/**
* The color to use when rendering types in completion text.
*/
private String typeColor;
/**
* During rendering, whether the item being rendered is selected.
*/
private boolean selected;
/**
* During rendering, this is the "real" background color of the item being
* rendered (i.e., what its background color is if it isn't selected).
*/
private Color realBG;
/**
* The color to use for function arguments.
*/
private String paramColor;
/**
* An icon to use when no appropriate icon is found.
*/
private Icon emptyIcon;
/**
* Used in rendering calculations.
*/
private Rectangle paintTextR;
/**
* An optional delegate renderer (primarily for Substance).
*/
private DefaultListCellRenderer delegate;
private static final String SUBSTANCE_RENDERER_CLASS_NAME =
"org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer";
/**
* Keeps the HTML descriptions from "wrapping" in the list, which cuts off
* words.
*/
private static final String PREFIX = "<html><nobr>";
/**
* Constructor.
*/
public CompletionCellRenderer() {
init();
}
/**
* Constructor. This is primarily a hook for Substance, or any other
* Look and Feel whose renderers look drastically different than standard
* <code>DefaultListCellRenderer</code>s. Everything except for the text
* rendering will be done by the delegate. In almost all scenarios, you
* will want to use the no-argument constructor instead of this one.
*
* @param delegate The delegate renderer.
* @see #delegateToSubstanceRenderer()
*/
public CompletionCellRenderer(DefaultListCellRenderer delegate) {
setDelegateRenderer(delegate);
init();
}
/**
* Creates the icon to use if no icon is found for a specific completion.
* The default implementation returns a 16x16 empty icon.
*
* @return The icon.
* @see #getEmptyIcon()
*/
protected Icon createEmptyIcon() {
return new EmptyIcon(16);
}
/**
* Returns a decent "parameter" color based on the current default
* foreground color.
*
* @return The parameter color to use.
*/
private String createParamColor() {
return Util.isLightForeground(getForeground()) ?
Util.getHexString(Util.getHyperlinkForeground()): "#aa0077";
}
/**
* Returns a decent "type" color based on the current default foreground
* color.
*
* @return The type color to use.
*/
private String createTypeColor() {
return "#808080";
}
/**
* Attempts to delegate rendering to a Substance cell renderer. This
* should only be called if Substance is known to be on the classpath.
*
* @throws Exception If Substance is not on the classpath, or some other
* error occurs creating the Substance cell renderer.
* @see Util#getUseSubstanceRenderers()
* @see #setDelegateRenderer(DefaultListCellRenderer)
*/
public void delegateToSubstanceRenderer() throws Exception {
Class<?> clazz = Class.forName(SUBSTANCE_RENDERER_CLASS_NAME);
DefaultListCellRenderer delegate =
(DefaultListCellRenderer)clazz.newInstance();
setDelegateRenderer(delegate);
}
/**
* Returns the background color to use on alternating lines.
*
* @return The alternate background color. If this is <code>null</code>,
* alternating colors are not used.
* @see #setAlternateBackground(Color)
*/
public static Color getAlternateBackground() {
return altBG;
}
/**
* Returns the delegate renderer, or <code>null</code> if there is none.
*
* @return The delegate renderer.
* @see #setDelegateRenderer(DefaultListCellRenderer)
*/
public DefaultListCellRenderer getDelegateRenderer() {
return delegate;
}
/**
* Returns the font used when rendering completions.
*
* @return The font. If this is <code>null</code>, then the default list
* font is used.
* @see #setDisplayFont(Font)
*/
public Font getDisplayFont() {
return font;
}
/**
* Returns the icon to use if no icon is found for a specific completion.
* This icon is lazily created if necessary.
*
* @return The icon.
* @see #createEmptyIcon()
*/
protected Icon getEmptyIcon() {
if (emptyIcon==null) {
emptyIcon = createEmptyIcon();
}
return emptyIcon;
}
/**
* Returns an icon.
*
* @param resource The icon to retrieve. This should either be a file,
* or a resource loadable by the current ClassLoader.
* @return The icon.
*/
protected Icon getIcon(String resource) {
URL url = getClass().getResource(resource);
if (url==null) {
File file = new File(resource);
try {
url = file.toURI().toURL();
} catch (MalformedURLException mue) {
mue.printStackTrace(); // Never happens
}
}
return url!=null ? new ImageIcon(url) : null;
}
/**
* Returns the renderer.
*
* @param list The list of choices being rendered.
* @param value The {@link Completion} being rendered.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean selected, boolean hasFocus) {
super.getListCellRendererComponent(list,value,index,selected,hasFocus);
if (font!=null) {
setFont(font); // Overrides super's setFont(list.getFont()).
}
this.selected = selected;
this.realBG = altBG!=null && (index&1)==1 ? altBG : list.getBackground();
Completion c = (Completion)value;
setIcon(c.getIcon());
if (c instanceof FunctionCompletion) {
FunctionCompletion fc = (FunctionCompletion)value;
prepareForFunctionCompletion(list, fc, index, selected, hasFocus);
}
else if (c instanceof VariableCompletion) {
VariableCompletion vc = (VariableCompletion)value;
prepareForVariableCompletion(list, vc, index, selected, hasFocus);
}
else if (c instanceof TemplateCompletion) {
TemplateCompletion tc = (TemplateCompletion)value;
prepareForTemplateCompletion(list, tc, index, selected, hasFocus);
}
else if (c instanceof MarkupTagCompletion) {
MarkupTagCompletion mtc = (MarkupTagCompletion)value;
prepareForMarkupTagCompletion(list, mtc, index, selected, hasFocus);
}
else {
prepareForOtherCompletion(list, c, index, selected, hasFocus);
}
// A delegate renderer might do its own alternate row striping
// (Substance does).
if (delegate!=null) {
delegate.getListCellRendererComponent(list, getText(), index,
selected, hasFocus);
delegate.setFont(getFont());
delegate.setIcon(getIcon());
return delegate;
}
if (!selected && (index&1)==1 && altBG!=null) {
setBackground(altBG);
}
return this;
}
/**
* Returns whether the types of fields and return types of methods are
* shown in the completion text.
*
* @return Whether to show the types.
* @see #setShowTypes(boolean)
*/
public boolean getShowTypes() {
return showTypes;
}
private void init() {
//setDisplayFont(new Font("Monospaced", Font.PLAIN, 12));
setShowTypes(true);
typeColor = createTypeColor();
paramColor = createParamColor();
paintTextR = new Rectangle();
}
@Override
protected void paintComponent(Graphics g) {
//super.paintComponent(g);
g.setColor(realBG);
int iconW = 0;
if (getIcon()!=null) {
iconW = getIcon().getIconWidth();
}
if (selected && iconW>0) { // The icon area is never in the "selection"
g.fillRect(0, 0, iconW, getHeight());
g.setColor(getBackground());
g.fillRect(iconW,0, getWidth()-iconW,getHeight());
}
else {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
if (getIcon()!=null) {
getIcon().paintIcon(this, g, 0, 0);
}
String text = getText();
if (text != null) {
paintTextR.setBounds(iconW,0, getWidth()-iconW,getHeight());
paintTextR.x += 3; // Force a slight margin
int space = paintTextR.height - g.getFontMetrics().getHeight();
View v = (View)getClientProperty(BasicHTML.propertyKey);
if (v != null) {
// HTML rendering doesn't auto-center vertically, for some
// reason
paintTextR.y += space/2;
paintTextR.height -= space;
v.paint(g, paintTextR);
}
else {
int textX = paintTextR.x;
int textY = paintTextR.y;// + g.getFontMetrics().getAscent();
//System.out.println(g.getFontMetrics().getAscent());
g.drawString(text, textX, textY);
}
}
}
/**
* Prepares this renderer to display a function completion.
*
* @param list The list of choices being rendered.
* @param fc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForFunctionCompletion(JList list,
FunctionCompletion fc, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(fc.getName());
char paramListStart = fc.getProvider().getParameterListStart();
if (paramListStart!=0) { // 0 => no start char
sb.append(paramListStart);
}
int paramCount = fc.getParamCount();
for (int i=0; i<paramCount; i++) {
FunctionCompletion.Parameter param = fc.getParam(i);
String type = param.getType();
String name = param.getName();
if (type!=null) {
if (!selected) {
sb.append("<font color='").append(paramColor).append("'>");
}
sb.append(type);
if (!selected) {
sb.append("</font>");
}
if (name!=null) {
sb.append(' ');
}
}
if (name!=null) {
sb.append(name);
}
if (i<paramCount-1) {
sb.append(fc.getProvider().getParameterListSeparator());
}
}
char paramListEnd = fc.getProvider().getParameterListEnd();
if (paramListEnd!=0) { // 0 => No parameter list end char
sb.append(paramListEnd);
}
if (getShowTypes() && fc.getType()!=null) {
sb.append(" : ");
if (!selected) {
sb.append("<font color='").append(typeColor).append("'>");
}
sb.append(fc.getType());
if (!selected) {
sb.append("</font>");
}
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a markup tag completion.
*
* @param list The list of choices being rendered.
* @param mc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForMarkupTagCompletion(JList list,
MarkupTagCompletion mc, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(mc.getName());
setText(sb.toString());
}
/**
* Prepares this renderer to display a completion not specifically handled
* elsewhere.
*
* @param list The list of choices being rendered.
* @param c The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForOtherCompletion(JList list,
Completion c, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(c.getInputText());
if (c instanceof BasicCompletion) {
String definition = ((BasicCompletion)c).getShortDescription();
if (definition!=null) {
sb.append(" - ");
if (!selected) {
sb.append("<font color='").append(typeColor).append("'>");
}
sb.append(definition);
if (!selected) {
sb.append("</font>");
}
}
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a template completion.
*
* @param list The list of choices being rendered.
* @param tc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForTemplateCompletion(JList list,
TemplateCompletion tc, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(tc.getInputText());
String definition = tc.getShortDescription();
if (definition!=null) {
sb.append(" - ");
if (!selected) {
sb.append("<font color='").append(typeColor).append("'>");
}
sb.append(definition);
if (!selected) {
sb.append("</font>");
}
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a variable completion.
*
* @param list The list of choices being rendered.
* @param vc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForVariableCompletion(JList list,
VariableCompletion vc, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(vc.getName());
if (getShowTypes() && vc.getType()!=null) {
sb.append(" : ");
if (!selected) {
sb.append("<font color='").append(typeColor).append("'>");
}
sb.append(vc.getType());
if (!selected) {
sb.append("</font>");
}
}
setText(sb.toString());
}
/**
* Sets the background color to use on alternating lines.
*
* @param altBG The new alternate background color. If this is
* <code>null</code>, alternating lines will not use different
* background colors.
* @see #getAlternateBackground()
*/
public static void setAlternateBackground(Color altBG) {
CompletionCellRenderer.altBG = altBG;
}
/**
* Sets the delegate renderer. Most users will never use this method; it
* is primarily a hook for Substance and other Look and Feels whose
* renderers look drastically different from the standard
* <code>DefaultListCellRenderer</code>.
*
* @param delegate The new delegate renderer. If this is <code>null</code>,
* the default rendering of this component is used.
* @see #getDelegateRenderer()
* @see #delegateToSubstanceRenderer()
*/
public void setDelegateRenderer(DefaultListCellRenderer delegate) {
this.delegate = delegate;
}
/**
* Sets the font to use when rendering completion items.
*
* @param font The font to use. If this is <code>null</code>, then
* the default list font is used.
* @see #getDisplayFont()
*/
public void setDisplayFont(Font font) {
this.font = font;
}
/**
* Sets the icon to display based off of a completion, falling back to the
* empty icon if the completion has no icon.
*
* @param completion The completion to check.
* @see #setIconWithDefault(Completion, Icon)
*/
protected void setIconWithDefault(Completion completion) {
setIconWithDefault(completion, getEmptyIcon());
}
/**
* Sets the icon to display based off of a completion, falling back to a
* default icon if the completion has no icon.
*
* @param completion The completion to check.
* @param defaultIcon The icon to use if <code>completion</code> does not
* specify an icon.
* @see #setIconWithDefault(Completion)
*/
protected void setIconWithDefault(Completion completion, Icon defaultIcon) {
Icon icon = completion.getIcon();
setIcon(icon!=null ? icon :
(defaultIcon!=null ? defaultIcon : emptyIcon));
}
/**
* Sets the color to use for function arguments.
*
* @param color The color to use. This is ignored if <code>null</code>.
* @see #setTypeColor(Color)
*/
public void setParamColor(Color color) {
if (color!=null) {
paramColor = Util.getHexString(color);
}
}
/**
* Sets whether the types of fields and return types of methods are
* shown in the completion text.
*
* @param show Whether to show the types.
* @see #getShowTypes()
*/
public void setShowTypes(boolean show) {
this.showTypes = show;
}
/**
* Sets the color to use for function/field types. Note that if
* {@link #getShowTypes()} returns <code>false</code>, this property
* effectively does nothing.
*
* @param color The color to use for types. This is ignored if
* <code>null</code>.
* @see #setShowTypes(boolean)
* @see #setParamColor(Color)
*/
public void setTypeColor(Color color) {
if (color!=null) {
typeColor = Util.getHexString(color);
}
}
/**
* Overridden to update our delegate, if necessary.
*/
@Override
public void updateUI() {
super.updateUI();
if (delegate!=null) {
SwingUtilities.updateComponentTreeUI(delegate);
}
paramColor = createParamColor();
}
}
@@ -0,0 +1,90 @@
/*
* 12/22/2008
*
* CompletionListModel.java - A model that allows bulk addition of elements.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.AbstractListModel;
/**
* A list model implementation that allows the bulk addition of elements.
* This is the only feature missing from <code>DefaultListModel</code> that
* we need.
*
* @author Robert Futrell
* @version 1.0
*/
class CompletionListModel extends AbstractListModel {
/**
* Container for items in this model.
*/
private List<Completion> delegate;
/**
* Constructor.
*/
public CompletionListModel() {
delegate = new ArrayList<Completion>();
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns (unless it throws an exception).
*
* @see #setContents(Collection)
*/
public void clear() {
int end = delegate.size()-1;
delegate.clear();
if (end >= 0) {
fireIntervalRemoved(this, 0, end);
}
}
/**
* {@inheritDoc}
*/
@Override
public Object getElementAt(int index) {
return delegate.get(index);
}
/**
* {@inheritDoc}
*/
@Override
public int getSize() {
return delegate.size();
}
/**
* Sets the contents of this model. All previous contents are removed.
*
* @param contents The new contents of this model.
*/
public void setContents(Collection<Completion> contents) {
clear();
int count = contents.size();
if (count>0) {
delegate.addAll(contents);
fireIntervalAdded(this, 0, count-1); // endpoints included (!)
}
}
}
@@ -0,0 +1,213 @@
/*
* 12/21/2008
*
* CompletionProvider.java - Provides autocompletion values based on the
* text currently in a text component.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Point;
import java.util.List;
import javax.swing.ListCellRenderer;
import javax.swing.text.JTextComponent;
/**
* Provides autocompletion values to an {@link AutoCompletion}.<p>
*
* Completion providers can have an optional parent. Parents are searched for
* completions when their children are. This allows for chaining of completion
* providers.
*
* @author Robert Futrell
* @version 1.0
*/
public interface CompletionProvider {
/**
* Clears the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). After this method is called, functions and
* methods will not have their parameters auto-completed.
*
* @see #setParameterizedCompletionParams(char, String, char)
*/
public void clearParameterizedCompletionParams();
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.
*
* @param comp The text component.
* @return The text. A return value of <code>null</code> means nothing
* should be auto-completed; a value of an empty string
* (<code>""</code>) means auto-completion should still be
* considered (i.e., all possible choices are valid).
*/
public String getAlreadyEnteredText(JTextComponent comp);
/**
* Gets the possible completions for the text component at the current
* caret position.
*
* @param comp The text component.
* @return The list of {@link Completion}s. If no completions are
* available, this method should return an empty list.
*/
public List<Completion> getCompletions(JTextComponent comp);
/**
* Returns the completions that have been entered at the specified visual
* location. This can be used for tool tips when the user hovers the
* mouse over completed text.
*
* @param comp The text component.
* @param p The position, usually from a <tt>MouseEvent</tt>.
* @return The completions, or an empty list if there are none.
*/
public List<Completion> getCompletionsAt(JTextComponent comp, Point p);
/**
* Returns the cell renderer for completions returned from this provider.
*
* @return The cell renderer, or <code>null</code> if the default should
* be used.
* @see #setListCellRenderer(ListCellRenderer)
*/
public ListCellRenderer getListCellRenderer();
/**
* Returns an object that can return a list of completion choices for
* parameters. This is used when a user code-completes a parameterized
* completion, such as a function or method. For any parameter to the
* function/method, this object can return possible completions.
*
* @return The parameter choices provider, or <code>null</code> if
* none is installed.
*/
public ParameterChoicesProvider getParameterChoicesProvider();
/**
* Returns a list of parameterized completions that have been entered
* at the current caret position of a text component (and thus can have
* their completion choices displayed).
*
* @param tc The text component.
* @return The list of {@link ParameterizedCompletion}s. If no completions
* are available, this may be <code>null</code>.
*/
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc);
/**
* Returns the text that marks the end of a list of parameters to a
* function or method.
*
* @return The text for a parameter list end, for example,
* '<code>)</code>'.
* @see #getParameterListStart()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public char getParameterListEnd();
/**
* Returns the text that separates parameters to a function or method.
*
* @return The text that separates parameters, for example,
* "<code>, </code>".
* @see #getParameterListStart()
* @see #getParameterListEnd()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public String getParameterListSeparator();
/**
* Returns the text that marks the start of a list of parameters to a
* function or method.
*
* @return The text for a parameter list start, for example,
* "<code>(</code>".
* @see #getParameterListEnd()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public char getParameterListStart();
/**
* Returns the parent completion provider.
*
* @return The parent completion provider.
* @see #setParent(CompletionProvider)
*/
public CompletionProvider getParent();
/**
* This method is called if auto-activation is enabled in the parent
* {@link AutoCompletion} after the user types a single character. This
* provider should check the text at the current caret position of the
* text component, and decide whether auto-activation would be appropriate
* here. For example, a <code>CompletionProvider</code> for Java might
* want to return <code>true</code> for this method only if the last
* character typed was a '<code>.</code>'.
*
* @param tc The text component.
* @return Whether auto-activation would be appropriate.
*/
public boolean isAutoActivateOkay(JTextComponent tc);
/**
* Sets the renderer to use when displaying completion choices.
*
* @param r The renderer to use.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer r);
/**
* Sets the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). If this method isn't called, functions
* and methods will not have their parameters auto-completed.
*
* @param listStart The character that marks the beginning of a list of
* parameters, such as '<tt>(</tt>' in C or Java.
* @param separator Text that should separate parameters in a parameter
* list when one is inserted. For example, "<tt>, </tt>".
* @param listEnd The character that marks the end of a list of parameters,
* such as '<tt>)</tt>' in C or Java.
* @throws IllegalArgumentException If either <tt>listStart</tt> or
* <tt>listEnd</tt> is not printable ASCII, or if
* <tt>separator</tt> is <code>null</code> or an empty string.
* @see #clearParameterizedCompletionParams()
*/
public void setParameterizedCompletionParams(char listStart,
String separator, char listEnd);
/**
* Sets the parent completion provider.
*
* @param parent The parent provider. <code>null</code> means there will
* be no parent provider.
* @see #getParent()
*/
public void setParent(CompletionProvider parent);
}
@@ -0,0 +1,276 @@
/*
* 02/06/2010
*
* CompletionProviderBase.java - Base completion provider implementation.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.ListCellRenderer;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Segment;
/**
* A base class for all standard completion providers. This class implements
* functionality that should be sharable across all <tt>CompletionProvider</tt>
* implementations.
*
* @author Robert Futrell
* @version 1.0
* @see AbstractCompletionProvider
*/
public abstract class CompletionProviderBase implements CompletionProvider {
/**
* The parent completion provider.
*/
private CompletionProvider parent;
/**
* The renderer to use for completions from this provider. If this is
* <code>null</code>, a default renderer is used.
*/
private ListCellRenderer listCellRenderer;
/**
* Text that marks the beginning of a parameter list, for example, '('.
*/
private char paramListStart;
/**
* Text that marks the end of a parameter list, for example, ')'.
*/
private char paramListEnd;
/**
* Text that separates items in a parameter list, for example, ", ".
*/
private String paramListSeparator;
/**
* Whether auto-activation should occur after letters.
*/
private boolean autoActivateAfterLetters;
/**
* Non-letter chars that should cause auto-activation to occur.
*/
private String autoActivateChars;
/**
* Provides completion choices for a parameterized completion's parameters.
*/
private ParameterChoicesProvider paramChoicesProvider;
/**
* A segment to use for fast char access.
*/
private Segment s = new Segment();
protected static final String EMPTY_STRING = "";
/**
* Comparator used to sort completions by their relevance before sorting
* them lexicographically.
*/
private static final Comparator<Completion> sortByRelevanceComparator =
new SortByRelevanceComparator();
/**
* {@inheritDoc}
*/
@Override
public void clearParameterizedCompletionParams() {
paramListEnd = paramListStart = 0;
paramListSeparator = null;
}
/**
* {@inheritDoc}
*/
@Override
public List<Completion> getCompletions(JTextComponent comp) {
List<Completion> completions = getCompletionsImpl(comp);
if (parent!=null) {
List<Completion> parentCompletions = parent.getCompletions(comp);
if (parentCompletions!=null) {
completions.addAll(parentCompletions);
Collections.sort(completions);
}
}
// NOTE: We can't sort by relevance prior to this; we need to have
// things alphabetical so we can easily narrow down completions to
// those starting with what was already typed.
if (/*sortByRelevance*/true) {
Collections.sort(completions, sortByRelevanceComparator);
}
return completions;
}
/**
* Does the dirty work of creating a list of completions.
*
* @param comp The text component to look in.
* @return The list of possible completions, or an empty list if there
* are none.
*/
protected abstract List<Completion> getCompletionsImpl(JTextComponent comp);
/**
* {@inheritDoc}
*/
@Override
public ListCellRenderer getListCellRenderer() {
return listCellRenderer;
}
/**
* {@inheritDoc}
*/
@Override
public ParameterChoicesProvider getParameterChoicesProvider() {
return paramChoicesProvider;
}
/**
* {@inheritDoc}
*/
@Override
public char getParameterListEnd() {
return paramListEnd;
}
/**
* {@inheritDoc}
*/
@Override
public String getParameterListSeparator() {
return paramListSeparator;
}
/**
* {@inheritDoc}
*/
@Override
public char getParameterListStart() {
return paramListStart;
}
/**
* {@inheritDoc}
*/
@Override
public CompletionProvider getParent() {
return parent;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAutoActivateOkay(JTextComponent tc) {
Document doc = tc.getDocument();
char ch = 0;
try {
doc.getText(tc.getCaretPosition(), 1, s);
ch = s.first();
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
return (autoActivateAfterLetters && Character.isLetter(ch)) ||
(autoActivateChars!=null && autoActivateChars.indexOf(ch)>-1);
}
/**
* Sets the characters that auto-activation should occur after. A Java
* completion provider, for example, might want to set <code>others</code>
* to "<code>.</code>", to allow auto-activation for members of an object.
*
* @param letters Whether auto-activation should occur after any letter.
* @param others A string of (non-letter) chars that auto-activation should
* occur after. This may be <code>null</code>.
*/
public void setAutoActivationRules(boolean letters, String others) {
autoActivateAfterLetters = letters;
autoActivateChars = others;
}
/**
* Sets the param choices provider. This is used when a user
* code-completes a parameterized completion, such as a function or method.
* For any parameter to the function/method, this object can return
* possible completions.
*
* @param pcp The parameter choices provider, or <code>null</code> for
* none.
* @see #getParameterChoicesProvider()
*/
public void setParameterChoicesProvider(ParameterChoicesProvider pcp) {
paramChoicesProvider = pcp;
}
/**
* {@inheritDoc}
*/
@Override
public void setListCellRenderer(ListCellRenderer r) {
listCellRenderer = r;
}
/**
* {@inheritDoc}
*/
@Override
public void setParameterizedCompletionParams(char listStart,
String separator, char listEnd) {
if (listStart<0x20 || listStart==0x7F) {
throw new IllegalArgumentException("Invalid listStart");
}
if (listEnd<0x20 || listEnd==0x7F) {
throw new IllegalArgumentException("Invalid listEnd");
}
if (separator==null || separator.length()==0) {
throw new IllegalArgumentException("Invalid separator");
}
paramListStart = listStart;
paramListSeparator = separator;
paramListEnd = listEnd;
}
/**
* {@inheritDoc}
*/
@Override
public void setParent(CompletionProvider parent) {
this.parent = parent;
}
}
@@ -0,0 +1,446 @@
/*
* 02/06/2010
*
* CompletionXMLParser.java - Parses XML representing code completion for a
* C-like language.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parser for an XML file describing a procedural language such as C. XML
* files will be validated against the <code>CompletionXml.dtd</code> DTD
* found in this package.
*
* @author Robert Futrell
* @version 1.0
*/
public class CompletionXMLParser extends DefaultHandler {
/**
* The completions found after parsing the XML.
*/
private List<Completion> completions;
/**
* The provider we're getting completions for.
*/
private CompletionProvider provider;
/**
* The completion provider to use when loading classes, such as custom
* {@link FunctionCompletion}s.
*/
private ClassLoader completionCL;
private String name;
private String type;
private String returnType;
private StringBuilder returnValDesc;
private StringBuilder desc;
private String paramName;
private String paramType;
private StringBuilder paramDesc;
private List<ParameterizedCompletion.Parameter> params;
private String definedIn;
private boolean doingKeywords;
private boolean inKeyword;
private boolean gettingReturnValDesc;
private boolean gettingDesc;
private boolean gettingParams;
private boolean inParam;
private boolean gettingParamDesc;
private boolean inCompletionTypes;
private char paramStartChar;
private char paramEndChar;
private String paramSeparator;
/**
* If specified in the XML, this class will be used instead of
* {@link FunctionCompletion} when appropriate. This class should extend
* <tt>FunctionCompletion</tt>, or stuff will break.
*/
private String funcCompletionType;
/**
* The class loader to use to load custom completion classes, such as
* the one defined by {@link #funcCompletionType}. If this is
* <code>null</code>, then a default class loader is used. This field
* will usually be <code>null</code>.
*/
private static ClassLoader DEFAULT_COMPLETION_CLASS_LOADER;
/**
* Constructor.
*
* @param provider The provider to get completions for.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider) {
this(provider, null);
}
/**
* Constructor.
*
* @param provider The provider to get completions for.
* @param cl The class loader to use, if necessary, when loading classes
* from the XML (custom {@link FunctionCompletion}s, for example).
* This may be <code>null</code> if the default is to be used, or
* if the XML does not define specific classes for completion types.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider, ClassLoader cl) {
this.provider = provider;
this.completionCL = cl;
if (completionCL==null) {
// May also be null, but that's okay.
completionCL = DEFAULT_COMPLETION_CLASS_LOADER;
}
completions = new ArrayList<Completion>();
params = new ArrayList<ParameterizedCompletion.Parameter>(1);
desc = new StringBuilder();
paramDesc = new StringBuilder();
returnValDesc = new StringBuilder();
paramStartChar = paramEndChar = 0;
paramSeparator = null;
}
/**
* Called when character data inside an element is found.
*/
@Override
public void characters(char[] ch, int start, int length) {
if (gettingDesc) {
desc.append(ch, start, length);
}
else if (gettingParamDesc) {
paramDesc.append(ch, start, length);
}
else if (gettingReturnValDesc) {
returnValDesc.append(ch, start, length);
}
}
private FunctionCompletion createFunctionCompletion() {
FunctionCompletion fc = null;
if (funcCompletionType!=null) {
try {
Class<?> clazz = null;
if (completionCL!=null) {
clazz = Class.forName(funcCompletionType, true,
completionCL);
}
else {
clazz = Class.forName(funcCompletionType);
}
Constructor<?> c = clazz.getDeclaredConstructor(
CompletionProvider.class, String.class, String.class);
fc = (FunctionCompletion)c.newInstance(provider, name,
returnType);
} catch (RuntimeException re) { // FindBugs
throw re;
} catch (Exception e) {
e.printStackTrace();
}
}
if (fc==null) { // Fallback if completion failed for some reason
fc = new FunctionCompletion(provider, name, returnType);
}
if (desc.length()>0) {
fc.setShortDescription(desc.toString());
desc.setLength(0);
}
fc.setParams(params);
fc.setDefinedIn(definedIn);
if (returnValDesc.length()>0) {
fc.setReturnValueDescription(returnValDesc.toString());
returnValDesc.setLength(0);
}
return fc;
}
private BasicCompletion createOtherCompletion() {
BasicCompletion bc = new BasicCompletion(provider, name);
if (desc.length()>0) {
bc.setSummary(desc.toString());
desc.setLength(0);
}
return bc;
}
private MarkupTagCompletion createMarkupTagCompletion() {
MarkupTagCompletion mc = new MarkupTagCompletion(provider,
name);
if (desc.length()>0) {
mc.setDescription(desc.toString());
desc.setLength(0);
}
mc.setAttributes(params);
mc.setDefinedIn(definedIn);
return mc;
}
private VariableCompletion createVariableCompletion() {
VariableCompletion vc = new VariableCompletion(provider,
name, returnType);
if (desc.length()>0) {
vc.setShortDescription(desc.toString());
desc.setLength(0);
}
vc.setDefinedIn(definedIn);
return vc;
}
/**
* Called when an element is closed.
*/
@Override
public void endElement(String uri, String localName, String qName) {
if ("keywords".equals(qName)) {
doingKeywords = false;
}
else if (doingKeywords) {
if ("keyword".equals(qName)) {
Completion c = null;
if ("function".equals(type)) {
c = createFunctionCompletion();
}
else if ("constant".equals(type)) {
c = createVariableCompletion();
}
else if ("tag".equals(type)) { // Markup tag, such as HTML
c = createMarkupTagCompletion();
}
else if ("other".equals(type)) {
c = createOtherCompletion();
}
else {
throw new InternalError("Unexpected type: " + type);
}
completions.add(c);
inKeyword = false;
}
else if (inKeyword) {
if ("returnValDesc".equals(qName)) {
gettingReturnValDesc = false;
}
else if (gettingParams) {
if ("params".equals(qName)) {
gettingParams = false;
}
else if ("param".equals(qName)) {
FunctionCompletion.Parameter param =
new FunctionCompletion.Parameter(paramType,
paramName);
if (paramDesc.length()>0) {
param.setDescription(paramDesc.toString());
paramDesc.setLength(0);
}
params.add(param);
inParam = false;
}
else if (inParam) {
if ("desc".equals(qName)) {
gettingParamDesc = false;
}
}
}
else if ("desc".equals(qName)) {
gettingDesc = false;
}
}
}
else if (inCompletionTypes) {
if ("completionTypes".equals(qName)) {
inCompletionTypes = false;
}
}
}
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
/**
* Returns the completions found after parsing the XML.
*
* @return The completions.
*/
public List<Completion> getCompletions() {
return completions;
}
/**
* Returns the parameter end character specified.
*
* @return The character, or 0 if none was specified.
*/
public char getParamEndChar() {
return paramEndChar;
}
/**
* Returns the parameter end string specified.
*
* @return The string, or <code>null</code> if none was specified.
*/
public String getParamSeparator() {
return paramSeparator;
}
/**
* Returns the parameter start character specified.
*
* @return The character, or 0 if none was specified.
*/
public char getParamStartChar() {
return paramStartChar;
}
private static final char getSingleChar(String str) {
return str.length()==1 ? str.charAt(0) : 0;
}
/**
* Resets this parser to grab more completions.
*
* @param provider The new provider to get completions for.
*/
public void reset(CompletionProvider provider) {
this.provider = provider;
completions.clear();
doingKeywords = inKeyword = gettingDesc = gettingParams = inParam =
gettingParamDesc = false;
paramStartChar = paramEndChar = 0;
paramSeparator = null;
}
@Override
public InputSource resolveEntity(String publicID,
String systemID) throws SAXException {
return new InputSource(getClass().
getResourceAsStream("CompletionXml.dtd"));
}
/**
* Sets the class loader to use when loading custom classes to use for
* various {@link Completion} types, such as {@link FunctionCompletion}s,
* from XML.<p>
*
* Users should very rarely have a need to use this method.
*
* @param cl The class loader to use. If this is <code>null</code>, then
* a default is used.
*/
public static void setDefaultCompletionClassLoader(ClassLoader cl) {
DEFAULT_COMPLETION_CLASS_LOADER = cl;
}
/**
* Called when an element starts.
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attrs) {
if ("keywords".equals(qName)) {
doingKeywords = true;
}
else if (doingKeywords) {
if ("keyword".equals(qName)) {
name = attrs.getValue("name");
type = attrs.getValue("type");
returnType = attrs.getValue("returnType");
params.clear();
definedIn = attrs.getValue("definedIn");
inKeyword = true;
}
else if (inKeyword) {
if ("returnValDesc".equals(qName)) {
gettingReturnValDesc = true;
}
else if ("params".equals(qName)) {
gettingParams = true;
}
else if (gettingParams) {
if ("param".equals(qName)) {
paramName = attrs.getValue("name");
paramType = attrs.getValue("type");
inParam = true;
}
if (inParam) {
if ("desc".equals(qName)) {
gettingParamDesc = true;
}
}
}
else if ("desc".equals(qName)) {
gettingDesc = true;
}
}
}
else if ("environment".equals(qName)) {
paramStartChar = getSingleChar(attrs.getValue("paramStartChar"));
paramEndChar = getSingleChar(attrs.getValue("paramEndChar"));
paramSeparator = attrs.getValue("paramSeparator");
//paramTerminal = attrs.getValua("terminal");
}
else if ("completionTypes".equals(qName)) {
inCompletionTypes = true;
}
else if (inCompletionTypes) {
if ("functionCompletionType".equals(qName)) {
funcCompletionType = attrs.getValue("type");
}
}
}
@Override
public void warning(SAXParseException e) throws SAXException {
throw e;
}
}
@@ -0,0 +1,376 @@
/*
* 12/21/2008
*
* DefaultCompletionProvider.java - A basic completion provider implementation.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Segment;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
/**
* A basic completion provider implementation. This provider has no
* understanding of language semantics. It simply checks the text entered up
* to the caret position for a match against known completions. This is all
* that is needed in the majority of cases.
*
* @author Robert Futrell
* @version 1.0
*/
public class DefaultCompletionProvider extends AbstractCompletionProvider {
protected Segment seg;
/**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}.
*/
private String lastCompletionsAtText;
/**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)},
* since this may be called multiple times in succession (this is usually
* called by <tt>JTextComponent.getToolTipText()</tt>, and if the user
* wiggles the mouse while a tool tip is displayed, this method gets
* repeatedly called. It can be costly so we try to speed it up a tad).
*/
private List<Completion> lastParameterizedCompletionsAt;
/**
* Constructor. The returned provider will not be aware of any completions.
*
* @see #addCompletion(Completion)
*/
public DefaultCompletionProvider() {
init();
}
/**
* Creates a completion provider that provides completion for a simple
* list of words.
*
* @param words The words to offer as completion suggestions. If this is
* <code>null</code>, no completions will be known.
* @see BasicCompletion
*/
public DefaultCompletionProvider(String[] words) {
init();
addWordCompletions(words);
}
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.<p>
*
* This method returns all characters before the caret that are matched
* by {@link #isValidChar(char)}.
*
* {@inheritDoc}
*/
@Override
public String getAlreadyEnteredText(JTextComponent comp) {
Document doc = comp.getDocument();
int dot = comp.getCaretPosition();
Element root = doc.getDefaultRootElement();
int index = root.getElementIndex(dot);
Element elem = root.getElement(index);
int start = elem.getStartOffset();
int len = dot-start;
try {
doc.getText(start, len, seg);
} catch (BadLocationException ble) {
ble.printStackTrace();
return EMPTY_STRING;
}
int segEnd = seg.offset + len;
start = segEnd - 1;
while (start>=seg.offset && isValidChar(seg.array[start])) {
start--;
}
start++;
len = segEnd - start;
return len==0 ? EMPTY_STRING : new String(seg.array, start, len);
}
/**
* {@inheritDoc}
*/
@Override
public List<Completion> getCompletionsAt(JTextComponent tc, Point p) {
int offset = tc.viewToModel(p);
if (offset<0 || offset>=tc.getDocument().getLength()) {
lastCompletionsAtText = null;
return lastParameterizedCompletionsAt = null;
}
Segment s = new Segment();
Document doc = tc.getDocument();
Element root = doc.getDefaultRootElement();
int line = root.getElementIndex(offset);
Element elem = root.getElement(line);
int start = elem.getStartOffset();
int end = elem.getEndOffset() - 1;
try {
doc.getText(start, end-start, s);
// Get the valid chars before the specified offset.
int startOffs = s.offset + (offset-start) - 1;
while (startOffs>=s.offset && isValidChar(s.array[startOffs])) {
startOffs--;
}
// Get the valid chars at and after the specified offset.
int endOffs = s.offset + (offset-start);
while (endOffs<s.offset+s.count && isValidChar(s.array[endOffs])) {
endOffs++;
}
int len = endOffs - startOffs - 1;
if (len<=0) {
return lastParameterizedCompletionsAt = null;
}
String text = new String(s.array, startOffs+1, len);
if (text.equals(lastCompletionsAtText)) {
return lastParameterizedCompletionsAt;
}
// Get a list of all Completions matching the text.
List<Completion> list = getCompletionByInputText(text);
lastCompletionsAtText = text;
return lastParameterizedCompletionsAt = list;
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
lastCompletionsAtText = null;
return lastParameterizedCompletionsAt = null;
}
/**
* {@inheritDoc}
*/
@Override
public List<ParameterizedCompletion> getParameterizedCompletions(
JTextComponent tc) {
List<ParameterizedCompletion> list = null;
// If this provider doesn't support parameterized completions,
// bail out now.
char paramListStart = getParameterListStart();
if (paramListStart==0) {
return list; // null
}
int dot = tc.getCaretPosition();
Segment s = new Segment();
Document doc = tc.getDocument();
Element root = doc.getDefaultRootElement();
int line = root.getElementIndex(dot);
Element elem = root.getElement(line);
int offs = elem.getStartOffset();
int len = dot - offs - 1/*paramListStart.length()*/;
if (len<=0) { // Not enough chars on line for a method.
return list; // null
}
try {
doc.getText(offs, len, s);
// Get the identifier preceding the '(', ignoring any whitespace
// between them.
offs = s.offset + len - 1;
while (offs>=s.offset && Character.isWhitespace(s.array[offs])) {
offs--;
}
int end = offs;
while (offs>=s.offset && isValidChar(s.array[offs])) {
offs--;
}
String text = new String(s.array, offs+1, end-offs);
// Get a list of all Completions matching the text, but then
// narrow it down to just the ParameterizedCompletions.
List<Completion> l = getCompletionByInputText(text);
if (l!=null && !l.isEmpty()) {
for (int i=0; i<l.size(); i++) {
Object o = l.get(i);
if (o instanceof ParameterizedCompletion) {
if (list==null) {
list = new ArrayList<ParameterizedCompletion>(1);
}
list.add((ParameterizedCompletion)o);
}
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
return list;
}
/**
* Initializes this completion provider.
*/
protected void init() {
seg = new Segment();
}
/**
* Returns whether the specified character is valid in an auto-completion.
* The default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses
* can override this method to change what characters are matched.
*
* @param ch The character.
* @return Whether the character is valid.
*/
protected boolean isValidChar(char ch) {
return Character.isLetterOrDigit(ch) || ch=='_';
}
/**
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param file An XML file to load from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(File file) throws IOException {
BufferedInputStream bin = new BufferedInputStream(
new FileInputStream(file));
try {
loadFromXML(bin);
} finally {
bin.close();
}
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
*
* @param in The input stream to read from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in) throws IOException {
loadFromXML(in, null);
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
*
* @param in The input stream to read from.
* @param cl The class loader to use when loading any extra classes defined
* in the XML, such as custom {@link FunctionCompletion}s. This
* may be <code>null</code> if the default is to be used, or if no
* custom completions are defined in the XML.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in, ClassLoader cl) throws IOException {
//long start = System.currentTimeMillis();
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
CompletionXMLParser handler = new CompletionXMLParser(this, cl);
BufferedInputStream bin = new BufferedInputStream(in);
try {
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(bin, handler);
List<Completion> completions = handler.getCompletions();
addCompletions(completions);
char startChar = handler.getParamStartChar();
if (startChar!=0) {
char endChar = handler.getParamEndChar();
String sep = handler.getParamSeparator();
if (endChar!=0 && sep!=null && sep.length()>0) { // Sanity
setParameterizedCompletionParams(startChar, sep, endChar);
}
}
} catch (SAXException se) {
throw new IOException(se.toString());
} catch (ParserConfigurationException pce) {
throw new IOException(pce.toString());
} finally {
//long time = System.currentTimeMillis() - start;
//System.out.println("XML loaded in: " + time + "ms");
bin.close();
}
}
/**
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param resource A resource the current ClassLoader can get to.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(String resource) throws IOException {
ClassLoader cl = getClass().getClassLoader();
InputStream in = cl.getResourceAsStream(resource);
if (in==null) {
File file = new File(resource);
if (file.isFile()) {
in = new FileInputStream(file);
}
else {
throw new IOException("No such resource: " + resource);
}
}
BufferedInputStream bin = new BufferedInputStream(in);
try {
loadFromXML(bin);
} finally {
bin.close();
}
}
}
@@ -0,0 +1,94 @@
/*
* 12/22/2008
*
* DelegatingCellRenderer.java - A renderer for Completions that will delegate
* to the Completion's provider's renderer, if there is one.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Component;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
/**
* List cell renderer that delegates to a {@link CompletionProvider}'s
* renderer, if it has one. If it doesn't, it calls into a fallback renderer.
* If a fallback renderer isn't specified, it simply renders
* <code>(({@link Completion})value).toString()</code>.
*
* @author Robert Futrell
* @version 1.0
*/
class DelegatingCellRenderer extends DefaultListCellRenderer {
/**
* The renderer to fall back on if one isn't specified by a provider.
* This is usually <tt>this</tt>.
*/
private ListCellRenderer fallback;
/**
* Returns the fallback cell renderer.
*
* @return The fallback cell renderer.
* @see #setFallbackCellRenderer(ListCellRenderer)
*/
public ListCellRenderer getFallbackCellRenderer() {
return fallback;
}
/**
* {@inheritDoc}
*/
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean selected, boolean hasFocus) {
Completion c = (Completion)value;
CompletionProvider p = c.getProvider();
ListCellRenderer r = p.getListCellRenderer();
if (r!=null) {
return r.getListCellRendererComponent(list, value, index, selected,
hasFocus);
}
if (fallback==null) {
return super.getListCellRendererComponent(list, value, index,
selected, hasFocus);
}
return fallback.getListCellRendererComponent(list, value, index,
selected, hasFocus);
}
/**
* Sets the fallback cell renderer.
*
* @param fallback The fallback cell renderer. If this is
* <code>null</code>, <tt>this</tt> will be used.
* @see #getFallbackCellRenderer()
*/
public void setFallbackCellRenderer(ListCellRenderer fallback) {
this.fallback = fallback;
}
/**
* {@inheritDoc}
*/
@Override
public void updateUI() {
super.updateUI();
if ((fallback instanceof JComponent) && fallback!=this) {
((JComponent)fallback).updateUI();
}
}
}
@@ -0,0 +1,31 @@
/*
* 05/11/2012
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
/**
* Passed to {@link ExternalURLHandler}s as a way for them to display a summary
* for a new completion in response to a link event.
*
* @author Robert Futrell
* @version 1.0
* @see ExternalURLHandler
*/
public interface DescWindowCallback {
/**
* Callback allowing a new code completion's description to be displayed
* in the description window.
*
* @param completion The new completion.
* @param anchor The anchor to scroll to, or <code>null</code> if none.
*/
public void showSummaryFor(Completion completion, String anchor);
}
@@ -0,0 +1,52 @@
/*
* 04/29/2010
*
* EmptyIcon.java - The canonical icon that paints nothing.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Component;
import java.awt.Graphics;
import java.io.Serializable;
import javax.swing.Icon;
/**
* A standard icon that doesn't paint anything. This can be used when some
* <code>Completion</code>s have icons and others don't, to visually align the
* text of all completions.
*
* @author Robert Futrell
* @version 1.0
*/
public class EmptyIcon implements Icon, Serializable {
private int size;
public EmptyIcon(int size) {
this.size = size;
}
@Override
public int getIconHeight() {
return size;
}
@Override
public int getIconWidth() {
return size;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
}
}
@@ -0,0 +1,46 @@
/*
* 12/23/2008
*
* ExternalURLHandler.java - Implementations can be registered as a callback
* to handle the user clicking on external URL's.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import javax.swing.event.HyperlinkEvent;
/**
* A callback for when an external URL is clicked in the description window.
* If no handler is installed, and if running in Java 6, the system default
* web browser is used to open the URL. If not running Java 6, nothing will
* happen. If you want browser support for pre-Java 6 JRE's, you will need
* to register one of these callbacks on your {@link AutoCompletion}, and
* open the URL in a web browser yourself.<p>
*
* Alternatively, folks implementing robust code completion support for a
* language might install an <code>ExternalURLHandler</code> to handle
* navigating through linked documentation of objects, functions, etc.
*
* @author Robert Futrell
* @version 1.0
* @see AutoCompletion#setExternalURLHandler(ExternalURLHandler)
*/
public interface ExternalURLHandler {
/**
* Called when an external URL is clicked in the description window.
*
* @param e The event containing the hyperlink clicked.
* @param c The completion currently being displayed.
* @param callback Allows you to display new content in the description
* window.
*/
public void urlClicked(HyperlinkEvent e, Completion c,
DescWindowCallback callback);
}
@@ -0,0 +1,189 @@
/*
* 04/26/2010
*
* FastListUI.java - A JList UI implementation that computes the preferred size
* of all cells really fast, to facilitate lists of possibly thousands of items
* rendered with HTML, which is slow with BasicListUI extensions.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.SystemColor;
import javax.swing.JViewport;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicListUI;
/**
* A custom list UI, used by the completion choices list. If the number of
* completion choices is "large," it does a fast estimate of the preferred
* width and height of list items. This allows HTML renderers to be used (such
* as {@link CompletionCellRenderer}), with thousands of completion choices,
* with no performance penalty. With standard BasicListUI subclasses, this can
* cause very poor performance <b>each time</b> the list is displayed, which
* is bad for lists that are repeatedly hidden and re-displayed, such as
* completion choices. This is all because the calculation to get the
* preferred size of each list item, when it is displayed with HTML, is slow.
*
* @author Robert Futrell
* @version 1.0
*/
class FastListUI extends BasicListUI {
/**
* Whether the selection background was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
*/
private boolean overriddenBackground;
/**
* Whether the selection foreground was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
*/
private boolean overriddenForeground;
/**
* If there are more than this many completions in a single list, this
* UI will estimate the cell width and height needed for each item instead
* of computing it, for performance reasons.
*/
private static final int ESTIMATION_THRESHOLD = 200;
private Color determineSelectionBackground() {
Color c = UIManager.getColor("List.selectionBackground");
if (c==null) {
c = UIManager.getColor("nimbusSelectionBackground");
if (c==null) { // Not Nimbus, but still need a value - fallback
c = UIManager.getColor("textHighlight");
if (c==null) {
c = SystemColor.textHighlight;
}
}
}
// Nimbus unfortunately requires a Color, not a ColorUIResource, for
// the background override to work. This causes this color to "stick"
// even if the LAF is changed to something else later. "c" here may
// actually be a ColorUIResource
return new Color(c.getRGB());//new ColorUIResource(c);
}
private Color determineSelectionForeground() {
Color c = UIManager.getColor("List.selectionForeground");
if (c==null) {
c = UIManager.getColor("nimbusSelectedText");
if (c==null) { // Not Nimbus, but still need a value - fallback
c = UIManager.getColor("textHighlightText");
if (c==null) {
c = SystemColor.textHighlightText;
}
}
}
// Nimbus unfortunately requires Color, not ColorUIResource, and "c"
// may actually be a ColorUIResource
return new Color(c.getRGB());
}
/**
* Overridden to ensure we have selection background/foreground colors
* defined, even if we're in some weirdo LAF such as Nimbus which doesn't
* define them. Since FastListUI extends BasicListUI, we need these values
* to be defined.
*/
@Override
protected void installDefaults() {
super.installDefaults();
if (list.getSelectionBackground()==null) {
list.setSelectionBackground(determineSelectionBackground());
overriddenBackground = true;
}
if (list.getSelectionForeground()==null) {
list.setSelectionForeground(determineSelectionForeground());
overriddenForeground = true;
}
}
/**
* Overridden to work around a Nimbus issue.
*/
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
if (overriddenBackground) {
list.setSelectionBackground(null);
}
if (overriddenForeground) {
list.setSelectionForeground(null);
}
}
/**
* Recalculates the cell width and height of each cell in the list. This
* method is overridden to do a fast estimation if the completion list is
* too long, to improve performance for lists with huge amounts of
* completions.
*/
@Override
protected void updateLayoutState() {
ListModel model = list.getModel();
int itemCount = model.getSize();
// If the item count is small enough to run fast on practically all
// machines, go ahead and use the super implementation to determine
// the optimal cell sizes.
if (itemCount<ESTIMATION_THRESHOLD) {
super.updateLayoutState();
return;
}
// Otherwise, assume all cells are the same height as the first cell,
// and estimate the necessary width.
ListCellRenderer renderer = list.getCellRenderer();
cellWidth = list.getWidth();
if (list.getParent() instanceof JViewport) { // Always true for us
cellWidth = list.getParent().getWidth();
}
//System.out.println(cellWidth);
// We're getting a fixed cell height for all cells
cellHeights = null;
if (renderer!=null && itemCount>0) {
Object value = model.getElementAt(0);
java.awt.Component c = renderer.getListCellRendererComponent(list,
value, 0, false, false);
rendererPane.add(c);
Dimension cellSize = c.getPreferredSize();
cellHeight = cellSize.height;
cellWidth = Math.max(cellWidth, cellSize.width);
}
else {
cellHeight = 20;
}
}
}
@@ -0,0 +1,399 @@
/*
* 12/22/2008
*
* FunctionCompletion.java - A completion representing a function.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
/**
* A completion choice representing a function.
*
* @author Robert Futrell
* @version 1.0
*/
public class FunctionCompletion extends VariableCompletion
implements ParameterizedCompletion {
/**
* Parameters to the function.
*/
private List<Parameter> params;
/**
* A description of the return value of this function.
*/
private String returnValDesc;
/**
* Used to improve performance of sorting FunctionCompletions.
*/
private String compareString;
/**
* Constructor.
*
* @param provider The parent provider.
* @param name The name of this function.
* @param returnType The return type of this function.
*/
public FunctionCompletion(CompletionProvider provider, String name,
String returnType) {
super(provider, name, returnType);
}
@Override
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>");
sb.append(getDefinitionString());
sb.append("</b>");
}
/**
* Adds HTML describing the parameters to this function to a buffer.
*
* @param sb The buffer to append to.
*/
protected void addParameters(StringBuilder sb) {
// TODO: Localize me
int paramCount = getParamCount();
if (paramCount>0) {
sb.append("<b>Parameters:</b><br>");
sb.append("<center><table width='90%'><tr><td>");
for (int i=0; i<paramCount; i++) {
Parameter param = getParam(i);
sb.append("<b>");
sb.append(param.getName()!=null ? param.getName() :
param.getType());
sb.append("</b>&nbsp;");
String desc = param.getDescription();
if (desc!=null) {
sb.append(desc);
}
sb.append("<br>");
}
sb.append("</td></tr></table></center><br><br>");
}
if (returnValDesc!=null) {
sb.append("<b>Returns:</b><br><center><table width='90%'><tr><td>");
sb.append(returnValDesc);
sb.append("</td></tr></table></center><br><br>");
}
}
/**
* Overridden to compare methods by their comparison strings.
*
* @param c2 A <code>Completion</code> to compare to.
* @return The sort order.
*/
@Override
public int compareTo(Completion c2) {
int rc = -1;
if (c2==this) {
rc = 0;
}
else if (c2 instanceof FunctionCompletion) {
rc = getCompareString().compareTo(
((FunctionCompletion)c2).getCompareString());
}
else {
rc = super.compareTo(c2);
}
return rc;
}
/**
* Returns a string used to compare this method completion to another.
*
* @return The comparison string.
*/
private String getCompareString() {
/*
* This string compares the following parts of methods in this order,
* to optimize sort order in completion lists.
*
* 1. First, by name
* 2. Next, by number of parameters.
* 3. Finally, by parameter type.
*/
if (compareString==null) {
StringBuilder sb = new StringBuilder(getName());
// NOTE: This will fail if a method has > 99 parameters (!)
int paramCount = getParamCount();
if (paramCount<10) {
sb.append('0');
}
sb.append(paramCount);
for (int i=0; i<paramCount; i++) {
String type = getParam(i).getType();
sb.append(type);
if (i<paramCount-1) {
sb.append(',');
}
}
compareString = sb.toString();
}
return compareString;
}
/**
* Returns the "definition string" for this function completion. For
* example, for the C "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
*/
@Override
public String getDefinitionString() {
StringBuilder sb = new StringBuilder();
// Add the return type if applicable (C macros like NULL have no type).
String type = getType();
if (type!=null) {
sb.append(type).append(' ');
}
// Add the item being described's name.
sb.append(getName());
// Add parameters for functions.
CompletionProvider provider = getProvider();
char start = provider.getParameterListStart();
if (start!=0) {
sb.append(start);
}
for (int i=0; i<getParamCount(); i++) {
Parameter param = getParam(i);
type = param.getType();
String name = param.getName();
if (type!=null) {
sb.append(type);
if (name!=null) {
sb.append(' ');
}
}
if (name!=null) {
sb.append(name);
}
if (i<params.size()-1) {
sb.append(provider.getParameterListSeparator());
}
}
char end = provider.getParameterListEnd();
if (end!=0) {
sb.append(end);
}
return sb.toString();
}
@Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(
JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info =
new ParameterizedCompletionInsertionInfo();
StringBuilder sb = new StringBuilder();
char paramListStart = getProvider().getParameterListStart();
if (paramListStart!='\0') {
sb.append(paramListStart);
}
int dot = tc.getCaretPosition() + sb.length();
int paramCount = getParamCount();
// Get the range in which the caret can move before we hide
// this tool tip.
int minPos = dot;
Position maxPos = null;
try {
maxPos = tc.getDocument().createPosition(dot-sb.length()+1);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
info.setCaretRange(minPos, maxPos);
int firstParamLen = 0;
// Create the text to insert (keep it one completion for
// performance and simplicity of undo/redo).
int start = dot;
for (int i=0; i<paramCount; i++) {
Parameter param = getParam(i);
String paramText = getParamText(param);
if (i==0) {
firstParamLen = paramText.length();
}
sb.append(paramText);
int end = start + paramText.length();
info.addReplacementLocation(start, end);
// Patch for param. list separators with length > 2 -
// thanks to Matthew Adereth!
String sep = getProvider().getParameterListSeparator();
if (i<paramCount-1 && sep!=null) {
sb.append(sep);
start = end + sep.length();
}
}
sb.append(getProvider().getParameterListEnd());
int endOffs = dot + sb.length();
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());
return info;
}
/**
* {@inheritDoc}
*/
@Override
public Parameter getParam(int index) {
return params.get(index);
}
/**
* Returns the number of parameters to this function.
*
* @return The number of parameters to this function.
* @see #getParam(int)
*/
@Override
public int getParamCount() {
return params==null ? 0 : params.size();
}
/**
* {@inheritDoc}
*/
@Override
public boolean getShowParameterToolTip() {
return true;
}
/**
* Returns the text to insert for a parameter.
*
* @param param The parameter.
* @return The text.
*/
private String getParamText(ParameterizedCompletion.Parameter param) {
String text = param.getName();
if (text==null) {
text = param.getType();
if (text==null) { // Shouldn't ever happen
text = "arg";
}
}
return text;
}
/**
* Returns the description of the return value of this function.
*
* @return The description, or <code>null</code> if there is none.
* @see #setReturnValueDescription(String)
*/
public String getReturnValueDescription() {
return returnValDesc;
}
/**
* {@inheritDoc}
*/
@Override
public String getSummary() {
StringBuilder sb = new StringBuilder();
addDefinitionString(sb);
if (!possiblyAddDescription(sb)) {
sb.append("<br><br><br>");
}
addParameters(sb);
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String getToolTipText() {
String text = getSummary();
if (text==null) {
text = getDefinitionString();
}
return text;
}
/**
* Sets the parameters to this function.
*
* @param params The parameters. This should be a list of
* {@link ParameterizedCompletion.Parameter}s.
* @see #getParam(int)
* @see #getParamCount()
*/
public void setParams(List<Parameter> params) {
if (params!=null) {
// Deep copy so parsing can re-use its array.
this.params = new ArrayList<Parameter>(params);
}
}
/**
* Sets the description of the return value of this function.
*
* @param desc The description.
* @see #getReturnValueDescription()
*/
public void setReturnValueDescription(String desc) {
this.returnValDesc = desc;
}
}
@@ -0,0 +1,431 @@
/*
* 01/03/2009
*
* LanguageAwareCompletionProvider.java - A completion provider that is aware
* of the language it is working with.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.List;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.ToolTipSupplier;
/**
* A completion provider for the C programming language (and other languages
* with similar syntax). This provider simply delegates to another provider,
* depending on whether the caret is in:
*
* <ul>
* <li>Code (plain text)</li>
* <li>A string</li>
* <li>A comment</li>
* <li>A documentation comment</li>
* </ul>
*
* This allows for different completion choices in comments than in code,
* for example.<p>
*
* This provider also implements the
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it
* to display tooltips for completion choices. Thus the standard
* {@link VariableCompletion} and {@link FunctionCompletion} completions should
* be able to display tooltips with the variable declaration or function
* definition (provided the <tt>RSyntaxTextArea</tt> was registered with the
* <tt>javax.swing.ToolTipManager</tt>).
*
* @author Robert Futrell
* @version 1.0
*/
public class LanguageAwareCompletionProvider extends CompletionProviderBase
implements ToolTipSupplier {
/**
* The provider to use when no provider is assigned to a particular token
* type.
*/
private CompletionProvider defaultProvider;
/**
* The provider to use when completing in a string.
*/
private CompletionProvider stringCompletionProvider;
/**
* The provider to use when completing in a comment.
*/
private CompletionProvider commentCompletionProvider;
/**
* The provider to use while in documentation comments.
*/
private CompletionProvider docCommentCompletionProvider;
/**
* Constructor subclasses can use when they don't have their default
* provider created at construction time. They should call
* {@link #setDefaultCompletionProvider(CompletionProvider)} in this
* constructor.
*/
protected LanguageAwareCompletionProvider() {
}
/**
* Constructor.
*
* @param defaultProvider The provider to use when no provider is assigned
* to a particular token type. This cannot be <code>null</code>.
*/
public LanguageAwareCompletionProvider(CompletionProvider defaultProvider) {
setDefaultCompletionProvider(defaultProvider);
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException Always.
* @see #setParameterizedCompletionParams(char, String, char)
*/
@Override
public void clearParameterizedCompletionParams() {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public String getAlreadyEnteredText(JTextComponent comp) {
if (!(comp instanceof RSyntaxTextArea)) {
return EMPTY_STRING;
}
CompletionProvider provider = getProviderFor(comp);
return provider!=null ? provider.getAlreadyEnteredText(comp) : null;
}
/**
* Returns the completion provider to use for comments.
*
* @return The completion provider to use.
* @see #setCommentCompletionProvider(CompletionProvider)
*/
public CompletionProvider getCommentCompletionProvider() {
return commentCompletionProvider;
}
/**
* {@inheritDoc}
*/
@Override
public List<Completion> getCompletionsAt(JTextComponent tc, Point p) {
return defaultProvider==null ? null :
defaultProvider.getCompletionsAt(tc, p);
}
/**
* Does the dirty work of creating a list of completions.
*
* @param comp The text component to look in.
* @return The list of possible completions, or an empty list if there
* are none.
*/
@Override
protected List<Completion> getCompletionsImpl(JTextComponent comp) {
if (comp instanceof RSyntaxTextArea) {
CompletionProvider provider = getProviderFor(comp);
if (provider!=null) {
return provider.getCompletions(comp);
}
}
return Collections.emptyList();
}
/**
* Returns the completion provider used when one isn't defined for a
* particular token type.
*
* @return The completion provider to use.
* @see #setDefaultCompletionProvider(CompletionProvider)
*/
public CompletionProvider getDefaultCompletionProvider() {
return defaultProvider;
}
/**
* Returns the completion provider to use for documentation comments.
*
* @return The completion provider to use.
* @see #setDocCommentCompletionProvider(CompletionProvider)
*/
public CompletionProvider getDocCommentCompletionProvider() {
return docCommentCompletionProvider;
}
/**
* {@inheritDoc}
*/
@Override
public List<ParameterizedCompletion> getParameterizedCompletions(
JTextComponent tc) {
// Parameterized completions can only come from the "code" completion
// provider. We do not do function/method completions while editing
// strings or comments.
CompletionProvider provider = getProviderFor(tc);
return provider==defaultProvider ?
provider.getParameterizedCompletions(tc) : null;
}
/**
* {@inheritDoc}
*/
@Override
public char getParameterListEnd() {
return defaultProvider.getParameterListEnd();
}
/**
* {@inheritDoc}
*/
@Override
public String getParameterListSeparator() {
return defaultProvider.getParameterListSeparator();
}
/**
* {@inheritDoc}
*/
@Override
public char getParameterListStart() {
return defaultProvider.getParameterListStart();
}
/**
* Returns the completion provider to use at the current caret position in
* a text component.
*
* @param comp The text component to check.
* @return The completion provider to use.
*/
private CompletionProvider getProviderFor(JTextComponent comp) {
RSyntaxTextArea rsta = (RSyntaxTextArea)comp;
RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
int line = rsta.getCaretLineNumber();
Token t = doc.getTokenListForLine(line);
if (t==null) {
return getDefaultCompletionProvider();
}
int dot = rsta.getCaretPosition();
Token curToken = RSyntaxUtilities.getTokenAtOffset(t, dot);
if (curToken==null) { // At end of the line
int type = doc.getLastTokenTypeOnLine(line);
if (type==Token.NULL) {
Token temp = t.getLastPaintableToken();
if (temp==null) {
return getDefaultCompletionProvider();
}
type = temp.getType();
}
// TokenMakers can use types < 0 for "internal types." This
// gives them a chance to map their internal types back to "real"
// types to get completion providers.
else if (type<0) {
type = doc.getClosestStandardTokenTypeForInternalType(type);
}
switch (type) {
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
default:
return getDefaultCompletionProvider();
}
}
// FIXME: This isn't always a safe assumption.
if (dot==curToken.getOffset()) { // At the very beginning of a new token
// Need to check previous token for its type before deciding.
// Previous token may also be on previous line!
return getDefaultCompletionProvider();
}
switch (curToken.getType()) {
case Token.LITERAL_STRING_DOUBLE_QUOTE:
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
case Token.NULL:
case Token.WHITESPACE:
case Token.IDENTIFIER:
case Token.VARIABLE:
case Token.PREPROCESSOR:
case Token.DATA_TYPE:
case Token.FUNCTION:
case Token.OPERATOR:
return getDefaultCompletionProvider();
}
return null; // In a token type we can't auto-complete from.
}
/**
* Returns the completion provider to use for strings.
*
* @return The completion provider to use.
* @see #setStringCompletionProvider(CompletionProvider)
*/
public CompletionProvider getStringCompletionProvider() {
return stringCompletionProvider;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAutoActivateOkay(JTextComponent tc) {
CompletionProvider provider = getProviderFor(tc);
return provider!=null ? provider.isAutoActivateOkay(tc) : false;
}
/**
* Sets the comment completion provider.
*
* @param provider The provider to use in comments.
* @see #getCommentCompletionProvider()
*/
public void setCommentCompletionProvider(CompletionProvider provider) {
this.commentCompletionProvider = provider;
}
/**
* Sets the default completion provider.
*
* @param provider The provider to use when no provider is assigned to a
* particular token type. This cannot be <code>null</code>.
* @see #getDefaultCompletionProvider()
*/
public void setDefaultCompletionProvider(CompletionProvider provider) {
if (provider==null) {
throw new IllegalArgumentException("provider cannot be null");
}
this.defaultProvider = provider;
}
/**
* Sets the documentation comment completion provider.
*
* @param provider The provider to use in comments.
* @see #getDocCommentCompletionProvider()
*/
public void setDocCommentCompletionProvider(CompletionProvider provider) {
this.docCommentCompletionProvider = provider;
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException Always.
* @see #clearParameterizedCompletionParams()
*/
@Override
public void setParameterizedCompletionParams(char listStart,
String separator, char listEnd) {
throw new UnsupportedOperationException();
}
/**
* Sets the completion provider to use while in a string.
*
* @param provider The provider to use.
* @see #getStringCompletionProvider()
*/
public void setStringCompletionProvider(CompletionProvider provider) {
stringCompletionProvider = provider;
}
/**
* Returns the tool tip to display for a mouse event.<p>
*
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be
* registered with the <tt>javax.swing.ToolTipManager</tt> like so:
*
* <pre>
* ToolTipManager.sharedInstance().registerComponent(textArea);
* </pre>
*
* @param textArea The text area.
* @param e The mouse event.
* @return The tool tip text, or <code>null</code> if none.
*/
@Override
public String getToolTipText(RTextArea textArea, MouseEvent e) {
String tip = null;
List<Completion> completions = getCompletionsAt(textArea, e.getPoint());
if (completions!=null && completions.size()>0) {
// Only ever 1 match for us in C...
Completion c = completions.get(0);
tip = c.getToolTipText();
}
return tip;
}
}
@@ -0,0 +1,26 @@
/*
* 05/16/2012
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.net.URL;
/**
* Possibly redirects one URL to another. Useful if you want "external" URL's
* in code completion documentation to point to a local copy instead, for
* example.
*
* @author Robert Futrell
* @version 1.0
*/
public interface LinkRedirector {
public URL possiblyRedirect(URL original);
}
@@ -0,0 +1,242 @@
/*
* 01/06/2009
*
* MarkupTagComletion.java - A completion representing a tag in markup, such
* as HTML or XML.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.ArrayList;
import java.util.List;
import org.fife.ui.autocomplete.ParameterizedCompletion.Parameter;
/**
* A completion representing a tag in markup, such as HTML or XML.
*
* @author Robert Futrell
* @version 1.0
*/
public class MarkupTagCompletion extends AbstractCompletion {
private String name;
private String desc;
private String definedIn;
/**
* Attributes of the tag.
*/
private List<Parameter> attrs;
/**
* Constructor.
*
* @param provider The parent provider instance.
* @param name The name of the tag.
*/
public MarkupTagCompletion(CompletionProvider provider, String name) {
super(provider);
this.name = name;
}
/**
* Adds HTML describing the attributes of this tag to a buffer.
*
* @param sb The buffer to append to.
*/
protected void addAttributes(StringBuilder sb) {
// TODO: Localize me.
int attrCount = getAttributeCount();
if (attrCount>0) {
sb.append("<b>Attributes:</b><br>");
sb.append("<center><table width='90%'><tr><td>");
for (int i=0; i<attrCount; i++) {
Parameter attr = getAttribute(i);
sb.append("&nbsp;&nbsp;&nbsp;<b>");
sb.append(attr.getName()!=null ? attr.getName() :
attr.getType());
sb.append("</b>&nbsp;");
String desc = attr.getDescription();
if (desc!=null) {
sb.append(desc);
}
sb.append("<br>");
}
sb.append("</td></tr></table></center><br><br>");
}
}
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(name).append("</b>");
}
/**
* Returns all attributes of this tag.
*
* @return A list of {@link ParameterizedCompletion.Parameter}s.
* @see #getAttribute(int)
* @see #getAttributeCount()
*/
public List<Parameter> getAttributes() {
return attrs;
}
/**
* Returns the specified {@link ParameterizedCompletion.Parameter}.
*
* @param index The index of the attribute to retrieve.
* @return The attribute.
* @see #getAttributeCount()
*/
public Parameter getAttribute(int index) {
return attrs.get(index);
}
/**
* Returns the number of attributes of this tag.
*
* @return The number of attributes of this tag.
* @see #getAttribute(int)
*/
public int getAttributeCount() {
return attrs==null ? 0 : attrs.size();
}
/**
* Returns where this variable is defined.
*
* @return Where this variable is defined.
* @see #setDefinedIn(String)
*/
public String getDefinedIn() {
return definedIn;
}
/**
* Returns a short description of this variable. This should be an
* HTML snippet.
*
* @return A short description of this variable. This may be
* <code>null</code>.
* @see #setDescription(String)
*/
public String getDescription() {
return desc;
}
/**
* Returns the name of this tag.
*
* @return The name of this tag.
*/
public String getName() {
return name;
}
/**
* {@inheritDoc}
*/
@Override
public String getReplacementText() {
return getName();
}
/**
* {@inheritDoc}
*/
@Override
public String getSummary() {
StringBuilder sb = new StringBuilder();
addDefinitionString(sb);
possiblyAddDescription(sb);
addAttributes(sb);
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
*
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn!=null) {
sb.append("<hr>Defined in:"); // TODO: Localize me
sb.append(" <em>").append(definedIn).append("</em>");
}
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
*
* @param sb The buffer to append to.
*/
protected void possiblyAddDescription(StringBuilder sb) {
if (desc!=null) {
sb.append("<hr><br>");
sb.append(desc);
sb.append("<br><br><br>");
}
}
/**
* Sets where this variable is defined.
*
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
this.definedIn = definedIn;
}
/**
* Sets the short description of this tag. This should be an
* HTML snippet.
*
* @param desc A short description of this tag. This may be
* <code>null</code>.
* @see #getDescription()
*/
public void setDescription(String desc) {
this.desc = desc;
}
/**
* Sets the attributes of this tag.
*
* @param attrs The attributes.
* @see #getAttribute(int)
* @see #getAttributeCount()
*/
public void setAttributes(List<? extends Parameter> attrs) {
// Deep copy so parsing can re-use its array.
this.attrs = new ArrayList<Parameter>(attrs);
}
}
@@ -0,0 +1,143 @@
/*
* 04/26/2009
*
* OutlineHighlightPainter.java - Highlight painter that draws an outline
* around its text.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
/**
* Highlight painter that draws an outline around the text. This is used to
* draw bounds around function/method parameters.
*
* @author Robert Futrell
* @version 1.0
*/
/*
* NOTE: Whenever you see text like "Workaround for Java Highlight issues",
* this is because highlighted text in a JTextComponent gets "pushed" forward
* when the caret is at the Highlight's start, when we need it to instead get
* prepended to. For this reason, the auto-complete package adds its Highlights
* 1 char too long (1 char earlier than where it should really start), but only
* paint the Highlight from the 2nd char on.
*/
class OutlineHighlightPainter extends
DefaultHighlighter.DefaultHighlightPainter {
/**
* DefaultHighlightPainter doesn't allow changing color, so we must cache
* ours here.
*/
private Color color;
/**
* Constructor.
*
* @param color The color to draw the bounding boxes with. This cannot
* be <code>null</code>.
*/
public OutlineHighlightPainter(Color color) {
super(color);
setColor(color);
}
/**
* Returns the color to paint bounding boxes with.
*
* @return The color.
* @see #setColor(Color)
*/
@Override
public Color getColor() {
return color;
}
/**
* {@inheritDoc}
*/
@Override
public Shape paintLayer(Graphics g, int p0, int p1, Shape viewBounds,
JTextComponent c, View view) {
g.setColor(getColor());
p0++; // Workaround for Java Highlight issues.
// This special case isn't needed for most standard Swing Views (which
// always return a width of 1 for modelToView() calls), but it is
// needed for RSTA views, which actually return the width of chars for
// modelToView calls. But this should be faster anyway, as we
// short-circuit and do only one modelToView() for one offset.
if (p0==p1) {
try {
Shape s = view.modelToView(p0, viewBounds,
Position.Bias.Forward);
Rectangle r = s.getBounds();
g.drawLine(r.x, r.y, r.x, r.y+r.height);
return r;
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
return null;
}
}
if (p0 == view.getStartOffset() && p1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
Rectangle alloc;
if (viewBounds instanceof Rectangle) {
alloc = (Rectangle) viewBounds;
} else {
alloc = viewBounds.getBounds();
}
g.drawRect(alloc.x, alloc.y, alloc.width - 1, alloc.height - 1);
return alloc;
}
// Should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(p0, Position.Bias.Forward, p1,
Position.Bias.Backward, viewBounds);
Rectangle r = (shape instanceof Rectangle) ? (Rectangle) shape
: shape.getBounds();
g.drawRect(r.x, r.y, r.width - 1, r.height - 1);
return r;
} catch (BadLocationException e) { // Never happens
e.printStackTrace();
return null;
}
}
/**
* Sets the color to paint the bounding boxes with.
*
* @param color The new color. This cannot be <code>null</code>.
* @see #getColor()
*/
public void setColor(Color color) {
if (color==null) {
throw new IllegalArgumentException("color cannot be null");
}
this.color = color;
}
}
@@ -0,0 +1,40 @@
/*
* 12/14/2010
*
* ParameterChoicesProvider.java - Provides completions for a
* ParameterizedCompletion's parameters.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.List;
import javax.swing.text.JTextComponent;
/**
* Provides completions for a {@link ParameterizedCompletion}'s parameters.
* So, for example, if the user code-completes a function or method, if
* a <code>ParameterChoicesProvider</code> is installed, it can return possible
* completions for the parameters to that function or method.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ParameterChoicesProvider {
/**
* Returns a list of choices for a specific parameter.
*
* @param tc The text component.
* @param param The currently focused parameter.
* @return The list of parameters. This may be <code>null</code> for
* "no parameters," but might also be an empty list.
*/
public List<Completion> getParameterChoices(JTextComponent tc,
ParameterizedCompletion.Parameter param);
}
@@ -0,0 +1,172 @@
/*
* 12/11/2010
*
* ParameterizedCompletion.java - A completion option.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import javax.swing.text.JTextComponent;
/**
* A completion option that takes parameters, such as a function or method.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ParameterizedCompletion extends Completion {
/**
* Returns the "definition string" for this completion. For example,
* for the C "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
*/
public String getDefinitionString();
/**
* Returns the specified {@link Parameter}.
*
* @param index The index of the parameter to retrieve.
* @return The parameter.
* @see #getParamCount()
*/
public Parameter getParam(int index);
/**
* Returns the number of parameters this completion takes.
*
* @return The number of parameters this completion takes.
* @see #getParam(int)
*/
public int getParamCount();
public ParameterizedCompletionInsertionInfo getInsertionInfo(
JTextComponent tc, boolean replaceTabsWithSpaces);
/**
* Returns whether a tool tip displaying assistance for each parameter
* while it is being edited is appropriate for this completion.
*
* @return Whether the tool tip is appropriate to display.
*/
public boolean getShowParameterToolTip();
/**
* A parameter passed to a parameterized {@link Completion}.
*/
public static class Parameter {
private String name;
private Object type;
private String desc;
private boolean isEndParam;
/**
* 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.
*/
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() {
return desc;
}
public String getName() {
return name;
}
/**
* Returns the type of this parameter, as a string.
*
* @return The type of the parameter, or <code>null</code> for none.
*/
public String getType() {
return type==null ? null : type.toString();
}
/**
* Returns the object used to describe the type of this parameter.
*
* @return The type object, or <code>null</code> for none.
*/
public Object getTypeObject() {
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;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (getType()!=null) {
sb.append(getType());
}
if (getName()!=null) {
if (getType()!=null) {
sb.append(' ');
}
sb.append(getName());
}
return sb.toString();
}
}
}
@@ -0,0 +1,314 @@
/*
* 12/11/2010
*
* ParameterizedCompletionChoicesWindow.java - A list of likely choices for a
* parameter.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* A small popup window offering a list of likely choices for a parameter
* when the user has code-completed a parameterized completion. For example,
* if they have just code-completed the C function "<code>fprintf</code>",
* when entering the file name, this popup might display all local variables
* of type "<code>char *</code>".
*
* @author Robert Futrell
* @version 1.0
*/
public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* The parent AutoCompletion instance.
*/
private AutoCompletion ac;
/**
* The list of completion choices.
*/
private JList list;
/**
* The currently displayed completion choices.
*/
private DefaultListModel model;
/**
* A list of lists of choices for each parameter.
*/
private List<List<Completion>> choicesListList;
/**
* The scroll pane containing the list.
*/
private JScrollPane sp;
/**
* Comparator used to sort completions by their relevance before sorting
* them lexicographically.
*/
private static final Comparator<Completion> sortByRelevanceComparator =
new SortByRelevanceComparator();
/**
* Constructor.
*
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
* @param context The completion context.
*/
public ParameterizedCompletionChoicesWindow(Window parent,
AutoCompletion ac,
final ParameterizedCompletionContext context) {
super(parent);
this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation();
model = new DefaultListModel();
list = new JList(model);
if (ac.getParamChoicesRenderer()!=null) {
list.setCellRenderer(ac.getParamChoicesRenderer());
}
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount()==2) {
context.insertSelectedChoice();
}
}
});
sp = new JScrollPane(list);
setContentPane(sp);
applyComponentOrientation(o);
setFocusableWindowState(false);
// Give apps a chance to decorate us with drop shadows, etc.
PopupWindowDecorator decorator = PopupWindowDecorator.get();
if (decorator!=null) {
decorator.decorate(this);
}
}
/**
* Returns the selected value.
*
* @return The selected value, or <code>null</code> if nothing is
* selected.
*/
public String getSelectedChoice() {
Completion c = (Completion)list.getSelectedValue();
return c==null ? null : c.toString();
}
/**
* Changes the selected index.
*
* @param amount The amount by which to change the selected index.
*/
public void incSelection(int amount) {
int selection = list.getSelectedIndex();
selection += amount;
if (selection<0) {
// Account for nothing selected yet
selection = model.getSize()-1;//+= model.getSize();
}
else {
selection %= model.getSize();
}
list.setSelectedIndex(selection);
list.ensureIndexIsVisible(selection);
}
/**
* Initializes this window to offer suggestions for the parameters of
* a specific completion.
*
* @param pc The completion whose parameters we should offer suggestions
* for.
*/
public void initialize(ParameterizedCompletion pc) {
CompletionProvider provider = pc.getProvider();
ParameterChoicesProvider pcp = provider.getParameterChoicesProvider();
if (pcp==null) {
choicesListList = null;
return;
}
int paramCount = pc.getParamCount();
choicesListList = new ArrayList<List<Completion>>(paramCount);
JTextComponent tc = ac.getTextComponent();
for (int i=0; i<paramCount; i++) {
ParameterizedCompletion.Parameter param = pc.getParam(i);
List<Completion> choices = pcp.getParameterChoices(tc, param);
choicesListList.add(choices);
}
}
/**
* Sets the location of this window relative to the given rectangle.
*
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
// description window, if applicable) both fit in the same window in
// a multi-monitor environment. To do this, we decide which monitor
// the rectangle "r" is in, and use that one (just pick top-left corner
// as the defining point).
Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
//Dimension screenSize = tooltip.getToolkit().getScreenSize();
// Try putting our stuff "below" the caret first.
int y = r.y + r.height + 5;
// Get x-coordinate of completions. Try to align left edge with the
// caret first.
int x = r.x;
if (x<screenBounds.x) {
x = screenBounds.x;
}
else if (x+getWidth()>screenBounds.x+screenBounds.width) { // completions don't fit
x = screenBounds.x + screenBounds.width - getWidth();
}
setLocation(x, y);
}
/**
* Displays the choices for the specified parameter matching the given
* text. This will display or hide this popup window as necessary.
*
* @param param The index of the parameter the caret is currently in.
* This may be <code>-1</code> if not in a parameter (i.e., on
* the comma between parameters).
* @param prefix Text in the parameter before the dot. This may
* be <code>null</code> to represent the empty string.
*/
public void setParameter(int param, String prefix) {
model.clear();
List<Completion> temp = new ArrayList<Completion>();
if (choicesListList!=null && param>=0 && param<choicesListList.size()) {
List<Completion> choices = choicesListList.get(param);
if (choices!=null) {
for (Completion c : choices) {
String choice = c.getReplacementText();
if (prefix==null || Util.startsWithIgnoreCase(choice, prefix)) {
temp.add(c);
}
}
}
// Sort completions appropriately.
Comparator<Completion> c = null;
if (/*sortByRelevance*/true) {
c = sortByRelevanceComparator;
}
Collections.sort(temp, c);
for (int i=0; i<temp.size(); i++) {
model.addElement(temp.get(i));
}
int visibleRowCount = Math.min(model.size(), 10);
list.setVisibleRowCount(visibleRowCount);
// Toggle visibility, if necessary.
if (visibleRowCount==0 && isVisible()) {
setVisible(false);
}
else if (visibleRowCount>0) {
Dimension size = getPreferredSize();
if (size.width<150) {
setSize(150, size.height);
}
else {
pack();
}
// Make sure nothing is ever obscured by vertical scroll bar.
if (sp.getVerticalScrollBar()!=null &&
sp.getVerticalScrollBar().isVisible()) {
size = getSize();
int w = size.width + sp.getVerticalScrollBar().getWidth()+5;
setSize(w, size.height);
}
list.setSelectedIndex(0);
list.ensureIndexIsVisible(0);
if (!isVisible()) {
setVisible(true);
}
}
}
else {
setVisible(false);
}
}
/**
* Toggles the visibility of this popup window.
*
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
if (visible!=isVisible()) {
// i.e. if no possibilities matched what's been typed
if (visible && model.size()==0) {//list.getVisibleRowCount()==0) {
return;
}
super.setVisible(visible);
}
}
/**
* Updates the <tt>LookAndFeel</tt> of this window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(this);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,211 @@
/*
* 12/21/2008
*
* ParameterizedCompletionDescriptionToolTip.java - A "tool tip" displaying
* information on the function or method currently being entered.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.BorderLayout;
import java.awt.Rectangle;
import java.awt.Window;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
/**
* A "tool tip" that displays information on the function or method currently
* being entered.
*
* @author Robert Futrell
* @version 1.0
*/
class ParameterizedCompletionDescriptionToolTip {
/**
* The actual tool tip.
*/
private JWindow tooltip;
/**
* The label that holds the description.
*/
private JLabel descLabel;
/**
* The completion being described.
*/
private ParameterizedCompletion pc;
/**
* Constructor.
*
* @param owner The parent window.
* @param ac The parent auto-completion.
* @param pc The completion being described.
*/
public ParameterizedCompletionDescriptionToolTip(Window owner,
ParameterizedCompletionContext context,
AutoCompletion ac, ParameterizedCompletion pc) {
tooltip = new JWindow(owner);
this.pc = pc;
descLabel = new JLabel();
descLabel.setBorder(BorderFactory.createCompoundBorder(
TipUtil.getToolTipBorder(),
BorderFactory.createEmptyBorder(2, 5, 2, 5)));
descLabel.setOpaque(true);
descLabel.setBackground(TipUtil.getToolTipBackground());
// It appears that if a JLabel is set as a content pane directly, when
// using the JDK's opacity API's, it won't paint its background, even
// if label.setOpaque(true) is called. You have to have a container
// underneath it for it to paint its background. Thus, we embed our
// label in a parent JPanel to handle this case.
//tooltip.setContentPane(descLabel);
JPanel panel = new JPanel(new BorderLayout());
panel.add(descLabel);
tooltip.setContentPane(panel);
// Give apps a chance to decorate us with drop shadows, etc.
PopupWindowDecorator decorator = PopupWindowDecorator.get();
if (decorator!=null) {
decorator.decorate(tooltip);
}
updateText(0);
tooltip.setFocusableWindowState(false);
}
/**
* Returns whether this tool tip is visible.
*
* @return Whether this tool tip is visible.
* @see #setVisible(boolean)
*/
public boolean isVisible() {
return tooltip.isVisible();
}
/**
* Sets the location of this tool tip relative to the given rectangle.
*
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
// description window, if applicable) both fit in the same window in
// a multi-monitor environment. To do this, we decide which monitor
// the rectangle "r" is in, and use that one (just pick top-left corner
// as the defining point).
Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
//Dimension screenSize = tooltip.getToolkit().getScreenSize();
// Try putting our stuff "above" the caret first.
int y = r.y - 5 - tooltip.getHeight();
if (y<0) {
y = r.y + r.height + 5;
}
// Get x-coordinate of completions. Try to align left edge with the
// caret first.
int x = r.x;
if (x<screenBounds.x) {
x = screenBounds.x;
}
else if (x+tooltip.getWidth()>screenBounds.x+screenBounds.width) { // completions don't fit
x = screenBounds.x + screenBounds.width - tooltip.getWidth();
}
tooltip.setLocation(x, y);
}
/**
* Toggles the visibility of this tool tip.
*
* @param visible Whether this tool tip should be visible.
* @see #isVisible()
*/
public void setVisible(boolean visible) {
tooltip.setVisible(visible);
}
/**
* Updates the text in the tool tip to have the current parameter
* displayed in bold.
*
* @param selectedParam The index of the selected parameter.
* @return Whether the text needed to be updated.
*/
public boolean updateText(int selectedParam) {
StringBuilder sb = new StringBuilder("<html>");
int paramCount = pc.getParamCount();
for (int i=0; i<paramCount; i++) {
if (i==selectedParam) {
sb.append("<b>");
}
// Some parameter types may have chars in them unfriendly to HTML
// (such as type parameters in Java). We need to take care to
// escape these.
String temp = pc.getParam(i).toString();
sb.append(RSyntaxUtilities.escapeForHtml(temp, "<br>", false));
if (i==selectedParam) {
sb.append("</b>");
}
if (i<paramCount-1) {
sb.append(pc.getProvider().getParameterListSeparator());
}
}
if (selectedParam>=0 && selectedParam<paramCount) {
ParameterizedCompletion.Parameter param =
pc.getParam(selectedParam);
String desc = param.getDescription();
if (desc!=null) {
sb.append("<br>");
sb.append(desc);
}
}
descLabel.setText(sb.toString());
tooltip.pack();
return true;
}
/**
* Updates the <tt>LookAndFeel</tt> of this window and the description
* window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(tooltip);
}
}
@@ -0,0 +1,251 @@
/*
* 05/26/2012
*
* ParameterizedCompletionInsertionInfo.java - Used internally to track the
* changes required for a specific parameterized completion.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
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
* parameters are in the inserted text, etc.
*
* @author Robert Futrell
* @version 1.0
*/
class ParameterizedCompletionInsertionInfo {
private int minOffs;
private Position maxOffs;
private int defaultEnd;
private int selStart;
private int selEnd;
private String textToInsert;
private List<DocumentRange> replacementLocations;
private List<ReplacementCopy> replacementCopies;
public ParameterizedCompletionInsertionInfo() {
defaultEnd = -1;
}
public void addReplacementCopy(String id, int start, int end) {
if (replacementCopies==null) {
replacementCopies = new ArrayList<ReplacementCopy>(1);
}
replacementCopies.add(new ReplacementCopy(id, start, end));
}
/**
* Marks a region of the replacement text as representing a variable name
* or some other construct that the user should replace.
*
* @param start The start offset.
* @param end The end offset.
* @see #getReplacementCount()
* @see #getReplacementLocation(int)
*/
public void addReplacementLocation(int start, int end) {
if (replacementLocations==null) {
replacementLocations = new ArrayList<DocumentRange>(1);
}
replacementLocations.add(new DocumentRange(start, end));
}
public int getDefaultEndOffs() {
return defaultEnd>-1 ? defaultEnd : getMaxOffset().getOffset();
}
/**
* Returns the maximum offset the caret can move to before being outside
* of the text inserted for this completion.
*
* @return The maximum offset.
* @see #getMinOffset()
*/
public Position getMaxOffset() {
return maxOffs;
}
/**
* Returns the minimum offset the caret can move to before being outside
* of the text inserted for this completion.
*
* @return The minimum offset.
* @see #getMaxOffset()
*/
public int getMinOffset() {
return minOffs;
}
public int getReplacementCopyCount() {
return replacementCopies==null ? 0 : replacementCopies.size();
}
/**
* Returns the number of replacements in the completion.
*
* @return The number of replacements in the completion.
*/
public int getReplacementCount() {
return replacementLocations==null ? 0 : replacementLocations.size();
}
public ReplacementCopy getReplacementCopy(int index) {
return replacementCopies.get(index);
}
/**
* Returns the starting- and ending-offsets of the replacement regions
* in the completion.
*
* @param index The replacement region.
* @return The range in the document of that replacement region.
* @see #getReplacementCount()
*/
public DocumentRange getReplacementLocation(int index) {
return replacementLocations.get(index);
}
/**
* Returns the offset that should be the end of the initially selected
* text when the completion is inserted (i.e., the end offset of the first
* replacement region).
*
* @return The end offset for the initial selection.
* @see #getSelectionStart()
*/
public int getSelectionEnd() {
return selEnd;
}
/**
* Returns the offset that should be the start of the initially selected
* text when the completion is inserted (i.e., the start offset of the
* first replacement region).
*
* @return The start offset for the initial selection.
* @see #getSelectionEnd()
*/
public int getSelectionStart() {
return selStart;
}
/**
* Returns the actual text to insert when the completion is selected.
*
* @return The text to insert.
* @see #setTextToInsert(String)
*/
public String getTextToInsert() {
return textToInsert;
}
/**
* Returns whether or not there is an initial selected region for the
* completion (i.e., whether the completion actually has any parameters).
*
* @return Whether there is a region to initially select for the completion.
*/
public boolean hasSelection() {
return selEnd!=selStart;
}
/**
* Sets the initially selected region for the completion.
*
* @param selStart The selection start.
* @param selEnd The selection end.
* @see #getSelectionEnd()
* @see #getSelectionStart()
*/
public void setInitialSelection(int selStart, int selEnd) {
this.selStart = selStart;
this.selEnd = selEnd;
}
/**
* Sets the document range the caret can move around in before being
* outside of the text inserted for the completion.
*
* @param minOffs The minimum offset.
* @param maxOffs The maximum offset, that will track its location as the
* document is modified.
* @see #getMinOffset()
* @see #getMaxOffset()
*/
public void setCaretRange(int minOffs, Position maxOffs) {
this.minOffs = minOffs;
this.maxOffs = maxOffs;
}
public void setDefaultEndOffs(int end) {
defaultEnd = end;
}
/**
* Sets the text to insert for the completion.
*
* @param text The text to insert.
* @see #getTextToInsert()
*/
public void setTextToInsert(String text) {
this.textToInsert = text;
}
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;
}
}
}
@@ -0,0 +1,154 @@
/*
* 12/02/2013
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
/**
* An <code>AutoCompletion</code> that adds the ability to cycle through a set
* of <code>CompletionProvider</code>s via the trigger key. This allows the
* application to logically "group together" completions of similar kinds;
* for example, Java code completions vs. template completions.<p>
*
* Usage:
* <pre>
* XPathDynamicCompletionProvider dynamicProvider = new XPathDynamicCompletionProvider();
* RoundRobinAutoCompletion ac = new RoundRobinAutoCompletion(dynamicProvider);
* XPathCompletionProvider staticProvider = new XPathCompletionProvider();
* ac.addCompletionProvider(staticProvider);
* ac.setXXX(..);
* ...
* ac.install(textArea);
* </pre>
*
* @author mschlegel
*/
public class RoundRobinAutoCompletion extends AutoCompletion {
/** The List of CompletionProviders to use */
private List<CompletionProvider> cycle = new ArrayList<CompletionProvider>();
/**
* Constructor.
*
* @param provider A single completion provider.
* @see #addCompletionProvider(CompletionProvider)
*/
public RoundRobinAutoCompletion(CompletionProvider provider) {
super(provider);
cycle.add(provider);
// principal requirement for round-robin
setHideOnCompletionProviderChange(false);
// this is required since otherwise, on empty list of completions for
// one of the CompletionProviders, round-robin completion would not
// work
setHideOnNoText(false);
// this is required to prevent single choice of 1st provider to choose
// the completion since the user may want the second provider to be
// chosen.
setAutoCompleteSingleChoices(false);
}
/**
* Adds an additional <code>CompletionProvider</code> to the list to
* cycle through.
*
* @param provider The new completion provider.
*/
public void addCompletionProvider(CompletionProvider provider) {
cycle.add(provider);
}
/**
* Moves to the next Provider internally. Needs refresh of the popup window
* to display the changes.
*
* @return true if the next provider was the default one (thus returned to
* the default view). May be used in case you like to hide the
* popup in this case.
*/
public boolean advanceProvider() {
CompletionProvider currentProvider = getCompletionProvider();
int i = (cycle.indexOf(currentProvider)+1) % cycle.size();
setCompletionProvider(cycle.get(i));
return i==0;
}
/**
* Overridden to provide our own implementation of the action.
*/
@Override
protected Action createAutoCompleteAction() {
return new CycleAutoCompleteAction();
}
/**
* Resets the cycle to use the default provider on next refresh.
*/
public void resetProvider() {
CompletionProvider currentProvider = getCompletionProvider();
CompletionProvider defaultProvider = cycle.get(0);
if (currentProvider != defaultProvider) {
setCompletionProvider(defaultProvider);
}
}
/**
* An implementation of the auto-complete action that ensures the proper
* <code>CompletionProvider</code> is displayed based on the context in
* which the user presses the trigger key.
*/
private class CycleAutoCompleteAction extends AutoCompleteAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isAutoCompleteEnabled()) {
if (isPopupVisible()) {
// The popup is already visible, and user pressed the
// trigger-key. In this case, move to next provider.
advanceProvider();
}
else {
// Be sure to start with the default provider
resetProvider();
}
//Check if there are completions from the current provider. If not, advance to the next provider and display that one.
//A completion provider can force displaying "his" empty completion pop-up by returning an empty BasicCompletion. This is useful when the user is typing backspace and you like to display the first provider always first.
for (int i=1; i<cycle.size(); i++) {
List<Completion> completions = getCompletionProvider().getCompletions(getTextComponent());
if (completions.size() > 0) {
//nothing to do, just let the current provider display
break;
}
else{
//search for non-empty completions
advanceProvider();
}
}
}
super.actionPerformed(e);
}
}
// TODO add label "Ctrl-Space for <next provider name>" to the popup window
}
@@ -0,0 +1,117 @@
/*
* 12/22/2008
*
* ShorhandCompletion.java - A completion that is shorthand for some other
* text.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
/**
* A completion where the input text is shorthand for (really, just different
* than) the actual text to be inserted. For example, the input text
* "<code>sysout</code>" could be associated with the completion
* "<code>System.out.println(</code>" in Java.
*
* @author Robert Futrell
* @version 1.0
*/
public class ShorthandCompletion extends BasicCompletion {
/**
* The text the user can start typing that will match this completion.
*/
private String inputText;
/**
* Constructor.
*
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText,
String replacementText) {
super(provider, replacementText);
this.inputText = inputText;
}
/**
* Constructor.
*
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText,
String replacementText, String shortDesc) {
super(provider, replacementText, shortDesc);
this.inputText = inputText;
}
/**
* Constructor.
*
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be <code>null</code>.
* @param summary The summary of this completion. This should be HTML.
* This may be <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText,
String replacementText, String shortDesc, String summary) {
super(provider, replacementText, shortDesc, summary);
this.inputText = inputText;
}
/**
* Returns the text the user must start typing to get this completion.
*
* @return The text the user must start to input.
*/
@Override
public String getInputText() {
return inputText;
}
/**
* If a summary has been set, that summary is returned. Otherwise, the
* replacement text is returned.
*
* @return A description of this completion (the text that will be
* inserted).
* @see #getReplacementText()
*/
@Override
public String getSummary() {
String summary = super.getSummary();
return summary!=null ? summary : ("<html><body>" + getSummaryBody());
}
/**
* Returns the "body" of the HTML returned by {@link #getSummary()} when
* no summary text has been set. This is defined to return the replacement
* text in a monospaced font.
*
* @return The summary text's body, if no other summary has been defined.
* @see #getReplacementText()
*/
protected String getSummaryBody() {
return "<code>" + getReplacementText();
}
}
+257
View File
@@ -0,0 +1,257 @@
/*
* 12/23/2008
*
* SizeGrip.java - A size grip component that sits at the bottom of the window,
* allowing the user to easily resize that window.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.ComponentOrientation;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;
/**
* A component that allows its parent window to be resizable, similar to the
* size grip seen on status bars. This is essentially a copy of the class with
* the same name in RSyntaxTextArea, but is duplicated to prevent a dependency
* on that library.
*
* @author Robert Futrell
* @version 1.0
*/
class SizeGrip extends JPanel {
/**
* The size grip to use if we're on OS X.
*/
private Image osxSizeGrip;
public SizeGrip() {
MouseHandler adapter = new MouseHandler();
addMouseListener(adapter);
addMouseMotionListener(adapter);
setPreferredSize(new Dimension(16, 16));
}
/**
* Overridden to ensure that the cursor for this component is appropriate
* for the orientation.
*
* @param o The new orientation.
*/
@Override
public void applyComponentOrientation(ComponentOrientation o) {
possiblyFixCursor(o.isLeftToRight());
super.applyComponentOrientation(o);
}
/**
* Creates and returns the OS X size grip image.
*
* @return The OS X size grip.
*/
private Image createOSXSizeGrip() {
ClassLoader cl = getClass().getClassLoader();
URL url = cl.getResource("org/fife/ui/autocomplete/osx_sizegrip.png");
if (url==null) {
// We're not running in a jar - we may be debugging in Eclipse,
// for example
File f = new File("../AutoComplete/src/org/fife/ui/autocomplete/osx_sizegrip.png");
if (f.isFile()) {
try {
url = f.toURI().toURL();
} catch (MalformedURLException mue) { // Never happens
mue.printStackTrace();
return null;
}
}
else {
return null; // Can't find resource or image file
}
}
Image image = null;
try {
image = ImageIO.read(url);
} catch (IOException ioe) { // Never happens
ioe.printStackTrace();
}
return image;
}
/**
* Paints this panel.
*
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension dim = getSize();
if (osxSizeGrip!=null) {
g.drawImage(osxSizeGrip, dim.width-16, dim.height-16, null);
return;
}
Color c1 = UIManager.getColor("Label.disabledShadow");
Color c2 = UIManager.getColor("Label.disabledForeground");
ComponentOrientation orientation = getComponentOrientation();
if (orientation.isLeftToRight()) {
int width = dim.width -= 3;
int height = dim.height -= 3;
g.setColor(c1);
g.fillRect(width-9,height-1, 3,3);
g.fillRect(width-5,height-1, 3,3);
g.fillRect(width-1,height-1, 3,3);
g.fillRect(width-5,height-5, 3,3);
g.fillRect(width-1,height-5, 3,3);
g.fillRect(width-1,height-9, 3,3);
g.setColor(c2);
g.fillRect(width-9,height-1, 2,2);
g.fillRect(width-5,height-1, 2,2);
g.fillRect(width-1,height-1, 2,2);
g.fillRect(width-5,height-5, 2,2);
g.fillRect(width-1,height-5, 2,2);
g.fillRect(width-1,height-9, 2,2);
}
else {
int height = dim.height -= 3;
g.setColor(c1);
g.fillRect(10,height-1, 3,3);
g.fillRect(6,height-1, 3,3);
g.fillRect(2,height-1, 3,3);
g.fillRect(6,height-5, 3,3);
g.fillRect(2,height-5, 3,3);
g.fillRect(2,height-9, 3,3);
g.setColor(c2);
g.fillRect(10,height-1, 2,2);
g.fillRect(6,height-1, 2,2);
g.fillRect(2,height-1, 2,2);
g.fillRect(6,height-5, 2,2);
g.fillRect(2,height-5, 2,2);
g.fillRect(2,height-9, 2,2);
}
}
/**
* Ensures that the cursor for this component is appropriate for the
* orientation.
*
* @param ltr Whether the current component orientation is LTR.
*/
protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR;
if (ltr) {
cursor = Cursor.NW_RESIZE_CURSOR;
}
if (cursor!=getCursor().getType()) {
setCursor(Cursor.getPredefinedCursor(cursor));
}
}
@Override
public void updateUI() {
super.updateUI();
// TODO: Key off of Aqua LaF, not just OS X, as this size grip looks
// bad on other LaFs on Mac such as Nimbus.
if (System.getProperty("os.name").contains("OS X")) {
if (osxSizeGrip==null) {
osxSizeGrip = createOSXSizeGrip();
}
}
else { // Clear memory in case of runtime LaF change.
osxSizeGrip = null;
}
}
/**
* Listens for mouse events on this panel and resizes the parent window
* appropriately.
*/
/*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using
* the locations relative to the corner component because the latter proved
* buggy - stretch the window too wide and some kind of arithmetic error
* started happening somewhere - our window would grow way too large.
*/
private class MouseHandler extends MouseInputAdapter {
private Point origPos;
@Override
public void mouseDragged(MouseEvent e) {
Point newPos = e.getPoint();
SwingUtilities.convertPointToScreen(newPos, SizeGrip.this);
int xDelta = newPos.x - origPos.x;
int yDelta = newPos.y - origPos.y;
Window wind = SwingUtilities.getWindowAncestor(SizeGrip.this);
if (wind!=null) { // Should always be true
if (getComponentOrientation().isLeftToRight()) {
int w = wind.getWidth();
if (newPos.x>=wind.getX()) {
w += xDelta;
}
int h = wind.getHeight();
if (newPos.y>=wind.getY()) {
h += yDelta;
}
wind.setSize(w,h);
}
else { // RTL
int newW = Math.max(1, wind.getWidth()-xDelta);
int newH = Math.max(1, wind.getHeight()+yDelta);
wind.setBounds(newPos.x, wind.getY(), newW, newH);
}
// invalidate()/validate() needed pre-1.6.
wind.invalidate();
wind.validate();
}
origPos.setLocation(newPos);
}
@Override
public void mousePressed(MouseEvent e) {
origPos = e.getPoint();
SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
}
@Override
public void mouseReleased(MouseEvent e) {
origPos = null;
}
}
}
@@ -0,0 +1,34 @@
/*
* 12/17/2010
*
* SortByRelevanceComparator.java - Sorts two Completions by relevance before
* sorting them lexicographically.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.Comparator;
/**
* Compares two <code>Completion</code>s by their relevance before
* sorting them lexicographically.
*
* @author Robert Futrell
* @version 1.0
*/
public class SortByRelevanceComparator implements Comparator<Completion> {
@Override
public int compare(Completion c1, Completion c2) {
int rel1 = c1.getRelevance();
int rel2 = c2.getRelevance();
int diff = rel2 - rel1;//rel1 - rel2;
return diff==0 ? c1.compareTo(c2) : diff;
}
}
@@ -0,0 +1,391 @@
/*
* 05/26/2012
*
* TemplateCompletion.java - A completion used to insert boilerplate code
* snippets that have arbitrary sections the user will want to change, such as
* for-loops.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.ArrayList;
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.autocomplete.TemplatePiece.Text;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
/**
* A completion made up of a template with arbitrary parameters that the user
* can tab through and fill in. This completion type is useful for inserting
* common boilerplate code, such as for-loops.<p>
*
* 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} &lt; ${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>
*
* Two dollar signs in a row ("<code>$$</code>") will be evaluated as a single
* dollar sign. Otherwise, the template parsing is pretty straightforward and
* fault-tolerant.<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
*/
public class TemplateCompletion extends AbstractCompletion
implements ParameterizedCompletion {
private List<TemplatePiece> pieces;
private String inputText;
private String definitionString;
private String shortDescription;
private String summary;
/**
* The template's parameters.
*/
private List<Parameter> params;
public TemplateCompletion(CompletionProvider provider,
String inputText, String definitionString, String template) {
this(provider, inputText, definitionString, template, null, null);
}
public TemplateCompletion(CompletionProvider provider,
String inputText, String definitionString, String template,
String shortDescription, String summary) {
super(provider);
this.inputText = inputText;
this.definitionString = definitionString;
this.shortDescription = shortDescription;
this.summary = summary;
pieces = new ArrayList<TemplatePiece>(3);
params = new ArrayList<Parameter>(3);
parse(template);
}
private void addTemplatePiece(TemplatePiece piece) {
pieces.add(piece);
if (piece instanceof Param && !"cursor".equals(piece.getText())) {
final String type = null; // TODO
Parameter param = new Parameter(type, piece.getText());
params.add(param);
}
}
@Override
public String getInputText() {
return inputText;
}
private String getPieceText(int index, String leadingWS) {
TemplatePiece piece = pieces.get(index);
String text = piece.getText();
if (text.indexOf('\n')>-1) {
text = text.replaceAll("\n", "\n" + leadingWS);
}
return text;
}
/**
* Returns <code>null</code>; template completions insert all of their
* text via <code>getInsertionInfo()</code>.
*
* @return <code>null</code> always.
*/
@Override
public String getReplacementText() {
return null;
}
@Override
public String getSummary() {
return summary;
}
@Override
public String getDefinitionString() {
return definitionString;
}
public String getShortDescription() {
return shortDescription;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getShowParameterToolTip() {
return false;
}
@Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(
JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info =
new ParameterizedCompletionInsertionInfo();
StringBuilder sb = new StringBuilder();
int dot = tc.getCaretPosition();
// 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) {
ble.printStackTrace(); // Never happens
}
info.setCaretRange(minPos, maxPos);
int selStart = dot; // Default value
int selEnd = selStart;
Document doc = tc.getDocument();
String leadingWS = null;
try {
leadingWS = RSyntaxUtilities.getLeadingWhitespace(doc, dot);
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
leadingWS = "";
}
// Create the text to insert (keep it one completion for
// performance and simplicity of undo/redo).
int start = dot;
for (int i=0; i<pieces.size(); i++) {
TemplatePiece piece = pieces.get(i);
String text = getPieceText(i, leadingWS);
if (piece instanceof Text) {
if (replaceTabsWithSpaces) {
start = possiblyReplaceTabsWithSpaces(sb, text, tc, start);
}
else {
sb.append(text);
start += text.length();
}
}
else if (piece instanceof Param && "cursor".equals(text)) {
defaultEndOffs = start;
}
else {
int end = start + text.length();
sb.append(text);
if (piece instanceof Param) {
info.addReplacementLocation(start, end);
if (selStart==dot) {
selStart = start;
selEnd = selStart + text.length();
}
}
else if (piece instanceof ParamCopy) {
info.addReplacementCopy(piece.getText(), start, end);
}
start = end;
}
}
// Highlight the first parameter. If no params were specified, move
// the caret to the ${cursor} location, if specified
if (selStart==minPos && selStart==selEnd && getParamCount()==0) {
if (defaultEndOffs>-1) { // ${cursor} specified
selStart = selEnd = defaultEndOffs;
}
}
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;
}
/**
* {@inheritDoc}
*/
@Override
public Parameter getParam(int index) {
return params.get(index);
}
/**
* {@inheritDoc}
*/
@Override
public int getParamCount() {
return params==null ? 0 : params.size();
}
/**
* 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 = closingCurly + 1;
}
break;
}
}
if (lastOffs<template.length()) {
String text = template.substring(lastOffs);
addTemplatePiece(new TemplatePiece.Text(text));
}
}
private int possiblyReplaceTabsWithSpaces(StringBuilder sb, String text,
JTextComponent tc, int start) {
int tab = text.indexOf('\t');
if (tab>-1) {
int startLen = sb.length();
int size = 4;
Document doc = tc.getDocument();
if (doc != null) {
Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute);
if (i != null) {
size = i.intValue();
}
}
String tabStr = "";
for (int i=0; i<size; i++) {
tabStr += " ";
}
int lastOffs = 0;
do {
sb.append(text.substring(lastOffs, tab));
sb.append(tabStr);
lastOffs = tab + 1;
} while ((tab=text.indexOf('\t', lastOffs))>-1);
sb.append(text.substring(lastOffs));
start += sb.length() - startLen;
}
else {
sb.append(text);
start += text.length();
}
return start;
}
/**
* Sets the short description of this template completion.
*
* @param shortDesc The new short description.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
this.shortDescription = shortDesc;
}
@Override
public String toString() {
return getDefinitionString();
}
}
@@ -0,0 +1,89 @@
/*
* 06/17/2012
*
* TemplatePiece.java - A logical piece of a template completion.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
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;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.Text: text=" + text + "]";
}
}
public class Param implements TemplatePiece {
String text;
public Param(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.Param: param=" + text + "]";
}
}
public class ParamCopy implements TemplatePiece {
private String text;
public ParamCopy(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.ParamCopy: param=" + text + "]";
}
}
}
+179
View File
@@ -0,0 +1,179 @@
/*
* 08/13/2009
*
* TipUtil.java - Utility methods for homemade tool tips.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.Font;
import java.awt.SystemColor;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ColorUIResource;
import javax.swing.text.html.HTMLDocument;
/**
* Static utility methods for homemade tool tips.<p>
*
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class
* of the same name, but isn't re-used to prevent a hard dependency on the
* RSTA library.
*
* @author Robert Futrell
* @version 1.0
*/
class TipUtil {
private TipUtil() {
}
/**
* Returns the default background color to use for tool tip windows.
*
* @return The default background color.
*/
public static Color getToolTipBackground() {
Color c = UIManager.getColor("ToolTip.background");
// Tooltip.background is wrong color on Nimbus (!)
boolean isNimbus = isNimbusLookAndFeel();
if (c==null || isNimbus) {
c = UIManager.getColor("info"); // Used by Nimbus (and others)
if (c==null || (isNimbus && isDerivedColor(c))) {
c = SystemColor.info; // System default
}
}
// Workaround for a bug (?) with Nimbus - calling JLabel.setBackground()
// with a ColorUIResource does nothing, must be a normal Color
if (c instanceof ColorUIResource) {
c = new Color(c.getRGB());
}
return c;
}
/**
* Returns the border used by tool tips in this look and feel.
*
* @return The border.
*/
public static Border getToolTipBorder() {
Border border = UIManager.getBorder("ToolTip.border");
if (border==null || isNimbusLookAndFeel()) {
border = UIManager.getBorder("nimbusBorder");
if (border==null) {
border = BorderFactory.createLineBorder(SystemColor.controlDkShadow);
}
}
return border;
}
/**
* Returns whether a color is a Nimbus DerivedColor, which is troublesome
* in that it doesn't use its RGB values (uses HSB instead?) and so
* querying them is useless.
*
* @param c The color to check.
* @return Whether it is a DerivedColor
*/
private static final boolean isDerivedColor(Color c) {
return c!=null && (c.getClass().getName().endsWith(".DerivedColor") ||
c.getClass().getName().endsWith(".DerivedColor$UIResource"));
}
/**
* Returns whether the Nimbus Look and Feel is installed.
*
* @return Whether the current LAF is Nimbus.
*/
private static final boolean isNimbusLookAndFeel() {
return UIManager.getLookAndFeel().getName().equals("Nimbus");
}
/**
* Tweaks a <code>JEditorPane</code> so it can be used to render the
* content in a focusable pseudo-tool tip. It is assumed that the editor
* pane is using an <code>HTMLDocument</code>.
*
* @param textArea The editor pane to tweak.
*/
public static void tweakTipEditorPane(JEditorPane textArea) {
// Jump through a few hoops to get things looking nice in Nimbus
boolean isNimbus = isNimbusLookAndFeel();
if (isNimbus) {
Color selBG = textArea.getSelectionColor();
Color selFG = textArea.getSelectedTextColor();
textArea.setUI(new javax.swing.plaf.basic.BasicEditorPaneUI());
textArea.setSelectedTextColor(selFG);
textArea.setSelectionColor(selBG);
}
textArea.setEditable(false); // Required for links to work!
textArea.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
// Make selection visible even though we are not (initially) focusable.
textArea.getCaret().setSelectionVisible(true);
// Set the foreground color. Important because when rendering HTML,
// default foreground becomes black, which may not match all LAF's
// (e.g. Substance).
Color fg = UIManager.getColor("Label.foreground");
if (fg==null || (isNimbus && isDerivedColor(fg))) {
fg = SystemColor.textText;
}
textArea.setForeground(fg);
// Make it use the "tool tip" background color.
textArea.setBackground(TipUtil.getToolTipBackground());
// Force JEditorPane to use a certain font even in HTML.
// All standard LookAndFeels, even Nimbus (!), define Label.font.
Font font = UIManager.getFont("Label.font");
if (font == null) { // Try to make a sensible default
font = new Font("SansSerif", Font.PLAIN, 12);
}
HTMLDocument doc = (HTMLDocument) textArea.getDocument();
doc.getStyleSheet().addRule(
"body { font-family: " + font.getFamily() +
"; font-size: " + font.getSize() + "pt" +
"; color: " + Util.getHexString(fg) + "; }");
// Always add link foreground rule. Unfortunately these CSS rules
// stack each time the LaF is changed (how can we overwrite them
// without clearing out the important "standard" ones?).
Color linkFG = Util.getHyperlinkForeground();
doc.getStyleSheet().addRule(
"a { color: " + Util.getHexString(linkFG) + "; }");
URL url = TipUtil.class.getResource("bullet_black.png");
if (url!=null) {
doc.getStyleSheet().addRule(
"ul { list-style-image: '" + url.toString() + "'; }");
}
}
}
+323
View File
@@ -0,0 +1,323 @@
/*
* 12/21/2008
*
* Util.java - Utility methods for the autocompletion package.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.Color;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.lang.reflect.Method;
import java.net.URI;
import java.security.AccessControlException;
import java.util.regex.Pattern;
import javax.swing.JLabel;
import javax.swing.UIManager;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* Utility methods for the auto-complete framework.
*
* @author Robert Futrell
* @version 1.0
*/
public class Util {
/**
* If a system property is defined with this name and set, ignoring case,
* to <code>true</code>, this library will not attempt to use Substance
* renderers. Otherwise, if a Substance Look and Feel is installed, we
* will attempt to use Substance cell renderers in all of our dropdowns.<p>
*
* Note that we do not have a build dependency on Substance, so all access
* to Substance stuff is done via reflection. We will fall back onto
* default renderers if something goes horribly wrong.
*/
public static final String PROPERTY_DONT_USE_SUBSTANCE_RENDERERS =
"org.fife.ui.autocomplete.DontUseSubstanceRenderers";
/**
* If this system property is <code>true</code>, then even the "main" two
* auto-complete windows will allow window decorations via
* {@link PopupWindowDecorator}. If this property is undefined or
* <code>false</code>, they won't honor such decorations. This is due to
* certain performance issues with translucent windows (used for drop
* shadows), even as of Java 7u2.
*/
public static final String PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS =
"org.fife.ui.autocomplete.allowDecoratedAutoCompleteWindows";
/**
* Used for the color of hyperlinks when a LookAndFeel uses light text
* against a dark background.
*/
private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
private static final Pattern TAG_PATTERN = Pattern.compile("<[^>]*>");
private static final boolean useSubstanceRenderers;
private static boolean desktopCreationAttempted;
private static Object desktop;
private static final Object LOCK_DESKTOP_CREATION = new Object();
/**
* Attempts to open a web browser to the specified URI.
*
* @param uri The URI to open. If this is <code>null</code>, nothing
happens and this method returns <code>false</code>.
* @return Whether the operation was successful. This will be
* <code>false</code> on JRE's older than 1.6.
*/
public static boolean browse(URI uri) {
boolean success = false;
if (uri!=null) {
Object desktop = getDesktop();
if (desktop!=null) {
try {
Method m = desktop.getClass().getDeclaredMethod(
"browse", new Class[] { URI.class });
m.invoke(desktop, new Object[] { uri });
success = true;
} catch (RuntimeException re) {
throw re; // Keep FindBugs happy
} catch (Exception e) {
// Ignore, just return "false" below.
}
}
}
return success;
}
/**
* Returns the singleton <code>java.awt.Desktop</code> instance, or
* <code>null</code> if it is unsupported on this platform (or the JRE
* is older than 1.6).
*
* @return The desktop, as an {@link Object}.
*/
private static Object getDesktop() {
synchronized (LOCK_DESKTOP_CREATION) {
if (!desktopCreationAttempted) {
desktopCreationAttempted = true;
try {
Class<?> desktopClazz = Class.forName("java.awt.Desktop");
Method m = desktopClazz.
getDeclaredMethod("isDesktopSupported");
boolean supported= ((Boolean)m.invoke(null)).booleanValue();
if (supported) {
m = desktopClazz.getDeclaredMethod("getDesktop");
desktop = m.invoke(null);
}
} catch (RuntimeException re) {
throw re; // Keep FindBugs happy
} catch (Exception e) {
// Ignore; keeps desktop as null.
}
}
}
return desktop;
}
/**
* Returns a hex string for the specified color, suitable for HTML.
*
* @param c The color.
* @return The string representation, in the form "<code>#rrggbb</code>",
* or <code>null</code> if <code>c</code> is <code>null</code>.
*/
public static String getHexString(Color c) {
if (c==null) {
return null;
}
// Don't assume 0xff alpha
//return "#" + Integer.toHexString(c.getRGB()&0xffffff).substring(2);
StringBuilder sb = new StringBuilder("#");
int r = c.getRed();
if (r<16) {
sb.append('0');
}
sb.append(Integer.toHexString(r));
int g = c.getGreen();
if (g<16) {
sb.append('0');
}
sb.append(Integer.toHexString(g));
int b = c.getBlue();
if (b<16) {
sb.append('0');
}
sb.append(Integer.toHexString(b));
return sb.toString();
}
/**
* Returns the color to use for hyperlink-style components. This method
* will return <code>Color.blue</code> unless it appears that the current
* LookAndFeel uses light text on a dark background, in which case a
* brighter alternative is returned.
*
* @return The color to use for hyperlinks.
*/
static final Color getHyperlinkForeground() {
// This property is defined by all standard LaFs, even Nimbus (!),
// but you never know what crazy LaFs there are...
Color fg = UIManager.getColor("Label.foreground");
if (fg==null) {
fg = new JLabel().getForeground();
}
return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;
}
/**
* Returns the screen coordinates for the monitor that contains the
* specified point. This is useful for setups with multiple monitors,
* to ensure that popup windows are positioned properly.
*
* @param x The x-coordinate, in screen coordinates.
* @param y The y-coordinate, in screen coordinates.
* @return The bounds of the monitor that contains the specified point.
*/
public static Rectangle getScreenBoundsForPoint(int x, int y) {
GraphicsEnvironment env = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice[] devices = env.getScreenDevices();
for (int i=0; i<devices.length; i++) {
GraphicsConfiguration config = devices[i].getDefaultConfiguration();
Rectangle gcBounds = config.getBounds();
if (gcBounds.contains(x, y)) {
return gcBounds;
}
}
// If point is outside all monitors, default to default monitor (?)
return env.getMaximumWindowBounds();
}
/**
* Give apps a chance to decorate us with drop shadows, etc. Since very
* scrolly things such as lists (of e.g. completions) are *very* slow when
* in per-pixel translucent windows, even as of Java 7u2, we force the user
* to specify an extra option for the two "main" auto-complete windows.
*
* @return Whether to allow decorating the main auto-complete windows.
* @see #PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS
*/
public static boolean getShouldAllowDecoratingMainAutoCompleteWindows() {
try {
return Boolean.getBoolean(
PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS);
} catch (AccessControlException ace) { // We're in an applet.
return false;
}
}
/**
* Returns whether we should attempt to use Substance cell renderers and
* styles for things such as completion choices, if a Substance Look and
* Feel is installed. If this is <code>false</code>, we'll use our
* standard rendering for completions, even when Substance is being used.
*
* @return Whether to use Substance renderers if Substance is installed.
*/
public static boolean getUseSubstanceRenderers() {
return useSubstanceRenderers;
}
/**
* Returns whether the specified color is "light" to use as a foreground.
* Colors that return <code>true</code> indicate that the current Look and
* Feel probably uses light text colors on a dark background.
*
* @param fg The foreground color.
* @return Whether it is a "light" foreground color.
*/
public static final boolean isLightForeground(Color fg) {
return fg.getRed()>0xa0 && fg.getGreen()>0xa0 && fg.getBlue()>0xa0;
}
/**
* Returns whether a string starts with a specified prefix, ignoring case.
* This method does not support characters outside of the BMP.
*
* @param str The string to check. This cannot be {@code null}.
* @param prefix The prefix to check for. This cannot be {@code null}.
* @return Whether {@code str} starts with {@code prefix}, ignoring case.
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
int prefixLength = prefix.length();
if (str.length() >= prefixLength) {
return str.regionMatches(true, 0, prefix, 0, prefixLength);
}
return false;
}
/**
* Strips any HTML from a string. The string must start with
* "<code>&lt;html&gt;</code>" for markup tags to be stripped.
*
* @param text The string.
* @return The string, with any HTML stripped.
*/
public static String stripHtml(String text) {
if (text==null || !text.startsWith("<html>")) {
return text;
}
// TODO: Micro-optimize me, might be called in renderers and loops
return TAG_PATTERN.matcher(text).replaceAll("");
}
static {
boolean use = true;
try {
use = !Boolean.getBoolean(PROPERTY_DONT_USE_SUBSTANCE_RENDERERS);
} catch (AccessControlException ace) { // We're in an applet.
use = true;
}
useSubstanceRenderers = use;
}
}
@@ -0,0 +1,196 @@
/*
* 12/22/2008
*
* VariableCompletion.java - A completion for a variable.
*
* This library is distributed under a modified BSD license. See the included
* AutoComplete.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import javax.swing.text.JTextComponent;
/**
* A completion for a variable (or constant) in a programming language.<p>
*
* This completion type uses its <tt>shortDescription</tt> property as part of
* its summary returned by {@link #getSummary()}; for this reason, it may be
* a little longer (even much longer), if desired, than what is recommended
* for <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used
* in {@link #toString()} for <tt>ListCellRenderers</tt>).
*
* @author Robert Futrell
* @version 1.0
*/
public class VariableCompletion extends BasicCompletion {
/**
* The variable's type.
*/
private String type;
/**
* What library (for example) this variable is defined in.
*/
private String definedIn;
/**
* Constructor.
*
* @param provider The parent provider.
* @param name The name of this variable.
* @param type The type of this variable (e.g. "<code>int</code>",
* "<code>String</code>", etc.).
*/
public VariableCompletion(CompletionProvider provider, String name,
String type) {
super(provider, name);
this.type = type;
}
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(getDefinitionString()).append("</b>");
}
public String getDefinitionString() {
StringBuilder sb = new StringBuilder();
// Add the return type if applicable (C macros like NULL have no type).
if (type!=null) {
sb.append(type).append(' ');
}
// Add the item being described's name.
sb.append(getName());
return sb.toString();
}
/**
* Returns where this variable is defined.
*
* @return Where this variable is defined.
* @see #setDefinedIn(String)
*/
public String getDefinedIn() {
return definedIn;
}
/**
* Returns the name of this variable.
*
* @return The name.
*/
public String getName() {
return getReplacementText();
}
/**
* {@inheritDoc}
*/
@Override
public String getSummary() {
StringBuilder sb = new StringBuilder();
addDefinitionString(sb);
possiblyAddDescription(sb);
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.<p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
*/
@Override
public String getToolTipText() {
return getDefinitionString();
}
/**
* Returns the type of this variable.
*
* @return The type.
*/
public String getType() {
return type;
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
*
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn!=null) {
sb.append("<hr>Defined in:"); // TODO: Localize me
sb.append(" <em>").append(definedIn).append("</em>");
}
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
*
* @param sb The buffer to append to.
* @return Whether there was a description to add.
*/
protected boolean possiblyAddDescription(StringBuilder sb) {
if (getShortDescription()!=null) {
sb.append("<hr><br>");
sb.append(getShortDescription());
sb.append("<br><br><br>");
return true;
}
return false;
}
/**
* Sets where this variable is defined.
*
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
this.definedIn = definedIn;
}
/**
* Overridden to return the name of the variable being completed.
*
* @return A string representation of this completion.
*/
@Override
public String toString() {
return getName();
}
}
@@ -0,0 +1,288 @@
/*
* 01/25/2009
*
* AbstractJFlexCTokenMaker.java - Base class for token makers that use curly
* braces to denote code blocks, such as C, C++, Java, Perl, etc.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.event.ActionEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import org.fife.ui.rtextarea.RTextArea;
/**
* Base class for JFlex-based token makers using C-style syntax. This class
* knows how to:
*
* <ul>
* <li>Auto-indent after opening braces and parens
* <li>Automatically close multi-line and documentation comments
* </ul>
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
private final Action INSERT_BREAK_ACTION;
private static final Pattern MLC_PATTERN =
Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
protected AbstractJFlexCTokenMaker() {
INSERT_BREAK_ACTION = createInsertBreakAction();
}
/**
* Creates and returns the action to use when the user inserts a newline.
* The default implementation intelligently closes multi-line comments.
* Subclasses can override.
*
* @return The action.
* @see #getInsertBreakAction()
*/
protected Action createInsertBreakAction() {
return new CStyleInsertBreakAction();
}
/**
* Returns <code>true</code> always as C-style languages use curly braces
* to denote code blocks.
*
* @return <code>true</code> always.
*/
@Override
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return true;
}
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
* An action is returned that handles newlines differently in multi-line
* comments.
*
* @return The action.
*/
@Override
public Action getInsertBreakAction() {
return INSERT_BREAK_ACTION;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getMarkOccurrencesOfTokenType(int type) {
return type==Token.IDENTIFIER || type==Token.FUNCTION;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getShouldIndentNextLineAfter(Token t) {
if (t!=null && t.length()==1) {
char ch = t.charAt(0);
return ch=='{' || ch=='(';
}
return false;
}
/**
* Returns whether a given token is an internal token type that represents
* an MLC or documentation comment continuing on to the next line. This is
* done by languages such as JavaScript that are a little more verbose
* than necessary so that their code can be copy-and-pasted into other
* <code>TokenMaker</code>s that use them as nested languages (such as
* HTML, JSP, etc.).
*
* @param t The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of
* a line for an MLC/doc comment continuing on to the next line.
*/
private boolean isInternalEolTokenForMLCs(Token t) {
int type = t.getType();
if (type<0) {
type = getClosestStandardTokenTypeForInternalType(type);
return type==TokenTypes.COMMENT_MULTILINE ||
type==TokenTypes.COMMENT_DOCUMENTATION;
}
return false;
}
/**
* Action that knows how to special-case inserting a newline in a
* multi-line comment for languages like C and Java.
*/
protected class CStyleInsertBreakAction extends
RSyntaxTextAreaEditorKit.InsertBreakAction {
@Override
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
if (!textArea.isEditable() || !textArea.isEnabled()) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
return;
}
RSyntaxTextArea rsta = (RSyntaxTextArea)getTextComponent(e);
RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
int line = textArea.getCaretLineNumber();
int type = doc.getLastTokenTypeOnLine(line);
if (type<0) {
type = doc.getClosestStandardTokenTypeForInternalType(type);
}
// Only in MLC's should we try this
if (type==Token.COMMENT_DOCUMENTATION ||
type==Token.COMMENT_MULTILINE) {
insertBreakInMLC(e, rsta, line);
}
else {
handleInsertBreak(rsta, true);
}
}
/**
* Returns whether the MLC token containing <code>offs</code> appears
* to have a "nested" comment (i.e., contains "<code>/*</code>"
* somewhere inside of it). This implies that it is likely a "new" MLC
* and needs to be closed. While not foolproof, this is usually good
* enough of a sign.
*
* @param textArea
* @param line
* @param offs
* @return Whether a comment appears to be nested inside this one.
*/
private boolean appearsNested(RSyntaxTextArea textArea,
int line, int offs) {
final int firstLine = line; // Remember the line we start at.
while (line<textArea.getLineCount()) {
Token t = textArea.getTokenListForLine(line);
int i = 0;
// If examining the first line, start at offs.
if (line++==firstLine) {
t = RSyntaxUtilities.getTokenAtOffset(t, offs);
if (t==null) { // offs was at end of the line
continue;
}
i = t.documentToToken(offs);
}
else {
i = t.getTextOffset();
}
int textOffset = t.getTextOffset();
while (i<textOffset+t.length()-1) {
if (t.charAt(i-textOffset)=='/' && t.charAt(i-textOffset+1)=='*') {
return true;
}
i++;
}
// If tokens come after this one on this line, our MLC ended.
if ((t=t.getNextToken())!=null && !isInternalEolTokenForMLCs(t)) {
return false;
}
}
return true; // No match - MLC goes to the end of the file
}
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea,
int line) {
Matcher m = null;
int start = -1;
int end = -1;
String text = null;
try {
start = textArea.getLineStartOffset(line);
end = textArea.getLineEndOffset(line);
text = textArea.getText(start, end-start);
m = MLC_PATTERN.matcher(text);
} catch (BadLocationException ble) { // Never happens
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
ble.printStackTrace();
return;
}
if (m.lookingAt()) {
String leadingWS = m.group(1);
String mlcMarker = m.group(2);
// If the caret is "inside" any leading whitespace or MLC
// marker, move it to the end of the line.
int dot = textArea.getCaretPosition();
if (dot>=start &&
dot<start+leadingWS.length()+mlcMarker.length()) {
// If we're in the whitespace before the very start of the
// MLC though, just insert a normal newline
if (mlcMarker.charAt(0)=='/') {
handleInsertBreak(textArea, true);
return;
}
textArea.setCaretPosition(end-1);
}
else {
// Ensure caret is at the "end" of any whitespace
// immediately after the '*', but before any possible
// non-whitespace chars.
boolean moved = false;
while (dot<end-1 &&
Character.isWhitespace(text.charAt(dot-start))) {
moved = true;
dot++;
}
if (moved) {
textArea.setCaretPosition(dot);
}
}
boolean firstMlcLine = mlcMarker.charAt(0)=='/';
boolean nested = appearsNested(textArea, line,
start+leadingWS.length()+2);
String header = leadingWS +
(firstMlcLine ? " * " : "*") +
m.group(3);
textArea.replaceSelection("\n" + header);
if (nested) {
dot = textArea.getCaretPosition(); // Has changed
textArea.insert("\n" + leadingWS + " */", dot);
textArea.setCaretPosition(dot);
}
}
else {
handleInsertBreak(textArea, true);
}
}
}
}
@@ -0,0 +1,52 @@
/*
* 03/23/2005
*
* AbstractJFlexTokenMaker.java - Base class for token makers generated from
* programs such as JFlex.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* Base class for JFlex-generated token makers. This class attempts to factor
* out all common code from these classes. Many methods <em>almost</em> could
* be factored out into this class, but cannot because they reference JFlex
* variables that we cannot access from this class.
*
* @author Robert Futrell
* @version 0.1
*/
public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
protected Segment s;
protected int start; // Just for states.
protected int offsetShift; // As parser always starts at 0, but our line doesn't.
/**
* Declared here so we can define overloads that refer to this method.
*
* @param newState The new JFlex state to enter.
*/
public abstract void yybegin(int newState);
/**
* Starts a new JFlex state and changes the current language index.
*
* @param state The new JFlex state to enter.
* @param languageIndex The new language index.
*/
protected void yybegin(int state, int languageIndex) {
yybegin(state);
setLanguageIndex(languageIndex);
}
}
@@ -0,0 +1,67 @@
/*
* 11/07/2004
*
* AbstractTokenMaker.java - An abstract implementation of TokenMaker.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* An abstract implementation of the
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
* be overridden for every language for which you want to provide
* syntax highlighting.<p>
*
* @see Token
*
* @author Robert Futrell
* @version 0.2
*/
public abstract class AbstractTokenMaker extends TokenMakerBase {
/**
* Hash table of words to highlight and what token type they are.
* The keys are the words to highlight, and their values are the
* token types, for example, <code>Token.RESERVED_WORD</code> or
* <code>Token.FUNCTION</code>.
*/
protected TokenMap wordsToHighlight;
/**
* Constructor.
*/
public AbstractTokenMaker() {
wordsToHighlight = getWordsToHighlight();
}
/**
* Returns the words to highlight for this programming language.
*
* @return A <code>TokenMap</code> containing the words to highlight for
* this programming language.
*/
public abstract TokenMap getWordsToHighlight();
/**
* Removes the token last added from the linked list of tokens. The
* programmer should never have to call this directly; it can be called
* by subclasses of <code>TokenMaker</code> if necessary.
*/
public void removeLastToken() {
if (previousToken==null) {
firstToken = currentToken = null;
}
else {
currentToken = previousToken;
currentToken.setNextToken(null);
}
}
}
@@ -0,0 +1,134 @@
/*
* 12/14/08
*
* AbstractTokenMakerFactory.java - Base class for TokenMaker implementations.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Base class for {@link TokenMakerFactory} implementations. A mapping from
* language keys to the names of {@link TokenMaker} classes is stored.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* A mapping from keys to the names of {@link TokenMaker} implementation
* class names. When {@link #getTokenMaker(String)} is called with a key
* defined in this map, a <code>TokenMaker</code> of the corresponding type
* is returned.
*/
private Map<String, Object> tokenMakerMap;
/**
* Constructor.
*/
protected AbstractTokenMakerFactory() {
tokenMakerMap = new HashMap<String, Object>();
initTokenMakerMap();
}
/**
* Returns a {@link TokenMaker} for the specified key.
*
* @param key The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
* if none matches the specified key.
*/
@Override
protected TokenMaker getTokenMakerImpl(String key) {
TokenMakerCreator tmc = (TokenMakerCreator)tokenMakerMap.get(key);
if (tmc!=null) {
try {
return tmc.create();
} catch (RuntimeException re) { // FindBugs
throw re;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* Populates the mapping from keys to instances of
* <code>TokenMakerCreator</code>s. Subclasses should override this method
* and call one of the <code>putMapping</code> overloads to register
* {@link TokenMaker}s for syntax constants.
*
* @see #putMapping(String, String)
* @see #putMapping(String, String, ClassLoader)
*/
protected abstract void initTokenMakerMap();
/**
* {@inheritDoc}
*/
@Override
public Set<String> keySet() {
return tokenMakerMap.keySet();
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
*
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @see #putMapping(String, String, ClassLoader)
*/
public void putMapping(String key, String className) {
putMapping(key, className, null);
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
*
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @param cl The class loader to use when loading the class.
* @see #putMapping(String, String)
*/
public void putMapping(String key, String className, ClassLoader cl) {
tokenMakerMap.put(key, new TokenMakerCreator(className, cl));
}
/**
* Wrapper that handles the creation of TokenMaker instances.
*/
private static class TokenMakerCreator {
private String className;
private ClassLoader cl;
public TokenMakerCreator(String className, ClassLoader cl) {
this.className = className;
this.cl = cl!=null ? cl : getClass().getClassLoader();
}
public TokenMaker create() throws Exception {
return (TokenMaker)Class.forName(className, true, cl).newInstance();
}
}
}
@@ -0,0 +1,68 @@
/*
* 02/06/2011
*
* ActiveLineRangeEvent.java - Notifies listeners of an "active line range"
* change in an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventObject;
/**
* The event fired by {@link RSyntaxTextArea}s when the active line range
* changes.
*
* @author Robert Futrell
* @version 1.0
*/
public class ActiveLineRangeEvent extends EventObject {
private int min;
private int max;
/**
* Constructor.
*
* @param source The text area.
* @param min The first line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param max The last line in the active line range, or
* <code>-1</code> if the line range is being cleared.
*/
public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
super(source);
this.min = min;
this.max = max;
}
/**
* Returns the last line in the active line range.
*
* @return The last line, or <code>-1</code> if the range is being
* cleared.
* @see #getMin()
*/
public int getMax() {
return max;
}
/**
* Returns the first line in the active line range.
*
* @return The first line, or <code>-1</code> if the range is being
* cleared.
* @see #getMax()
*/
public int getMin() {
return min;
}
}
@@ -0,0 +1,44 @@
/*
* 02/06/2011
*
* ActiveLineRangeListener.java - Listens for "active line range" changes
* in an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventListener;
/**
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
* If a text area contains some semantic knowledge of the programming language
* being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
* caret moves into a new "block" of code. Listeners can listen for these
* events and respond accordingly.<p>
*
* See the <code>RSTALanguageSupport</code> project at
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some
* <code>LanguageSupport</code> implementations that may broadcast these
* events. Note that if an RSTA/LanguageSupport does not support broadcasting
* these events, the listener will simply never receive any notifications.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ActiveLineRangeListener extends EventListener {
/**
* Called whenever the "active line range" changes.
*
* @param e Information about the line range change. If there is no longer
* an "active line range," the "minimum" and "maximum" line values
* should both be <code>-1</code>.
*/
void activeLineRangeChanged(ActiveLineRangeEvent e);
}
@@ -0,0 +1,401 @@
/*
* 02/21/2005
*
* CodeTemplateManager.java - manages code templates.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
/**
* Manages "code templates."<p>
*
* All methods in this class are synchronized for thread safety, but as a
* best practice, you should probably only modify the templates known to a
* <code>CodeTemplateManager</code> on the EDT. Modifying a
* <code>CodeTemplate</code> retrieved from a <code>CodeTemplateManager</code>
* while <em>not</em> on the EDT could cause problems.<p>
*
* For more flexible boilerplate code insertion, consider using the
* <a href="http://javadoc.fifesoft.com/autocomplete/org/fife/ui/autocomplete/TemplateCompletion.html">
* TemplateCompletion class</a> in the
* <a href="https://github.com/bobbylight/AutoComplete">AutoComplete
* add-on library</a>.
*
* @author Robert Futrell
* @version 1.0
*/
public class CodeTemplateManager {
private int maxTemplateIDLength;
private List<CodeTemplate> templates;
private Segment s;
private TemplateComparator comparator;
private File directory;
/**
* Constructor.
*/
public CodeTemplateManager() {
s = new Segment();
comparator = new TemplateComparator();
templates = new ArrayList<CodeTemplate>();
}
/**
* Registers the specified template with this template manager.
*
* @param template The template to register.
* @throws IllegalArgumentException If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(CodeTemplate)
* @see #removeTemplate(String)
*/
public synchronized void addTemplate(CodeTemplate template) {
if (template==null) {
throw new IllegalArgumentException("template cannot be null");
}
templates.add(template);
sortTemplates();
}
/**
* Returns the template that should be inserted at the current caret
* position, assuming the trigger character was pressed.
*
* @param textArea The text area that's getting text inserted into it.
* @return A template that should be inserted, if appropriate, or
* <code>null</code> if no template should be inserted.
*/
public synchronized CodeTemplate getTemplate(RSyntaxTextArea textArea) {
int caretPos = textArea.getCaretPosition();
int charsToGet = Math.min(caretPos, maxTemplateIDLength);
try {
Document doc = textArea.getDocument();
doc.getText(caretPos-charsToGet, charsToGet, s);
@SuppressWarnings("unchecked")
int index = Collections.binarySearch(templates, s, comparator);
return index>=0 ? (CodeTemplate)templates.get(index) : null;
} catch (BadLocationException ble) {
ble.printStackTrace();
throw new InternalError("Error in CodeTemplateManager");
}
}
/**
* Returns the number of templates this manager knows about.
*
* @return The template count.
*/
public synchronized int getTemplateCount() {
return templates.size();
}
/**
* Returns the templates currently available.
*
* @return The templates available.
*/
public synchronized CodeTemplate[] getTemplates() {
CodeTemplate[] temp = new CodeTemplate[templates.size()];
return templates.toArray(temp);
}
/**
* Returns whether the specified character is a valid character for a
* <code>CodeTemplate</code> id.
*
* @param ch The character to check.
* @return Whether the character is a valid template character.
*/
public static final boolean isValidChar(char ch) {
return RSyntaxUtilities.isLetterOrDigit(ch) || ch=='_';
}
/**
* Returns the specified code template.
*
* @param template The template to remove.
* @return <code>true</code> if the template was removed, <code>false</code>
* if the template was not in this template manager.
* @throws IllegalArgumentException If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(String)
* @see #addTemplate(CodeTemplate)
*/
public synchronized boolean removeTemplate(CodeTemplate template) {
if (template==null) {
throw new IllegalArgumentException("template cannot be null");
}
// TODO: Do a binary search
return templates.remove(template);
}
/**
* Returns the code template with the specified id.
*
* @param id The id to check for.
* @return The code template that was removed, or <code>null</code> if
* there was no template with the specified ID.
* @throws IllegalArgumentException If <code>id</code> is <code>null</code>.
* @see #removeTemplate(CodeTemplate)
* @see #addTemplate(CodeTemplate)
*/
public synchronized CodeTemplate removeTemplate(String id) {
if (id==null) {
throw new IllegalArgumentException("id cannot be null");
}
// TODO: Do a binary search
for (Iterator<CodeTemplate> i=templates.iterator(); i.hasNext();) {
CodeTemplate template = i.next();
if (id.equals(template.getID())) {
i.remove();
return template;
}
}
return null;
}
/**
* Replaces the current set of available templates with the ones
* specified.
*
* @param newTemplates The new set of templates. Note that we will
* be taking a shallow copy of these and sorting them.
*/
public synchronized void replaceTemplates(CodeTemplate[] newTemplates) {
templates.clear();
if (newTemplates!=null) {
for (int i=0; i<newTemplates.length; i++) {
templates.add(newTemplates[i]);
}
}
sortTemplates(); // Also recomputes maxTemplateIDLength.
}
/**
* Saves all templates as XML files in the current template directory.
*
* @return Whether or not the save was successful.
*/
public synchronized boolean saveTemplates() {
if (templates==null) {
return true;
}
if (directory==null || !directory.isDirectory()) {
return false;
}
// Blow away all old XML files to start anew, as some might be from
// templates we're removed from the template manager.
File[] oldXMLFiles = directory.listFiles(new XMLFileFilter());
if (oldXMLFiles==null) {
return false; // Either an IOException or it isn't a directory.
}
int count = oldXMLFiles.length;
for (int i=0; i<count; i++) {
/*boolean deleted = */oldXMLFiles[i].delete();
}
// Save all current templates as XML.
boolean wasSuccessful = true;
for (CodeTemplate template : templates) {
File xmlFile = new File(directory, template.getID() + ".xml");
try {
XMLEncoder e = new XMLEncoder(new BufferedOutputStream(
new FileOutputStream(xmlFile)));
e.writeObject(template);
e.close();
} catch (IOException ioe) {
ioe.printStackTrace();
wasSuccessful = false;
}
}
return wasSuccessful;
}
/**
* Sets the directory in which to look for templates. Calling this
* method adds any new templates found in the specified directory to
* the templates already registered.
*
* @param dir The new directory in which to look for templates.
* @return The new number of templates in this template manager, or
* <code>-1</code> if the specified directory does not exist.
*/
public synchronized int setTemplateDirectory(File dir) {
if (dir!=null && dir.isDirectory()) {
this.directory = dir;
File[] files = dir.listFiles(new XMLFileFilter());
int newCount = files==null ? 0 : files.length;
int oldCount = templates.size();
List<CodeTemplate> temp =
new ArrayList<CodeTemplate>(oldCount+newCount);
temp.addAll(templates);
for (int i=0; i<newCount; i++) {
try {
XMLDecoder d = new XMLDecoder(new BufferedInputStream(
new FileInputStream(files[i])));
Object obj = d.readObject();
if (!(obj instanceof CodeTemplate)) {
d.close();
throw new IOException("Not a CodeTemplate: " +
files[i].getAbsolutePath());
}
temp.add((CodeTemplate)obj);
d.close();
} catch (/*IO, NoSuchElement*/Exception e) {
// NoSuchElementException can be thrown when reading
// an XML file not in the format expected by XMLDecoder.
// (e.g. CodeTemplates in an old format).
e.printStackTrace();
}
}
templates = temp;
sortTemplates();
return getTemplateCount();
}
return -1;
}
/**
* Removes any null entries in the current set of templates (if
* any), sorts the remaining templates, and computes the new
* maximum template ID length.
*/
private synchronized void sortTemplates() {
// Get the maximum length of a template ID.
maxTemplateIDLength = 0;
// Remove any null entries (should only happen because of
// IOExceptions, etc. when loading from files), and sort
// the remaining list.
for (Iterator<CodeTemplate> i=templates.iterator(); i.hasNext();) {
CodeTemplate temp = i.next();
if (temp==null || temp.getID()==null) {
i.remove();
}
else {
maxTemplateIDLength = Math.max(maxTemplateIDLength,
temp.getID().length());
}
}
Collections.sort(templates);
}
/**
* A comparator that takes a <code>CodeTemplate</code> as its first
* parameter and a <code>Segment</code> as its second, and knows
* to compare the template's ID to the segment's text.
*/
@SuppressWarnings("rawtypes")
private static class TemplateComparator implements Comparator, Serializable{
@Override
public int compare(Object template, Object segment) {
// Get template start index (0) and length.
CodeTemplate t = (CodeTemplate)template;
final char[] templateArray = t.getID().toCharArray();
int i = 0;
int len1 = templateArray.length;
// Find "token" part of segment and get its offset and length.
Segment s = (Segment)segment;
char[] segArray = s.array;
int len2 = s.count;
int j = s.offset + len2 - 1;
while (j>=s.offset && isValidChar(segArray[j])) {
j--;
}
j++;
int segShift = j - s.offset;
len2 -= segShift;
int n = Math.min(len1, len2);
while (n-- != 0) {
char c1 = templateArray[i++];
char c2 = segArray[j++];
if (c1 != c2) {
return c1 - c2;
}
}
return len1 - len2;
}
}
/**
* A file filter that accepts only XML files.
*/
private static class XMLFileFilter implements FileFilter {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".xml");
}
}
}
@@ -0,0 +1,112 @@
/*
* 03/09/2013
*
* DefaultOccurrenceMarker - Marks occurrences of the current token for most
* languages.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The default implementation of {@link OccurrenceMarker}. It goes through
* the document and marks all instances of the specified token.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultOccurrenceMarker implements OccurrenceMarker {
/**
* {@inheritDoc}
*/
@Override
public Token getTokenToMark(RSyntaxTextArea textArea) {
// Get the token at the caret position.
int line = textArea.getCaretLineNumber();
Token tokenList = textArea.getTokenListForLine(line);
Caret c = textArea.getCaret();
int dot = c.getDot();
Token t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
if (t==null /* EOL */ || !isValidType(textArea, t) ||
RSyntaxUtilities.isNonWordChar(t)) {
// Try to the "left" of the caret.
dot--;
try {
if (dot>=textArea.getLineStartOffset(line)) {
t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
return t;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@Override
public void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
markOccurrencesOfToken(doc, t, h, p);
}
/**
* Highlights all instances of tokens identical to <code>t</code> in the
* specified document.
*
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
public static final void markOccurrencesOfToken(RSyntaxDocument doc,
Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
char[] lexeme = t.getLexeme().toCharArray();
int type = t.getType();
int lineCount = doc.getDefaultRootElement().getElementCount();
for (int i=0; i<lineCount; i++) {
Token temp = doc.getTokenListForLine(i);
while (temp!=null && temp.isPaintable()) {
if (temp.is(type, lexeme)) {
try {
int end = temp.getEndOffset();
h.addMarkedOccurrenceHighlight(temp.getOffset(),end,p);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
temp = temp.getNextToken();
}
}
}
}
@@ -0,0 +1,143 @@
/*
* 10/28/2004
*
* DefaultTokenFactory.java - Default token factory.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* This class generates tokens for a {@link TokenMaker}. This class is here
* because it reuses tokens when they aren't needed anymore to prevent
* This class doesn't actually create new tokens every time
* <code>createToken</code> is called. Instead, it internally keeps a stack of
* available already-created tokens. When more tokens are needed to properly
* display a line, more tokens are added to the available stack. This saves
* from needless repetitive memory allocation. However, it makes it IMPERATIVE
* that users call <code>resetTokenList</code> when creating a new token list so
* that the token maker can keep an accurate list of available tokens.<p>
*
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
* needs it!
*
* @author Robert Futrell
* @version 0.1
*/
class DefaultTokenFactory implements TokenFactory {
private int size;
private int increment;
private TokenImpl[] tokenList;
private int currentFreeToken;
protected static final int DEFAULT_START_SIZE = 30;
protected static final int DEFAULT_INCREMENT = 10;
/**
* Constructor.
*/
DefaultTokenFactory() {
this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
}
/**
* Constructor.
*
* @param size The initial number of tokens in this factory.
* @param increment How many tokens to increment by when the stack gets
* empty.
*/
DefaultTokenFactory(int size, int increment) {
this.size = size;
this.increment = increment;
this.currentFreeToken = 0;
// Give us some tokens to initially work with.
tokenList = new TokenImpl[size];
for (int i=0; i<size; i++) {
tokenList[i] = new TokenImpl();
}
}
/**
* Adds tokens to the internal token list. This is called whenever a
* request is made and no more tokens are available.
*/
private void augmentTokenList() {
TokenImpl[] temp = new TokenImpl[size + increment];
System.arraycopy(tokenList,0, temp,0, size);
size += increment;
tokenList = temp;
for (int i=0; i<increment; i++) {
tokenList[size-i-1] = new TokenImpl();
}
//System.err.println("... size up to: " + size);
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken() {
TokenImpl token = tokenList[currentFreeToken];
token.text = null;
token.setType(Token.NULL);
token.setOffset(-1);
token.setNextToken(null);
currentFreeToken++;
if (currentFreeToken==size) {
augmentTokenList();
}
return token;
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final Segment line, final int beg,
final int end, final int startOffset, final int type) {
return createToken(line.array, beg,end, startOffset, type);
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final char[] line, final int beg,
final int end, final int startOffset, final int type) {
TokenImpl token = tokenList[currentFreeToken];
token.set(line, beg,end, startOffset, type);
currentFreeToken++;
if (currentFreeToken==size) {
augmentTokenList();
}
return token;
}
/**
* Resets the state of this token maker. This method should be called
* by the <code>TokenMaker</code> every time a token list is generated for
* a new line so the tokens can be reused.
*/
@Override
public void resetAllTokens() {
currentFreeToken = 0;
}
}
@@ -0,0 +1,83 @@
/*
* 12/14/2008
*
* DefaultTokenMakerFactory.java - The default TokenMaker factory.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* The default implementation of <code>TokenMakerFactory</code>. This factory
* can create {@link TokenMaker}s for all languages known to
* {@link RSyntaxTextArea}.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory
implements SyntaxConstants {
/**
* {@inheritDoc}
*/
@Override
protected void initTokenMakerMap() {
String pkg = "org.fife.ui.rsyntaxtextarea.modes.";
putMapping(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker");
putMapping(SYNTAX_STYLE_ACTIONSCRIPT, pkg + "ActionScriptTokenMaker");
putMapping(SYNTAX_STYLE_ASSEMBLER_X86, pkg + "AssemblerX86TokenMaker");
putMapping(SYNTAX_STYLE_BBCODE, pkg + "BBCodeTokenMaker");
putMapping(SYNTAX_STYLE_C, pkg + "CTokenMaker");
putMapping(SYNTAX_STYLE_CLOJURE, pkg + "ClojureTokenMaker");
putMapping(SYNTAX_STYLE_CPLUSPLUS, pkg + "CPlusPlusTokenMaker");
putMapping(SYNTAX_STYLE_CSHARP, pkg + "CSharpTokenMaker");
putMapping(SYNTAX_STYLE_CSS, pkg + "CSSTokenMaker");
putMapping(SYNTAX_STYLE_D, pkg + "DTokenMaker");
putMapping(SYNTAX_STYLE_DART, pkg + "DartTokenMaker");
putMapping(SYNTAX_STYLE_DELPHI, pkg + "DelphiTokenMaker");
putMapping(SYNTAX_STYLE_DOCKERFILE, pkg + "DockerTokenMaker");
putMapping(SYNTAX_STYLE_DTD, pkg + "DtdTokenMaker");
putMapping(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
putMapping(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
putMapping(SYNTAX_STYLE_HOSTS, pkg + "HostsTokenMaker");
putMapping(SYNTAX_STYLE_HTACCESS, pkg + "HtaccessTokenMaker");
putMapping(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
putMapping(SYNTAX_STYLE_INI, pkg + "IniTokenMaker");
putMapping(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
putMapping(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
putMapping(SYNTAX_STYLE_JSON_WITH_COMMENTS, pkg + "JshintrcTokenMaker");
putMapping(SYNTAX_STYLE_JSON, pkg + "JsonTokenMaker");
putMapping(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
putMapping(SYNTAX_STYLE_LATEX, pkg + "LatexTokenMaker");
putMapping(SYNTAX_STYLE_LESS, pkg + "LessTokenMaker");
putMapping(SYNTAX_STYLE_LISP, pkg + "LispTokenMaker");
putMapping(SYNTAX_STYLE_LUA, pkg + "LuaTokenMaker");
putMapping(SYNTAX_STYLE_MAKEFILE, pkg + "MakefileTokenMaker");
putMapping(SYNTAX_STYLE_MXML, pkg + "MxmlTokenMaker");
putMapping(SYNTAX_STYLE_NSIS, pkg + "NSISTokenMaker");
putMapping(SYNTAX_STYLE_PERL, pkg + "PerlTokenMaker");
putMapping(SYNTAX_STYLE_PHP, pkg + "PHPTokenMaker");
putMapping(SYNTAX_STYLE_PROPERTIES_FILE,pkg + "PropertiesFileTokenMaker");
putMapping(SYNTAX_STYLE_PYTHON, pkg + "PythonTokenMaker");
putMapping(SYNTAX_STYLE_RUBY, pkg + "RubyTokenMaker");
putMapping(SYNTAX_STYLE_SAS, pkg + "SASTokenMaker");
putMapping(SYNTAX_STYLE_SCALA, pkg + "ScalaTokenMaker");
putMapping(SYNTAX_STYLE_SQL, pkg + "SQLTokenMaker");
putMapping(SYNTAX_STYLE_TCL, pkg + "TclTokenMaker");
putMapping(SYNTAX_STYLE_TYPESCRIPT, pkg + "TypeScriptTokenMaker");
putMapping(SYNTAX_STYLE_UNIX_SHELL, pkg + "UnixShellTokenMaker");
putMapping(SYNTAX_STYLE_VISUAL_BASIC, pkg + "VisualBasicTokenMaker");
putMapping(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
putMapping(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
putMapping(SYNTAX_STYLE_YAML, pkg + "YamlTokenMaker");
}
}
@@ -0,0 +1,279 @@
/*
* 03/16/2013
*
* DefaultTokenPainter - Standard implementation of a token painter.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.text.TabExpander;
/**
* Standard implementation of a token painter.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultTokenPainter implements TokenPainter {
/**
* Rectangle used for filling token backgrounds.
*/
private Rectangle2D.Float bgRect;
/**
* Micro-optimization; buffer used to compute tab width. If the width is
* correct it's not re-allocated, to prevent lots of very small garbage.
* Only used when painting tab lines.
*/
private static char[] tabBuf;
DefaultTokenPainter() {
bgRect = new Rectangle2D.Float();
}
/**
* {@inheritDoc}
*/
@Override
public final float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e) {
return paint(token, g, x,y, host, e, 0);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart) {
return paintImpl(token, g, x, y, host, e, clipStart, false, false);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean paintBG) {
return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
}
/**
* Paints the background of a token.
*
* @param x The x-coordinate of the token.
* @param y The y-coordinate of the token.
* @param width The width of the token (actually, the width of the part of
* the token to paint).
* @param height The height of the token.
* @param g The graphics context with which to paint.
* @param fontAscent The ascent of the token's font.
* @param host The text area.
* @param color The color with which to paint.
*/
protected void paintBackground(float x, float y, float width, float height,
Graphics2D g, int fontAscent, RSyntaxTextArea host,
Color color) {
g.setColor(color);
bgRect.setRect(x,y-fontAscent, width,height);
//g.fill(bgRect);
g.fillRect((int)x, (int)(y-fontAscent), (int)width, (int)height);
}
/**
* Does the dirty-work of actually painting the token.
*/
protected float paintImpl(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean selected, boolean useSTC) {
int origX = (int)x;
int textOffs = token.getTextOffset();
char[] text = token.getTextArray();
int end = textOffs + token.length();
float nextX = x;
int flushLen = 0;
int flushIndex = textOffs;
Color fg = useSTC ? host.getSelectedTextColor() :
host.getForegroundForToken(token);
Color bg = selected ? null : host.getBackgroundForToken(token);
g.setFont(host.getFontForTokenType(token.getType()));
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
for (int i=textOffs; i<end; i++) {
switch (text[i]) {
case '\t':
nextX = e.nextTabStop(
x + fm.charsWidth(text, flushIndex,flushLen), 0);
if (bg!=null) {
paintBackground(x,y, nextX-x,fm.getHeight(),
g, fm.getAscent(), host, bg);
}
if (flushLen > 0) {
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
flushLen = 0;
}
flushIndex = i + 1;
x = nextX;
break;
default:
flushLen += 1;
break;
}
}
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
java.awt.Rectangle r = host.getMatchRectangle();
if (flushLen>0 && nextX>=clipStart) {
if (bg!=null) {
paintBackground(x,y, nextX-x,fm.getHeight(),
g, fm.getAscent(), host, bg);
if (token.length()==1 && r!=null && r.x==x) {
((RSyntaxTextAreaUI)host.getUI()).paintMatchedBracketImpl(
g, host, r);
}
}
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
}
if (host.getUnderlineForToken(token)) {
g.setColor(fg);
int y2 = (int)(y+1);
g.drawLine(origX,y2, (int)nextX,y2);
}
// Don't check if it's whitespace - some TokenMakers may return types
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
// This also allows us to paint tab lines for MLC's.
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
paintTabLines(token, origX, (int)y, (int)nextX, g, e, host);
}
return nextX;
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, boolean useSTC) {
return paintSelected(token, g, x, y, host, e, 0, useSTC);
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean useSTC) {
return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC);
}
/**
* Paints dotted "tab" lines; that is, lines that show where your caret
* would go to on the line if you hit "tab". This visual effect is usually
* done in the leading whitespace token(s) of lines.
*
* @param token The token to render.
* @param x The starting x-offset of this token. It is assumed that this
* is the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading whitespace.
* @param y The baseline where this token was painted.
* @param endX The ending x-offset of this token.
* @param g The graphics context.
* @param e Used to expand tabs.
* @param host The text area.
*/
protected void paintTabLines(Token token, int x, int y, int endX,
Graphics2D g, TabExpander e, RSyntaxTextArea host) {
// We allow tab lines to be painted in more than just Token.WHITESPACE,
// i.e. for MLC's and Token.IDENTIFIERS (for TokenMakers that return
// whitespace as identifiers for performance). But we only paint tab
// lines for the leading whitespace in the token. So, if this isn't a
// WHITESPACE token, figure out the leading whitespace's length.
if (token.getType()!=Token.WHITESPACE) {
int offs = 0;
for (; offs<token.length(); offs++) {
if (!RSyntaxUtilities.isWhitespace(token.charAt(offs))) {
break; // MLC text, etc.
}
}
if (offs<2) { // Must be at least two spaces to see tab line
return;
}
//endX = x + (int)getWidthUpTo(offs, host, e, x);
endX = (int)token.getWidthUpTo(offs, host, e, 0);
}
// Get the length of a tab.
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
int tabSize = host.getTabSize();
if (tabBuf==null || tabBuf.length<tabSize) {
tabBuf = new char[tabSize];
for (int i=0; i<tabSize; i++) {
tabBuf[i] = ' ';
}
}
// Note different token types (MLC's, whitespace) could possibly be
// using different fonts, which means we can't cache the actual width
// of a tab as it may be different per-token-type. We could keep a
// per-token-type cache, but we'd have to clear it whenever they
// modified token styles.
int tabW = fm.charsWidth(tabBuf, 0, tabSize);
// Draw any tab lines. Here we're assuming that "x" is the left
// margin of the editor.
g.setColor(host.getTabLineColor());
int x0 = x + tabW;
int y0 = y - fm.getAscent();
if ((y0&1)>0) {
// Only paint on even y-pixels to prevent doubling up between lines
y0++;
}
// TODO: Go to endX (inclusive) if this token is last token in the line
Token next = token.getNextToken();
if (next==null || !next.isPaintable()) {
endX++;
}
while (x0<endX) {
int y1 = y0;
int y2 = y0 + host.getLineHeight();
while (y1<y2) {
g.drawLine(x0, y1, x0, y1);
y1 += 2;
}
//g.drawLine(x0,y0, x0,y0+host.getLineHeight());
x0 += tabW;
}
}
}
@@ -0,0 +1,169 @@
/*
* 08/11/2009
*
* DocumentRange.java - A range of text in a document.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* A range of text in a document.
*
* @author Robert Futrell
* @version 1.0
*/
public class DocumentRange implements Comparable<DocumentRange> {
private int startOffs;
private int endOffs;
/**
* Constructor.
*
* @param startOffs The starting offset in the document, inclusive.
* @param endOffs The ending offset in the document, exclusive.
* @throws IllegalArgumentException If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
*/
public DocumentRange(int startOffs, int endOffs) {
set(startOffs, endOffs);
}
/**
* Compares this document range to another.
*
* @param other Another document range.
* @return How the two should be sorted relative to each other.
*/
@Override
public int compareTo(DocumentRange other) {
if (other==null) {
return 1;
}
int diff = startOffs - other.startOffs;
if (diff!=0) {
return diff;
}
return endOffs - other.endOffs;
}
/**
* Returns whether this document range is equal to another one.
*
* @param other Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal
* to this one.
*/
@Override
public boolean equals(Object other) {
if (other==this) {
return true;
}
if (other instanceof DocumentRange) {
return this.compareTo((DocumentRange)other)==0;
}
return false;
}
/**
* Gets the end offset of the range.
*
* @return The end offset.
* @see #getStartOffset()
*/
public int getEndOffset() {
return endOffs;
}
/**
* Gets the starting offset of the range.
*
* @return The starting offset.
* @see #getEndOffset()
*/
public int getStartOffset() {
return startOffs;
}
/**
* Overridden simply as a best practice, since {@link #equals(Object)} is
* overridden.
*
* @return The hash code for this object.
*/
@Override
public int hashCode() {
return startOffs + endOffs;
}
/**
* Returns whether this document range has zero length. This can happen,
* for example, with regex searches of forms like
* <code>"foo|"</code>, where the right-hand sub-expression matches empty
* strings.
*
* @return Whether this document range has zero length.
*/
public boolean isZeroLength() {
return startOffs == endOffs;
}
/**
* Sets the document range.
*
* @param start The new start value, inclusive.
* @param end The new end value, exclusive.
* @throws IllegalArgumentException If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
*/
public void set(int start, int end) {
if (start<0 || end<0) {
throw new IllegalArgumentException(
"start and end must be >= 0 (" + start + "-" + end + ")");
}
if (end<start) {
throw new IllegalArgumentException(
"'end' cannot be less than 'start' (" +
start + "-" + end + ")");
}
this.startOffs = start;
this.endOffs = end;
}
/**
* Returns a string representation of this object.
*
* @return A string representation of this object.
*/
@Override
public String toString() {
return "[DocumentRange: " + startOffs + "-" + endOffs + "]";
}
/**
* Translates this document range by a given amount.
*
* @param amount The amount to translate this range by.
* @return This (modified) range.
*/
public DocumentRange translate(int amount) {
startOffs += amount;
endOffs += amount;
return this;
}
}
@@ -0,0 +1,924 @@
/*
* 08/10/2009
*
* ErrorStrip.java - A component that can visually show Parser messages (syntax
* errors, etc.) in an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice;
import org.fife.ui.rtextarea.RTextArea;
/**
* A component to sit alongside an {@link RSyntaxTextArea} that displays
* colored markers for locations of interest (parser errors, marked
* occurrences, etc.).<p>
*
* <code>ErrorStrip</code>s display <code>ParserNotice</code>s from
* {@link Parser}s. Currently, the only way to get lines flagged in this
* component is to register a <code>Parser</code> on an RSyntaxTextArea and
* return <code>ParserNotice</code>s for each line to display an icon for.
* The severity of each notice must be at least the threshold set by
* {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)}
* to be displayed in this error strip. The default threshold is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.<p>
*
* An <code>ErrorStrip</code> can be added to a UI like so:
* <pre>
* textArea = createTextArea();
* textArea.addParser(new MyParser(textArea)); // Identifies lines to display
* scrollPane = new RTextScrollPane(textArea, true);
* ErrorStrip es = new ErrorStrip(textArea);
* JPanel temp = new JPanel(new BorderLayout());
* temp.add(scrollPane);
* temp.add(es, BorderLayout.LINE_END);
* </pre>
*
* @author Robert Futrell
* @version 0.5
*/
// Possible improvements:
// 1. Handle marked occurrence changes & "mark all" changes separately from
// parser changes. For each property change, call a method that removes
// the notices being reloaded from the Markers (removing any Markers that
// are now "empty").
//
public class ErrorStrip extends JPanel {
/**
* The text area.
*/
private RSyntaxTextArea textArea;
/**
* Listens for events in this component.
*/
private transient Listener listener;
/**
* Whether "marked occurrences" in the text area should be shown in this
* error strip.
*/
private boolean showMarkedOccurrences;
/**
* Whether markers for "mark all" highlights should be shown in this
* error strip.
*/
private boolean showMarkAll;
/**
* Mapping of colors to brighter colors. This is kept to prevent
* unnecessary creation of the same Colors over and over.
*/
private Map<Color, Color> brighterColors;
/**
* Only notices of this severity (or worse) will be displayed in this
* error strip.
*/
private ParserNotice.Level levelThreshold;
/**
* Whether the caret marker's location should be rendered.
*/
private boolean followCaret;
/**
* The color to use for the caret marker.
*/
private Color caretMarkerColor;
/**
* Where we paint the caret marker.
*/
private int caretLineY;
/**
* The last location of the caret marker.
*/
private int lastLineY;
/**
* Generates the tool tips for markers in this error strip.
*/
private transient ErrorStripMarkerToolTipProvider markerToolTipProvider;
/**
* The preferred width of this component.
*/
private static final int PREFERRED_WIDTH = 14;
private static final ResourceBundle MSG = ResourceBundle.getBundle(
"org.fife.ui.rsyntaxtextarea.ErrorStrip");
/**
* Constructor.
*
* @param textArea The text area we are examining.
*/
public ErrorStrip(RSyntaxTextArea textArea) {
this.textArea = textArea;
listener = new Listener();
ToolTipManager.sharedInstance().registerComponent(this);
setLayout(null); // Manually layout Markers as they can overlap
addMouseListener(listener);
setShowMarkedOccurrences(true);
setShowMarkAll(true);
setLevelThreshold(ParserNotice.Level.WARNING);
setFollowCaret(true);
setCaretMarkerColor(Color.BLACK);
setMarkerToolTipProvider(null); // Install default
}
/**
* Overridden so we only start listening for parser notices when this
* component (and presumably the text area) are visible.
*/
@Override
public void addNotify() {
super.addNotify();
textArea.addCaretListener(listener);
textArea.addPropertyChangeListener(
RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);
textArea.addPropertyChangeListener(
RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);
textArea.addPropertyChangeListener(
RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
textArea.addPropertyChangeListener(
RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
refreshMarkers();
}
/**
* Manually manages layout since this component uses no layout manager.
*/
@Override
public void doLayout() {
for (int i=0; i<getComponentCount(); i++) {
Marker m = (Marker)getComponent(i);
m.updateLocation();
}
listener.caretUpdate(null); // Force recalculation of caret line pos
}
/**
* Returns a "brighter" color.
*
* @param c The color.
* @return A brighter color.
*/
private Color getBrighterColor(Color c) {
if (brighterColors==null) {
brighterColors = new HashMap<Color, Color>(5); // Usually small
}
Color brighter = brighterColors.get(c);
if (brighter==null) {
// Don't use c.brighter() as it doesn't work well for blue, and
// also doesn't return something brighter "enough."
int r = possiblyBrighter(c.getRed());
int g = possiblyBrighter(c.getGreen());
int b = possiblyBrighter(c.getBlue());
brighter = new Color(r, g, b);
brighterColors.put(c, brighter);
}
return brighter;
}
/**
* returns the color to use when painting the caret marker.
*
* @return The caret marker color.
* @see #setCaretMarkerColor(Color)
*/
public Color getCaretMarkerColor() {
return caretMarkerColor;
}
/**
* Returns whether the caret's position should be drawn.
*
* @return Whether the caret's position should be drawn.
* @see #setFollowCaret(boolean)
*/
public boolean getFollowCaret() {
return followCaret;
}
/**
* {@inheritDoc}
*/
@Override
public Dimension getPreferredSize() {
int height = textArea.getPreferredScrollableViewportSize().height;
return new Dimension(PREFERRED_WIDTH, height);
}
/**
* Returns the minimum severity a parser notice must be for it to be
* displayed in this error strip. This will be one of the constants
* defined in the <code>ParserNotice</code> class.
*
* @return The minimum severity.
* @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
*/
public ParserNotice.Level getLevelThreshold() {
return levelThreshold;
}
/**
* Returns whether "mark all" highlights are shown in this error strip.
*
* @return Whether markers are shown for "mark all" highlights.
* @see #setShowMarkAll(boolean)
*/
public boolean getShowMarkAll() {
return showMarkAll;
}
/**
* Returns whether marked occurrences are shown in this error strip.
*
* @return Whether marked occurrences are shown.
* @see #setShowMarkedOccurrences(boolean)
*/
public boolean getShowMarkedOccurrences() {
return showMarkedOccurrences;
}
/**
* {@inheritDoc}
*/
@Override
public String getToolTipText(MouseEvent e) {
String text = null;
int line = yToLine(e.getY());
if (line>-1) {
text = MSG.getString("Line");
text = MessageFormat.format(text, Integer.valueOf(line+1));
}
return text;
}
/**
* Returns the y-offset in this component corresponding to a line in the
* text component.
*
* @param line The line.
* @return The y-offset.
* @see #yToLine(int)
*/
private int lineToY(int line) {
int h = textArea.getVisibleRect().height;
float lineCount = textArea.getLineCount();
return (int)(((line-1)/(lineCount-1)) * (h-2));
}
/**
* Overridden to (possibly) draw the caret's position.
*
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (caretLineY>-1) {
g.setColor(getCaretMarkerColor());
g.fillRect(0, caretLineY, getWidth(), 2);
}
}
/**
* Returns a possibly brighter component for a color.
*
* @param i An RGB component for a color (0-255).
* @return A possibly brighter value for the component.
*/
private static int possiblyBrighter(int i) {
if (i<255) {
i += (int)((255-i)*0.8f);
}
return i;
}
/**
* Refreshes the markers displayed in this error strip.
*/
private void refreshMarkers() {
removeAll(); // listener is removed in Marker.removeNotify()
Map<Integer, Marker> markerMap = new HashMap<Integer, Marker>();
List<ParserNotice> notices = textArea.getParserNotices();
for (ParserNotice notice : notices) {
if (notice.getLevel().isEqualToOrWorseThan(levelThreshold) ||
(notice instanceof TaskNotice)) {
Integer key = Integer.valueOf(notice.getLine());
Marker m = markerMap.get(key);
if (m==null) {
m = new Marker(notice);
m.addMouseListener(listener);
markerMap.put(key, m);
add(m);
}
else {
m.addNotice(notice);
}
}
}
if (getShowMarkedOccurrences() && textArea.getMarkOccurrences()) {
List<DocumentRange> occurrences = textArea.getMarkedOccurrences();
addMarkersForRanges(occurrences, markerMap, textArea.getMarkOccurrencesColor());
}
if (getShowMarkAll() /*&& textArea.getMarkAll()*/) {
Color markAllColor = textArea.getMarkAllHighlightColor();
List<DocumentRange> ranges = textArea.getMarkAllHighlightRanges();
addMarkersForRanges(ranges, markerMap, markAllColor);
}
revalidate();
repaint();
}
/**
* Adds markers for a list of ranges in the document.
*
* @param ranges The list of ranges in the document.
* @param markerMap A mapping from line number to <code>Marker</code>.
* @param color The color to use for the markers.
*/
private void addMarkersForRanges(List<DocumentRange> ranges,
Map<Integer, Marker> markerMap, Color color) {
for (DocumentRange range : ranges) {
int line = 0;
try {
line = textArea.getLineOfOffset(range.getStartOffset());
} catch (BadLocationException ble) { // Never happens
continue;
}
ParserNotice notice = new MarkedOccurrenceNotice(range, color);
Integer key = Integer.valueOf(line);
Marker m = markerMap.get(key);
if (m==null) {
m = new Marker(notice);
m.addMouseListener(listener);
markerMap.put(key, m);
add(m);
}
else {
if (!m.containsMarkedOccurence()) {
m.addNotice(notice);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeNotify() {
super.removeNotify();
textArea.removeCaretListener(listener);
textArea.removePropertyChangeListener(
RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);
textArea.removePropertyChangeListener(
RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);
textArea.removePropertyChangeListener(
RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
textArea.removePropertyChangeListener(
RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
}
/**
* Sets the color to use when painting the caret marker.
*
* @param color The new caret marker color.
* @see #getCaretMarkerColor()
*/
public void setCaretMarkerColor(Color color) {
if (color!=null) {
caretMarkerColor = color;
listener.caretUpdate(null); // Force repaint
}
}
/**
* Toggles whether the caret's current location should be drawn.
*
* @param follow Whether the caret's current location should be followed.
* @see #getFollowCaret()
*/
public void setFollowCaret(boolean follow) {
if (followCaret!=follow) {
if (followCaret) {
repaint(0,caretLineY, getWidth(),2); // Erase
}
caretLineY = -1;
lastLineY = -1;
followCaret = follow;
listener.caretUpdate(null); // Possibly repaint
}
}
/**
* Sets the minimum severity a parser notice must be for it to be displayed
* in this error strip. This should be one of the constants defined in
* the <code>ParserNotice</code> class. The default value is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
*
* @param level The new severity threshold.
* @see #getLevelThreshold()
* @see ParserNotice
*/
public void setLevelThreshold(ParserNotice.Level level) {
levelThreshold = level;
if (isDisplayable()) {
refreshMarkers();
}
}
/**
* Sets the provider of tool tips for markers in this error strip.
* Applications can use this method to control the content and format of
* the tool tip descriptions of line markers.
*
* @param provider The provider. If this is <code>null</code>, a default
* implementation will be used.
*/
public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) {
markerToolTipProvider = provider != null ? provider :
new DefaultErrorStripMarkerToolTipProvider();
}
/**
* Sets whether "mark all" highlights are shown in this error strip.
*
* @param show Whether to show markers for "mark all" highlights.
* @see #getShowMarkAll()
*/
public void setShowMarkAll(boolean show) {
if (show!=showMarkAll) {
showMarkAll = show;
if (isDisplayable()) { // Skip this when we're first created
refreshMarkers();
}
}
}
/**
* Sets whether marked occurrences are shown in this error strip.
*
* @param show Whether to show marked occurrences.
* @see #getShowMarkedOccurrences()
*/
public void setShowMarkedOccurrences(boolean show) {
if (show!=showMarkedOccurrences) {
showMarkedOccurrences = show;
if (isDisplayable()) { // Skip this when we're first created
refreshMarkers();
}
}
}
/**
* Returns the line in the text area corresponding to a y-offset in this
* component.
*
* @param y The y-offset.
* @return The line.
* @see #lineToY(int)
*/
private int yToLine(int y) {
int line = -1;
int h = textArea.getVisibleRect().height;
if (y<h) {
float at = y/(float)h;
line = Math.round((textArea.getLineCount()-1)*at);
}
return line;
}
/**
* The default implementation of the provider of tool tips for markers in
* an error strip.
*
* @author predi
*/
private static class DefaultErrorStripMarkerToolTipProvider
implements ErrorStripMarkerToolTipProvider {
@Override
public String getToolTipText(List<ParserNotice> notices) {
String text = null;
if (notices.size()==1) {
text = notices.get(0).getMessage();
}
else { // > 1
StringBuilder sb = new StringBuilder("<html>");
sb.append(MSG.getString("MultipleMarkers"));
sb.append("<br>");
for (int i=0; i<notices.size(); i++) {
ParserNotice pn = notices.get(i);
sb.append("&nbsp;&nbsp;&nbsp;- ");
sb.append(pn.getMessage());
sb.append("<br>");
}
text = sb.toString();
}
return text;
}
}
/**
* Returns tool tip text for the markers in an {@link ErrorStrip} that
* denote one or more parser notices.
*
* @author predi
*/
public interface ErrorStripMarkerToolTipProvider {
/**
* Returns the tool tip text for a marker in an <code>ErrorStrip</code>
* that denotes a given list of parser notices.
*
* @param notices The list of parser notices.
* @return The tool tip text. This may be HTML. Returning
* <code>null</code> will result in no tool tip being displayed.
*/
String getToolTipText(List<ParserNotice> notices);
}
/**
* Listens for events in the error strip and its markers.
*/
private class Listener extends MouseAdapter
implements PropertyChangeListener, CaretListener {
private Rectangle visibleRect = new Rectangle();
@Override
public void caretUpdate(CaretEvent e) {
if (getFollowCaret()) {
int line = textArea.getCaretLineNumber();
float percent = line / (float)(textArea.getLineCount()-1);
textArea.computeVisibleRect(visibleRect);
caretLineY = (int)(visibleRect.height*percent);
if (caretLineY!=lastLineY) {
repaint(0,lastLineY, getWidth(), 2); // Erase old position
repaint(0,caretLineY, getWidth(), 2);
lastLineY = caretLineY;
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
Component source = (Component)e.getSource();
if (source instanceof Marker) {
((Marker)source).mouseClicked(e);
return;
}
int line = yToLine(e.getY());
if (line>-1) {
try {
int offs = textArea.getLineStartOffset(line);
textArea.setCaretPosition(offs);
} catch (BadLocationException ble) { // Never happens
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
}
}
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String propName = e.getPropertyName();
// If they change whether marked occurrences are visible in editor
if (RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY.equals(propName)) {
if (getShowMarkedOccurrences()) {
refreshMarkers();
}
}
// If parser notices changed.
// TODO: Don't update "mark all/occurrences" markers.
else if (RSyntaxTextArea.PARSER_NOTICES_PROPERTY.equals(propName)) {
refreshMarkers();
}
// If marked occurrences changed.
// TODO: Only update "mark occurrences" markers, not all of them.
else if (RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY.
equals(propName)) {
if (getShowMarkedOccurrences()) {
refreshMarkers();
}
}
// If "mark all" occurrences changed.
// TODO: Only update "mark all" markers, not all of them.
else if (RTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY.
equals(propName)) {
if (getShowMarkAll()) {
refreshMarkers();
}
}
}
}
/**
* A notice that wraps a "marked occurrence" instance.
*/
private class MarkedOccurrenceNotice implements ParserNotice {
private DocumentRange range;
private Color color;
MarkedOccurrenceNotice(DocumentRange range, Color color) {
this.range = range;
this.color = color;
}
@Override
public int compareTo(ParserNotice other) {
return 0; // Value doesn't matter
}
@Override
public boolean containsPosition(int pos) {
return pos>=range.getStartOffset() && pos<range.getEndOffset();
}
@Override
public boolean equals(Object o) {
// FindBugs - Define equals() when defining compareTo()
if (!(o instanceof ParserNotice)) {
return false;
}
return compareTo((ParserNotice)o)==0;
}
@Override
public Color getColor() {
return color;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getKnowsOffsetAndLength() {
return true;
}
@Override
public int getLength() {
return range.getEndOffset() - range.getStartOffset();
}
@Override
public Level getLevel() {
return Level.INFO; // Won't matter
}
@Override
public int getLine() {
try {
return textArea.getLineOfOffset(range.getStartOffset())+1;
} catch (BadLocationException ble) {
return 0;
}
}
@Override
public String getMessage() {
String text = null;
try {
String word = textArea.getText(range.getStartOffset(),
getLength());
text = MSG.getString("OccurrenceOf");
text = MessageFormat.format(text, word);
} catch (BadLocationException ble) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
}
return text;
}
@Override
public int getOffset() {
return range.getStartOffset();
}
@Override
public Parser getParser() {
return null;
}
@Override
public boolean getShowInEditor() {
return false; // Value doesn't matter
}
@Override
public String getToolTipText() {
return null;
}
@Override
public int hashCode() { // FindBugs, since we override equals()
return 0; // Value doesn't matter for us.
}
}
/**
* A "marker" in this error strip, representing one or more notices.
*/
private class Marker extends JComponent {
private List<ParserNotice> notices;
Marker(ParserNotice notice) {
notices = new ArrayList<ParserNotice>(1); // Usually just 1
addNotice(notice);
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setSize(getPreferredSize());
ToolTipManager.sharedInstance().registerComponent(this);
}
public void addNotice(ParserNotice notice) {
notices.add(notice);
}
public boolean containsMarkedOccurence() {
boolean result = false;
for (int i=0; i<notices.size(); i++) {
if (notices.get(i) instanceof MarkedOccurrenceNotice) {
result = true;
break;
}
}
return result;
}
public Color getColor() {
// Return the color for the highest-level parser.
Color c = null;
int lowestLevel = Integer.MAX_VALUE; // ERROR is 0
for (ParserNotice notice : notices) {
if (notice.getLevel().getNumericValue()<lowestLevel) {
lowestLevel = notice.getLevel().getNumericValue();
c = notice.getColor();
}
}
return c;
}
@Override
public Dimension getPreferredSize() {
int w = PREFERRED_WIDTH - 4; // 2-pixel empty border
return new Dimension(w, 5);
}
@Override
public String getToolTipText() {
return markerToolTipProvider.getToolTipText(Collections.
unmodifiableList(notices));
}
protected void mouseClicked(MouseEvent e) {
ParserNotice pn = notices.get(0);
int offs = pn.getOffset();
int len = pn.getLength();
if (offs>-1 && len>-1) { // These values are optional
DocumentRange range = new DocumentRange(offs, offs + len);
RSyntaxUtilities.selectAndPossiblyCenter(textArea, range, true);
}
else {
int line = pn.getLine();
try {
offs = textArea.getLineStartOffset(line);
textArea.getFoldManager().ensureOffsetNotInClosedFold(offs);
textArea.setCaretPosition(offs);
} catch (BadLocationException ble) { // Never happens
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
}
}
}
@Override
protected void paintComponent(Graphics g) {
// TODO: Give "priorities" and always pick color of a notice with
// highest priority (e.g. parsing errors will usually be red).
Color borderColor = getColor();
if (borderColor==null) {
borderColor = Color.DARK_GRAY;
}
Color fillColor = getBrighterColor(borderColor);
int w = getWidth();
int h = getHeight();
g.setColor(fillColor);
g.fillRect(0,0, w,h);
g.setColor(borderColor);
g.drawRect(0,0, w-1,h-1);
}
@Override
public void removeNotify() {
super.removeNotify();
ToolTipManager.sharedInstance().unregisterComponent(this);
removeMouseListener(listener);
}
public void updateLocation() {
int line = notices.get(0).getLine();
int y = lineToY(line);
setLocation(2, y);
}
}
}
@@ -0,0 +1,3 @@
Line=Line: {0}
MultipleMarkers=Multiple markers at this line:
OccurrenceOf=Occurrence of "{0}"
@@ -0,0 +1,3 @@
Line=Line: {0}
MultipleMarkers=\u0647\u0646\u0627\u0643 \u0639\u062f\u0629 \u062a\u062d\u062f\u064a\u062f\u0627\u062a \u0641\u064a \u0647\u0630\u0627 \u0627\u0644\u0633\u0637\u0631:
OccurrenceOf=\u0639\u062f\u062f \u0645\u0631\u0627\u062a \u062d\u062f\u0648\u062b"{0}"
@@ -0,0 +1,3 @@
Line=Zeile: {0}
MultipleMarkers=Mehrfache Markierungen in dieser Zeile:
OccurrenceOf=Auftreten von "{0}"
@@ -0,0 +1,3 @@
Line=Linea: {0}
MultipleMarkers=M\u00faltiples marcadores en esta linea:
OccurrenceOf=Ocurrencia de "{0}"
@@ -0,0 +1,3 @@
Line=Ligne: {0}
MultipleMarkers=Plusieurs rep\u00e8res sur cette ligne:
OccurrenceOf=Pr\u00e9sence de "{0}"
@@ -0,0 +1,3 @@
Line=Sor: {0}
MultipleMarkers=T\u00f6bb jel\u00f6l\u0151 ebben a sorban:
OccurrenceOf=A(z) "{0}" el\u0151fordul\u00e1sa
@@ -0,0 +1,3 @@
Line=Line: {0}
MultipleMarkers=Multiple markers at this line:
OccurrenceOf=Occurrence of "{0}"
@@ -0,0 +1,3 @@
Line=Linea: {0}
MultipleMarkers=Multipli punti a questa linea:
OccurrenceOf=Occorrenze di "{0}"
@@ -0,0 +1,3 @@
Line=\u884c: {0}
MultipleMarkers=\u3053\u306e\u884c\u306e\u4e2d\u306b\u3044\u304f\u3064\u306e\u30de\u30fc\u30ab\u30fc\u306f\u3042\u308a\u307e\u3059:
OccurrenceOf=\u51fa\u73fe\u56de\u6570 "{0}"
@@ -0,0 +1,3 @@
Line=\ud589: {0}
MultipleMarkers=\uc774 \ud589\uc5d0 \ubcf5\uc218\uc758 \ub9c8\ucee4 \uc874\uc7ac:
OccurrenceOf="{0}" \ubc1c\uc0dd
@@ -0,0 +1,3 @@
Line=Line: {0}
MultipleMarkers=Multiple markers at this line:
OccurrenceOf=Occurrence of "{0}"
@@ -0,0 +1,3 @@
Line=Linia: {0}
MultipleMarkers=Wiele znacznik w tej linii:
OccurrenceOf=Wyst\u0119powanie "{0}"
@@ -0,0 +1,3 @@
Line=Linha: {0}
MultipleMarkers=M\u00faltiplos marcadores nesta linha:
OccurrenceOf=Ocorr\u00eancia de: "{0}"
@@ -0,0 +1,3 @@
Line=\u0421\u0442\u0440\u043e\u043a\u0430: {0}
MultipleMarkers=\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0440\u043a\u0435\u0440\u043e\u0432 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435:
OccurrenceOf=\u0412\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0435 "{0}"
@@ -0,0 +1,3 @@
Line=Sat\u0131r: {0}
MultipleMarkers=Bu sat\u0131rda birden fazla i\u015faret\u00e7i:
OccurrenceOf="{0}"\u0131n bulunmas\u0131
@@ -0,0 +1,3 @@
Line=\u884c: {0}
MultipleMarkers=\u6b64\u884c\u6709\u591a\u6807\u8bb0
OccurrenceOf=\u6709"{0}"\u51fa\u73b0
@@ -0,0 +1,3 @@
Line=Line: {0}
MultipleMarkers=Multiple markers at this line:
OccurrenceOf=Occurrence of "{0}"
@@ -0,0 +1,121 @@
/*
* 11/13/2008
*
* FileFileLocation.java - The location of a local file.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* The location of a local file.
*
* @author Robert Futrell
* @version 1.0
*/
class FileFileLocation extends FileLocation {
/**
* The file. This may or may not actually exist.
*/
private File file;
/**
* Constructor.
*
* @param file The local file.
*/
FileFileLocation(File file) {
try {
// Useful on Windows and OS X.
this.file = file.getCanonicalFile();
} catch (IOException ioe) {
this.file = file;
}
}
/**
* {@inheritDoc}
*/
@Override
protected long getActualLastModified() {
return file.lastModified();
}
/**
* Returns the full path to the file.
*
* @return The full path to the file.
* @see #getFileName()
*/
@Override
public String getFileFullPath() {
return file.getAbsolutePath();
}
/**
* {@inheritDoc}
*/
@Override
public String getFileName() {
return file.getName();
}
/**
* {@inheritDoc}
*/
@Override
protected InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
/**
* {@inheritDoc}
*/
@Override
protected OutputStream getOutputStream() throws IOException {
return new FileOutputStream(file);
}
/**
* Always returns <code>true</code>.
*
* @return <code>true</code> always.
* @see #isLocalAndExists()
*/
@Override
public boolean isLocal() {
return true;
}
/**
* Since file locations of this type are guaranteed to be local, this
* method returns whether the file exists.
*
* @return Whether this local file actually exists.
* @see #isLocal()
*/
@Override
public boolean isLocalAndExists() {
return file.exists();
}
}
@@ -0,0 +1,152 @@
/*
* 11/13/2008
*
* FileLocation.java - Holds the location of a local or remote file.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Holds the location of a local or remote file. This provides a common way
* to read, write, and check properties of both local and remote files.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param fileFullPath The full path to a local file.
* @return The file's location.
*/
public static FileLocation create(String fileFullPath) {
if (fileFullPath.startsWith("http://") ||
fileFullPath.startsWith("https://") ||
fileFullPath.startsWith("ftp://")) {
try {
return new URLFileLocation(new URL(fileFullPath));
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(
"Not a valid URL: " + fileFullPath, mue);
}
}
return new FileFileLocation(new File(fileFullPath));
}
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param file A local file.
* @return The file's location.
*/
public static FileLocation create(File file) {
return new FileFileLocation(file);
}
/**
* Creates a {@link FileLocation} instance for the specified file.
*
* @param url The URL of a file.
* @return The file's location.
*/
public static FileLocation create(URL url) {
if ("file".equalsIgnoreCase(url.getProtocol())) {
return new FileFileLocation(new File(url.getPath()));
}
return new URLFileLocation(url);
}
/**
* Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
* computed (such as for a remote file).
*
* @return The last time this file was modified.
*/
protected abstract long getActualLastModified();
/**
* Returns the full path to the file. This will be stripped of
* sensitive information such as passwords for remote files.
*
* @return The full path to the file.
* @see #getFileName()
*/
public abstract String getFileFullPath();
/**
* Returns the name of the file.
*
* @return The name of the file.
* @see #getFileFullPath()
*/
public abstract String getFileName();
/**
* Opens an input stream for reading from this file.
*
* @return The input stream.
* @throws IOException If the file does not exist, or some other IO error
* occurs.
*/
protected abstract InputStream getInputStream() throws IOException;
/**
* Opens an output stream for writing this file.
*
* @return An output stream.
* @throws IOException If an IO error occurs.
*/
protected abstract OutputStream getOutputStream() throws IOException;
/**
* Returns whether this file location is a local file.
*
* @return Whether this is a local file.
* @see #isLocalAndExists()
*/
public abstract boolean isLocal();
/**
* Returns whether this file location is a local file that already
* exists.
*
* @return Whether this file is local and actually exists.
* @see #isLocal()
*/
public abstract boolean isLocalAndExists();
/**
* Returns whether this file location is a remote location.
*
* @return Whether this is a remote file location.
*/
public boolean isRemote() {
return !isLocal();
}
}
@@ -0,0 +1,251 @@
/*
* 03/07/2012
*
* FoldingAwareIconRowHeader - Icon row header that paints itself correctly
* even when code folding is enabled.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.Icon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rtextarea.GutterIconInfo;
import org.fife.ui.rtextarea.IconRowHeader;
/**
* A row header component that takes code folding into account when painting
* itself.
*
* @author Robert Futrell
* @version 1.0
*/
public class FoldingAwareIconRowHeader extends IconRowHeader {
/**
* Constructor.
*
* @param textArea The parent text area.
*/
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea);
}
/**
* {@inheritDoc}
*/
@Override
protected void paintComponent(Graphics g) {
// When line wrap is not enabled, take the faster code path.
if (textArea==null) {
return;
}
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
FoldManager fm = rsta.getFoldManager();
if (!fm.isCodeFoldingSupportedAndEnabled()) {
super.paintComponent(g);
return;
}
visibleRect = g.getClipBounds(visibleRect);
if (visibleRect==null) { // ???
visibleRect = getVisibleRect();
}
//System.out.println("IconRowHeader repainting: " + visibleRect);
if (visibleRect==null) {
return;
}
paintBackgroundImpl(g, visibleRect);
if (textArea.getLineWrap()) {
paintComponentWrapped(g);
return;
}
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
textAreaInsets = textArea.getInsets(textAreaInsets);
if (visibleRect.y<textAreaInsets.top) {
visibleRect.height -= (textAreaInsets.top - visibleRect.y);
visibleRect.y = textAreaInsets.top;
}
// Get the first line to paint.
int cellHeight = textArea.getLineHeight();
int topLine = (visibleRect.y-textAreaInsets.top)/cellHeight;
// Get where to start painting (top of the row).
// We need to be "scrolled up" up just enough for the missing part of
// the first line.
int y = topLine*cellHeight + textAreaInsets.top;
// AFTER calculating visual offset to paint at, account for folding.
topLine += fm.getHiddenLineCountAbove(topLine, true);
// Paint the active line range.
if (activeLineRangeStart>-1 && activeLineRangeEnd>-1) {
Color activeLineRangeColor = getActiveLineRangeColor();
g.setColor(activeLineRangeColor);
try {
int realY1 = rsta.yForLine(activeLineRangeStart);
if (realY1>-1) { // Not in a collapsed fold...
int y1 = realY1;//Math.max(y, realY1);
int y2 = rsta.yForLine(activeLineRangeEnd);
if (y2==-1) { // In a collapsed fold
y2 = y1;
}
y2 += cellHeight - 1;
if (y2<visibleRect.y || y1>visibleRect.y+visibleRect.height) {
//System.out.println("... nothing to paint, bailing...");
return;
}
y1 = Math.max(y, realY1);
y2 = Math.min(y2, visibleRect.y+visibleRect.height);
//System.out.println(y1 + "... " + y2 + "; " + realY1 + ", " + visibleRect);
int j = y1;
while (j<=y2) {
int yEnd = Math.min(y2, j+getWidth());
int xEnd = yEnd-j;
g.drawLine(0,j, xEnd,yEnd);
j += 2;
}
int i = 2;
while (i<getWidth()) {
int yEnd = y1 + getWidth() - i;
g.drawLine(i,y1, getWidth(),yEnd);
i += 2;
}
if (realY1>=y && realY1<visibleRect.y+visibleRect.height) {
g.drawLine(0,realY1, getWidth(),realY1);
}
if (y2>=y && y2<visibleRect.y+visibleRect.height) {
g.drawLine(0,y2, getWidth(),y2);
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
// Paint icons
if (trackingIcons!=null) {
int lastLine = textArea.getLineCount() - 1;
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
GutterIconInfo ti = getTrackingIcon(i);
int offs = ti.getMarkedOffset();
if (offs>=0 && offs<=doc.getLength()) {
int line = root.getElementIndex(offs);
if (line<=lastLine && line>=topLine) {
try {
Icon icon = ti.getIcon();
if (icon!=null) {
int lineY = rsta.yForLine(line);
if (lineY>=y && lineY<=visibleRect.y+visibleRect.height) {
int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
icon.paintIcon(this, g, 0, y2);
lastLine = line-1; // Paint only 1 icon per line
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
else if (line<topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
/**
* Paints icons when line wrapping is enabled. Note that this does not
* override the parent class's implementation to avoid this version being
* called when line wrapping is disabled.
*/
private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows:
// - visibleRect is the "visible" area of the text area; e.g.
// [0,100, 300,100+(lineCount*cellHeight)-1].
// actualTop.y is the topmost-pixel in the first logical line we
// paint. Note that we may well not paint this part of the logical
// line, as it may be broken into many physical lines, with the first
// few physical lines scrolled past. Note also that this is NOT the
// visible rect of this line number list; this line number list has
// visible rect == [0,0, insets.left-1,visibleRect.height-1].
// We avoid using modelToView/viewToModel where possible, as these
// methods trigger a parsing of the line into syntax tokens, which is
// costly. It's cheaper to just grab the child views' bounds.
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
// boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int topPosition = textArea.viewToModel(
new Point(visibleRect.x,visibleRect.y));
int topLine = root.getElementIndex(topPosition);
int topY = visibleRect.y;
int bottomY = visibleRect.y + visibleRect.height;
int cellHeight = textArea.getLineHeight();
// Paint icons
if (trackingIcons!=null) {
int lastLine = textArea.getLineCount() - 1;
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
GutterIconInfo ti = getTrackingIcon(i);
Icon icon = ti.getIcon();
if (icon!=null) {
int iconH = icon.getIconHeight();
int offs = ti.getMarkedOffset();
if (offs>=0 && offs<=doc.getLength()) {
int line = root.getElementIndex(offs);
if (line<=lastLine && line>=topLine) {
try {
int lineY = rsta.yForLine(line);
if (lineY<=bottomY && (lineY+iconH>=topY)) {
int y2 = lineY + (cellHeight-iconH)/2;
ti.getIcon().paintIcon(this, g, 0, y2);
lastLine = line-1; // Paint only 1 icon per line
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
else if (line<topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
}
}
@@ -0,0 +1,411 @@
/*
* 12/01/2014
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks occurrences of the current token for HTML. Tags that require a
* closing tag have their "opposite" tag closed.
*
* @author Robert Futrell
* @version 1.0
*/
public class HtmlOccurrenceMarker implements OccurrenceMarker {
private static final char[] CLOSE_TAG_START = { '<', '/' };
private static final char[] TAG_SELF_CLOSE = { '/', '>' };
private static final Set<String> TAGS_REQUIRING_CLOSING =
getRequiredClosingTags();
public static final Set<String> getRequiredClosingTags() {
final String[] tags = {
"html",
"head",
"title",
"style",
"script",
"noscript",
"body",
"section",
"nav",
"article",
"aside",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"header",
"footer",
"address",
"pre",
"dialog",
"blockquote",
"ol",
"ul",
"dl",
"a",
"q",
"cite",
"em",
"strong",
"small",
"mark",
"dfn",
"abbr",
"time",
"progress",
"meter",
"code",
"var",
"samp",
"kbd",
"sub",
"sup",
"span",
"i",
"b",
"bdo",
"ruby",
"rt",
"rp",
"ins",
"del",
"figure",
"iframe",
"object",
"video",
"audio",
"canvas",
"map",
"table",
"caption",
"form",
"fieldset",
"label",
"button",
"select",
"datalist",
"textarea",
"output",
"details",
"bb",
"menu",
"legend",
"div",
// Obsolete elements
"acronym",
"applet",
"big",
"blink",
"center",
"dir",
"font",
"frame",
"frameset",
"isindex",
"listing",
"marquee",
"nobr",
"noembed",
"noframes",
"plaintext",
"s",
"spacer",
"strike",
"tt",
"u",
"xmp",
};
return new HashSet<String>(Arrays.asList(tags));
}
/**
* If the caret is inside of a tag, this method returns the token
* representing the tag name; otherwise, <code>null</code> is returned.<p>
*
* Currently, this method only checks for tag names on the same line as
* the caret, for simplicity. In the future it could check prior lines
* until the tag name is found.
*
* @param textArea The text area.
* @param occurrenceMarker The occurrence marker.
* @return The token to mark occurrences of. Note that, if the
* specified occurrence marker identifies tokens other than
* tag names, these other element types may be returned.
*/
public static final Token getTagNameTokenForCaretOffset(
RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
// Get the tag name token.
// For now, we only check for tags on the current line, for simplicity.
int dot = textArea.getCaretPosition();
Token t = textArea.getTokenListForLine(textArea.getCaretLineNumber());
Token toMark = null;
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_NAME) {
toMark = t;
}
// Check for the token containing the caret before checking
// if it's the close token.
if (t.getEndOffset()==dot || t.containsPosition(dot)) {
// Some languages, like PHP, mark functions/variables (PHP,
// JavaScirpt) as well as HTML tags.
if (occurrenceMarker.isValidType(textArea, t) &&
t.getType()!=Token.MARKUP_TAG_NAME) {
return t;
}
if (t.containsPosition(dot)) {
break;
}
}
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('>') || t.is(TAG_SELF_CLOSE)) {
toMark = null;
}
}
t = t.getNextToken();
}
return toMark;
}
/**
* {@inheritDoc}
*/
@Override
public Token getTokenToMark(RSyntaxTextArea textArea) {
return getTagNameTokenForCaretOffset(textArea, this);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@Override
public void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
if (t.getType()!=Token.MARKUP_TAG_NAME) {
DefaultOccurrenceMarker.markOccurrencesOfToken(doc, t, h, p);
return;
}
String lexemeStr = t.getLexeme();
char[] lexeme = lexemeStr.toCharArray();
lexemeStr = lexemeStr.toLowerCase();
int tokenOffs = t.getOffset();
Element root = doc.getDefaultRootElement();
int lineCount = root.getElementCount();
int curLine = root.getElementIndex(t.getOffset());
int depth = 0;
// For now, we only check for tags on the current line, for
// simplicity. Tags spanning multiple lines aren't common anyway.
boolean found = false;
boolean forward = true;
t = doc.getTokenListForLine(curLine);
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<') && t.getOffset()+1==tokenOffs) {
// Don't try to match a tag that is optionally closed (or
// closing is forbidden entirely).
if (TAGS_REQUIRING_CLOSING.contains(lexemeStr)) {
found = true;
}
break;
}
else if (t.is(CLOSE_TAG_START) && t.getOffset()+2==tokenOffs) {
// Searching backward, we assume we can find the opening
// tag. Don't really care if it's valid or not.
found = true;
forward = false;
break;
}
}
t = t.getNextToken();
}
if (!found) {
return;
}
if (forward) {
t = t.getNextToken().getNextToken();
do {
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.is(CLOSE_TAG_START)) {
Token match = t.getNextToken();
if (match!=null && match.is(lexeme)) {
if (depth>0) {
depth--;
}
else {
try {
int end = match.getOffset() + match.length();
h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
end = tokenOffs + match.length();
h.addMarkedOccurrenceHighlight(tokenOffs, end, p);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
return; // We're done!
}
}
}
else if (t.isSingleChar('<')) {
t = t.getNextToken();
if (t!=null && t.is(lexeme)) {
depth++;
}
}
}
t = t==null ? null : t.getNextToken();
}
if (++curLine<lineCount) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine<lineCount);
}
else { // !forward
// Idea: Get all opening and closing tags of the relevant type on
// the current line. Find the opening tag paired to the closing
// tag we found originally; if it's not on this line, keep going
// to the previous line.
List<Entry> openCloses = new ArrayList<Entry>();
boolean inPossibleMatch = false;
t = doc.getTokenListForLine(curLine);
final int endBefore = tokenOffs - 2; // Stop before "</".
do {
while (t!=null && t.getOffset()<endBefore && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<')) {
Token next = t.getNextToken();
if (next!=null) {
if (next.is(lexeme)) {
openCloses.add(new Entry(true, next));
inPossibleMatch = true;
}
else {
inPossibleMatch = false;
}
t = next;
}
}
else if (t.isSingleChar('>')) {
inPossibleMatch = false;
}
else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
openCloses.remove(openCloses.size()-1);
inPossibleMatch = false;
}
else if (t.is(CLOSE_TAG_START)) {
Token next = t.getNextToken();
if (next!=null) {
// Invalid XML might not have a match
if (next.is(lexeme)) {
openCloses.add(new Entry(false, next));
}
t = next;
}
}
}
t = t.getNextToken();
}
for (int i=openCloses.size()-1; i>=0; i--) {
Entry entry = openCloses.get(i);
depth += entry.open ? -1 : 1;
if (depth==-1) {
try {
Token match = entry.t;
int end = match.getOffset() + match.length();
h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
end = tokenOffs + match.length();
h.addMarkedOccurrenceHighlight(tokenOffs, end, p);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
openCloses.clear();
return;
}
}
openCloses.clear();
if (--curLine>=0) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine>=0);
}
}
/**
* Used internally when searching backward for a matching "open" tag.
*/
private static class Entry {
private boolean open;
private Token t;
Entry(boolean open, Token t) {
this.open = open;
this.t = t;
}
}
}
@@ -0,0 +1,49 @@
/*
* 02/16/2012
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* Generates hyperlinks in a document. If one of these is installed on an
* <code>RSyntaxTextArea</code> it is queried when the mouse is moved and
* hyperlinks are enabled. If the user is not hovering over a "real" hyperlink
* (e.g. "http://www.google.com"), the link generator is asked if a text region
* at the mouse position should be considered a hyperlink. If so, a result
* object is returned, describing exactly what region of text is the link, and
* where it goes to.<p>
*
* This interface is typically used by applications providing advanced support
* for programming languages, such as IDEs. For example, an implementation of
* this class could identify the token under the mouse position as a "variable,"
* and the hyperlink returned would select the variable's declaration in the
* document.
*
* @author Robert Futrell
* @version 1.0
*/
public interface LinkGenerator {
/**
* If a region of text under the mouse position should be considered a
* hyperlink, a result object is returned. This object describes what
* region of text is the link, and what action to perform if the link is
* clicked.
*
* @param textArea The text component.
* @param offs The offset in the document under the mouse position.
* @return The link information, or <code>null</code> if no link is at the
* specified offset.
*/
LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offs);
}
@@ -0,0 +1,53 @@
/*
* 02/16/2012
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.event.HyperlinkEvent;
/**
* A result object from a {@link LinkGenerator}. Implementations of this class
* specify what action to execute when the user clicks on the "link" specified
* by the <code>LinkGenerator</code>. Typically, this will do something like
* select another region of text in the document (the declaration of the
* variable at the mouse position), or open another file in the parent
* application, etc.
*
* @author Robert Futrell
* @version 1.0
* @see SelectRegionLinkGeneratorResult
*/
public interface LinkGeneratorResult {
/**
* Executes the action associated with this object. If the result is a
* URL to open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this
* method itself.
*
* @return The hyperlink event to broadcast from the text area, or
* <code>null</code> if the action's behavior occurs in this method
* directly.
*/
HyperlinkEvent execute();
/**
* Returns the starting offset of the link specified by the parent
* <code>LinkGenerator</code>.
*
* @return The offset.
*/
int getSourceOffset();
}
@@ -0,0 +1,289 @@
/*
* 01/06/2009
*
* MarkOccurrencesSupport.java - Handles marking all occurrences of the
* currently selected identifier in a text area.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks all occurrences of the token at the current caret position, if it is
* an identifier.
*
* @author Robert Futrell
* @version 1.0
* @see OccurrenceMarker
*/
class MarkOccurrencesSupport implements CaretListener, ActionListener {
private RSyntaxTextArea textArea;
private Timer timer;
private SmartHighlightPainter p;
/**
* The default color used to mark occurrences.
*/
static final Color DEFAULT_COLOR = new Color(224, 224, 224);
/**
* The default delay.
*/
static final int DEFAULT_DELAY_MS = 1000;
/**
* Constructor. Creates a listener with a 1 second delay.
*/
MarkOccurrencesSupport() {
this(DEFAULT_DELAY_MS);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
*/
MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param color The color to use to mark the occurrences. This cannot be
* <code>null</code>.
*/
MarkOccurrencesSupport(int delay, Color color) {
timer = new Timer(delay, this);
timer.setRepeats(false);
p = new SmartHighlightPainter();
setColor(color);
}
/**
* Called after the caret has been moved and a fixed time delay has
* elapsed. This locates and highlights all occurrences of the identifier
* at the caret position, if any.<p>
*
* Callers should not call this method directly, but should rather prefer
* {@link #doMarkOccurrences()} to mark occurrences.
*
* @param e The event.
* @see #doMarkOccurrences()
*/
@Override
public void actionPerformed(ActionEvent e) {
// Don't do anything if they are selecting text.
Caret c = textArea.getCaret();
if (c.getDot()!=c.getMark()) {
return;
}
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
OccurrenceMarker occurrenceMarker = doc.getOccurrenceMarker();
boolean occurrencesChanged = false;
if (occurrenceMarker!=null) {
doc.readLock();
try {
Token t = occurrenceMarker.getTokenToMark(textArea);
if (t!=null && occurrenceMarker.isValidType(textArea, t) &&
!RSyntaxUtilities.isNonWordChar(t)) {
clear();
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
occurrenceMarker.markOccurrences(doc, t, h, p);
//textArea.repaint();
// TODO: Do a textArea.repaint() instead of repainting each
// marker as it's added if count is huge
occurrencesChanged = true;
}
} finally {
doc.readUnlock();
//time = System.currentTimeMillis() - time;
//System.out.println("MarkOccurrencesSupport took: " + time + " ms");
}
}
if (occurrencesChanged) {
textArea.fireMarkedOccurrencesChanged();
}
}
/**
* Called when the caret moves in the text area.
*
* @param e The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
timer.restart();
}
/**
* Removes all highlights added to the text area by this listener.
*/
void clear() {
if (textArea!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
h.clearMarkOccurrencesHighlights();
}
}
/**
* Immediately marks all occurrences of the token at the current caret
* position.
*/
public void doMarkOccurrences() {
timer.stop();
actionPerformed(null);
}
/**
* Returns the color being used to mark occurrences.
*
* @return The color being used.
* @see #setColor(Color)
*/
public Color getColor() {
return (Color)p.getPaint();
}
/**
* Returns the delay, in milliseconds.
*
* @return The delay.
* @see #setDelay(int)
*/
public int getDelay() {
return timer.getDelay();
}
/**
* Returns whether a border is painted around marked occurrences.
*
* @return Whether a border is painted.
* @see #setPaintBorder(boolean)
* @see #getColor()
*/
public boolean getPaintBorder() {
return p.getPaintBorder();
}
/**
* Installs this listener on a text area. If it is already installed on
* another text area, it is uninstalled first.
*
* @param textArea The text area to install on.
*/
public void install(RSyntaxTextArea textArea) {
if (this.textArea!=null) {
uninstall();
}
this.textArea = textArea;
textArea.addCaretListener(this);
if (textArea.getMarkOccurrencesColor()!=null) {
setColor(textArea.getMarkOccurrencesColor());
}
}
/**
* Sets the color to use when marking occurrences.
*
* @param color The color to use.
* @see #getColor()
* @see #setPaintBorder(boolean)
*/
public void setColor(Color color) {
p.setPaint(color);
if (textArea!=null) {
clear();
caretUpdate(null); // Force a highlight repaint.
}
}
/**
* Sets the delay between the last caret position change and when the
* text is scanned for matching identifiers. A delay is needed to prevent
* repeated scanning while the user is typing.
*
* @param delay The new delay.
* @see #getDelay()
*/
public void setDelay(int delay) {
timer.setInitialDelay(delay);
}
/**
* Toggles whether a border is painted around marked highlights.
*
* @param paint Whether to paint a border.
* @see #getPaintBorder()
* @see #setColor(Color)
*/
public void setPaintBorder(boolean paint) {
if (paint!=p.getPaintBorder()) {
p.setPaintBorder(paint);
if (textArea!=null) {
textArea.repaint();
}
}
}
/**
* Uninstalls this listener from the current text area. Does nothing if
* it not currently installed on any text area.
*
* @see #install(RSyntaxTextArea)
*/
public void uninstall() {
if (textArea!=null) {
clear();
textArea.removeCaretListener(this);
}
}
}
@@ -0,0 +1,245 @@
/*
* 07/03/2016
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
/**
* A tool tip-like popup that shows the line of code containing the bracket
* matched to that at the caret position, if it is scrolled out of the user's
* viewport.
*
* @author Robert Futrell
* @version 1.0
*/
class MatchedBracketPopup extends JWindow {
private RSyntaxTextArea textArea;
private transient Listener listener;
private static final int LEFT_EMPTY_BORDER = 5;
MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int
offsToRender) {
super(parent);
this.textArea = textArea;
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createCompoundBorder(
TipUtil.getToolTipBorder(),
BorderFactory.createEmptyBorder(2, LEFT_EMPTY_BORDER, 5, 5)));
cp.setBackground(TipUtil.getToolTipBackground());
setContentPane(cp);
cp.add(new JLabel(getText(offsToRender)));
installKeyBindings();
listener = new Listener();
setLocation();
}
/**
* Overridden to ensure this popup stays in a specific size range.
*/
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (size!=null) {
size.width = Math.min(size.width, 800);
}
return size;
}
private String getText(int offsToRender) {
int line = 0;
try {
line = textArea.getLineOfOffset(offsToRender);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
return null;
}
int lastLine = line + 1;
// Render prior line if the open brace line has no other text on it
if (line > 0) {
try {
int startOffs = textArea.getLineStartOffset(line);
int length = textArea.getLineEndOffset(line) - startOffs;
String text = textArea.getText(startOffs, length);
if (text.trim().length() == 1) {
line--;
}
} catch (BadLocationException ble) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
ble.printStackTrace();
}
}
Font font = textArea.getFontForTokenType(TokenTypes.IDENTIFIER);
StringBuilder sb = new StringBuilder("<html>");
sb.append("<style>body { font-size:\"").append(font.getSize());
sb.append("pt\" }</style><nobr>");
while (line < lastLine) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
t.appendHTMLRepresentation(sb, textArea, true, true);
t = t.getNextToken();
}
sb.append("<br>");
line++;
}
return sb.toString();
}
/**
* Adds key bindings to this popup.
*/
private void installKeyBindings() {
InputMap im = getRootPane().getInputMap(
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = getRootPane().getActionMap();
KeyStroke escapeKS = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
im.put(escapeKS, "onEscape");
am.put("onEscape", new EscapeAction());
}
/**
* Positions this popup to be in the top right-hand corner of the parent
* editor.
*/
private void setLocation() {
Point topLeft = textArea.getVisibleRect().getLocation();
SwingUtilities.convertPointToScreen(topLeft, textArea);
topLeft.y = Math.max(topLeft.y - 24, 0);
setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y);
}
/**
* Action performed when Escape is pressed in this popup.
*/
private class EscapeAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
listener.uninstallAndHide();
}
}
/**
* Listens for events in this popup.
*/
private class Listener extends WindowAdapter implements ComponentListener {
Listener() {
addWindowFocusListener(this);
// If anything happens to the "parent" window, hide this popup
Window parent = (Window)getParent();
parent.addWindowFocusListener(this);
parent.addWindowListener(this);
parent.addComponentListener(this);
}
@Override
public void componentResized(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentMoved(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentShown(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentHidden(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void windowActivated(WindowEvent e) {
checkForParentWindowEvent(e);
}
@Override
public void windowLostFocus(WindowEvent e) {
uninstallAndHide();
}
@Override
public void windowIconified(WindowEvent e) {
checkForParentWindowEvent(e);
}
private boolean checkForParentWindowEvent(WindowEvent e) {
if (e.getSource()==getParent()) {
uninstallAndHide();
return true;
}
return false;
}
private void uninstallAndHide() {
Window parent = (Window)getParent();
parent.removeWindowFocusListener(this);
parent.removeWindowListener(this);
parent.removeComponentListener(this);
removeWindowFocusListener(this);
setVisible(false);
dispose();
}
}
}
@@ -0,0 +1,63 @@
/*
* 03/09/2013
*
* OccurrenceMarker - Marks occurrences of the current token.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* An <code>OccurrenceMarker</code> is called when the caret stops moving after
* a short period. If the current {@link TokenMaker} returns an instance of
* this class, it is told to mark all occurrences of the identifier at the
* caret position.
*
* @author Robert Futrell
* @version 1.0
*/
public interface OccurrenceMarker {
/**
* Returns the token to mark occurrences, of, provided it matches the
* criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}.
* For most languages, this method should return the token at the caret
* position.
*
* @param textArea The text area.
* @return The token to (possibly) mark occurrences of, or
* <code>null</code> if none.
*/
Token getTokenToMark(RSyntaxTextArea textArea);
/**
* Returns whether the specified token is a type that we can do a
* "mark occurrences" of. Typically, this will delegate to
* {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
*
* @param textArea The text area.
* @param t The token.
* @return Whether we should mark all occurrences of this token.
*/
boolean isValidType(RSyntaxTextArea textArea, Token t);
/**
* Called when occurrences of a token should be marked.
*
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
}
@@ -0,0 +1,829 @@
/*
* 09/26/2005
*
* ParserManager.java - Manages the parsing of an RSyntaxTextArea's document,
* if necessary.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Position;
import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rsyntaxtextarea.parser.ToolTipInfo;
import org.fife.ui.rtextarea.RDocument;
import org.fife.ui.rtextarea.RTextAreaHighlighter.HighlightInfo;
/**
* Manages running a parser object for an <code>RSyntaxTextArea</code>.
*
* @author Robert Futrell
* @version 0.9
*/
class ParserManager implements DocumentListener, ActionListener,
HyperlinkListener, PropertyChangeListener {
private RSyntaxTextArea textArea;
private List<Parser> parsers;
private Timer timer;
private boolean running;
private Parser parserForTip;
private Position firstOffsetModded;
private Position lastOffsetModded;
/**
* Mapping of notices to their highlights in the editor. Can't use a Map
* since parsers could return two <code>ParserNotice</code>s that compare
* equally via <code>equals()</code>. Real-world example: The Perl
* compiler will return 2+ identical error messages if the same error is
* committed in a single line more than once.
*/
private List<NoticeHighlightPair> noticeHighlightPairs;
/**
* Painter used to underline errors.
*/
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter =
new SquiggleUnderlineHighlightPainter(Color.RED);
/**
* If this system property is set to <code>true</code>, debug messages
* will be printed to stdout to help diagnose parsing issues.
*/
private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
/**
* Whether to print debug messages while running parsers.
*/
private static final boolean DEBUG_PARSING;
/**
* The default delay between the last key press and when the document
* is parsed, in milliseconds.
*/
private static final int DEFAULT_DELAY_MS = 1250;
/**
* Constructor.
*
* @param textArea The text area whose document the parser will be
* parsing.
*/
ParserManager(RSyntaxTextArea textArea) {
this(DEFAULT_DELAY_MS, textArea);
}
/**
* Constructor.
*
* @param delay The delay between the last key press and when the document
* is parsed.
* @param textArea The text area whose document the parser will be
* parsing.
*/
ParserManager(int delay, RSyntaxTextArea textArea) {
this.textArea = textArea;
textArea.getDocument().addDocumentListener(this);
textArea.addPropertyChangeListener("document", this);
parsers = new ArrayList<Parser>(1); // Usually small
timer = new Timer(delay, this);
timer.setRepeats(false);
running = true;
}
/**
* Called when the timer fires (e.g. it's time to parse the document).
*
* @param e The event.
*/
@Override
public void actionPerformed(ActionEvent e) {
// Sanity check - should have >1 parser if event is fired.
int parserCount = getParserCount();
if (parserCount==0) {
return;
}
long begin = 0;
if (DEBUG_PARSING) {
begin = System.currentTimeMillis();
}
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
Element root = doc.getDefaultRootElement();
int firstLine = firstOffsetModded==null ? 0 :
root.getElementIndex(firstOffsetModded.getOffset());
int lastLine = lastOffsetModded==null ? root.getElementCount()-1 :
root.getElementIndex(lastOffsetModded.getOffset());
firstOffsetModded = lastOffsetModded = null;
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
}
String style = textArea.getSyntaxEditingStyle();
doc.readLock();
try {
for (int i=0; i<parserCount; i++) {
Parser parser = getParser(i);
if (parser.isEnabled()) {
ParseResult res = parser.parse(doc, style);
addParserNoticeHighlights(res);
}
else {
clearParserNoticeHighlights(parser);
}
}
textArea.fireParserNoticesChange();
} finally {
doc.readUnlock();
}
if (DEBUG_PARSING) {
float time = (System.currentTimeMillis()-begin)/1000f;
System.out.println("Total parsing time: " + time + " seconds");
}
}
/**
* Adds a parser for the text area.
*
* @param parser The new parser. If this is <code>null</code>, nothing
* happens.
* @see #getParser(int)
* @see #removeParser(Parser)
*/
public void addParser(Parser parser) {
if (parser!=null && !parsers.contains(parser)) {
if (running) {
timer.stop();
}
parsers.add(parser);
if (parsers.size()==1) {
// Okay to call more than once.
ToolTipManager.sharedInstance().registerComponent(textArea);
}
if (running) {
timer.restart();
}
}
}
/**
* Adds highlights for a list of parser notices. Any current notices
* from the same Parser, in the same parsed range, are removed.
*
* @param res The result of a parsing.
* @see #clearParserNoticeHighlights()
*/
private void addParserNoticeHighlights(ParseResult res) {
// Parsers are supposed to return at least empty ParseResults, but
// we'll be defensive here.
if (res==null) {
return;
}
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Adding parser notices from " +
res.getParser());
}
if (noticeHighlightPairs==null) {
noticeHighlightPairs = new ArrayList<NoticeHighlightPair>();
}
removeParserNotices(res);
List<ParserNotice> notices = res.getNotices();
if (notices.size()>0) { // Guaranteed non-null
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
for (ParserNotice notice : notices) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... adding: " + notice);
}
try {
HighlightInfo highlight = null;
if (notice.getShowInEditor()) {
highlight = h.addParserHighlight(notice,
parserErrorHighlightPainter);
}
noticeHighlightPairs.add(new NoticeHighlightPair(notice, highlight));
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
}
}
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Done adding parser notices from " +
res.getParser());
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
*/
@Override
public void changedUpdate(DocumentEvent e) {
}
private void clearParserNoticeHighlights() {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (h!=null) {
h.clearParserHighlights();
}
if (noticeHighlightPairs!=null) {
noticeHighlightPairs.clear();
}
}
/**
* Removes all parser notice highlights for a specific parser.
*
* @param parser The parser whose highlights to remove.
*/
private void clearParserNoticeHighlights(Parser parser) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (h!=null) {
h.clearParserHighlights(parser);
}
if (noticeHighlightPairs!=null) {
Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator();
while (i.hasNext()) {
NoticeHighlightPair pair = i.next();
if (pair.notice.getParser()==parser) {
i.remove();
}
}
}
}
/**
* Removes all parsers and any highlights they have created.
*
* @see #addParser(Parser)
*/
public void clearParsers() {
timer.stop();
clearParserNoticeHighlights();
parsers.clear();
textArea.fireParserNoticesChange();
}
/**
* Forces the given {@link Parser} to re-parse the content of this text
* area.<p>
*
* This method can be useful when a <code>Parser</code> can be configured
* as to what notices it returns. For example, if a Java language parser
* can be configured to set whether no serialVersionUID is a warning,
* error, or ignored, this method can be called after changing the expected
* notice type to have the document re-parsed.
*
* @param parser The index of the <code>Parser</code> to re-run.
* @see #getParser(int)
*/
public void forceReparsing(int parser) {
Parser p = getParser(parser);
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
String style = textArea.getSyntaxEditingStyle();
doc.readLock();
try {
if (p.isEnabled()) {
ParseResult res = p.parse(doc, style);
addParserNoticeHighlights(res);
}
else {
clearParserNoticeHighlights(p);
}
textArea.fireParserNoticesChange();
} finally {
doc.readUnlock();
}
}
/**
* Returns the delay between the last "concurrent" edit and when the
* document is re-parsed.
*
* @return The delay, in milliseconds.
* @see #setDelay(int)
*/
public int getDelay() {
return timer.getDelay();
}
/**
* Returns the specified parser.
*
* @param index The index of the parser.
* @return The parser.
* @see #getParserCount()
* @see #addParser(Parser)
* @see #removeParser(Parser)
*/
public Parser getParser(int index) {
return parsers.get(index);
}
/**
* Returns the number of registered parsers.
*
* @return The number of registered parsers.
*/
public int getParserCount() {
return parsers.size();
}
/**
* Returns a list of the current parser notices for this text area.
* This method (like most Swing methods) should only be called on the
* EDT.
*
* @return The list of notices. This will be an empty list if there are
* none.
*/
public List<ParserNotice> getParserNotices() {
List<ParserNotice> notices = new ArrayList<ParserNotice>();
if (noticeHighlightPairs!=null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
notices.add(pair.notice);
}
}
return notices;
}
/**
* Returns the tool tip to display for a mouse event at the given
* location. This method is overridden to give a registered parser a
* chance to display a tool tip (such as an error description when the
* mouse is over an error highlight).
*
* @param e The mouse event.
* @return The tool tip to display, and possibly a hyperlink event handler.
*/
public ToolTipInfo getToolTipText(MouseEvent e) {
String tip = null;
HyperlinkListener listener = null;
parserForTip = null;
Point p = e.getPoint();
// try {
int pos = textArea.viewToModel(p);
/*
Highlighter.Highlight[] highlights = textArea.getHighlighter().
getHighlights();
for (int i=0; i<highlights.length; i++) {
Highlighter.Highlight h = highlights[i];
//if (h instanceof ParserNoticeHighlight) {
// ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
int start = h.getStartOffset();
int end = h.getEndOffset();
if (start<=pos && end>=pos) {
//return pnh.getMessage();
return textArea.getText(start, end-start);
}
//}
}
*/
if (noticeHighlightPairs!=null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
ParserNotice notice = pair.notice;
if (noticeContainsPosition(notice, pos) &&
noticeContainsPointInView(notice, p)) {
tip = notice.getToolTipText();
parserForTip = notice.getParser();
if (parserForTip instanceof HyperlinkListener) {
listener = (HyperlinkListener)parserForTip;
}
break;
}
}
}
// } catch (BadLocationException ble) {
// ble.printStackTrace(); // Should never happen.
// }
URL imageBase = parserForTip==null ? null : parserForTip.getImageBase();
return new ToolTipInfo(tip, listener, imageBase);
}
/**
* Called when the document is modified.
*
* @param e The document event.
*/
public void handleDocumentEvent(DocumentEvent e) {
if (running && parsers.size()>0) {
timer.restart();
}
}
/**
* Called when the user clicks a hyperlink in a
* {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}.
*
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (parserForTip!=null && parserForTip.getHyperlinkListener()!=null) {
parserForTip.getHyperlinkListener().linkClicked(textArea, e);
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
*/
@Override
public void insertUpdate(DocumentEvent e) {
// Keep track of the first and last offset modified. Some parsers are
// smart and will only re-parse this section of the file.
try {
int offs = e.getOffset();
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
firstOffsetModded = e.getDocument().createPosition(offs);
}
offs = e.getOffset() + e.getLength();
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
lastOffsetModded = e.getDocument().createPosition(offs);
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Shouldn't happen
}
handleDocumentEvent(e);
}
/**
* Returns whether a parser notice contains the specified offset.
*
* @param notice The notice.
* @param offs The offset.
* @return Whether the notice contains the offset.
*/
private boolean noticeContainsPosition(ParserNotice notice, int offs){
if (notice.getKnowsOffsetAndLength()) {
return notice.containsPosition(offs);
}
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int line = notice.getLine();
if (line<0) { // Defensive against possible bad user-defined notices.
return false;
}
Element elem = root.getElement(line);
return elem != null && offs>=elem.getStartOffset() && offs<elem.getEndOffset();
}
/**
* Since <code>viewToModel()</code> returns the <em>closest</em> model
* position, and the position doesn't <em>necessarily</em> contain the
* point passed in as an argument, this method checks whether the point is
* indeed contained in the view rectangle for the specified offset.
*
* @param notice The parser notice.
* @param p The point possibly contained in the view range of the
* parser notice.
* @return Whether the parser notice actually contains the specified point
* in the view.
*/
private boolean noticeContainsPointInView(ParserNotice notice,
Point p) {
try {
int start, end;
if (notice.getKnowsOffsetAndLength()) {
start = notice.getOffset();
end = start + notice.getLength() - 1;
}
else {
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int line = notice.getLine();
// Defend against possible bad user-defined notices.
if (line<0) {
return false;
}
Element elem = root.getElement(line);
start = elem.getStartOffset();
end = elem.getEndOffset() - 1;
}
Rectangle r1 = textArea.modelToView(start);
Rectangle r2 = textArea.modelToView(end);
if (r1.y!=r2.y) {
// If the notice spans multiple lines, give them the benefit
// of the doubt. This is only "wrong" if the user is in empty
// space "to the right" of the error marker when it ends at the
// end of a line anyway.
return true;
}
r1.y--; // Be a tiny bit lenient.
r1.height += 2; // Ditto
return p.x>=r1.x && p.x<(r2.x+r2.width) &&
p.y>=r1.y && p.y<(r1.y+r1.height);
} catch (BadLocationException ble) { // Never occurs
// Give them the benefit of the doubt, should 99% of the time be
// true anyway
return true;
}
}
/**
* Called when a property we're interested in changes.
*
* @param e The property change event.
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if ("document".equals(name)) {
// The document switched out from under us
RDocument old = (RDocument)e.getOldValue();
if (old != null) {
old.removeDocumentListener(this);
}
RDocument newDoc = (RDocument)e.getNewValue();
if (newDoc != null) {
newDoc.addDocumentListener(this);
}
}
}
/**
* Removes a parser.
*
* @param parser The parser to remove.
* @return Whether the parser was found.
* @see #addParser(Parser)
* @see #getParser(int)
*/
public boolean removeParser(Parser parser) {
removeParserNotices(parser);
boolean removed = parsers.remove(parser);
if (removed) {
textArea.fireParserNoticesChange();
}
return removed;
}
/**
* Removes all parser notices (and clears highlights in the editor) from
* a particular parser.
*
* @param parser The parser.
*/
private void removeParserNotices(Parser parser) {
if (noticeHighlightPairs!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator(); i.hasNext();) {
NoticeHighlightPair pair = i.next();
if (pair.notice.getParser()==parser && pair.highlight!=null) {
h.removeParserHighlight(pair.highlight);
i.remove();
}
}
}
}
/**
* Removes any currently stored notices (and the corresponding highlights
* from the editor) from the same Parser, and in the given line range,
* as in the results.
*
* @param res The results.
*/
private void removeParserNotices(ParseResult res) {
if (noticeHighlightPairs!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator(); i.hasNext();) {
NoticeHighlightPair pair = i.next();
boolean removed = false;
if (shouldRemoveNotice(pair.notice, res)) {
if (pair.highlight!=null) {
h.removeParserHighlight(pair.highlight);
}
i.remove();
removed = true;
}
if (DEBUG_PARSING) {
String text = removed ? "[DEBUG]: ... notice removed: " :
"[DEBUG]: ... notice not removed: ";
System.out.println(text + pair.notice);
}
}
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
*/
@Override
public void removeUpdate(DocumentEvent e) {
// Keep track of the first and last offset modified. Some parsers are
// smart and will only re-parse this section of the file. Note that
// for removals, only the line at the removal start needs to be
// re-parsed.
try {
int offs = e.getOffset();
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
firstOffsetModded = e.getDocument().createPosition(offs);
}
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
lastOffsetModded = e.getDocument().createPosition(offs);
}
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
handleDocumentEvent(e);
}
/**
* Restarts parsing the document.
*
* @see #stopParsing()
*/
public void restartParsing() {
timer.restart();
running = true;
}
/**
* Sets the delay between the last "concurrent" edit and when the document
* is re-parsed.
*
* @param millis The new delay, in milliseconds. This must be greater
* than <code>0</code>.
* @see #getDelay()
*/
public void setDelay(int millis) {
if (running) {
timer.stop();
}
timer.setInitialDelay(millis);
timer.setDelay(millis);
if (running) {
timer.start();
}
}
/**
* Returns whether a parser notice should be removed, based on a parse
* result.
*
* @param notice The notice in question.
* @param res The result.
* @return Whether the notice should be removed.
*/
private boolean shouldRemoveNotice(ParserNotice notice,
ParseResult res) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... ... shouldRemoveNotice " +
notice + ": " + (notice.getParser()==res.getParser()));
}
// NOTE: We must currently remove all notices for the parser. Parser
// implementors are required to parse the entire document each parsing
// request, as RSTA is not yet sophisticated enough to determine the
// minimum range of text to parse (and ParserNotices' locations aren't
// updated when the Document is mutated, which would be a requirement
// for this as well).
// return same_parser && (in_reparsed_range || in_deleted_end_of_doc)
return notice.getParser()==res.getParser();
}
/**
* Stops parsing the document.
*
* @see #restartParsing()
*/
public void stopParsing() {
timer.stop();
running = false;
}
/**
* Mapping of a parser notice to its highlight in the editor.
*/
private static class NoticeHighlightPair {
private ParserNotice notice;
private HighlightInfo highlight;
NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) {
this.notice = notice;
this.highlight = highlight;
}
}
static {
boolean debugParsing = false;
try {
debugParsing = Boolean.getBoolean(PROPERTY_DEBUG_PARSING);
} catch (AccessControlException ace) {
// Likely an applet's security manager.
debugParsing = false; // FindBugs
}
DEBUG_PARSING = debugParsing;
}
}
@@ -0,0 +1,68 @@
/*
* 01/11/2011
*
* PopupWindowDecorator.java - Hook allowing hosting applications to decorate
* JWindows created by the AutoComplete library.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.JWindow;
/**
* A hook allowing hosting applications to decorate JWindows created by the
* AutoComplete library. For example, you could use the
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows
* to the windows.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class PopupWindowDecorator {
/**
* The singleton instance of this class.
*/
private static PopupWindowDecorator decorator;
/**
* Callback called whenever an appropriate JWindow is created by the
* AutoComplete library. Implementations can decorate the window however
* they see fit.
*
* @param window The newly-created window.
*/
public abstract void decorate(JWindow window);
/**
* Returns the singleton instance of this class. This should only be
* called on the EDT.
*
* @return The singleton instance of this class, or <code>null</code>
* for none.
* @see #set(PopupWindowDecorator)
*/
public static PopupWindowDecorator get() {
return decorator;
}
/**
* Sets the singleton instance of this class. This should only be called
* on the EDT.
*
* @param decorator The new instance of this class. This may be
* <code>null</code>.
* @see #get()
*/
public static void set(PopupWindowDecorator decorator) {
PopupWindowDecorator.decorator = decorator;
}
}
@@ -0,0 +1,62 @@
/*
* 02/10/2009
*
* RSTAView.java - An <code>RSyntaxTextArea</code> view.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Rectangle;
import javax.swing.text.BadLocationException;
/**
* Utility methods for RSyntaxTextArea's views.
*
* @author Robert Futrell
* @version 1.0
*/
interface RSTAView {
/**
* Returns the y-coordinate of the specified line.<p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
*
* @param alloc The area the text area can render into.
* @param line The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>line</code> isn't a valid line
* number for this document.
*/
int yForLine(Rectangle alloc, int line) throws BadLocationException;
/**
* Returns the y-coordinate of the line containing a specified offset.<p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
*
* @param alloc The area the text area can render into.
* @param offs The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>offs</code> isn't a valid offset
* into the document.
*/
int yForLineContaining(Rectangle alloc, int offs)
throws BadLocationException;
}
@@ -0,0 +1,672 @@
/*
* 10/16/2004
*
* RSyntaxDocument.java - A document capable of syntax highlighting, used by
* RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Iterator;
import javax.swing.Action;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.modes.AbstractMarkupTokenMaker;
import org.fife.ui.rtextarea.RDocument;
import org.fife.util.DynamicIntArray;
import me.ByteEdit.main.JavaAssemblyTokenMaker;
/**
* The document used by {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea}.
* This document is like <code>javax.swing.text.PlainDocument</code> except that
* it also keeps track of syntax highlighting in the document. It has a "style"
* attribute associated with it that determines how syntax highlighting is done
* (i.e., what language is being highlighted).
* <p>
*
* Instances of <code>RSyntaxTextArea</code> will only accept instances of
* <code>RSyntaxDocument</code>, since it is this document that keeps track of
* syntax highlighting. All others will cause an exception to be thrown.
* <p>
*
* To change the language being syntax highlighted at any time, you merely have
* to call {@link #setSyntaxStyle}. Other than that, this document can be
* treated like any other save one caveat: all <code>DocumentEvent</code>s of
* type <code>CHANGE</code> use their offset and length values to represent the
* first and last lines, respectively, that have had their syntax coloring
* change. This is really a hack to increase the speed of the painting code and
* should really be corrected, but oh well.
*
* @author Robert Futrell
* @version 1.0
*/
public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants {
/**
* Creates a {@link TokenMaker} appropriate for a given programming language.
*/
private transient TokenMakerFactory tokenMakerFactory;
/**
* Splits text into tokens for the current programming language.
*/
private transient TokenMaker tokenMaker;
/**
* The current syntax style. Only cached to keep this class serializable.
*/
private String syntaxStyle;
/**
* Array of values representing the "last token type" on each line. This is used
* in cases such as multi-line comments: if the previous line ended with an
* (unclosed) multi-line comment, we can use this knowledge and start the
* current line's syntax highlighting in multi-line comment state.
*/
protected transient DynamicIntArray lastTokensOnLines;
private transient int lastLine = -1;
private transient Token cachedTokenList;
private transient int useCacheCount = 0;
private transient int tokenRetrievalCount = 0;
private transient Segment s;
/**
* If this is set to <code>true</code>, debug information about how much token
* caching is helping is printed to stdout.
*/
private static final boolean DEBUG_TOKEN_CACHING = false;
/**
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param syntaxStyle
* The syntax highlighting scheme to use.
*/
public RSyntaxDocument(String syntaxStyle) {
this(null, syntaxStyle);
}
/**
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this is
* <code>null</code>, a default factory is used.
* @param syntaxStyle
* The syntax highlighting scheme to use.
*/
public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
putProperty(tabSizeAttribute, Integer.valueOf(5));
lastTokensOnLines = new DynamicIntArray(400);
lastTokensOnLines.add(Token.NULL); // Initial (empty) line.
s = new Segment();
setTokenMakerFactory(tmf);
setSyntaxStyle(syntaxStyle);
}
/**
* Alerts all listeners to this document of an insertion. This is overridden so
* we can update our syntax highlighting stuff.
* <p>
* The syntax highlighting stuff has to be here instead of in
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called by
* the undo/redo actions, but this method is.
*
* @param e
* The change.
*/
@Override
protected void fireInsertUpdate(DocumentEvent e) {
cachedTokenList = null;
/*
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and "last tokens on lines"
* structure.
*/
Element lineMap = getDefaultRootElement();
DocumentEvent.ElementChange change = e.getChange(lineMap);
Element[] added = change == null ? null : change.getChildrenAdded();
int numLines = lineMap.getElementCount();
int line = lineMap.getElementIndex(e.getOffset());
int previousLine = line - 1;
int previousTokenType = (previousLine > -1 ? lastTokensOnLines.get(previousLine) : Token.NULL);
// If entire lines were added...
if (added != null && added.length > 0) {
Element[] removed = change.getChildrenRemoved();
int numRemoved = removed != null ? removed.length : 0;
int endBefore = line + added.length - numRemoved;
// System.err.println("... adding lines: " + line + " - " + (endBefore-1));
// System.err.println("... ... added: " + added.length + ", removed:" +
// numRemoved);
for (int i = line; i < endBefore; i++) {
setSharedSegment(i); // Loads line i's text into s.
int tokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
lastTokensOnLines.add(i, tokenType);
// System.err.println("--------- lastTokensOnLines.size() == " +
// lastTokensOnLines.getSize());
previousTokenType = tokenType;
} // End of for (int i=line; i<endBefore; i++).
// Update last tokens for lines below until they stop changing.
updateLastTokensBelow(endBefore, numLines, previousTokenType);
} // End of if (added!=null && added.length>0).
// Otherwise, text was inserted on a single line...
else {
// Update last tokens for lines below until they stop changing.
updateLastTokensBelow(line, numLines, previousTokenType);
} // End of else.
// Let all listeners know about the insertion.
super.fireInsertUpdate(e);
}
/**
* This method is called AFTER the content has been inserted into the document
* and the element structure has been updated.
* <p>
* The syntax-highlighting updates need to be done here (as opposed to an
* override of <code>postRemoveUpdate</code>) as this method is called in
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is not.
* <p>
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and "last tokens on lines"
* structure.
*
* @param chng
* The change that occurred.
* @see #removeUpdate
*/
@Override
protected void fireRemoveUpdate(DocumentEvent chng) {
cachedTokenList = null;
Element lineMap = getDefaultRootElement();
int numLines = lineMap.getElementCount();
DocumentEvent.ElementChange change = chng.getChange(lineMap);
Element[] removed = change == null ? null : change.getChildrenRemoved();
// If entire lines were removed...
if (removed != null && removed.length > 0) {
int line = change.getIndex(); // First line entirely removed.
int previousLine = line - 1; // Line before that.
int previousTokenType = (previousLine > -1 ? lastTokensOnLines.get(previousLine) : Token.NULL);
Element[] added = change.getChildrenAdded();
int numAdded = added == null ? 0 : added.length;
// Remove the cached last-token values for the removed lines.
int endBefore = line + removed.length - numAdded;
// System.err.println("... removing lines: " + line + " - " + (endBefore-1));
// System.err.println("... added: " + numAdded + ", removed: " +
// removed.length);
lastTokensOnLines.removeRange(line, endBefore); // Removing values for lines [line-(endBefore-1)].
// System.err.println("--------- lastTokensOnLines.size() == " +
// lastTokensOnLines.getSize());
// Update last tokens for lines below until they've stopped changing.
updateLastTokensBelow(line, numLines, previousTokenType);
} // End of if (removed!=null && removed.size()>0).
// Otherwise, text was removed from just one line...
else {
int line = lineMap.getElementIndex(chng.getOffset());
if (line >= lastTokensOnLines.getSize()) {
return; // If we're editing the last line in a document...
}
int previousLine = line - 1;
int previousTokenType = (previousLine > -1 ? lastTokensOnLines.get(previousLine) : Token.NULL);
// System.err.println("previousTokenType for line : " + previousLine + " is " +
// previousTokenType);
// Update last tokens for lines below until they've stopped changing.
updateLastTokensBelow(line, numLines, previousTokenType);
}
// Let all of our listeners know about the removal.
super.fireRemoveUpdate(chng);
}
/**
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
*
* @param type
* The token type.
* @return The closest "standard" token type. If a mapping is not defined for
* this language, then <code>type</code> is returned.
*/
public int getClosestStandardTokenTypeForInternalType(int type) {
return tokenMaker.getClosestStandardTokenTypeForInternalType(type);
}
/**
* Returns whether closing markup tags should be automatically completed. This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()} also
* returns <code>true</code>.
*
* @return Whether markup closing tags should be automatically completed.
* @see #getLanguageIsMarkup()
*/
public boolean getCompleteMarkupCloseTags() {
// TODO: Remove terrible dependency on AbstractMarkupTokenMaker
return getLanguageIsMarkup() && ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags();
}
/**
* Returns whether the current programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
*
* @param languageIndex
* The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested languages (such
* as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @return Whether curly braces denote code blocks.
*/
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex);
}
/**
* Returns whether the current language is a markup language, such as HTML, XML
* or PHP.
*
* @return Whether the current language is a markup language.
*/
public boolean getLanguageIsMarkup() {
return tokenMaker.isMarkupLanguage();
}
/**
* Returns the token type of the last token on the given line.
*
* @param line
* The line to inspect.
* @return The token type of the last token on the specified line. If the line
* is invalid, an exception is thrown.
*/
public int getLastTokenTypeOnLine(int line) {
return lastTokensOnLines.get(line);
}
/**
* Returns the text to place at the beginning and end of a line to "comment" it
* in this programming language.
*
* @return The start and end strings to add to a line to "comment" it out. A
* <code>null</code> value for either means there is no string to add
* for that part. A value of <code>null</code> for the array means this
* language does not support commenting/uncommenting lines.
*/
public String[] getLineCommentStartAndEnd(int languageIndex) {
return tokenMaker.getLineCommentStartAndEnd(languageIndex);
}
/**
* Returns whether tokens of the specified type should have "mark occurrences"
* enabled for the current programming language.
*
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences" enabled.
*/
boolean getMarkOccurrencesOfTokenType(int type) {
return tokenMaker.getMarkOccurrencesOfTokenType(type);
}
/**
* Returns the occurrence marker for the current language.
*
* @return The occurrence marker.
*/
OccurrenceMarker getOccurrenceMarker() {
return tokenMaker.getOccurrenceMarker();
}
/**
* This method returns whether auto indentation should be done if Enter is
* pressed at the end of the specified line.
*
* @param line
* The line to check.
* @return Whether an extra indentation should be done.
*/
public boolean getShouldIndentNextLine(int line) {
Token t = getTokenListForLine(line);
t = t.getLastNonCommentNonWhitespaceToken();
return tokenMaker.getShouldIndentNextLineAfter(t);
}
/**
* Returns the syntax style being used.
*
* @return The syntax style.
* @see #setSyntaxStyle(String)
*/
public String getSyntaxStyle() {
return syntaxStyle;
}
/**
* Returns a token list for the specified segment of text representing the
* specified line number. This method is basically a wrapper for
* <code>tokenMaker.getTokenList</code> that takes into account the last token
* on the previous line to assure token accuracy.
*
* @param line
* The line number of <code>text</code> in the document, &gt;= 0.
* @return A token list representing the specified line.
*/
public final Token getTokenListForLine(int line) {
tokenRetrievalCount++;
if (line == lastLine && cachedTokenList != null) {
if (DEBUG_TOKEN_CACHING) {
useCacheCount++;
System.err.println("--- Using cached line; ratio now: " + useCacheCount + "/" + tokenRetrievalCount);
}
return cachedTokenList;
}
lastLine = line;
Element map = getDefaultRootElement();
Element elem = map.getElement(line);
int startOffset = elem.getStartOffset();
// int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset() - 1:
// elem.getEndOffset() - 1);
int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
try {
getText(startOffset, endOffset - startOffset, s);
} catch (BadLocationException ble) {
ble.printStackTrace();
return null;
}
int initialTokenType = line == 0 ? Token.NULL : getLastTokenTypeOnLine(line - 1);
// return tokenMaker.getTokenList(s, initialTokenType, startOffset);
cachedTokenList = tokenMaker.getTokenList(s, initialTokenType, startOffset);
return cachedTokenList;
}
boolean insertBreakSpecialHandling(ActionEvent e) {
Action a = tokenMaker.getInsertBreakAction();
if (a != null) {
a.actionPerformed(e);
return true;
}
return false;
}
/**
* Returns whether a character could be part of an "identifier" token in a
* specific language. This is used to identify such things as the bounds of the
* "word" to select on double-clicking.
*
* @param languageIndex
* The language index the character was found in.
* @param ch
* The character.
* @return Whether the character could be part of an "identifier" token.
*/
public boolean isIdentifierChar(int languageIndex, char ch) {
return tokenMaker.isIdentifierChar(languageIndex, ch);
}
/**
* Returns an iterator over the paintable tokens in this document. Results are
* undefined if this document is modified while the iterator is being iterated
* through, so this should only be used on the EDT.
* <p>
*
* The <code>remove()</code> method of the returned iterator will throw an
* <code>UnsupportedOperationException</code>.
*
* @return An iterator.
*/
@Override
public Iterator<Token> iterator() {
return new TokenIterator(this);
}
/**
* Deserializes a document.
*
* @param in
* The stream to read from.
* @throws ClassNotFoundException
* @throws IOException
*/
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
// Install default TokenMakerFactory. To support custom TokenMakers,
// both JVM's should install default TokenMakerFactories that support
// the language they want to use beforehand.
setTokenMakerFactory(null);
// Handle other transient stuff
this.s = new Segment();
int lineCount = getDefaultRootElement().getElementCount();
lastTokensOnLines = new DynamicIntArray(lineCount);
setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker
}
/**
* Makes our private <code>Segment s</code> point to the text in our document
* referenced by the specified element. Note that <code>line</code> MUST be a
* valid line number in the document.
*
* @param line
* The line number you want to get.
*/
private void setSharedSegment(int line) {
Element map = getDefaultRootElement();
// int numLines = map.getElementCount();
Element element = map.getElement(line);
if (element == null) {
throw new InternalError("Invalid line number: " + line);
}
int startOffset = element.getStartOffset();
// int endOffset = (line==numLines-1 ?
// element.getEndOffset()-1 : element.getEndOffset() - 1);
int endOffset = element.getEndOffset() - 1; // Why always "-1"?
try {
getText(startOffset, endOffset - startOffset, s);
} catch (BadLocationException ble) {
throw new InternalError("Text range not in document: " + startOffset + "-" + endOffset);
}
}
/**
* Sets the syntax style being used for syntax highlighting in this document.
* What styles are supported by a document is determined by its
* {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s
* support all languages built into <code>RSyntaxTextArea</code>.
*
* @param styleKey
* The new style to use, such as
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is not
* known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @see #setSyntaxStyle(TokenMaker)
* @see #getSyntaxStyle()
*/
public void setSyntaxStyle(String styleKey) {
if (styleKey.equals("text/javaassembly")) {
tokenMaker = new JavaAssemblyTokenMaker();
} else {
tokenMaker = tokenMakerFactory.getTokenMaker(styleKey);
}
updateSyntaxHighlightingInformation();
this.syntaxStyle = styleKey;
}
/**
* Sets the syntax style being used for syntax highlighting in this document.
* You should call this method if you've created a custom token maker for a
* language not normally supported by <code>RSyntaxTextArea</code>.
*
* @param tokenMaker
* The new token maker to use.
* @see #setSyntaxStyle(String)
*/
public void setSyntaxStyle(TokenMaker tokenMaker) {
this.tokenMaker = tokenMaker;
updateSyntaxHighlightingInformation();
this.syntaxStyle = "text/unknown"; // TODO: Make me public?
}
/**
* Sets the token maker factory used by this document.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this is
* <code>null</code>, a default factory is used.
*/
public void setTokenMakerFactory(TokenMakerFactory tmf) {
tokenMakerFactory = tmf != null ? tmf : TokenMakerFactory.getDefaultInstance();
}
/**
* Loops through the last-tokens-on-lines array from a specified point onward,
* updating last-token values until they stop changing. This should be called
* when lines are updated/inserted/removed, as doing so may cause lines below to
* change color.
*
* @param line
* The first line to check for a change in last-token value.
* @param numLines
* The number of lines in the document.
* @param previousTokenType
* The last-token value of the line just before <code>line</code>.
* @return The last line that needs repainting.
*/
private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
int firstLine = line;
// Loop through all lines past our starting point. Update even the last
// line's info, even though there aren't any lines after it that depend
// on it changing for them to be changed, as its state may be used
// elsewhere in the library.
int end = numLines;
// System.err.println("--- end==" + end + " (numLines==" + numLines + ")");
while (line < end) {
setSharedSegment(line); // Sets s's text to that of line 'line' in the document.
int oldTokenType = lastTokensOnLines.get(line);
int newTokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
// System.err.println("---------------- line " + line + "; oldTokenType==" +
// oldTokenType + ", newTokenType==" + newTokenType + ", s=='" + s + "'");
// If this line's end-token value didn't change, stop here. Note
// that we're saying this line needs repainting; this is because
// the beginning of this line did indeed change color, but the
// end didn't.
if (oldTokenType == newTokenType) {
// System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
fireChangedUpdate(new DefaultDocumentEvent(firstLine, line, DocumentEvent.EventType.CHANGE));
return line;
}
// If the line's end-token value did change, update it and
// keep going.
// NOTE: "setUnsafe" is okay here as the bounds checking was
// already done in lastTokensOnLines.get(line) above.
lastTokensOnLines.setUnsafe(line, newTokenType);
previousTokenType = newTokenType;
line++;
} // End of while (line<numLines).
// If any lines had their token types changed, fire a changed update
// for them. The view will repaint the area covered by the lines.
// FIXME: We currently cheat and send the line range that needs to be
// repainted as the "offset and length" of the change, since this is
// what the view needs. We really should send the actual offset and
// length.
if (line > firstLine) {
// System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
fireChangedUpdate(new DefaultDocumentEvent(firstLine, line, DocumentEvent.EventType.CHANGE));
}
return line;
}
/**
* Updates internal state information; e.g. the "last tokens on lines" data.
* After this, a changed update is fired to let listeners know that the
* document's structure has changed.
* <p>
*
* This is called internally whenever the syntax style changes.
*/
private void updateSyntaxHighlightingInformation() {
// Reinitialize the "last token on each line" array. Note that since
// the actual text in the document isn't changing, the number of lines
// is the same.
Element map = getDefaultRootElement();
int numLines = map.getElementCount();
int lastTokenType = Token.NULL;
for (int i = 0; i < numLines; i++) {
setSharedSegment(i);
lastTokenType = tokenMaker.getLastTokenTypeOnLine(s, lastTokenType);
lastTokensOnLines.set(i, lastTokenType);
}
// Clear our token cache to force re-painting
lastLine = -1;
cachedTokenList = null;
// Let everybody know that syntax styles have (probably) changed.
fireChangedUpdate(new DefaultDocumentEvent(0, numLines - 1, DocumentEvent.EventType.CHANGE));
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,14 @@
ContextMenu.Folding=Folding
Action.ToggleCurrentFold.Name=Toggle Current Fold
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
Action.CollapseAllFolds.Name=Collapse All Folds
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Collapses all folds.
Action.CollapseCommentFolds.Name=Collapse All Comments
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
Action.ExpandAllFolds.Name=Expand All Folds
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Expands all folds.
@@ -0,0 +1,79 @@
/*
* 10/27/2004
*
* RSyntaxTextAreaDefaultInputMap.java - The default input map for
* RSyntaxTextAreas.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
import org.fife.ui.rtextarea.RTADefaultInputMap;
/**
* The default input map for an <code>RSyntaxTextArea</code>.
* Currently, the new key bindings include:
* <ul>
* <li>Shift+Tab indents the current line or currently selected lines
* to the left.
* </ul>
*
* @author Robert Futrell
* @version 1.0
*/
@SuppressWarnings({ "checkstyle:linelength" })
public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
/**
* Constructs the default input map for an <code>RSyntaxTextArea</code>.
*/
public RSyntaxTextAreaDefaultInputMap() {
int defaultMod = getDefaultModifier();
//int ctrl = InputEvent.CTRL_MASK;
int shift = InputEvent.SHIFT_MASK;
//int alt = InputEvent.ALT_MASK;
int defaultShift = defaultMod|shift;
put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
put(KeyStroke.getKeyStroke('}'), RSyntaxTextAreaEditorKit.rstaCloseCurlyBraceAction);
put(KeyStroke.getKeyStroke('/'), RSyntaxTextAreaEditorKit.rstaCloseMarkupTagAction);
int os = RSyntaxUtilities.getOS();
if (os==RSyntaxUtilities.OS_WINDOWS || os==RSyntaxUtilities.OS_MAC_OSX) {
// *nix causes trouble with CloseMarkupTagAction and ToggleCommentAction.
// It triggers both KEY_PRESSED ctrl+'/' and KEY_TYPED '/' events when the
// user presses ctrl+'/', but Windows and OS X do not. If we try to "move"
// the KEY_TYPED event for '/' to KEY_PRESSED, it'll work for Linux boxes
// with QWERTY keyboard layouts, but non-QUERTY users won't be able to type
// a '/' character at all then (!). Rather than try to hack together a
// solution by trying to detect the IM locale and do different things for
// different OSes & keyboard layouts, we do the simplest thing and
// (unfortunately) don't have a ToggleCommentAction for *nix out-of-the-box.
// Applications can add one easily enough if they want one.
put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
}
put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod), RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction);
// NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
// second parameter, we'd get the template action (keyPressed) AND the
// default space action (keyTyped).
//put(KeyStroke.getKeyStroke(' '), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,291 @@
/*
* 04/23/2009
*
* RSyntaxTextAreaHighlighter.java - Highlighter for RSyntaxTextAreas.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rtextarea.RTextAreaHighlighter;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to
* always paint "marked occurrences" highlights below selection highlights,
* and squiggle underline highlights above all other highlights.<p>
*
* Most of this code is copied from javax.swing.text.DefaultHighlighter;
* unfortunately, we cannot re-use much of it since it is package private.
*
* @author Robert Futrell
* @version 1.0
*/
public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
/**
* Marked occurrences in the document (to be painted separately from
* other highlights).
*/
private List<SyntaxLayeredHighlightInfoImpl> markedOccurrences;
/**
* Highlights from document parsers. These should be painted "on top of"
* all other highlights to ensure they are always above the selection.
*/
private List<SyntaxLayeredHighlightInfoImpl> parserHighlights;
/**
* The default color used for parser notices when none is specified.
*/
private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
/**
* Constructor.
*/
public RSyntaxTextAreaHighlighter() {
markedOccurrences = new ArrayList<SyntaxLayeredHighlightInfoImpl>();
parserHighlights = new ArrayList<SyntaxLayeredHighlightInfoImpl>(0); // Often unused
}
/**
* Adds a special "marked occurrence" highlight.
*
* @param start
* @param end
* @param p
* @return A tag to reference the highlight later.
* @throws BadLocationException
* @see #clearMarkOccurrencesHighlights()
*/
Object addMarkedOccurrenceHighlight(int start, int end,
SmartHighlightPainter p) throws BadLocationException {
Document doc = textArea.getDocument();
TextUI mapper = textArea.getUI();
// Always layered highlights for marked occurrences.
SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl();
i.setPainter(p);
i.setStartOffset(doc.createPosition(start));
// HACK: Use "end-1" to prevent chars the user types at the "end" of
// the highlight to be absorbed into the highlight (default Highlight
// behavior).
i.setEndOffset(doc.createPosition(end-1));
markedOccurrences.add(i);
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Adds a highlight from a parser.
*
* @param notice The notice from a {@link Parser}.
* @return A tag with which to reference the highlight.
* @throws BadLocationException
* @see #clearParserHighlights()
* @see #clearParserHighlights(Parser)
*/
HighlightInfo addParserHighlight(ParserNotice notice, HighlightPainter p)
throws BadLocationException {
Document doc = textArea.getDocument();
TextUI mapper = textArea.getUI();
int start = notice.getOffset();
int end = 0;
if (start==-1) { // Could just define an invalid line number
int line = notice.getLine();
Element root = doc.getDefaultRootElement();
if (line>=0 && line<root.getElementCount()) {
Element elem = root.getElement(line);
start = elem.getStartOffset();
end = elem.getEndOffset();
}
}
else {
end = start + notice.getLength();
}
// Always layered highlights for parser highlights.
SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl();
i.setPainter(p);
i.setStartOffset(doc.createPosition(start));
// HACK: Use "end-1" to prevent chars the user types at the "end" of
// the highlight to be absorbed into the highlight (default Highlight
// behavior).
i.setEndOffset(doc.createPosition(end-1));
i.notice = notice;//i.color = notice.getColor();
parserHighlights.add(i);
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Removes all "marked occurrences" highlights from the view.
*
* @see #addMarkedOccurrenceHighlight(int, int, SmartHighlightPainter)
*/
void clearMarkOccurrencesHighlights() {
// Don't remove via an iterator; since our List is an ArrayList, this
// implies tons of System.arrayCopy()s
for (HighlightInfo info : markedOccurrences) {
repaintListHighlight(info);
}
markedOccurrences.clear();
}
/**
* Removes all parser highlights.
*
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
*/
void clearParserHighlights() {
// Don't remove via an iterator; since our List is an ArrayList, this
// implies tons of System.arrayCopy()s
for (int i=0; i<parserHighlights.size(); i++) {
repaintListHighlight(parserHighlights.get(i));
}
parserHighlights.clear();
}
/**
* Removes all of the highlights for a specific parser.
*
* @param parser The parser.
*/
public void clearParserHighlights(Parser parser) {
Iterator<SyntaxLayeredHighlightInfoImpl> i = parserHighlights.iterator();
while (i.hasNext()) {
SyntaxLayeredHighlightInfoImpl info = i.next();
if (info.notice.getParser()==parser) {
if (info.width > 0 && info.height > 0) {
textArea.repaint(info.x, info.y, info.width, info.height);
}
i.remove();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void deinstall(JTextComponent c) {
super.deinstall(c);
markedOccurrences.clear();
parserHighlights.clear();
}
/**
* Returns a list of "marked occurrences" in the text area. If there are
* no marked occurrences, this will be an empty list.
*
* @return The list of marked occurrences, or an empty list if none. The
* contents of this list will be of type {@link DocumentRange}.
*/
public List<DocumentRange> getMarkedOccurrences() {
List<DocumentRange> list = new ArrayList<DocumentRange>(markedOccurrences.size());
for (HighlightInfo info : markedOccurrences) {
int start = info.getStartOffset();
int end = info.getEndOffset() + 1; // HACK
if (start <= end) {
// Occasionally a Marked Occurrence can have a lost end offset
// but not start offset (replacing entire text content with
// new content, and a marked occurrence is on the last token
// in the document).
DocumentRange range = new DocumentRange(start, end);
list.add(range);
}
}
return list;
}
@Override
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd,
Shape viewBounds, JTextComponent editor, View view) {
paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, markedOccurrences);
super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, parserHighlights);
}
/**
* Removes a parser highlight from this view.
*
* @param tag The reference to the highlight.
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
*/
void removeParserHighlight(HighlightInfo tag) {
repaintListHighlight(tag);
parserHighlights.remove(tag);
}
/**
* Highlight info implementation used for parser notices and marked
* occurrences.
*/
private static class SyntaxLayeredHighlightInfoImpl extends
LayeredHighlightInfoImpl {
private ParserNotice notice;//Color color; // Used only by Parser highlights.
@Override
public Color getColor() {
//return color;
Color color = null;
if (notice!=null) {
color = notice.getColor();
if (color==null) {
color = DEFAULT_PARSER_NOTICE_COLOR;
}
}
return color;
}
@Override
public String toString() {
return "[SyntaxLayeredHighlightInfoImpl: " +
"startOffs=" + getStartOffset() +
", endOffs=" + getEndOffset() +
", color=" + getColor() +
"]";
}
}
}
@@ -0,0 +1,269 @@
/*
* 02/24/2004
*
* RSyntaxTextAreaUI.java - UI for an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.InputMapUIResource;
import javax.swing.text.BadLocationException;
import javax.swing.text.EditorKit;
import javax.swing.text.Element;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaUI;
/**
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement
* syntax highlighting.
*
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxTextAreaUI extends RTextAreaUI {
private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
private static final EditorKit DEFAULT_KIT = new RSyntaxTextAreaEditorKit();
public static ComponentUI createUI(JComponent ta) {
return new RSyntaxTextAreaUI(ta);
}
/**
* Constructor.
*/
public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
super(rSyntaxTextArea);
}
/**
* Creates the view for an element.
*
* @param elem The element.
* @return The view.
*/
@Override
public View create(Element elem) {
RTextArea c = getRTextArea();
if (c instanceof RSyntaxTextArea) {
RSyntaxTextArea area = (RSyntaxTextArea) c;
View v;
if (area.getLineWrap()) {
v = new WrappedSyntaxView(elem);
}
else {
v = new SyntaxView(elem);
}
return v;
}
return null;
}
/**
* Creates the highlighter to use for syntax text areas.
*
* @return The highlighter.
*/
@Override
protected Highlighter createHighlighter() {
return new RSyntaxTextAreaHighlighter();
}
/**
* Returns the name to use to cache/fetch the shared action map. This
* should be overridden by subclasses if the subclass has its own custom
* editor kit to install, so its actions get picked up.
*
* @return The name of the cached action map.
*/
@Override
protected String getActionMapName() {
return SHARED_ACTION_MAP_NAME;
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc The text component for which this UI is installed.
* @return The editor capabilities.
* @see javax.swing.plaf.TextUI#getEditorKit
*/
@Override
public EditorKit getEditorKit(JTextComponent tc) {
return DEFAULT_KIT;
}
/**
* Get the InputMap to use for the UI.<p>
*
* This method is not named <code>getInputMap()</code> because there is
* a package-private method in <code>BasicTextAreaUI</code> with that name.
* Thus, creating a new method with that name causes certain compilers to
* issue warnings that you are not actually overriding the original method
* (since it is package-private).
*/
@Override
protected InputMap getRTextAreaInputMap() {
InputMap map = new InputMapUIResource();
InputMap shared = (InputMap)UIManager.get(SHARED_INPUT_MAP_NAME);
if (shared==null) {
shared = new RSyntaxTextAreaDefaultInputMap();
UIManager.put(SHARED_INPUT_MAP_NAME, shared);
}
//KeyStroke[] keys = shared.allKeys();
//for (int i=0; i<keys.length; i++)
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
map.setParent(shared);
return map;
}
@Override
protected void paintEditorAugmentations(Graphics g) {
super.paintEditorAugmentations(g);
paintMatchedBracket(g);
}
/**
* Paints the "matched bracket", if any.
*
* @param g The graphics context.
*/
protected void paintMatchedBracket(Graphics g) {
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
if (rsta.isBracketMatchingEnabled()) {
Rectangle match = rsta.getMatchRectangle();
if (match!=null) {
paintMatchedBracketImpl(g, rsta, match);
}
if (rsta.getPaintMatchedBracketPair()) {
Rectangle dotRect = rsta.getDotRectangle();
if (dotRect!=null) { // should always be true
paintMatchedBracketImpl(g, rsta, dotRect);
}
}
}
}
protected void paintMatchedBracketImpl(Graphics g, RSyntaxTextArea rsta,
Rectangle r) {
// We must add "-1" to the height because otherwise we'll paint below
// the region that gets invalidated.
if (rsta.getAnimateBracketMatching()) {
Color bg = rsta.getMatchedBracketBGColor();
final int arcWH = 5;
if (bg!=null) {
g.setColor(bg);
g.fillRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
}
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
}
else {
Color bg = rsta.getMatchedBracketBGColor();
if (bg!=null) {
g.setColor(bg);
g.fillRect(r.x,r.y, r.width,r.height-1);
}
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRect(r.x,r.y, r.width,r.height-1);
}
}
/**
* Gets called whenever a bound property is changed on this UI's
* <code>RSyntaxTextArea</code>.
*
* @param e The property change event.
*/
@Override
protected void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
// If they change the syntax scheme, we must do this so that
// WrappedSyntaxView(_TEST) updates its child views properly.
if (name.equals(RSyntaxTextArea.SYNTAX_SCHEME_PROPERTY)) {
modelChanged();
}
// Everything else is general to all RTextAreas.
else {
super.propertyChange(e);
}
}
/**
* Updates the view. This should be called when the underlying
* <code>RSyntaxTextArea</code> changes its syntax editing style.
*/
public void refreshSyntaxHighlighting() {
modelChanged();
}
/**
* Returns the y-coordinate of the specified line.<p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
*/
@Override
public int yForLine(int line) throws BadLocationException {
Rectangle alloc = getVisibleEditorRect();
if (alloc!=null) {
RSTAView view = (RSTAView)getRootView(textArea).getView(0);
return view.yForLine(alloc, line);
}
return -1;
}
/**
* Returns the y-coordinate of the line containing a specified offset.<p>
*
* This is faster than calling <code>modelToView(offs).y</code>, so it is
* preferred if you do not need the actual bounding box.
*/
@Override
public int yForLineContaining(int offs) throws BadLocationException {
Rectangle alloc = getVisibleEditorRect();
if (alloc!=null) {
RSTAView view = (RSTAView)getRootView(textArea).getView(0);
return view.yForLineContaining(alloc, offs);
}
return -1;
}
}
@@ -0,0 +1,14 @@
ContextMenu.Folding=\u0637\u0648\u064a
Action.ToggleCurrentFold.Name=\u063a\u064a\u0651\u0631 \u0648\u0636\u0639\u064a\u0629 \u0627\u0644\u0637\u0648\u064a\u0629 \u0627\u0644\u062d\u0627\u0644\u064a\u0629
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u063a\u064a\u064a\u0631 \u0648\u0636\u0639\u064a\u0629 \u0627\u0644\u0637\u0648\u064a\u0629 \u0641\u064a \u0645\u0648\u0642\u0639 \u0627\u0644\u0645\u0624\u0634\u0631 \u0627\u0644\u062d\u0627\u0644\u064a
Action.CollapseAllFolds.Name=\u0642\u0644\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0642\u0644\u064a\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
Action.CollapseCommentFolds.Name=\u0642\u0644\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u062a\u0639\u0644\u064a\u0642\u0627\u062a
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0642\u0644\u064a\u0635 \u062c\u0645\u064a\u0639 \u0637\u064a\u0627\u062a \u0627\u0644\u062a\u0639\u0644\u064a\u0642\u0627\u062a
Action.ExpandAllFolds.Name=\u0648\u0633\u0639 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0648\u0633\u064a\u0639 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a.
@@ -0,0 +1,14 @@
ContextMenu.Folding=Klappung
Action.ToggleCurrentFold.Name=Aktuellen Block umschalten
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Schaltet den Block an der Position der Schreibmarke um.
Action.CollapseAllFolds.Name=Action.CollapseAllFolds.Name=Alle Bl\u00f6cke zuklappen
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Klappt alle Bl\u00f6cke zu.
Action.CollapseCommentFolds.Name=Alle Kommentarbl\u00f6cke zuklappen
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Klappt alle Kommentarbl\u00f6cke zu.
Action.ExpandAllFolds.Name=Alle Bl\u00f6cke aufklappen
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Klappt alle Bl\u00f6cke auf.
@@ -0,0 +1,14 @@
ContextMenu.Folding=Folding
Action.ToggleCurrentFold.Name=Ver Fold (solapa) actual
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Mostrar el fold en la posici\u00f3n del cursor
Action.CollapseAllFolds.Name=Ocultar todos los folds
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Oculta todos los folds
Action.CollapseCommentFolds.Name=Ocultar todos los comentarios
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Oculta todos los comentarios
Action.ExpandAllFolds.Name=Expandir todos las solapas (folds)
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Expande todas las solapas (folds)
@@ -0,0 +1,14 @@
ContextMenu.Folding=Pliage
Action.ToggleCurrentFold.Name=Active ou d\u00e9sactive le pli actuel
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Active ou d\u00e9sactive le pli \u00e0 la position du curseur.
Action.CollapseAllFolds.Name=Comprimer tous les plis
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Comprime tous les plis.
Action.CollapseCommentFolds.Name=Comprimer tous les commentaires
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Comprime tous les plis commentaires.
Action.ExpandAllFolds.Name=D\u00e9comprimer tous les plis
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=D\u00e9comprime tous les plis.
@@ -0,0 +1,14 @@
ContextMenu.Folding=\u00d6sszevon\u00e1s
Action.ToggleCurrentFold.Name=A jelenlegi \u00f6sszevon\u00e1s megjelen\u00edt\u00e9se/rejt\u00e9se.
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Megjelen\u00edti, illetve elt\u00fcnteti az \u00f6sszevon\u00e1st a kurzor poz\u00edci\u00f3j\u00e1ban.
Action.CollapseAllFolds.Name=\u00d6sszevon\u00e1sok rejt\u00e9se.
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=\u00d6sszevon\u00e1sok rejt\u00e9se.
Action.CollapseCommentFolds.Name=Az \u00f6sszes megjegyz\u00e9s elrejt\u00e9se
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Megjegyz\u00e9s \u00f6sszevon\u00e1sok rejt\u00e9se.
Action.ExpandAllFolds.Name=\u00d6sszevon\u00e1sok kiterjeszt\u00e9se
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=\u00d6sszevon\u00e1sok kiterjeszt\u00e9se.
@@ -0,0 +1,14 @@
ContextMenu.Folding=Folding
Action.ToggleCurrentFold.Name=Toggle Current Fold
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
Action.CollapseAllFolds.Name=Collapse All Folds
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Collapses all folds.
Action.CollapseCommentFolds.Name=Collapse All Comments
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
Action.ExpandAllFolds.Name=Expand All Folds
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Expands all folds.
@@ -0,0 +1,14 @@
ContextMenu.Folding=Folding
Action.ToggleCurrentFold.Name=Imposta il fold corrente
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Imposta il fold alla posizione corrente.
Action.CollapseAllFolds.Name=Raggruppa tutti i fold
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Raggruppa tutti i fold.
Action.CollapseCommentFolds.Name=Raggruppa tutti i commenti
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Raggruppa tutti i commenti.
Action.ExpandAllFolds.Name=Espandi tutti i fold
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Espandi tutti i fold.
@@ -0,0 +1,14 @@
ContextMenu.Folding=\u30d5\u30a9\u30eb\u30c7\u30a3\u30f3\u30b0
Action.ToggleCurrentFold.Name=\u30ad\u30e3\u30ec\u30c3\u30c8\u5074\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u30c8\u30b0\u30eb\u3059\u308b\u3002
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=\u30ad\u30e3\u30ec\u30c3\u30c8\u5074\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u30c8\u30b0\u30eb\u3059\u308b\u3002
Action.CollapseAllFolds.Name=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=\u5168\u3066\u306e\u3075\u3049\u308b\u3069\u3092\u6298\u308a\u305f\u305f\u3080
Action.CollapseCommentFolds.Name=\u5168\u3066\u306e\u30b3\u30e1\u30f3\u30c8\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=\u5168\u3066\u306e\u30b3\u30e1\u30f3\u30c8\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
Action.ExpandAllFolds.Name=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u5c55\u958b\u3059\u308b
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u5c55\u958b\u3059\u308b
@@ -0,0 +1,14 @@
ContextMenu.Folding=\ucf54\ub4dc \uac10\ucd94\uae30
Action.ToggleCurrentFold.Name=\ud604\uc7ac \ucf54\ub4dc \uac10\ucd94\uae30 \ud1a0\uae00
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=\uce90\ub7ff \uc704\uce58\uc5d0\uc11c \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \ud1a0\uae00\ud569\ub2c8\ub2e4.
Action.CollapseAllFolds.Name=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30 \uc811\uae30
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \uc811\uc2b5\ub2c8\ub2e4.
Action.CollapseCommentFolds.Name=\ubaa8\ub4e0 \uc8fc\uc11d \uac10\ucd94\uae30 \uc811\uae30
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=\ubaa8\ub4e0 \uc8fc\uc11d \uac10\ucd94\uae30\ub97c \uc811\uc2b5\ub2c8\ub2e4.
Action.ExpandAllFolds.Name=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30 \ud3bc\uce58\uae30
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \ud3bc\uce69\ub2c8\ub2e4.
@@ -0,0 +1,14 @@
ContextMenu.Folding=Folding
Action.ToggleCurrentFold.Name=Toggle Current Fold
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
Action.CollapseAllFolds.Name=Collapse All Folds
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Collapses all folds.
Action.CollapseCommentFolds.Name=Collapse All Comments
Action.CollapseCommentFolds.Mnemonic=C
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
Action.ExpandAllFolds.Name=Expand All Folds
Action.ExpandAllFolds.Mnemonic=E
Action.ExpandAllFolds.Desc=Expands all folds.
Loaded 100 of 352 files, more files were not shown because too many files have changed in this diff. Show more