001: /*
002: * CompleteWord.java - Complete word dialog
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2000, 2001 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.gui;
024:
025: //{{{ Imports
026: import java.awt.Component;
027: import java.awt.Font;
028: import java.awt.Point;
029:
030: import java.awt.event.KeyEvent;
031:
032: import java.util.HashSet;
033: import java.util.Set;
034: import java.util.TreeSet;
035:
036: import javax.swing.DefaultListCellRenderer;
037: import javax.swing.JList;
038: import javax.swing.SwingUtilities;
039:
040: import org.gjt.sp.jedit.Buffer;
041: import org.gjt.sp.jedit.EditPane;
042: import org.gjt.sp.jedit.jEdit;
043: import org.gjt.sp.jedit.MiscUtilities;
044: import org.gjt.sp.jedit.TextUtilities;
045: import org.gjt.sp.jedit.View;
046:
047: import org.gjt.sp.jedit.syntax.KeywordMap;
048:
049: import org.gjt.sp.jedit.textarea.JEditTextArea;
050:
051: import org.gjt.sp.util.StandardUtilities;
052:
053: //}}}
054:
055: /**
056: * A completion popup class.
057: */
058: public class CompleteWord extends CompletionPopup {
059: //{{{ completeWord() method
060: public static void completeWord(View view) {
061: JEditTextArea textArea = view.getTextArea();
062: Buffer buffer = view.getBuffer();
063: int caretLine = textArea.getCaretLine();
064: int caret = textArea.getCaretPosition();
065:
066: if (!buffer.isEditable()) {
067: textArea.getToolkit().beep();
068: return;
069: }
070:
071: KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
072: String noWordSep = getNonAlphaNumericWordChars(buffer,
073: keywordMap);
074: String word = getWordToComplete(buffer, caretLine, caret,
075: noWordSep);
076: if (word == null) {
077: textArea.getToolkit().beep();
078: return;
079: }
080:
081: Completion[] completions = getCompletions(buffer, word, caret);
082:
083: if (completions.length == 0) {
084: textArea.getToolkit().beep();
085: }
086: //{{{ if there is only one competion, insert in buffer
087: else if (completions.length == 1) {
088: Completion c = completions[0];
089:
090: if (c.text.equals(word)) {
091: textArea.getToolkit().beep();
092: } else {
093: textArea.setSelectedText(c.text
094: .substring(word.length()));
095: }
096: } //}}}
097: //{{{ show popup if > 1
098: else {
099: String longestPrefix = MiscUtilities.getLongestPrefix(
100: completions, keywordMap != null ? keywordMap
101: .getIgnoreCase() : false);
102:
103: if (word.length() < longestPrefix.length()) {
104: buffer.insert(caret, longestPrefix.substring(word
105: .length()));
106: }
107:
108: textArea.scrollToCaret(false);
109: Point location = textArea.offsetToXY(caret - word.length());
110: location.y += textArea.getPainter().getFontMetrics()
111: .getHeight();
112:
113: SwingUtilities.convertPointToScreen(location, textArea
114: .getPainter());
115: new CompleteWord(view, longestPrefix, completions,
116: location, noWordSep);
117: } //}}}
118: } //}}}
119:
120: //{{{ CompleteWord constructor
121: public CompleteWord(View view, String word,
122: Completion[] completions, Point location, String noWordSep) {
123: super (view, location);
124:
125: this .noWordSep = noWordSep;
126: this .view = view;
127: this .textArea = view.getTextArea();
128: this .buffer = view.getBuffer();
129: this .word = word;
130:
131: reset(new Words(completions), true);
132: } //}}}
133:
134: //{{{ Private members
135:
136: //{{{ getNonAlphaNumericWordChars() method
137: private static String getNonAlphaNumericWordChars(Buffer buffer,
138: KeywordMap keywordMap) {
139: // figure out what constitutes a word character and what
140: // doesn't
141: String noWordSep = buffer.getStringProperty("noWordSep");
142: if (noWordSep == null)
143: noWordSep = "";
144: if (keywordMap != null) {
145: String keywordNoWordSep = keywordMap
146: .getNonAlphaNumericChars();
147: if (keywordNoWordSep != null)
148: noWordSep += keywordNoWordSep;
149: }
150:
151: return noWordSep;
152: } //}}}
153:
154: //{{{ getWordToComplete() method
155: private static String getWordToComplete(Buffer buffer,
156: int caretLine, int caret, String noWordSep) {
157: String line = buffer.getLineText(caretLine);
158: int dot = caret - buffer.getLineStartOffset(caretLine);
159: if (dot == 0)
160: return null;
161:
162: char ch = line.charAt(dot - 1);
163: if (!Character.isLetterOrDigit(ch)
164: && noWordSep.indexOf(ch) == -1) {
165: // attempting to expand non-word char
166: return null;
167: }
168:
169: int wordStart = TextUtilities.findWordStart(line, dot - 1,
170: noWordSep);
171: String word = line.substring(wordStart, dot);
172: if (word.length() == 0)
173: return null;
174:
175: return word;
176: } //}}}
177:
178: //{{{ getCompletions() method
179: private static Completion[] getCompletions(Buffer buffer,
180: String word, int caret) {
181: // build a list of unique words in all visible buffers
182: Set<Completion> completions = new TreeSet<Completion>(
183: new StandardUtilities.StringCompare());
184: Set<Buffer> buffers = new HashSet<Buffer>();
185:
186: // only complete current buffer's keyword map
187: KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
188: String noWordSep = getNonAlphaNumericWordChars(buffer,
189: keywordMap);
190:
191: View views = jEdit.getFirstView();
192: while (views != null) {
193: EditPane[] panes = views.getEditPanes();
194: for (int i = 0; i < panes.length; i++) {
195: Buffer b = panes[i].getBuffer();
196: if (buffers.contains(b))
197: continue;
198:
199: buffers.add(b);
200:
201: // only complete current buffer's keyword map
202: KeywordMap _keywordMap;
203: if (b == buffer)
204: _keywordMap = keywordMap;
205: else
206: _keywordMap = null;
207:
208: int offset = (b == buffer ? caret : 0);
209:
210: getCompletions(b, word, keywordMap, noWordSep, offset,
211: completions);
212: }
213:
214: views = views.getNext();
215: }
216:
217: Completion[] completionArray = completions
218: .toArray(new Completion[completions.size()]);
219:
220: return completionArray;
221: } //}}}
222:
223: //{{{ getCompletions() method
224: private static void getCompletions(Buffer buffer, String word,
225: KeywordMap keywordMap, String noWordSep, int caret,
226: Set<Completion> completions) {
227: int wordLen = word.length();
228:
229: //{{{ try to find matching keywords
230: if (keywordMap != null) {
231: String[] keywords = keywordMap.getKeywords();
232: for (int i = 0; i < keywords.length; i++) {
233: String _keyword = keywords[i];
234: if (_keyword.regionMatches(keywordMap.getIgnoreCase(),
235: 0, word, 0, wordLen)) {
236: Completion keyword = new Completion(_keyword, true);
237: if (!completions.contains(keyword)) {
238: completions.add(keyword);
239: }
240: }
241: }
242: } //}}}
243:
244: //{{{ loop through all lines of current buffer
245: for (int i = 0; i < buffer.getLineCount(); i++) {
246: String line = buffer.getLineText(i);
247: int start = buffer.getLineStartOffset(i);
248:
249: // check for match at start of line
250:
251: if (line.startsWith(word) && caret != start + word.length()) {
252: String _word = completeWord(line, 0, noWordSep);
253: Completion comp = new Completion(_word, false);
254:
255: // remove duplicates
256: if (!completions.contains(comp)) {
257: completions.add(comp);
258: }
259: }
260:
261: // check for match inside line
262: int len = line.length() - word.length();
263: for (int j = 0; j < len; j++) {
264: char c = line.charAt(j);
265: if (!Character.isLetterOrDigit(c)
266: && noWordSep.indexOf(c) == -1) {
267: if (line.regionMatches(j + 1, word, 0, wordLen)
268: && caret != start + j + word.length() + 1) {
269: String _word = completeWord(line, j + 1,
270: noWordSep);
271: Completion comp = new Completion(_word, false);
272:
273: // remove duplicates
274: if (!completions.contains(comp)) {
275: completions.add(comp);
276: }
277: }
278: }
279: }
280: } //}}}
281: } //}}}
282:
283: //{{{ completeWord() method
284: private static String completeWord(String line, int offset,
285: String noWordSep) {
286: // '+ 1' so that findWordEnd() doesn't pick up the space at the start
287: int wordEnd = TextUtilities.findWordEnd(line, offset + 1,
288: noWordSep);
289: return line.substring(offset, wordEnd);
290: } //}}}
291:
292: //{{{ Instance variables
293: private View view;
294: private JEditTextArea textArea;
295: private Buffer buffer;
296: private String word;
297: private String noWordSep;
298:
299: //}}}
300:
301: //{{{ Completion class
302: private static class Completion {
303: final String text;
304: final boolean keyword;
305:
306: Completion(String text, boolean keyword) {
307: this .text = text;
308: this .keyword = keyword;
309: }
310:
311: public String toString() {
312: return text;
313: }
314:
315: public int hashCode() {
316: return text.hashCode();
317: }
318:
319: public boolean equals(Object obj) {
320: if (obj instanceof Completion)
321: return ((Completion) obj).text.equals(text);
322: else
323: return false;
324: }
325: } //}}}
326:
327: //{{{ Words class
328: private class Words implements Candidates {
329: private final DefaultListCellRenderer renderer;
330: private final Completion[] completions;
331:
332: public Words(Completion[] completions) {
333: this .renderer = new DefaultListCellRenderer();
334: this .completions = completions;
335: }
336:
337: public int getSize() {
338: return completions.length;
339: }
340:
341: public boolean isValid() {
342: return true;
343: }
344:
345: public void complete(int index) {
346: String insertion = completions[index].toString().substring(
347: word.length());
348: textArea.setSelectedText(insertion);
349: }
350:
351: public Component getCellRenderer(JList list, int index,
352: boolean isSelected, boolean cellHasFocus) {
353: renderer.getListCellRendererComponent(list, null, index,
354: isSelected, cellHasFocus);
355:
356: Completion comp = completions[index];
357:
358: String text = comp.text;
359: Font font = list.getFont();
360:
361: if (index < 9)
362: text = (index + 1) + ": " + text;
363: else if (index == 9)
364: text = "0: " + text;
365:
366: if (comp.keyword)
367: font = font.deriveFont(Font.BOLD);
368:
369: renderer.setText(text);
370: renderer.setFont(font);
371: return renderer;
372: }
373:
374: public String getDescription(int index) {
375: return null;
376: }
377: } //}}}
378:
379: //{{{ resetWords() method
380: private void resetWords(String newWord) {
381: int caret = textArea.getCaretPosition();
382: Completion[] completions = getCompletions(buffer, newWord,
383: caret);
384: if (completions.length > 0) {
385: word = newWord;
386: reset(new Words(completions), true);
387: } else {
388: dispose();
389: }
390: } //}}}
391:
392: //}}}
393:
394: //{{{ keyPressed() medhod
395: protected void keyPressed(KeyEvent e) {
396: if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
397: textArea.backspace();
398: e.consume();
399:
400: if (word.length() == 1) {
401: dispose();
402: } else {
403: resetWords(word.substring(0, word.length() - 1));
404: }
405: }
406: } //}}}
407:
408: //{{{ keyTyped() medhod
409: protected void keyTyped(KeyEvent e) {
410: char ch = e.getKeyChar();
411: if (Character.isDigit(ch)) {
412: int index = ch - '0';
413: if (index == 0)
414: index = 9;
415: else
416: index--;
417: if (index < getCandidates().getSize()) {
418: setSelectedIndex(index);
419: if (doSelectedCompletion()) {
420: dispose();
421: }
422: return;
423: } else
424: /* fall through */;
425: }
426:
427: // \t handled above
428: if (ch != '\b' && ch != '\t') {
429: /* eg, foo<C+b>, will insert foobar, */
430: if (!Character.isLetterOrDigit(ch)
431: && noWordSep.indexOf(ch) == -1) {
432: doSelectedCompletion();
433: textArea.userInput(ch);
434: e.consume();
435: dispose();
436: return;
437: }
438:
439: textArea.userInput(ch);
440: e.consume();
441: resetWords(word + ch);
442: }
443: } //}}}
444: }
|