PageRenderTime 102ms CodeModel.GetById 83ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/gui/CompleteWord.java

#
Java | 508 lines | 376 code | 71 blank | 61 comment | 68 complexity | 0cb899081f303b6e9305e5649baaddb6 MD5 | raw file
  1/*
  2 * CompleteWord.java - Complete word dialog
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2000, 2001 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.gui;
 24
 25//{{{ Imports
 26import java.awt.Component;
 27import java.awt.Font;
 28import java.awt.Point;
 29
 30import java.awt.event.KeyEvent;
 31
 32import java.util.HashSet;
 33import java.util.Set;
 34import java.util.TreeSet;
 35import java.util.Arrays;
 36import java.util.Collection;
 37
 38import javax.swing.DefaultListCellRenderer;
 39import javax.swing.JList;
 40import javax.swing.SwingUtilities;
 41
 42import org.gjt.sp.jedit.Buffer;
 43import org.gjt.sp.jedit.EditPane;
 44import org.gjt.sp.jedit.visitors.JEditVisitorAdapter;
 45import org.gjt.sp.jedit.jEdit;
 46import org.gjt.sp.jedit.MiscUtilities;
 47import org.gjt.sp.jedit.TextUtilities;
 48import org.gjt.sp.jedit.View;
 49
 50import org.gjt.sp.jedit.syntax.KeywordMap;
 51
 52import org.gjt.sp.jedit.textarea.JEditTextArea;
 53
 54import org.gjt.sp.util.StandardUtilities;
 55//}}}
 56
 57/**
 58 * A completion popup class.
 59 */
 60public class CompleteWord extends CompletionPopup
 61{
 62	//{{{ completeWord() method
 63	public static void completeWord(View view)
 64	{
 65		JEditTextArea textArea = view.getTextArea();
 66		Buffer buffer = view.getBuffer();
 67		int caretLine = textArea.getCaretLine();
 68		int caret = textArea.getCaretPosition();
 69
 70		if(!buffer.isEditable())
 71		{
 72			textArea.getToolkit().beep();
 73			return;
 74		}
 75
 76		KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
 77		String noWordSep = getNonAlphaNumericWordChars(
 78			buffer,keywordMap);
 79		String word = getWordToComplete(buffer,caretLine,
 80			caret,noWordSep);
 81		if(word == null)
 82		{
 83			textArea.getToolkit().beep();
 84			return;
 85		}
 86
 87		Completion[] completions = getCompletions(buffer,word,caret);
 88
 89		if(completions.length == 0)
 90		{
 91			textArea.getToolkit().beep();
 92		}
 93		//{{{ if there is only one competion, insert in buffer
 94		else if(completions.length == 1)
 95		{
 96			Completion c = completions[0];
 97
 98			if(c.text.equals(word))
 99			{
100				textArea.getToolkit().beep();
101			}
102			else
103			{
104				textArea.replaceSelection(c.text.substring(
105					word.length()));
106			}
107		} //}}}
108		//{{{ show popup if > 1
109		else
110		{
111			String longestPrefix = MiscUtilities.getLongestPrefix(
112				completions,
113				keywordMap != null
114				? keywordMap.getIgnoreCase()
115				: false);
116
117			if (word.length() < longestPrefix.length())
118			{
119				buffer.insert(caret,longestPrefix.substring(
120					word.length()));
121			}
122
123			textArea.scrollToCaret(false);
124			Point location = textArea.offsetToXY(
125				caret - word.length());
126			location.y += textArea.getPainter().getLineHeight();
127
128			SwingUtilities.convertPointToScreen(location,
129				textArea.getPainter());
130			new CompleteWord(view,longestPrefix,
131				completions,location,noWordSep);
132		} //}}}
133	} //}}}
134
135	//{{{ CompleteWord constructor
136	public CompleteWord(View view, String word, Completion[] completions,
137		Point location, String noWordSep)
138	{
139		super(view, location);
140
141		this.noWordSep = noWordSep;
142		this.view = view;
143		this.textArea = view.getTextArea();
144		this.buffer = view.getBuffer();
145		this.word = word;
146
147		reset(new Words(completions), true);
148	} //}}}
149
150	//{{{ Private members
151
152	//{{{ getNonAlphaNumericWordChars() method
153	private static String getNonAlphaNumericWordChars(Buffer buffer,
154		KeywordMap keywordMap)
155	{
156		// figure out what constitutes a word character and what
157		// doesn't
158		String noWordSep = buffer.getStringProperty("noWordSep");
159		if(noWordSep == null)
160			noWordSep = "";
161		if(keywordMap != null)
162		{
163			String keywordNoWordSep = keywordMap.getNonAlphaNumericChars();
164			if(keywordNoWordSep != null)
165				noWordSep += keywordNoWordSep;
166		}
167
168		return noWordSep;
169	} //}}}
170
171	//{{{ getWordToComplete() method
172	private static String getWordToComplete(Buffer buffer, int caretLine,
173		int caret, String noWordSep)
174	{
175		CharSequence line = buffer.getLineSegment(caretLine);
176		int dot = caret - buffer.getLineStartOffset(caretLine);
177		if(dot == 0)
178			return null;
179
180		char ch = line.charAt(dot-1);
181		if(!Character.isLetterOrDigit(ch)
182			&& noWordSep.indexOf(ch) == -1)
183		{
184			// attempting to expand non-word char
185			return null;
186		}
187
188		int wordStart = TextUtilities.findWordStart(line,dot-1,noWordSep);
189		CharSequence word = line.subSequence(wordStart,dot);
190		if(word.length() == 0)
191			return null;
192
193		return word.toString();
194	} //}}}
195	
196	//{{{ getVisibleBuffers() method
197	private static Collection<Buffer> getVisibleBuffers()
198	{
199		final Set<Buffer> buffers = new HashSet<Buffer>();
200		jEdit.visit(new JEditVisitorAdapter()
201			{
202				@Override
203				public void visit(EditPane editPane)
204				{
205					buffers.add(editPane.getBuffer());
206				}
207			});
208		return buffers;
209	} //}}}
210
211	//{{{ getCompletions() method
212	private static Completion[] getCompletions(final Buffer buffer, final String word,
213		final int caret)
214	{
215		// build a list of unique words in all buffers, or visible buffers,
216		// depending on completeFromAllBuffers
217		final Set<Completion> completions = new TreeSet<Completion>(new StandardUtilities
218			.StringCompare<Completion>());
219
220		// only complete current buffer's keyword map
221		final KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
222		final String noWordSep = getNonAlphaNumericWordChars(
223			buffer,keywordMap);
224		
225		final Collection<Buffer> sourceBuffers = 
226			jEdit.getBooleanProperty("completeFromAllBuffers") ?
227				Arrays.asList(jEdit.getBuffers()) :
228				getVisibleBuffers();
229
230		for (Buffer b : sourceBuffers)
231		{
232			// only complete current buffer's keyword map
233			KeywordMap _keywordMap;
234			if(b == buffer)
235				_keywordMap = keywordMap;
236			else
237				_keywordMap = null;
238
239			int offset = (b == buffer ? caret : 0);
240
241			getCompletions(b,word,keywordMap,noWordSep,
242					offset,completions);
243		}
244
245		Completion[] completionArray = completions
246			.toArray(new Completion[completions.size()]);
247
248		return completionArray;
249	} //}}}
250
251	//{{{ getCompletions() method
252	private static void getCompletions(Buffer buffer, String word,
253		KeywordMap keywordMap, String noWordSep, int caret,
254		Set<Completion> completions)
255	{
256		int wordLen = word.length();
257
258		//{{{ try to find matching keywords
259		if(keywordMap != null)
260		{
261			String[] keywords = keywordMap.getKeywords();
262			for(int i = 0; i < keywords.length; i++)
263			{
264				String _keyword = keywords[i];
265				if(_keyword.regionMatches(keywordMap.getIgnoreCase(),
266					0,word,0,wordLen))
267				{
268					Completion keyword = new Completion(_keyword,true);
269					if(!completions.contains(keyword))
270					{
271						completions.add(keyword);
272					}
273				}
274			}
275		} //}}}
276
277		//{{{ loop through all lines of current buffer
278		for(int i = 0; i < buffer.getLineCount(); i++)
279		{
280			CharSequence line = buffer.getLineSegment(i);
281			int start = buffer.getLineStartOffset(i);
282
283			// check for match at start of line
284
285			if (StandardUtilities.startsWith(line, word) &&
286			    caret != start + word.length())
287			{
288				String _word = completeWord(line,0,noWordSep);
289				Completion comp = new Completion(_word,false);
290
291				// remove duplicates
292				if(!completions.contains(comp))
293				{
294					completions.add(comp);
295				}
296			}
297
298			// check for match inside line
299			int len = line.length() - word.length();
300			for(int j = 0; j < len; j++)
301			{
302				char c = line.charAt(j);
303				if(!Character.isLetterOrDigit(c) && noWordSep.indexOf(c) == -1)
304				{
305					if (StandardUtilities.regionMatches(line,j + 1,word,0,wordLen)
306						&& caret != start + j + word.length() + 1)
307					{
308						String _word = completeWord(line,j + 1,noWordSep);
309						Completion comp = new Completion(_word,false);
310
311						// remove duplicates
312						if(!completions.contains(comp))
313						{
314							completions.add(comp);
315						}
316					}
317				}
318			}
319		} //}}}
320	} //}}}
321
322	//{{{ completeWord() method
323	private static String completeWord(CharSequence line, int offset, String noWordSep)
324	{
325		// '+ 1' so that findWordEnd() doesn't pick up the space at the start
326		int wordEnd = TextUtilities.findWordEnd(line,offset + 1,noWordSep);
327		return line.subSequence(offset,wordEnd).toString();
328	} //}}}
329
330	//{{{ Instance variables
331	private View view;
332	private JEditTextArea textArea;
333	private Buffer buffer;
334	private String word;
335	private String noWordSep;
336	//}}}
337
338	//{{{ Completion class
339	private static class Completion
340	{
341		final String text;
342		final boolean keyword;
343
344		Completion(String text, boolean keyword)
345		{
346			this.text = text;
347			this.keyword = keyword;
348		}
349
350		public String toString()
351		{
352			return text;
353		}
354
355		public int hashCode()
356		{
357			return text.hashCode();
358		}
359
360		public boolean equals(Object obj)
361		{
362			if(obj instanceof Completion)
363				return ((Completion)obj).text.equals(text);
364			else
365				return false;
366		}
367	} //}}}
368
369	//{{{ Words class
370	private class Words implements Candidates
371	{
372		private final DefaultListCellRenderer renderer;
373		private final Completion[] completions;
374
375		public Words(Completion[] completions)
376		{
377			this.renderer = new DefaultListCellRenderer();
378			this.completions = completions;
379		}
380
381		public int getSize()
382		{
383			return completions.length;
384		}
385
386		public boolean isValid()
387		{
388			return true;
389		}
390
391		public void complete(int index)
392		{
393			String insertion = completions[index].toString().substring(word.length());
394			textArea.replaceSelection(insertion);
395		}
396
397		public Component getCellRenderer(JList list, int index,
398			boolean isSelected, boolean cellHasFocus)
399		{
400			renderer.getListCellRendererComponent(list,
401				null, index, isSelected, cellHasFocus);
402
403			Completion comp = completions[index];
404
405			String text = comp.text;
406			Font font = list.getFont();
407
408			if(index < 9)
409				text = (index + 1) + ": " + text;
410			else if(index == 9)
411				text = "0: " + text;
412
413			if(comp.keyword)
414				font = font.deriveFont(Font.BOLD);
415
416			renderer.setText(text);
417			renderer.setFont(font);
418			return renderer;
419		}
420
421		public String getDescription(int index)
422		{
423			return null;
424		}
425	} //}}}
426
427	//{{{ resetWords() method
428	private void resetWords(String newWord)
429	{
430		int caret = textArea.getCaretPosition();
431		Completion[] completions = getCompletions(
432			buffer,newWord,caret);
433		if(completions.length > 0)
434		{
435			word = newWord;
436			reset(new Words(completions), true);
437		}
438		else
439		{
440			dispose();
441		}
442	} //}}}
443
444	//}}}
445
446	//{{{ keyPressed() medhod
447	protected void keyPressed(KeyEvent e)
448	{
449		if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE)
450		{
451			textArea.backspace();
452			e.consume();
453
454			if(word.length() == 1)
455			{
456				dispose();
457			}
458			else
459			{
460				resetWords(word.substring(0,word.length() - 1));
461			}
462		}
463	} //}}}
464
465	//{{{ keyTyped() medhod
466	protected void keyTyped(KeyEvent e)
467	{
468		char ch = e.getKeyChar();
469		if(Character.isDigit(ch))
470		{
471			int index = ch - '0';
472			if(index == 0)
473				index = 9;
474			else
475				index--;
476			if(index < getCandidates().getSize())
477			{
478				setSelectedIndex(index);
479				if(doSelectedCompletion())
480				{
481					e.consume();
482					dispose();
483				}
484				return;
485			}
486			else
487				/* fall through */;
488		}
489
490		// \t handled above
491		if(ch != '\b' && ch != '\t')
492		{
493			/* eg, foo<C+b>, will insert foobar, */
494			if(!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)
495			{
496				doSelectedCompletion();
497				textArea.userInput(ch);
498				e.consume();
499				dispose();
500				return;
501			}
502
503			textArea.userInput(ch);
504			e.consume();
505			resetWords(word + ch);
506		}
507	} //}}}
508}