[autocomplete] 02/143: Initial population of AutoComplete and AutoCompleteDemo.

Benjamin Mesing ben at alioth.debian.org
Sat Oct 19 12:53:06 UTC 2013


This is an automated email from the git hooks/post-receive script.

ben pushed a commit to branch master
in repository autocomplete.

commit 85879faef43afdae839eeea0580108503989cb9a
Author: bobbylight <robert at fifesoft.com>
Date:   Wed Dec 24 20:11:47 2008 +0000

    Initial population of AutoComplete and AutoCompleteDemo.
---
 .classpath                                         |    8 +
 .project                                           |   17 +
 build.xml                                          |   94 +++
 .../autocomplete/AutoCompleteDescWindow.properties |    1 +
 img/localVar.png                                   |  Bin 0 -> 1026 bytes
 img/method.png                                     |  Bin 0 -> 1024 bytes
 .../fife/ui/autocomplete/AbstractCompletion.java   |  111 ++++
 .../autocomplete/AbstractCompletionProvider.java   |  291 +++++++++
 .../ui/autocomplete/AutoCompleteDescWindow.java    |  450 ++++++++++++++
 .../ui/autocomplete/AutoCompletePopupWindow.java   |  617 ++++++++++++++++++++
 src/org/fife/ui/autocomplete/AutoCompletion.java   |  574 ++++++++++++++++++
 src/org/fife/ui/autocomplete/Completion.java       |   95 +++
 .../fife/ui/autocomplete/CompletionListModel.java  |   99 ++++
 .../fife/ui/autocomplete/CompletionProvider.java   |  104 ++++
 .../ui/autocomplete/DelegatingCellRenderer.java    |   60 ++
 .../fife/ui/autocomplete/ExternalURLHandler.java   |   51 ++
 .../fife/ui/autocomplete/FunctionCompletion.java   |  213 +++++++
 .../ProceduralLanguageCellRenderer.java            |  193 ++++++
 .../ProceduralLanguageCompletionProvider.java      |  347 +++++++++++
 .../fife/ui/autocomplete/ShorthandCompletion.java  |   81 +++
 src/org/fife/ui/autocomplete/SizeGrip.java         |  193 ++++++
 src/org/fife/ui/autocomplete/Util.java             |  153 +++++
 .../fife/ui/autocomplete/VariableCompletion.java   |  193 ++++++
 src/org/fife/ui/autocomplete/WordCompletion.java   |   73 +++
 .../ui/autocomplete/WordCompletionProvider.java    |  130 +++++
 src/org/fife/ui/autocomplete/arrow_left.png        |  Bin 0 -> 345 bytes
 src/org/fife/ui/autocomplete/arrow_right.png       |  Bin 0 -> 349 bytes
 27 files changed, 4148 insertions(+)

diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..fe5be44
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="i18n"/>
+	<classpathentry kind="src" path="img"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..f69cc1c
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>AutoComplete</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..211d6b9
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+
+<!-- 
+
+	This is the Ant build script for autocomplete.jar.
+	Available targets include:
+	
+		1. compile:         Compiles all org.fife classes into ${class-dir}.
+		2. make-jar:        Creates the jar file.
+		3. make-source-zip: Creates a source zip file.
+		3. make-javadoc:    Creates the javadoc for RSyntaxTextArea.
+
+	Author:   Robert Futrell
+	Version:  0.1
+	Date:     12dec2008
+
+-->
+
+
+<project name="AutoComplete" default="make-jar" basedir=".">
+
+	<description>AutoComplete build file</description>
+
+
+	<!-- Set global properties for this build. -->
+	<property name="version"          value="0.1"/>
+	<property name="source-dir"       location="src"/>
+	<property name="class-dir"        location="ant-classes"/>
+	<property name="dist-dir"         location="dist"/>
+	<property name="doc-dir"          location="javadoc"/>
+	<property name="debug"            value="true"/>
+	<property name="debuglevel"       value="lines,var,source"/>
+	<property name="java-level"       value="1.4"/>
+
+
+	<!-- Compiles the classes.  -->
+	<target name="compile" description="Compile the source">
+		<delete includeEmptyDirs="true" quiet="true" dir="${class-dir}"/>
+		<mkdir dir="${class-dir}"/>
+		<javac srcdir="${source-dir}" destdir="${class-dir}"
+			classpath="../RSyntaxTextArea/dist/rsyntaxtextarea.jar"
+			deprecation="yes" debug="${debug}" debuglevel="${debuglevel}"
+			source="${java-level}" target="${java-level}"/>
+	</target>
+
+
+	<!-- Creates the jar file. -->
+	<target name="make-jar" depends="compile" description="Create the jar file">
+		<delete includeEmptyDirs="true" quiet="true" dir="${dist-dir}"/>
+		<mkdir dir="${dist-dir}"/>
+		<jar destfile="${dist-dir}/autocomplete.jar">
+			<fileset dir="${class-dir}"/>
+			<fileset dir="${source-dir}">
+				<include name="**/*.png"/>
+			</fileset>
+			<fileset dir="i18n"/>
+			<manifest>
+				<attribute name="Class-Path" value="rsyntaxtextarea.jar"/>
+				<attribute name="Specification-Title" value="AutoComplete"/>
+				<attribute name="Specification-Version" value="${version}"/>
+				<attribute name="Implementation-Title" value="org.fife.ui"/>
+				<attribute name="Implementation-Version" value="${version}"/>
+			</manifest>
+		</jar>
+		<copy todir="${dist-dir}" file="../RSyntaxTextArea/dist/rsyntaxtextarea.jar"/>
+	</target>
+
+
+	<!-- Builds the source zip file. -->
+	<target name="make-source-zip" description="Builds the source zip file">
+		<zip destfile="autocomplete_${version}_Source.zip">
+			<fileset dir=".">
+				<include name="src/**"/>
+				<include name="distfiles/**"/>
+				<include name="i18n/**"/>
+				<include name="lang/**"/>
+				<include name="build.xml"/>
+			</fileset>
+		</zip>
+	</target>
+
+
+	<!-- Builds the javadoc.  -->
+	<target name="make-javadoc" depends="compile">
+		<javadoc destdir="${doc-dir}" author="true" version="true"
+			 	breakiterator="yes">
+			<packageset dir="${source-dir}" defaultexcludes="yes">
+				<include name="org/**"/>
+			</packageset>
+		</javadoc>
+	</target>
+
+
+</project>
diff --git a/i18n/org/fife/ui/autocomplete/AutoCompleteDescWindow.properties b/i18n/org/fife/ui/autocomplete/AutoCompleteDescWindow.properties
new file mode 100644
index 0000000..cd199b3
--- /dev/null
+++ b/i18n/org/fife/ui/autocomplete/AutoCompleteDescWindow.properties
@@ -0,0 +1 @@
+NoDescAvailable=No description available
diff --git a/img/localVar.png b/img/localVar.png
new file mode 100644
index 0000000..a8e1cf1
Binary files /dev/null and b/img/localVar.png differ
diff --git a/img/method.png b/img/method.png
new file mode 100644
index 0000000..3308b72
Binary files /dev/null and b/img/method.png differ
diff --git a/src/org/fife/ui/autocomplete/AbstractCompletion.java b/src/org/fife/ui/autocomplete/AbstractCompletion.java
new file mode 100644
index 0000000..bc0dbd9
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/AbstractCompletion.java
@@ -0,0 +1,111 @@
+/*
+ * 12/21/2008
+ *
+ * AbstractCompletion.java - Base class for possible completions.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import javax.swing.text.JTextComponent;
+
+
+/**
+ * Base class for possible completions.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public abstract class AbstractCompletion implements Completion, Comparable {
+
+	/**
+	 * The provider that created this completion;
+	 */
+	private CompletionProvider provider;
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param provider The provider that created this completion.
+	 */
+	public AbstractCompletion(CompletionProvider provider) {
+		this.provider = provider;
+	}
+
+
+	/**
+	 * Compares this completion to another one lexicograhically, ignoring
+	 * case.
+	 *
+	 * @param o Another completion instance.
+	 * @return How this completion compares to the other one.
+	 */
+	public int compareTo(Object o) {
+		if (o==this) {
+			return 0;
+		}
+		else if (o instanceof Completion) {
+			Completion c2 = (Completion)o;
+			return toString().compareToIgnoreCase(c2.toString());
+		}
+		return -1;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getAlreadyEntered(JTextComponent comp) {
+		return provider.getAlreadyEnteredText(comp);
+	}
+
+
+	/**
+	 * 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()
+	 */
+	public String getInputText() {
+		return getReplacementText();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public CompletionProvider getProvider() {
+		return provider;
+	}
+
+
+	/**
+	 * Returns a string representation of this completion.  The default
+	 * implementation returns {@link #getInputText()}.
+	 *
+	 * @return A string representation of this completion.
+	 */
+	public String toString() {
+		return getInputText();
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/AbstractCompletionProvider.java b/src/org/fife/ui/autocomplete/AbstractCompletionProvider.java
new file mode 100644
index 0000000..e6bf206
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/AbstractCompletionProvider.java
@@ -0,0 +1,291 @@
+/*
+ * 12/21/2008
+ *
+ * AbstractCompletionProvider.java - Base class for completion providers.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+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.ListCellRenderer;
+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 implements CompletionProvider {
+
+	/**
+	 * The parent completion provider.
+	 */
+	private CompletionProvider parent;
+
+	/**
+	 * The completions this provider is aware of.  Subclasses should ensure
+	 * that this list is sorted alphabetically (case-insensitively).
+	 */
+	protected List completions;
+
+	/**
+	 * The renderer to use for completions from this provider.  If this is
+	 * <code>null</code>, a default renderer is used.
+	 */
+	private ListCellRenderer listCellRenderer;
+
+	/**
+	 * The case-insensitive {@link Completion} comparator.
+	 */
+	private Comparator comparator;
+
+	protected static final String EMPTY_STRING = "";
+
+
+	/**
+	 * Constructor.
+	 */
+	public AbstractCompletionProvider() {
+		comparator = new CaseInsensitiveComparator();
+	}
+
+
+	/**
+	 * 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) {
+		completions.add(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 the completion's provider isn't
+	 *         this <tt>CompletionProvider</tt>.
+	 * @see #addCompletion(Completion)
+	 * @see #removeCompletion(Completion)
+	 * @see #clear()
+	 */
+	public void addCompletions(List completions) {
+		this.completions.addAll(completions);
+		Collections.sort(this.completions);
+	}
+
+
+	/**
+	 * 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 the first <tt>Completion</tt> in this provider with the
+	 * specified input text.
+	 *
+	 * @param inputText The input text to search for.
+	 * @return The {@link Completion}, or <code>null</code> if there is no such
+	 *         <tt>Completion</tt>.
+	 */
+	public Completion getCompletionByInputText(String inputText) {
+		// TODO: Do a binary search for performance
+		for (int i=0; i<completions.size(); i++) {
+			Completion c = (Completion)completions.get(i);
+			if (c.getInputText().equals(inputText)) {
+				return c;
+			}
+		}
+		return null;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public final List getCompletions(JTextComponent comp) {
+		List completions = getCompletionsImpl(comp);
+		if (parent!=null) {
+			completions.addAll(parent.getCompletions(comp));
+			Collections.sort(completions);
+		}
+		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 List getCompletionsImpl(JTextComponent comp) {
+
+		List retVal = new ArrayList();
+		String text = getAlreadyEnteredText(comp);
+
+		int index = Collections.binarySearch(completions, text, comparator);
+		System.out.println(index + "(" + completions.size() + ")");
+		if (index<0) {
+			index = -index - 1;
+		}
+
+		while (index<completions.size()) {
+			Completion c = (Completion)completions.get(index);
+			if (startsWithIgnoreCase(c.getInputText(), text)) {
+				retVal.add(c);
+				index++;
+			}
+			else {
+				break;
+			}
+		}
+
+		return retVal;
+
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public ListCellRenderer getListCellRenderer() {
+		return listCellRenderer;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public CompletionProvider getParent() {
+		return parent;
+	}
+
+
+	/**
+	 * 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;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setListCellRenderer(ListCellRenderer r) {
+		listCellRenderer = r;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setParent(CompletionProvider parent) {
+		this.parent = parent;
+	}
+
+
+	/**
+	 * Returns whether <code>str</code> starts with <code>start</code>,
+	 * ignoring case.
+	 *
+	 * @param str The string to check.
+	 * @param start The prefix to check for.
+	 * @return Whether <code>str</code> starts with <code>start</code>,
+	 *         ignoring case.
+	 */
+	protected boolean startsWithIgnoreCase(String str, String start) {
+		int startLen = start.length();
+		if (str.length()>=startLen) {
+			for (int i=0; i<startLen; i++) {
+				char c1 = str.charAt(i);
+				char c2 = start.charAt(i);
+				if (Character.toLowerCase(c1)!=Character.toLowerCase(c2)) {
+					return false;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+
+
+	/**
+	 * A comparator that compares the input text of two {@link Completion}s
+	 * lexicographically, ignoring case.
+	 *
+	 * @author Robert Futrell
+	 * @version 1.0
+	 */
+	private static class CaseInsensitiveComparator implements Comparator,
+														Serializable {
+
+		public int compare(Object o1, Object o2) {
+			Completion c = (Completion)o1;
+			return String.CASE_INSENSITIVE_ORDER.compare(c.getInputText(), o2);
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/AutoCompleteDescWindow.java b/src/org/fife/ui/autocomplete/AutoCompleteDescWindow.java
new file mode 100644
index 0000000..60a63a5
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/AutoCompleteDescWindow.java
@@ -0,0 +1,450 @@
+/*
+ * 12/21/2008
+ *
+ * AutoCompleteDescWindow.java - A window containing a description of the
+ * currently selected completion.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Font;
+import java.awt.SystemColor;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+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.imageio.ImageIO;
+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.UIManager;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.html.HTMLDocument;
+
+
+/**
+ * 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 {
+
+	/**
+	 * The parent AutoCompletion instance.
+	 */
+	private AutoCompletion ac;
+
+	/**
+	 * Renders the HTML description.
+	 */
+	private JEditorPane descArea;
+
+	/**
+	 * 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 history;
+
+	/**
+	 * The current position in {@link #history}.
+	 */
+	private int historyPos;
+
+	/**
+	 * Used on OS X, where non-editable JEditorPanes don't have their cursors
+	 * made into hand cursors on hyperlink mouseover.
+	 */
+	private Cursor prevCursor;
+
+	/**
+	 * The resource bundle for this window.
+	 */
+	private ResourceBundle bundle;
+
+	/**
+	 * The resource bundle name.
+	 */
+	private static final String MSG =
+					"org.fife.ui.autocomplete.AutoCompleteDescWindow";
+
+	private static final boolean IS_OS_X = System.getProperty("os.name").
+													indexOf("OS X")>-1;
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param owner The parent window.
+	 * @param ac The parent autocompletion.
+	 */
+	public AutoCompleteDescWindow(Window owner, AutoCompletion ac) {
+
+		super(owner);
+		this.ac = ac;
+
+		JPanel cp = new JPanel(new BorderLayout());
+		cp.setBorder(BorderFactory.createLineBorder(Color.BLACK));
+
+		descArea = createDescArea();
+		JScrollPane sp = new JScrollPane(descArea);
+		sp.setViewportBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+		sp.setBackground(descArea.getBackground());
+		sp.getViewport().setBackground(descArea.getBackground());
+		cp.add(sp);
+
+		descWindowNavBar = new JToolBar();
+		backAction = new ToolBarBackAction();
+		forwardAction = new ToolBarForwardAction();
+		descWindowNavBar.setFloatable(false);
+		descWindowNavBar.add(new JButton(backAction));
+		descWindowNavBar.add(new JButton(forwardAction));
+
+		JPanel temp = new JPanel(new BorderLayout());
+		SizeGrip rp = new SizeGrip();
+		temp.add(descWindowNavBar, BorderLayout.LINE_START);
+		temp.add(rp, BorderLayout.LINE_END);
+		cp.add(temp, BorderLayout.SOUTH);
+
+		setContentPane(cp);
+
+		setFocusableWindowState(false);
+
+		history = new ArrayList(1); // Usually small
+		historyPos = -1;
+
+	}
+
+
+	/**
+	 * Sets the currently displayed description and updates the history.
+	 *
+	 * @param html The new description.
+	 */
+	private void addToHistory(String html) {
+		history.add(++historyPos, html);
+		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();
+	}
+
+
+	/**
+	 * Creates the customized JEditorPane used to render HTML documentation.
+	 *
+	 * @return The JEditorPane.
+	 */
+	private JEditorPane createDescArea() {
+
+		JEditorPane descArea = new JEditorPane("text/html", null);
+
+		// Jump through a few hoops to get things looking nice in Nimbus
+		if (UIManager.getLookAndFeel().getName().equals("Nimbus")) {
+			System.out.println("DEBUG: Creating Nimbus-specific changes");
+			Color selBG = descArea.getSelectionColor();
+			Color selFG = descArea.getSelectedTextColor();
+			descArea.setUI(new javax.swing.plaf.basic.BasicEditorPaneUI());
+			descArea.setSelectedTextColor(selFG);
+			descArea.setSelectionColor(selBG);
+		}
+
+		descArea.getCaret().setSelectionVisible(true);
+		descArea.setEditable(false);
+		descArea.addHyperlinkListener(this);
+
+		// Make it use "tooltip" background color.
+		descArea.setBackground(getDefaultBackground());
+
+		// Force JEditorPane to use a certain font even in HTML.
+		Font font = UIManager.getFont("Label.font");
+		HTMLDocument doc = (HTMLDocument)descArea.getDocument();
+		doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() +
+				"; font-size: " + font.getSize() + "pt; }");
+
+		return descArea;
+
+	}
+
+
+	/**
+	 * Returns the default background color to use for the description
+	 * window.
+	 *
+	 * @return The default background color.
+	 */
+	protected Color getDefaultBackground() {
+		System.out.println(UIManager.getColor("info"));
+		Color c = UIManager.getColor("ToolTip.background");
+		if (c==null) { // Some LookAndFeels like Nimbus
+			c = UIManager.getColor("info"); // Used by Nimbus (and others)
+			if (c==null) {
+				c = SystemColor.infoText; // System default
+			}
+		}
+		return c;
+	}
+
+
+	/**
+	 * 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.
+	 */
+	public void hyperlinkUpdate(HyperlinkEvent e) {
+System.out.println(descArea.isEnabled() + ", " + e);
+		HyperlinkEvent.EventType type = e.getEventType();
+
+		if (type.equals(HyperlinkEvent.EventType.ACTIVATED)) {
+			URL url = e.getURL();
+			if (url!=null) {
+				ExternalURLHandler handler = ac.getExternalURLHandler();
+				if (handler!=null) {
+					handler.urlClicked(url);
+					return;
+				}
+				// No handler - try loading in external browser (Java 6+ only).
+				try {
+					Util.browse(new URI(url.toString()));
+					//descArea.setText(null);
+					//descArea.read(url.openStream(), descArea.getDocument());
+					////descArea.setPage(url);
+					////descArea.setCaretPosition(0); // In case we scrolled
+					////addToHistory(descArea.getText());
+				} catch (/*IO*/URISyntaxException ioe) {
+					UIManager.getLookAndFeel().provideErrorFeedback(descArea);
+					ioe.printStackTrace();
+//					try {
+//						descArea.read(url.openStream(), null);
+//					} catch (IOException ioe2) {
+//						ioe2.printStackTrace();
+//						UIManager.getLookAndFeel().provideErrorFeedback(descArea);
+//					}
+				}
+			}
+			else { // 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();
+					Completion c = ((AbstractCompletionProvider)p).
+										getCompletionByInputText(name);
+					setDescriptionFor(c, true);
+				}
+			}
+		}
+
+		// OS X needs a little push to use the hand cursor for links in a
+		// non-editable JEditorPane
+		else if (IS_OS_X) {
+			boolean entered = HyperlinkEvent.EventType.ENTERED.equals(type);
+			if (entered) {
+				prevCursor = descArea.getCursor();
+				descArea.setCursor(Cursor.getPredefinedCursor(
+											Cursor.HAND_CURSOR));
+			}
+			else {
+				descArea.setCursor(prevCursor);
+			}
+		}
+
+	}
+
+
+	/**
+	 * Enables or disables the back and forward actions as appropriate.
+	 */
+	private void setActionStates() {
+		backAction.setEnabled(historyPos>0);
+		forwardAction.setEnabled(historyPos>-1 && historyPos<history.size()-1);
+	}
+
+
+	/**
+	 * 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) {
+		String desc = item==null ? null : item.getSummary();
+		if (desc==null) {
+			desc = "<html><em>" + getString("NoDescAvailable") + "</em>";
+		}
+		descArea.setText(desc);
+		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(desc);
+	}
+
+
+	/**
+	 * {@inheritDoc} 
+	 */
+	public void setVisible(boolean visible) {
+		if (!visible) {
+			clearHistory();
+		}
+		super.setVisible(visible);
+	}
+
+
+	/**
+	 * Action that moves to the previous description displayed.
+	 */
+	class ToolBarBackAction extends AbstractAction {
+
+		public ToolBarBackAction() {
+			ClassLoader cl = getClass().getClassLoader();
+			URL url = cl.getResource("org/fife/ui/autocomplete/arrow_left.png");
+			try {
+				Icon icon = new ImageIcon(ImageIO.read(url));
+				putValue(Action.SMALL_ICON, icon);
+			} catch (IOException ioe) { // Never happens
+				ioe.printStackTrace();
+				putValue(Action.SHORT_DESCRIPTION, "Back");
+			}
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (historyPos>0) {
+				descArea.setText((String)history.get(--historyPos));
+				descArea.setCaretPosition(0);
+				setActionStates();
+			}
+		}
+
+	}
+
+
+	/**
+	 * Action that moves to the previous description displayed.
+	 */
+	class ToolBarForwardAction extends AbstractAction {
+
+		public ToolBarForwardAction() {
+			ClassLoader cl = getClass().getClassLoader();
+			URL url = cl.getResource("org/fife/ui/autocomplete/arrow_right.png");
+			try {
+				Icon icon = new ImageIcon(ImageIO.read(url));
+				putValue(Action.SMALL_ICON, icon);
+			} catch (IOException ioe) { // Never happens
+				ioe.printStackTrace();
+				putValue(Action.SHORT_DESCRIPTION, "Forward");
+			}
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (history!=null && historyPos<history.size()-1) {
+				descArea.setText((String)history.get(++historyPos));
+				descArea.setCaretPosition(0);
+				setActionStates();
+			}
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java b/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
new file mode 100644
index 0000000..d14c08d
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
@@ -0,0 +1,617 @@
+/*
+ * 12/21/2008
+ *
+ * AutoCompletePopupWindow.java - A window containing a list of auto-complete
+ * choices.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JWindow;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.Caret;
+import javax.swing.text.Keymap;
+import javax.swing.text.JTextComponent;
+
+
+/**
+ * The actual popup window of choices.  When visible, this window intercepts
+ * certain keystrokes in the parent text component and uses them to navigate
+ * the completion choices instead.  If Enter or Escape is pressed, the window
+ * hides itself and notifies the {@link AutoCompletion} to insert the selected
+ * text.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class AutoCompletePopupWindow extends JWindow implements CaretListener,
+									ListSelectionListener, MouseListener {
+
+	private AutoCompletion ac;
+	private JList list;
+	private /*DefaultListModel*/CompletionListModel model;
+	private Action oldUpAction, oldDownAction, oldLeftAction, oldRightAction,
+			oldEnterAction, oldTabAction, oldEscapeAction, oldHomeAction,
+			oldEndAction, oldPageUpAction, oldPageDownAction;
+	private Action upAction, downAction, leftAction ,rightAction, enterAction,
+			escapeAction, homeAction, endAction, pageUpAction, pageDownAction;
+	private int lastLine;
+
+	private AutoCompleteDescWindow descWindow;
+	private boolean aboveCaret;
+
+	private static final boolean DEBUG = true;
+
+
+	public AutoCompletePopupWindow(Window parent, AutoCompletion ac) {
+
+		super(parent);
+
+		this.ac = ac;
+		model = new CompletionListModel();//DefaultListModel();
+		list = new JList(model);
+//list.setFixedCellWidth(300);
+//list.setFixedCellHeight(29);
+		list.setCellRenderer(new DelegatingCellRenderer());
+		list.addListSelectionListener(this);
+		list.addMouseListener(this);
+
+		JPanel contentPane = new JPanel(new BorderLayout());
+		JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+				JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+		// In 1.4, JScrollPane.setCorner() has a bug where it won't accept
+		// JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
+		// defined.  So we have to put the logic added in 1.5 to handle it
+		// here.
+		JPanel corner = new SizeGrip();
+		//sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
+		boolean isLeftToRight = getComponentOrientation().isLeftToRight();
+	    String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER :
+	    								JScrollPane.LOWER_LEFT_CORNER;
+	    sp.setCorner(str, corner);
+
+		contentPane.add(sp);
+		setContentPane(contentPane);
+		pack();
+
+		setFocusableWindowState(false);
+
+		lastLine = -1;
+
+	}
+
+
+//	public void addItem(Completion item) {
+//		model.addElement(item);
+//	}
+public void setCompletions(List completions) {
+	model.setContents(completions);
+}
+
+	public void caretUpdate(CaretEvent e) {
+		if (isVisible()) { // Should always be true
+			int line = ac.getLineOfCaret();
+			if (line!=lastLine) {
+lastLine = -1;
+				setVisible(false);
+			}
+			else {
+				doAutocomplete();
+			}
+		}
+		else if (DEBUG) {
+			Thread.dumpStack();
+		}
+	}
+
+
+//	public void clear() {
+//		model.clear();
+//	}
+
+
+	private void createActions() {
+		escapeAction = new EscapeAction();
+		upAction = new UpAction();
+		downAction = new DownAction();
+		leftAction = new LeftAction();
+		rightAction = new RightAction();
+		enterAction = new EnterAction();
+		homeAction = new HomeAction();
+		endAction = new EndAction();
+		pageUpAction = new PageUpAction();
+		pageDownAction = new PageDownAction();
+	}
+
+
+	protected void doAutocomplete() {
+		lastLine = ac.refreshPopupWindow();
+	}
+
+
+	/**
+	 * Returns the selected value, or <code>null</code> if nothing is selected.
+	 *
+	 * @return The selected value.
+	 */
+	public Completion getSelection() {
+		return (Completion)list.getSelectedValue();
+	}
+
+
+	public void mouseClicked(MouseEvent e) {
+		if (e.getClickCount()==2) {
+			ac.doCompletion();
+		}
+	}
+
+
+	public void mouseEntered(MouseEvent e) {
+	}
+
+
+	public void mouseExited(MouseEvent e) {
+	}
+
+
+	public void mousePressed(MouseEvent e) {
+	}
+
+
+	public void mouseReleased(MouseEvent e) {
+	}
+
+
+	/**
+	 * Positions the description window relative to the comletion choices
+	 * window.
+	 */
+	private void positionDescWindow() {
+
+		boolean showDescWindow = descWindow!=null && ac.getShowDescWindow();
+		if (!showDescWindow) {
+			return;
+		}
+
+		Dimension screenSize = getToolkit().getScreenSize();
+		//int totalH = Math.max(getHeight(), descWindow.getHeight());
+
+		// Try to position to the right first.
+		int x = getX() + getWidth() + 5;
+		if (x+descWindow.getWidth()>screenSize.width) { // doesn't fit
+			x = getX() - 5 - descWindow.getWidth();
+		}
+
+		int y = getY();
+		if (aboveCaret) {
+			y = y + getHeight() - descWindow.getHeight();
+		}
+
+		if (x!=descWindow.getX() || y!=descWindow.getY()) { 
+			descWindow.setLocation(x, y);
+		}
+
+	}
+
+
+	/**
+	 * Registers keyboard actions to listen for in the text component and
+	 * intercept.
+	 *
+	 * @see #unregisterActions()
+	 */
+	private void registerActions() {
+		System.err.println("Registering actions");
+
+		if (escapeAction == null) {
+			createActions();
+		}
+
+		JTextComponent comp = ac.getTextComponent();
+		Keymap km = comp.getKeymap();
+
+		KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+		oldEscapeAction = km.getAction(ks);
+		if (DEBUG && oldEscapeAction==escapeAction) {
+			Thread.dumpStack();
+			return;
+		}
+		km.addActionForKeyStroke(ks, escapeAction);
+
+		oldUpAction = replaceAction(km, KeyEvent.VK_UP, upAction);
+		oldDownAction = replaceAction(km, KeyEvent.VK_DOWN, downAction);
+		oldLeftAction = replaceAction(km, KeyEvent.VK_LEFT, leftAction);
+		oldRightAction = replaceAction(km, KeyEvent.VK_RIGHT, rightAction);
+		oldEnterAction = replaceAction(km, KeyEvent.VK_ENTER, enterAction);
+		oldTabAction = replaceAction(km, KeyEvent.VK_TAB, enterAction);
+		oldHomeAction = replaceAction(km, KeyEvent.VK_HOME, homeAction);
+		oldEndAction = replaceAction(km, KeyEvent.VK_END, endAction);
+		oldPageUpAction = replaceAction(km, KeyEvent.VK_PAGE_UP, pageUpAction);
+		oldPageDownAction = replaceAction(km, KeyEvent.VK_PAGE_DOWN, pageDownAction);
+
+		comp.addCaretListener(this);
+
+	}
+
+
+	/**
+	 * Replaces the action binded to a keystroke.
+	 *
+	 * @param km The map in which to replace the action.
+	 * @param key The keystroke whose action to replace.
+	 * @param a The action to associate with <code>key</code>.  If this is
+	 *        <code>null</code>, no keystroke will be associated with the key.
+	 * @return The previous action associated with the key, or <code>null</code>
+	 *         if there was none.
+	 */
+	private Action replaceAction(Keymap km, int key, Action a) {
+		KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
+		Action old = km.getAction(ks);
+		if (a!=null) {
+			km.addActionForKeyStroke(ks, a);
+		} else {
+			km.removeKeyStrokeBinding(ks);
+		}
+		return old;
+	}
+
+
+	public void selectFirstItem() {
+		if (model.getSize() > 0) {
+			list.setSelectedIndex(0);
+			list.ensureIndexIsVisible(0);
+		}
+	}
+
+
+	public void selectLastItem() {
+		int index = model.getSize() - 1;
+		if (index > -1) {
+			list.setSelectedIndex(index);
+			list.ensureIndexIsVisible(index);
+		}
+	}
+
+
+	public void selectNextItem() {
+		int index = list.getSelectedIndex();
+		if (index > -1) {
+			index = (index + 1) % model.getSize();
+			list.setSelectedIndex(index);
+			list.ensureIndexIsVisible(index);
+		}
+	}
+
+
+	public void selectPageDownItem() {
+		int visibleRowCount = list.getVisibleRowCount();
+		int i = Math.min(list.getModel().getSize()-1,
+						list.getSelectedIndex()+visibleRowCount);
+		list.setSelectedIndex(i);
+		list.ensureIndexIsVisible(i);
+	}
+
+
+	public void selectPageUpItem() {
+		int visibleRowCount = list.getVisibleRowCount();
+		int i = Math.max(0, list.getSelectedIndex()-visibleRowCount);
+		list.setSelectedIndex(i);
+		list.ensureIndexIsVisible(i);
+	}
+
+
+	public void selectPreviousItem() {
+		int index = list.getSelectedIndex();
+		switch (index) {
+		case 0:
+			index = list.getModel().getSize() - 1;
+			break;
+		case -1: // Check for an empty list (would be an error)
+			index = list.getModel().getSize() - 1;
+			if (index == -1) {
+				return;
+			}
+			break;
+		default:
+			index = index - 1;
+			break;
+		}
+		list.setSelectedIndex(index);
+		list.ensureIndexIsVisible(index);
+	}
+
+
+	public void setLocationRelativeTo(Rectangle r) {
+
+		boolean showDescWindow = descWindow!=null && ac.getShowDescWindow();
+		Dimension screenSize = getToolkit().getScreenSize();
+		int totalH = getHeight();
+		if (showDescWindow) {
+			totalH = Math.max(totalH, descWindow.getHeight());
+		}
+
+		// Try putting our stuff "below" the caret first.  We assume that the
+		// entire height of our stuff fits on the screen one way or the other.
+		aboveCaret = false;
+		int y = r.y + r.height + 10;
+		if (y+totalH>screenSize.height) {
+			y = r.y - 10 - getHeight();
+			aboveCaret = true;
+		}
+
+		// Get x-coordinate of completions.  Try to align left edge with the
+		// caret first.
+		int x = r.x;
+		if (x<0) {
+			x = 0;
+		}
+		else if (x+getWidth()>screenSize.width) { // completions don't fit
+			x = screenSize.width - getWidth();
+		}
+
+		setLocation(x, y);
+
+		// Position the description window, if neessary.
+		if (showDescWindow) {
+			positionDescWindow();
+		}
+
+	}
+
+
+	/**
+	 * Toggles the visibility of this popup window.
+	 *
+	 * @param visible Whether this window should be visible.
+	 */
+	public void setVisible(boolean visible) {
+		if (visible!=isVisible()) {
+			if (visible) {
+				registerActions();
+				lastLine = ac.getLineOfCaret();
+				selectFirstItem();
+				if (descWindow==null && ac.getShowDescWindow()) {
+					descWindow = new AutoCompleteDescWindow(this, ac);
+					descWindow.setSize(getSize());
+					descWindow.setLocation(getX()+getWidth()+5, getY());
+					// descWindow needs a kick-start the first time it's
+					// displayed.
+					Completion c = (Completion)list.getSelectedValue();
+					descWindow.setDescriptionFor(c);
+				}
+			}
+			else {
+				unregisterActions();
+			}
+			super.setVisible(visible);
+			if (descWindow!=null && ac.getShowDescWindow()) {
+				descWindow.setVisible(visible);
+			}
+		}
+	}
+
+
+	/**
+	 * Stops intercepting certain keystrokes from the text component.
+	 *
+	 * @see #registerActions()
+	 */
+	public void unregisterActions() {
+
+		System.err.println("Unregistering actions");
+		JTextComponent comp = ac.getTextComponent();
+		Keymap km = comp.getKeymap();
+
+		replaceAction(km, KeyEvent.VK_ESCAPE, oldEscapeAction);
+		replaceAction(km, KeyEvent.VK_UP, oldUpAction);
+		replaceAction(km, KeyEvent.VK_DOWN, oldDownAction);
+		replaceAction(km, KeyEvent.VK_LEFT, oldLeftAction);
+		replaceAction(km, KeyEvent.VK_RIGHT, oldRightAction);
+		replaceAction(km, KeyEvent.VK_ENTER, oldEnterAction);
+		replaceAction(km, KeyEvent.VK_TAB, oldTabAction);
+		replaceAction(km, KeyEvent.VK_HOME, oldHomeAction);
+		replaceAction(km, KeyEvent.VK_END, oldEndAction);
+		replaceAction(km, KeyEvent.VK_PAGE_UP, oldPageUpAction);
+		replaceAction(km, KeyEvent.VK_PAGE_DOWN, oldPageDownAction);
+
+		comp.removeCaretListener(this);
+
+	}
+
+
+	/**
+	 * Updates the <tt>LookAndFeel</tt> of this window and the description
+	 * window.
+	 */
+	public void updateUI() {
+		SwingUtilities.updateComponentTreeUI(this);
+		if (descWindow!=null) {
+			SwingUtilities.updateComponentTreeUI(descWindow);
+		}
+	}
+
+
+	/**
+	 * Called when a new item is selected in the popup list.
+	 *
+	 * @param e The event.
+	 */
+	public void valueChanged(ListSelectionEvent e) {
+		if (!e.getValueIsAdjusting()) {
+			Object value = list.getSelectedValue();
+			if (value!=null && descWindow!=null) {
+				descWindow.setDescriptionFor((Completion)value);
+				positionDescWindow();
+			}
+		}
+	}
+
+
+	class DownAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectNextItem();
+			}
+		}
+
+	}
+
+
+	class EndAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectLastItem();
+			}
+		}
+
+	}
+
+
+	class EnterAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				ac.doCompletion();
+			}
+		}
+
+	}
+
+
+	class EscapeAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				setVisible(false);
+			}
+		}
+
+	}
+
+
+	class HomeAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectFirstItem();
+			}
+		}
+
+	}
+
+
+	class LeftAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				JTextComponent comp = ac.getTextComponent();
+				Caret c = comp.getCaret();
+				int dot = c.getDot();
+				if (dot > 0) {
+					c.setDot(--dot);
+					// Ensure moving left hasn't moved us up a line, thus
+					// hiding the popup window.
+					if (comp.isVisible()) {
+if (lastLine!=-1)						doAutocomplete();
+					}
+				}
+			}
+		}
+
+	}
+
+
+	class PageDownAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectPageDownItem();
+			}
+		}
+
+	}
+
+
+	class PageUpAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectPageUpItem();
+			}
+		}
+
+	}
+
+
+	class RightAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				JTextComponent comp = ac.getTextComponent();
+				Caret c = comp.getCaret();
+				int dot = c.getDot();
+				if (dot < comp.getDocument().getLength()) {
+					c.setDot(++dot);
+					// Ensure moving right hasn't moved us up a line, thus
+					// hiding the popup window.
+					if (comp.isVisible()) {
+if (lastLine!=-1)						doAutocomplete();
+					}
+				}
+			}
+		}
+
+	}
+
+
+	class UpAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isVisible()) {
+				selectPreviousItem();
+			}
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/AutoCompletion.java b/src/org/fife/ui/autocomplete/AutoCompletion.java
new file mode 100644
index 0000000..88c693b
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/AutoCompletion.java
@@ -0,0 +1,574 @@
+/*
+ * 12/21/2008
+ *
+ * AutoCompletion.java - Handles auto-completion for a text component.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.List;
+import javax.swing.*;
+import javax.swing.text.*;
+
+
+/**
+ * Adds autocompletion to a text component.  Provides a popup window with a
+ * list of autocomplete choices on a given keystroke, such as Crtrl+Space.<p>
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+/*
+ * This class handles intercepting window and hierarchy events from the text
+ * component, so the popup window is only visible when it should be visible.
+ * It also handles communication between the CompletionProvider and the actual
+ * popup Window.
+ */
+public class AutoCompletion implements HierarchyListener, ComponentListener {
+
+	/**
+	 * The text component we're providing completion for.
+	 */
+	private JTextComponent textComponent;
+
+	/**
+	 * The parent window of {@link #textComponent}.
+	 */
+	private Window parentWindow;
+
+	/**
+	 * The popup window containing completion choices.
+	 */
+	private AutoCompletePopupWindow popupWindow;
+
+	/**
+	 * Provides the completion options relevant to the current caret position.
+	 */
+	private CompletionProvider provider;
+
+	/**
+	 * The handler to use when an external URL is clicked in the help
+	 * documentation.
+	 */
+	private ExternalURLHandler externalURLHandler;
+
+	/**
+	 * Whether the description window should be displayed along with the
+	 * completion choice window.
+	 */
+	private boolean showDescWindow;
+
+	/**
+	 * Whether autocomplete is enabled.
+	 */
+	private boolean autoCompleteEnabled;
+
+	/**
+	 * Whether or not, when there is only a single auto-complete option
+	 * that maches the text at the current text position, that text should
+	 * be auto-inserted, instead of the completion window displaying.
+	 */
+	private boolean autoCompleteSingleChoices;
+
+	/**
+	 * The keystroke that triggers the completion window.
+	 */
+	private KeyStroke trigger;
+
+	/**
+	 * The action previously assigned to {@link #trigger}, so we can reset it
+	 * if the user disables auto-completion.
+	 */
+	private Action oldTriggerAction;
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param provider The completion provider.  This cannot be
+	 *        <code>null</code>.
+	 */
+	public AutoCompletion(CompletionProvider provider) {
+		setCompletionProvider(provider);
+		setTriggerKey(getDefaultTriggerKey());
+		setAutoCompleteEnabled(true);
+		setAutoCompleteSingleChoices(true);
+		setShowDescWindow(false);
+	}
+
+
+	public void componentHidden(ComponentEvent e) {
+		hidePopupWindow();
+	}
+
+
+	public void componentMoved(ComponentEvent e) {
+		hidePopupWindow();
+	}
+
+
+	public void componentResized(ComponentEvent e) {
+		hidePopupWindow();
+	}
+
+
+	public void componentShown(ComponentEvent e) {
+	}
+
+
+	public void doCompletion() {
+		Completion comp = popupWindow.getSelection();
+		doCompletionImpl(comp);
+	}
+
+
+	private void doCompletionImpl(Completion c) {
+
+		JTextComponent textComp = getTextComponent();
+		String alreadyEntered = c.getAlreadyEntered(textComp);
+		hidePopupWindow();
+		Caret caret = textComp.getCaret();
+
+		int dot = caret.getDot();
+		caret.setDot(dot - alreadyEntered.length());
+		caret.moveDot(dot);
+		textComp.replaceSelection(c.getReplacementText());
+/*
+		Document doc = textComp.getDocument();
+		int end = caret.getDot();
+		int start = end - alreadyEntered.length();
+try {
+		if (doc instanceof AbstractDocument) {
+			((AbstractDocument)doc).replace(start, end-start, c.getReplacementText(), null);
+		}
+		else {
+			doc.remove(start, end-start);
+			doc.insertString(start, c.getReplacementText(), null);
+		}
+} catch (javax.swing.text.BadLocationException ble) { ble.printStackTrace(); }
+*/
+	}
+
+
+	/**
+	 * Returns whether, if a single autocomplete choice is available, it should
+	 * be automatically inserted, without displaying the popup menu.
+	 *
+	 * @return Whether to autocomplete single choices.
+	 * @see #setAutoCompleteSingleChoices(boolean)
+	 */
+	public boolean getAutoCompleteSingleChoices() {
+		return autoCompleteSingleChoices;
+	}
+
+
+	/**
+	 * Returns the default autocomplete "trigger key" for this OS.  For
+	 * Windows, for example, it is Ctrl+Space.
+	 *
+	 * @return The default autocomplete trigger key.
+	 */
+	public static KeyStroke getDefaultTriggerKey() {
+		// Default to CTRL, even on Mac, since Ctrl+Space activates Spotlight
+		int mask = Event.CTRL_MASK;
+		return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, mask);
+	}
+
+
+	/**
+	 * Returns the handler to use when an external URL is clicked in the
+	 * description window.
+	 *
+	 * @return The handler.
+	 * @see #setExternalURLHandler(ExternalURLHandler)
+	 */
+	public ExternalURLHandler getExternalURLHandler() {
+		return externalURLHandler;
+	}
+
+
+	int getLineOfCaret() {
+		Document doc = textComponent.getDocument();
+		Element root = doc.getDefaultRootElement();
+		return root.getElementIndex(textComponent.getCaretPosition());
+	}
+
+
+	CompletionProvider getProviderAtCaretPosition() {
+		// TODO: Delegate to provider in case the provider itself delegates.
+		//return provider.getProviderAtCaretPosition(textComponent);
+		return provider;
+	}
+
+
+	/**
+	 * Returns whether the "description window" should be shown alongside
+	 * the completion window.
+	 *
+	 * @return Whether the description window should be shown.
+	 * @see #setShowDescWindow(boolean)
+	 */
+	public boolean getShowDescWindow() {
+		return showDescWindow;
+	}
+
+
+	/**
+	 * Returns the text component for which autocompletion is enabled.
+	 *
+	 * @return The text component, or <code>null</code> if this
+	 *         {@link AutoCompletion} is not installed on any text component.
+	 * @see #install(JTextComponent)
+	 */
+	public JTextComponent getTextComponent() {
+		return textComponent;
+	}
+
+
+	/**
+	 * Returns the "trigger key" used for autocomplete.
+	 *
+	 * @return The trigger key.
+	 * @see #setTriggerKey(KeyStroke)
+	 */
+	public KeyStroke getTriggerKey() {
+		return trigger;
+	}
+
+
+	/**
+	 * Called when the component hierarchy for our text component changes.
+	 * When the text component is added to a new {@link Window}, this method
+	 * registers listeners on that <code>Window</code>.
+	 *
+	 * @param e The event.
+	 */
+	public void hierarchyChanged(HierarchyEvent e) {
+
+		// NOTE: e many be null as we call this method at other times.
+		//System.out.println("Hierarchy changed! " + e);
+
+		Window oldParentWindow = parentWindow;
+		parentWindow = SwingUtilities.getWindowAncestor(textComponent);
+		if (parentWindow!=oldParentWindow) {
+			if (oldParentWindow!=null) {
+				oldParentWindow.removeComponentListener(this);
+			}
+			if (parentWindow!=null) {
+				parentWindow.addComponentListener(this);
+			}
+		}
+
+	}
+
+
+	private void hidePopupWindow() {
+		if (popupWindow!=null) {
+			if (popupWindow.isVisible()) {
+				popupWindow.setVisible(false);
+			}
+		}
+	}
+
+
+	/**
+	 * Installs this autocompletion on a text component.  If this
+	 * {@link AutoCompletion} is already installed on another text component,
+	 * it is uninstalled first.
+	 *
+	 * @param c The text component.
+	 * @see #uninstall()
+	 */
+	public void install(JTextComponent c) {
+
+		if (textComponent!=null) {
+			uninstall();
+		}
+
+		this.textComponent = c;
+		installTriggerKey(getTriggerKey());
+
+		this.textComponent.addHierarchyListener(this);
+		hierarchyChanged(null); // In case textComponent is already in a window
+
+	}
+
+
+	/**
+	 * Installs a "trigger key" action onto the current text component.
+	 *
+	 * @param ks The keystroke that should trigger the action.
+	 * @see #uninstallTriggerKey() 
+	 */
+	private void installTriggerKey(KeyStroke ks) {
+		Keymap km = textComponent.getKeymap();
+		oldTriggerAction = km.getAction(ks);
+		km.addActionForKeyStroke(ks, new AutoCompleteAction());
+	}
+
+
+	/**
+	 * Returns whether autocompletion is enabled.
+	 *
+	 * @return Whether autocompletion is enabled.
+	 * @see #setAutoCompleteEnabled(boolean)
+	 */
+	public boolean isAutoCompleteEnabled() {
+		return autoCompleteEnabled;
+	}
+
+
+	private boolean isPopupVisible() {
+		return popupWindow!=null && popupWindow.isVisible();
+	}
+
+
+	/**
+	 * Refreshes the popup window.  First, this method gets the possible
+	 * completions for the current caret position.  If there are none, and the
+	 * popup is visible, it is hidden.  If there are some completions and the
+	 * popup is hidden, it is made visible and made to display the completions.
+	 * If there are some completions and the popup is visible, its list is
+	 * updated to the current set of completions.
+	 *
+	 * @return The current line number of the caret.
+	 */
+	protected int refreshPopupWindow() {
+
+		final List completions = provider.getCompletions(textComponent);
+		int count = completions.size();
+
+		if (count>1 || (count==1 && isPopupVisible()) ||
+				(count==1 && !getAutoCompleteSingleChoices())) {
+
+			if (popupWindow==null) {
+				popupWindow = new AutoCompletePopupWindow(parentWindow, this);
+			}
+
+			popupWindow.setCompletions(completions);
+//			popupWindow.clear();
+//			for (int i=0; i<completions.size(); i++) {
+//				
+//				popupWindow.addItem((Completion)completions.get(i));
+//			}
+			popupWindow.selectFirstItem();
+
+			if (!popupWindow.isVisible()) {
+				Rectangle r = null;
+				try {
+					r = textComponent.modelToView(textComponent.
+														getCaretPosition());
+				} catch (BadLocationException ble) {
+					ble.printStackTrace();
+					return -1;
+				}
+				Point p = new Point(r.x, r.y);
+				SwingUtilities.convertPointToScreen(p, textComponent);
+				r.x = p.x;
+				r.y = p.y;
+				popupWindow.setLocationRelativeTo(r);
+				popupWindow.setVisible(true);
+			}
+
+		}
+
+		else if (count==1) { // !isPopupVisible && autoCompleteSingleChoices
+			SwingUtilities.invokeLater(new Runnable() {
+				public void run() {
+					doCompletionImpl((Completion)completions.get(0));
+				}
+			});
+		}
+
+		else {
+			hidePopupWindow();
+		}
+
+		return getLineOfCaret();
+
+	}
+
+
+	/**
+	 * Sets whether autocompletion is enabled.
+	 *
+	 * @param enabled Whether autocompletion is enabled.
+	 * @see #isAutoCompleteEnabled()
+	 */
+	public void setAutoCompleteEnabled(boolean enabled) {
+		if (enabled!=autoCompleteEnabled) {
+			autoCompleteEnabled = enabled;
+			hidePopupWindow();
+		}
+	}
+
+
+	/**
+	 * Sets whether, if a single autocomplete choice is available, it should
+	 * be automatically inserted, without displaying the popup menu.
+	 *
+	 * @param autoComplete Whether to autocomplete single choices.
+	 * @see #getAutoCompleteSingleChoices()
+	 */
+	public void setAutoCompleteSingleChoices(boolean autoComplete) {
+		autoCompleteSingleChoices = autoComplete;
+	}
+
+
+	/**
+	 * Sets the completion provider being used.
+	 *
+	 * @param provider The new completion provider.  This cannot be
+	 *        <code>null</code>.
+	 * @throws IllegalArgumentException If <code>provider</code> is
+	 *         <code>null</code>.
+	 */
+	public void setCompletionProvider(CompletionProvider provider) {
+		if (provider==null) {
+			throw new IllegalArgumentException("provider cannot be null");
+		}
+		this.provider = provider;
+		hidePopupWindow(); // In case new choices should be displayed.
+	}
+
+
+	/**
+	 * Sets the handler to use when an external URL is clicked in the
+	 * description window.  This handler can perform some action, such as
+	 * open the URL in a web browser.  The default implementation will open
+	 * the URL in a browser, but only if running in Java 6.  If you want
+	 * browser support for Java 5 and below, you will have to install your own
+	 * handler to do so.
+	 *
+	 * @param handler The new handler.
+	 * @see #getExternalURLHandler()
+	 */
+	public void setExternalURLHandler(ExternalURLHandler handler) {
+		this.externalURLHandler = handler;
+	}
+
+
+	/**
+	 * Sets whether the "description window" should be shown beside the
+	 * completion window.
+	 *
+	 * @param show Whether to show the description window.
+	 * @see #getShowDescWindow()
+	 */
+	public void setShowDescWindow(boolean show) {
+		showDescWindow = show;
+	}
+
+
+	/**
+	 * Sets the keystroke that should be used to trigger the autocomplete
+	 * popup window.
+	 *
+	 * @param ks The keystroke.
+	 * @throws IllegalArgumentException If <code>ks</code> is <code>null</code>.
+	 * @see #getTriggerKey()
+	 */
+	public void setTriggerKey(KeyStroke ks) {
+		if (ks==null) {
+			throw new IllegalArgumentException("trigger key cannot be null");
+		}
+		if (!ks.equals(trigger)) {
+			if (textComponent!=null) {
+				// Put old trigger action back.
+				uninstallTriggerKey();
+				// Grab current action for new trigger and replace it.
+				installTriggerKey(ks);
+			}
+			trigger = ks;
+		}
+	}
+
+
+	/**
+	 * Uninstalls this autocompletion from its text component.  If it is not
+	 * installed on any text component, nothing happens.
+	 *
+	 * @see #install(JTextComponent)
+	 */
+	public void uninstall() {
+		if (textComponent!=null) {
+			hidePopupWindow(); // Unregisters listeners, actions, etc.
+			uninstallTriggerKey();
+			textComponent.removeHierarchyListener(this);
+			if (parentWindow!=null) {
+				parentWindow.removeComponentListener(this);
+			}
+			textComponent = null;
+		}
+	}
+
+
+	/**
+	 * Replaces the "trigger key" action with the one that was there
+	 * before autocompletion was installed.
+	 *
+	 * @see #installTriggerKey(KeyStroke)
+	 */
+	private void uninstallTriggerKey() {
+		Keymap km = textComponent.getKeymap();
+		if (oldTriggerAction!=null) {
+			km.addActionForKeyStroke(trigger, oldTriggerAction);
+		}
+		else {
+			km.removeKeyStrokeBinding(trigger);
+		}
+	}
+
+
+	/**
+	 * Updates the LookAndFeel of the popup window.  Applications can call
+	 * this method as appropriate if they support changing the LookAndFeel
+	 * at runtime.
+	 */
+	public void updateUI() {
+		if (popupWindow!=null) {
+			popupWindow.updateUI();
+		}
+	}
+
+
+	/**
+	 * The <code>Action</code> that displays the popup window if autocompletion
+	 * is enabled.
+	 *
+	 * @author Robert Futrell
+	 * @version 1.0
+	 */
+	class AutoCompleteAction extends AbstractAction {
+
+		public void actionPerformed(ActionEvent e) {
+			if (isAutoCompleteEnabled()) {
+				refreshPopupWindow();
+			}
+			else if (oldTriggerAction!=null) {
+				oldTriggerAction.actionPerformed(e);
+			}
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/Completion.java b/src/org/fife/ui/autocomplete/Completion.java
new file mode 100644
index 0000000..2afebbe
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/Completion.java
@@ -0,0 +1,95 @@
+/*
+ * 12/21/2008
+ *
+ * Completion.java - Represents a single completion choice.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import javax.swing.text.JTextComponent;
+
+
+/**
+ * Represents a completion choice.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ * @see AbstractCompletion
+ */
+public interface Completion {
+
+
+	/**
+	 * 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().getAlreadyEntered(comp)</code>.
+	 *
+	 * @param comp The text component.
+	 * @return The already-entered portion of this completion.
+	 */
+	public String getAlreadyEntered(JTextComponent comp);
+
+
+	/**
+	 * 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 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();
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/CompletionListModel.java b/src/org/fife/ui/autocomplete/CompletionListModel.java
new file mode 100644
index 0000000..2363617
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/CompletionListModel.java
@@ -0,0 +1,99 @@
+/*
+ * 12/22/2008
+ *
+ * CompletionListModel.java - A model that allows bulk addition of elements.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.util.ArrayList;
+import java.util.Collection;
+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 ArrayList delegate;
+
+
+	/**
+	 * Constructor.
+	 */
+	public CompletionListModel() {
+		delegate = new ArrayList();
+	}
+
+
+	/**
+	 * 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}
+	 */
+	public Object getElementAt(int index) {
+		return delegate.get(index);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	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 contents) {
+		clear();
+		if (contents.size()>0) {
+			delegate.addAll(contents);
+			fireIntervalAdded(this, 0, contents.size());
+		}
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/CompletionProvider.java b/src/org/fife/ui/autocomplete/CompletionProvider.java
new file mode 100644
index 0000000..26b86ea
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/CompletionProvider.java
@@ -0,0 +1,104 @@
+/*
+ * 12/21/2008
+ *
+ * CompletionProvider.java - Provides autocompletion values based on the
+ * text currently in a text component.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+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 {
+
+
+	/**
+	 * 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.
+	 */
+	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, an empty list is returned.
+	 */
+	public List getCompletions(JTextComponent comp);
+
+
+	/**
+	 * 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 the parent completion provider.
+	 *
+	 * @return The parent completion provider.
+	 * @see #setParent(CompletionProvider)
+	 */
+	public CompletionProvider getParent();
+
+
+	/**
+	 * Sets the renderer to use when displaying completion choices.
+	 *
+	 * @param r The renderer to use.
+	 * @see #getListCellRenderer()
+	 */
+	public void setListCellRenderer(ListCellRenderer r);
+
+
+	/**
+	 * 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);
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/DelegatingCellRenderer.java b/src/org/fife/ui/autocomplete/DelegatingCellRenderer.java
new file mode 100644
index 0000000..54a470c
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/DelegatingCellRenderer.java
@@ -0,0 +1,60 @@
+/*
+ * 12/22/2008
+ *
+ * DelegatingCellRenderer.java - A renderer for Completions that will delegate
+ * to the Completion's provider's renderer, if there is one.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.Component;
+import javax.swing.DefaultListCellRenderer;
+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 simply renders
+ * <code>(({@link Completion})value).toString()</code>.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class DelegatingCellRenderer extends DefaultListCellRenderer {
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	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);
+		}
+		return super.getListCellRendererComponent(list, value, index, selected,
+													hasFocus);
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/ExternalURLHandler.java b/src/org/fife/ui/autocomplete/ExternalURLHandler.java
new file mode 100644
index 0000000..c8234d8
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/ExternalURLHandler.java
@@ -0,0 +1,51 @@
+/*
+ * 12/23/2008
+ *
+ * ExternalURLHandler.java - Implementations can be registered as a callback
+ * to handle the user clicking on external URL's.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.net.URL;
+
+
+/**
+ * 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}.
+ *
+ * @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 url The URL.
+	 */
+	public void urlClicked(URL url);
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/FunctionCompletion.java b/src/org/fife/ui/autocomplete/FunctionCompletion.java
new file mode 100644
index 0000000..74352f7
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/FunctionCompletion.java
@@ -0,0 +1,213 @@
+/*
+ * 12/22/2008
+ *
+ * FunctionCompletion.java - A completion representing a function.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A completion choice representing a function.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class FunctionCompletion extends VariableCompletion {
+
+	/**
+	 * Parameters to the function.
+	 */
+	private List params;
+
+
+	/**
+	 * 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);
+	}
+
+
+	protected void addDefinitionString(StringBuffer sb) {
+
+		sb.append("<html><b>");
+
+		// Add the return type if applicable (C macros like NULL have no type).
+		if (getType()!=null) {
+			sb.append(getType());
+			sb.append(' ');
+		}
+
+		// Add the item being described's name.
+		sb.append(getName());
+
+		// Add parameters for functions.
+		sb.append('(');
+		for (int i=0; i<getParamCount(); i++) {
+			Parameter param = getParam(i);
+			sb.append(param.toString());
+			if (i<params.size()-1) {
+				sb.append(", ");
+			}
+		}
+		sb.append(')');
+
+		sb.append("</b>");
+
+	}
+
+
+	/**
+	 * Adds HTML describing the parameters to this function to a buffer.
+	 *
+	 * @param sb The buffer to append to.
+	 */
+	protected void addParameters(StringBuffer sb) {
+
+		if (params!=null && params.size()>0) {
+			sb.append("<b>Parameters:</b><br>"); // TODO: Localize me
+			for (int i=0; i<getParamCount(); i++) {
+				Parameter param = getParam(i);
+				sb.append("   <b>");
+				sb.append(param.getName()!=null ? param.getName() :
+							param.getType());
+				sb.append("</b> ");
+				String desc = param.getDescription();
+				if (desc!=null) {
+					sb.append(desc);
+				}
+				sb.append("<br>");
+			}
+			sb.append("<br><br>");
+		}
+
+		// TODO: Add description of return type.
+
+	}
+
+
+	/**
+	 * Returns the specified {@link Parameter}.
+	 *
+	 * @param index The index of the parameter to retrieve.
+	 * @return The parameter.
+	 * @see #getParamCount()
+	 */
+	public Parameter getParam(int index) {
+		return (Parameter)params.get(index);
+	}
+
+
+	/**
+	 * Returns the number of parameters to this function.
+	 *
+	 * @return The number of parameters to this function.
+	 * @see #getParam(int)
+	 */
+	public int getParamCount() {
+		return params==null ? 0 : params.size();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getSummary() {
+		StringBuffer sb = new StringBuffer();
+		addDefinitionString(sb);
+		possiblyAddDescription(sb);
+		addParameters(sb);
+		possiblyAddDefinedIn(sb);
+		return sb.toString();
+	}
+
+
+	/**
+	 * Sets the parameters to this function.
+	 *
+	 * @param params The parameters.  This should be a list of
+	 *        {@link Parameter}s.
+	 * @see #getParam(int)
+	 */
+	public void setParams(List params) {
+		// Deep copy so parsing can re-use its array.
+		this.params = new ArrayList(params);
+	}
+
+
+	/**
+	 * A parameter passed to a function.
+	 *
+	 * @author Robert Futrell
+	 * @version 1.0
+	 */
+	public static class Parameter {
+
+		private String name;
+		private String type;
+		private String desc;
+
+		public Parameter(String type, String name) {
+			this.name = name;
+			this.type = type;
+		}
+
+		public String getDescription() {
+			return desc;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public String getType() {
+			return type;
+		}
+
+		public void setDescription(String desc) {
+			this.desc = desc;
+		}
+
+		public String toString() {
+			StringBuffer sb = new StringBuffer();
+			if (type!=null) {
+				sb.append(type);
+			}
+			if (name!=null) {
+				if (type!=null) {
+					sb.append(' ');
+					sb.append(name);
+				}
+			}
+			return sb.toString();
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/ProceduralLanguageCellRenderer.java b/src/org/fife/ui/autocomplete/ProceduralLanguageCellRenderer.java
new file mode 100644
index 0000000..23f8479
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/ProceduralLanguageCellRenderer.java
@@ -0,0 +1,193 @@
+/*
+ * 12/23/2008
+ *
+ * ProceduralLanguageCellRenderer.java - Cell renderer for procedural
+ * languages.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+
+
+/**
+ * A cell renderer for {@link ProceduralLanguageCompletionProvider}.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class ProceduralLanguageCellRenderer extends DefaultListCellRenderer {
+
+	/**
+	 * The alternating background color.
+	 */
+	private Color altBG;
+
+	/**
+	 * The font to use when rendering items.
+	 */
+	private Font font;
+
+
+	/**
+	 * Constructor.
+	 */
+	public ProceduralLanguageCellRenderer() {
+		font = new Font("Monospaced", Font.PLAIN, 12);
+		altBG = new Color(248,248,248);
+	}
+
+
+	/**
+	 * Returns the background color to use on alternating lines.
+	 *
+	 * @return The altnernate background color.  If this is <code>null</code>,
+	 *         alternating colors are not used.
+	 * @see #setAlternateBackground(Color)
+	 */
+	public Color getAlternateBackground() {
+		return altBG;
+	}
+
+
+	/**
+	 * Returns the font used when rendering completions.
+	 *
+	 * @return The font.
+	 * @see #setDisplayFont(Font)
+	 */
+	public Font getDisplayFont() {
+		return font;
+	}
+
+
+	/**
+	 * 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.
+	 */
+	public Component getListCellRendererComponent(JList list, Object value,
+						int index, boolean selected, boolean hasFocus) {
+
+		super.getListCellRendererComponent(list,value,index,selected,hasFocus);
+//		System.out.println(index);
+		setFont(font); // Overrides super's setFont(list.getFont()).
+
+		if (value instanceof FunctionCompletion) {
+			FunctionCompletion fc = (FunctionCompletion)value;
+			StringBuffer sb = new StringBuffer("<html><b><em>" + fc.getName() + "</em></b>");
+			sb.append('(');
+			int paramCount = fc.getParamCount();
+			for (int i=0; i<paramCount; i++) {
+				FunctionCompletion.Parameter param = fc.getParam(i);
+				String type = param.getType();
+				if (type!=null) {
+					if (!selected) {
+						sb.append("<font color='#aa0077'>");
+					}
+					sb.append(type);
+					if (!selected) {
+						sb.append("</font>");
+					}
+				}
+				String name = param.getName();
+				if (name!=null) {
+					if (type!=null) {
+						sb.append(' ');
+						sb.append(name);
+					}
+				}
+				if (i<paramCount-1) {
+					sb.append(", ");
+				}
+			}
+			sb.append(") : ");
+			if (!selected) {
+				sb.append("<font color='#a0a0ff'>");
+			}
+			sb.append(fc.getType());
+			if (!selected) {
+				sb.append("</font>");
+			}
+			setText(sb.toString());
+		}
+
+		else if (value instanceof VariableCompletion) {
+			VariableCompletion vc = (VariableCompletion)value;
+			StringBuffer sb = new StringBuffer("<html><b><em>" + vc.getName() + "</em></b>");
+			if (vc.getType()!=null) {
+				sb.append(" : ");
+				if (!selected) {
+					sb.append("<font color='#a0a0ff'>");
+				}
+				sb.append(vc.getType());
+				if (!selected) {
+					sb.append("</font>");
+				}
+			}
+			setText(sb.toString());
+		}
+
+		if (!selected && (index&1)==0 && altBG!=null) {
+			setBackground(altBG);
+		}
+
+//		if (index==238) {
+//			Thread.dumpStack();
+//		}
+//setIcon(varIcon);
+		return this;
+
+	}
+
+
+	/**
+	 * 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 void setAlternateBackground(Color altBG) {
+		this.altBG = altBG;
+	}
+
+
+	/**
+	 * Sets the font to use when rendering completion items.
+	 *
+	 * @param font The font to use.
+	 * @see #getDisplayFont()
+	 */
+	public void setDisplayFont(Font font) {
+		this.font = font;
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/ProceduralLanguageCompletionProvider.java b/src/org/fife/ui/autocomplete/ProceduralLanguageCompletionProvider.java
new file mode 100644
index 0000000..b9194f6
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/ProceduralLanguageCompletionProvider.java
@@ -0,0 +1,347 @@
+/*
+ * 12/21/2008
+ *
+ * ProceduralLanguageCompletionProvider.java - A provider useful for procedural
+ * languages, such as C.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.ImageIcon;
+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.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * A completion provider that reads an XML file for a procedural language API
+ * (such as C).  That XML file can define the following things:
+ * 
+ * <ul>
+ *    <li>Functions, their parameters, and return types</li>
+ *    <li>Constants (such as <tt>#define</tt>s)</li>
+ *    <li>(Global) variables</li>
+ * </ul>
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ * 
+ */
+/*
+ * TODO: Make chars '(', ')' and ',' configurable.
+ */
+public class ProceduralLanguageCompletionProvider
+									extends AbstractCompletionProvider {
+
+	private Segment seg;
+
+	private String[] dataTypes;
+	private String dataTypeFG;
+	private boolean colorizeHeader;
+
+
+	public ProceduralLanguageCompletionProvider(String fileName) {
+
+		completions = new ArrayList();
+		seg = new Segment();
+
+		setColorizeHeader(false);
+
+		long start = System.currentTimeMillis();
+
+		SAXParserFactory factory = SAXParserFactory.newInstance();
+		XMLParser handler = new XMLParser();
+		try {
+			InputStream in = null;
+			File file = new File(fileName);
+			if (file.isFile()) {
+				in = new FileInputStream(file);
+			}
+			else {
+				ClassLoader cl = getClass().getClassLoader();
+				in = cl.getResourceAsStream(fileName);
+			}
+			try {
+				SAXParser saxParser = factory.newSAXParser();
+				saxParser.parse(in, handler);
+				this.completions = handler.getCompletions();
+				Collections.sort(this.completions);
+			} finally {
+				in.close();
+			}
+		} catch (Throwable err) {
+			err.printStackTrace ();
+		}
+
+		long time = System.currentTimeMillis() - start;
+		System.out.println("XML loaded in: " + time + "ms");
+
+		setListCellRenderer(new ProceduralLanguageCellRenderer());
+
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	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);
+
+	}
+
+
+	private String getColorFor(String text) {
+		int index = dataTypes==null ? -1 : Arrays.binarySearch(dataTypes, text);
+		return index>=0 ? dataTypeFG : null;
+	}
+
+
+	/**
+	 * Returns whether the description area should have its header information
+	 * syntax highlighted.
+	 *
+	 * @return Whether to color the description area's header.
+	 * @see #setColorizeHeader(boolean)
+	 */
+	public boolean getColorizeHeader() {
+		return colorizeHeader;
+	}
+
+
+	/**
+	 * Sets whether the header text of the description area should be
+	 * syntax highlighted.
+	 *
+	 * @param colorize Whether to colorize the header information in the
+	 *        description area.
+	 * @see #getColorizeHeader()
+	 */
+	public void setColorizeHeader(boolean colorize) {
+		colorizeHeader = colorize;
+	}
+
+
+	/**
+	 * Sets the color to use for data types in the header of a description
+	 * window.
+	 *
+	 * @param fg The foreground color to use.
+	 * @see #setDataTypes(String[])
+	 * @see #getColorizeHeader()
+	 */
+	public void setDataTypeForeground(Color fg) {
+		dataTypeFG = Util.getHexString(fg);
+	}
+
+
+	/**
+	 * Sets the identifiers to color as data types in the header of a
+	 * description window.
+	 *
+	 * @param types The identifiers for data types.  If this is
+	 *        <code>null</code>, nothing will be colorized as a data type.
+	 * @see #setDataTypeForeground(Color)
+	 * @see #getColorizeHeader()
+	 */
+	public void setDataTypes(String[] types) {
+		if (types==null) {
+			dataTypes = null;
+		}
+		else {
+			dataTypes = (String[])types.clone();
+			Arrays.sort(dataTypes, String.CASE_INSENSITIVE_ORDER);
+		}
+	}
+
+
+	/**
+	 * Returns whether the specified character is valid in an auto-completion.
+	 * Subclasses can override this method if their language supports a
+	 * different set of valid chars for auto-completable items.
+	 *
+	 * @param ch The character.
+	 * @return Whether the character is valid.
+	 */
+	protected boolean isValidChar(char ch) { 
+		return Character.isLetterOrDigit(ch) || ch=='_';
+	}
+
+
+	/**
+	 * Parser for an XMl file describing a procedural language such as C.
+	 *
+	 * @author Robert Futrell
+	 * @version 1.0
+	 */
+	private class XMLParser extends DefaultHandler {
+
+		private List completions;
+
+		private String name;
+		private String type;
+		private String returnType;
+		private StringBuffer desc;
+		private List params;
+		private String definedIn;
+		private boolean doingKeywords;
+		private boolean inKeyword;
+		private boolean gettingDesc;
+		private boolean gettingParams;
+
+		public XMLParser() {
+			completions = new ArrayList();
+			params = new ArrayList(1);
+			desc = new StringBuffer();
+		}
+
+		public void characters(char[] ch, int start, int length) {
+			if (gettingDesc) {
+				desc.append(ch, start, length);
+			}
+		}
+
+		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)) {
+						FunctionCompletion fc = new FunctionCompletion(
+								ProceduralLanguageCompletionProvider.this, name, returnType);
+						fc.setDescription(desc.toString());
+						fc.setParams(params);
+						fc.setDefinedIn(definedIn);
+						c = fc;
+					}
+					else if ("constant".equals(type)) {
+						VariableCompletion vc = new VariableCompletion(
+								ProceduralLanguageCompletionProvider.this, name, returnType);
+						vc.setDescription(desc.toString());
+						vc.setDefinedIn(definedIn);
+						c = vc;
+					}
+					else {
+						throw new InternalError("Unexpected type: " + type);
+					}
+					completions.add(c);
+					inKeyword = false;
+				}
+				else if (inKeyword) {
+					if ("desc".equals(qName)) {
+						gettingDesc = false;
+					}
+					else if ("params".equals(qName)) {
+						gettingParams = false;
+					}
+				}
+
+			}
+
+		}
+
+		public List getCompletions() {
+			return completions;
+		}
+
+		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 ("desc".equals(qName)) {
+						gettingDesc = true;
+						desc.setLength(0);
+					}
+					else if ("params".equals(qName)) {
+						gettingParams = true;
+					}
+					else if (gettingParams) {
+						if ("param".equals(qName)) {
+							String name = attrs.getValue("name");
+							String type = attrs.getValue("type");
+							FunctionCompletion.Parameter param =
+								new FunctionCompletion.Parameter(type, name);
+							// TODO: Get desc.
+							params.add(param);
+						}
+					}
+				}
+			}
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/ShorthandCompletion.java b/src/org/fife/ui/autocomplete/ShorthandCompletion.java
new file mode 100644
index 0000000..1070bd5
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/ShorthandCompletion.java
@@ -0,0 +1,81 @@
+/*
+ * 12/22/2008
+ *
+ * ShorhandCompletion.java - A completion that is shorthand for some other
+ * text.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+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 WordCompletion {
+
+	/**
+	 * 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;
+	}
+
+
+	/**
+	 * Returns the replacement text.  Subclasses can override this method to
+	 * return a more detailed description.
+	 *
+	 * @return A description of this completion (the text that will be
+	 *         inserted).
+	 * @see #getReplacementText()
+	 */
+	public String getSummary() {
+		return "<html><body><tt>" + getReplacementText();
+	}
+
+
+	/**
+	 * Returns the text the user must start typing to get this completion.
+	 *
+	 * @return The text the user must start to input.
+	 */
+	public String getInputText() {
+		return inputText;
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/SizeGrip.java b/src/org/fife/ui/autocomplete/SizeGrip.java
new file mode 100644
index 0000000..0552b82
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/SizeGrip.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+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.Point;
+import java.awt.Window;
+import java.awt.event.MouseEvent;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+
+/**
+ * A component that allows its parent window to be resizable, similar to the
+ * size grip seen on status bars.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class SizeGrip extends JPanel {
+
+
+	public SizeGrip() {
+		MouseHandler adapter = new MouseHandler();
+		addMouseListener(adapter);
+		addMouseMotionListener(adapter);
+		possiblyFixCursor(ComponentOrientation.getOrientation(getLocale()));
+		setPreferredSize(new Dimension(16, 16));
+	}
+
+
+	/**
+	 * Paints this panel.
+	 *
+	 * @param g The graphics context.
+	 */
+	protected void paintComponent(Graphics g) {
+
+		super.paintComponent(g);
+
+		Dimension dim = getSize();
+		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);
+		}
+
+	}
+
+
+	/**
+	 * Overridden to ensure that the cursor for this component is appropriate
+	 * for the orientation.
+	 *
+	 * @param o The new orientation.
+	 */
+	public void applyComponentOrientation(ComponentOrientation o) {
+		possiblyFixCursor(o);
+		super.applyComponentOrientation(o);
+	}
+
+
+	/**
+	 * Ensures that the cursor for this component is appropriate for the
+	 * orientation.
+	 *
+	 * @param o The new orientation.
+	 */
+	protected void possiblyFixCursor(ComponentOrientation o) {
+		int cursor = Cursor.NE_RESIZE_CURSOR;
+		if (o.isLeftToRight()) {
+			cursor = Cursor.NW_RESIZE_CURSOR;
+		}
+		if (cursor!=getCursor().getType()) {
+			setCursor(Cursor.getPredefinedCursor(cursor));
+		}
+	}
+
+
+	/**
+	 * Listens for mouse events on this panel and resizes the parent window
+	 * appropriately.
+	 *
+	 * @author Robert Futrell
+	 * @version 1.0
+	 */
+	/*
+	 * NOTE: We use SwingUtilities.convertPointToScreen() instead of just using
+	 * 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 javax.swing.event.MouseInputAdapter {
+
+		private Point origPos;
+
+		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
+				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);
+				// invalidate()/revalidate() needed pre-1.6.
+				wind.invalidate();
+				wind.validate();
+			}
+			origPos.setLocation(newPos);
+		}
+
+		public void mousePressed(MouseEvent e) {
+			origPos = e.getPoint();
+			SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
+		}
+
+		public void mouseReleased(MouseEvent e) {
+			origPos = null;
+		}
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/Util.java b/src/org/fife/ui/autocomplete/Util.java
new file mode 100644
index 0000000..e23aa40
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/Util.java
@@ -0,0 +1,153 @@
+/*
+ * 12/21/2008
+ *
+ * Util.java - Utility methods for the autocompletion package.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.awt.Color;
+import java.lang.reflect.Method;
+import java.net.URI;
+
+
+/**
+ * Utility methods for the autocomplete framework.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class Util {
+
+	private static boolean desktopCreationAttempted;
+	private static Object desktop;
+	private static final Object LOCK_DESKTOP_CREATION = new Object();
+
+
+	/**
+	 * 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;
+		}
+
+		StringBuffer sb = new StringBuffer("#");
+		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();
+
+	}
+
+
+	/**
+	 * 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", null);
+
+					boolean supported = ((Boolean)m.invoke(null, null)).
+												booleanValue();
+					if (supported) {
+						m = desktopClazz.getDeclaredMethod("getDesktop", null);
+						desktop = m.invoke(null, null);
+					}
+
+				} catch (RuntimeException re) {
+					throw re; // Keep FindBugs happy
+				} catch (Exception e) {
+					// Ignore; keeps desktop as null.
+				}
+
+			}
+
+		}
+
+		return desktop;
+
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/VariableCompletion.java b/src/org/fife/ui/autocomplete/VariableCompletion.java
new file mode 100644
index 0000000..68e7d92
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/VariableCompletion.java
@@ -0,0 +1,193 @@
+/*
+ * 12/22/2008
+ *
+ * VariableCompletion.java - A completion for a variable.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+
+/**
+ * A completion for a variable (or constant) in a programming language.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class VariableCompletion extends AbstractCompletion {
+
+	private String name;
+	private String type;
+	private String desc;
+	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);
+		this.name = name;
+		this.type = type;
+	}
+
+
+	protected void addDefinitionString(StringBuffer sb) {
+
+		sb.append("<html><b>");
+
+		// Add the return type if applicable (C macros like NULL have no type).
+		if (type!=null) {
+			sb.append(type);
+			sb.append(' ');
+		}
+
+		// Add the item being described's name.
+		sb.append(name);
+
+		sb.append("</b>");
+
+	}
+
+
+	/**
+	 * 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 variable.
+	 *
+	 * @return The name.
+	 */
+	public String getName() {
+		return name;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getSummary() {
+		StringBuffer sb = new StringBuffer();
+		addDefinitionString(sb);
+		possiblyAddDescription(sb);
+		possiblyAddDefinedIn(sb);
+		return sb.toString();
+	}
+
+
+	/**
+	 * Returns the type of this variable.
+	 *
+	 * @return The type.
+	 */
+	public String getType() {
+		return type;
+	}
+
+
+	/**
+	 * Returns the name of this variable.
+	 *
+	 * @return The text to autocomplete with.
+	 */
+	public String getReplacementText() {
+		return getName();
+	}
+
+
+	/**
+	 * Adds some HTML describing where this variable is defined, if this
+	 * information is known.
+	 *
+	 * @param sb The buffer to append to.
+	 */
+	protected void possiblyAddDefinedIn(StringBuffer 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(StringBuffer 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 variable.  This should be an
+	 * HTML snippet.
+	 *
+	 * @param desc A short description of this variable.  This may be
+	 *        <code>null</code>.
+	 * @see #getDescription()
+	 */
+	public void setDescription(String desc) {
+		this.desc = desc;
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/WordCompletion.java b/src/org/fife/ui/autocomplete/WordCompletion.java
new file mode 100644
index 0000000..a507d91
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/WordCompletion.java
@@ -0,0 +1,73 @@
+/*
+ * 12/21/2008
+ *
+ * WordCompletion.java - A completion for a single word.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+
+/**
+ * A completion for a single word.  Input that matches any number of the
+ * leading characters of this completion will match it.<p>
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class WordCompletion extends AbstractCompletion {
+
+	/**
+	 * The word that can be auto-completed.
+	 */
+	private String replacementText;
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param provider The provider that returns this completion.
+	 * @param replacementText The text to be made auto-completable.
+	 */
+	public WordCompletion(CompletionProvider provider, String replacementText) {
+		super(provider);
+		this.replacementText = replacementText;
+	}
+
+
+	/**
+	 * Returns <code>null</code> always.  Subclasses can override this method
+	 * if they wish to provide a description.
+	 *
+	 * @return This item's description.  This will always be
+	 *         <code>null</code>.
+	 */
+	public String getSummary() {
+		return null;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getReplacementText() {
+		return replacementText;
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/WordCompletionProvider.java b/src/org/fife/ui/autocomplete/WordCompletionProvider.java
new file mode 100644
index 0000000..70e68ad
--- /dev/null
+++ b/src/org/fife/ui/autocomplete/WordCompletionProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 12/21/2008
+ *
+ * WordCompletionProvider.java - A simple provider that lets the user choose
+ * from a list of words.
+ * Copyright (C) 2008 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ */
+package org.fife.ui.autocomplete;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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;
+
+
+/**
+ * A completion provider that simply uses a list of words.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class WordCompletionProvider extends AbstractCompletionProvider {
+
+	protected Segment seg;
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param words The words to offer as completion suggestions.  This
+	 *        cannot be <code>null</code>.
+	 */
+	public WordCompletionProvider(String[] words) {
+		completions = createCompletions(words);
+		seg = new Segment();
+	}
+
+
+	/**
+	 * Creates the completion array for an array of words.
+	 *
+	 * @param words The words.
+	 * @return The <tt>Completion</tt> list.  This will be sorted
+	 *         alphabetically.
+	 */
+	protected List createCompletions(String[] words) {
+		List completions = new ArrayList(words.length);
+		for (int i=0; i<words.length; i++) {
+			completions.add(new WordCompletion(this, words[i]));
+		}
+		Collections.sort(completions);
+		return completions;
+	}
+
+
+	/**
+	 * 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 metched
+	 * by  {@link #isValidChar(char)}.
+	 *
+	 * @param comp The text component.
+	 * @return The text.
+	 */
+	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);
+
+	}
+
+
+	/**
+	 * 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=='_';
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/arrow_left.png b/src/org/fife/ui/autocomplete/arrow_left.png
new file mode 100644
index 0000000..5dc6967
Binary files /dev/null and b/src/org/fife/ui/autocomplete/arrow_left.png differ
diff --git a/src/org/fife/ui/autocomplete/arrow_right.png b/src/org/fife/ui/autocomplete/arrow_right.png
new file mode 100644
index 0000000..b1a1819
Binary files /dev/null and b/src/org/fife/ui/autocomplete/arrow_right.png differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/autocomplete.git



More information about the pkg-java-commits mailing list