/* * CompleteWord.java - Complete word dialog * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2000, 2001 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.gui; //{{{ Imports import java.awt.Component; import java.awt.Font; import java.awt.Point; import java.awt.event.KeyEvent; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import java.util.Arrays; import java.util.Collection; import javax.swing.DefaultListCellRenderer; import javax.swing.JList; import javax.swing.SwingUtilities; import org.gjt.sp.jedit.Buffer; import org.gjt.sp.jedit.EditPane; import org.gjt.sp.jedit.visitors.JEditVisitorAdapter; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.MiscUtilities; import org.gjt.sp.jedit.TextUtilities; import org.gjt.sp.jedit.View; import org.gjt.sp.jedit.syntax.KeywordMap; import org.gjt.sp.jedit.textarea.JEditTextArea; import org.gjt.sp.util.StandardUtilities; //}}} /** A word completion popup. */ public class CompleteWord extends CompletionPopup { //{{{ completeWord() method public static void completeWord(View view) { JEditTextArea textArea = view.getTextArea(); Buffer buffer = view.getBuffer(); int caretLine = textArea.getCaretLine(); int caret = textArea.getCaretPosition(); if(!buffer.isEditable()) { textArea.getToolkit().beep(); return; } KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret); String noWordSep = getNonAlphaNumericWordChars( buffer,keywordMap); String word = getWordToComplete(buffer,caretLine, caret,noWordSep); if(word == null) { textArea.getToolkit().beep(); return; } Completion[] completions = getCompletions(buffer,word,caret); if(completions.length == 0) { textArea.getToolkit().beep(); } //{{{ if there is only one competion, insert in buffer else if(completions.length == 1) { Completion c = completions[0]; if(c.text.equals(word)) { textArea.getToolkit().beep(); } else { textArea.replaceSelection(c.text.substring( word.length())); } } //}}} //{{{ show popup if > 1 else { String longestPrefix = MiscUtilities.getLongestPrefix( completions, keywordMap != null && keywordMap.getIgnoreCase()); if (word.length() < longestPrefix.length()) { buffer.insert(caret,longestPrefix.substring( word.length())); } textArea.scrollToCaret(false); Point location = textArea.offsetToXY( caret - word.length()); location.y += textArea.getPainter().getLineHeight(); SwingUtilities.convertPointToScreen(location, textArea.getPainter()); new CompleteWord(view,longestPrefix, completions,location,noWordSep); } //}}} } //}}} //{{{ CompleteWord constructor public CompleteWord(View view, String word, Completion[] completions, Point location, String noWordSep) { super(view, location); this.noWordSep = noWordSep; this.view = view; this.textArea = view.getTextArea(); this.buffer = view.getBuffer(); this.word = word; reset(new Words(completions), true); } //}}} //{{{ Private members //{{{ getNonAlphaNumericWordChars() method private static String getNonAlphaNumericWordChars(Buffer buffer, KeywordMap keywordMap) { // figure out what constitutes a word character and what // doesn't String noWordSep = buffer.getStringProperty("noWordSep"); if(noWordSep == null) noWordSep = ""; if(keywordMap != null) { String keywordNoWordSep = keywordMap.getNonAlphaNumericChars(); if(keywordNoWordSep != null) noWordSep += keywordNoWordSep; } return noWordSep; } //}}} //{{{ getWordToComplete() method private static String getWordToComplete(Buffer buffer, int caretLine, int caret, String noWordSep) { CharSequence line = buffer.getLineSegment(caretLine); int dot = caret - buffer.getLineStartOffset(caretLine); if(dot == 0) return null; char ch = line.charAt(dot-1); if(!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1) { // attempting to expand non-word char return null; } int wordStart = TextUtilities.findWordStart(line,dot-1,noWordSep); CharSequence word = line.subSequence(wordStart,dot); if(word.length() == 0) return null; return word.toString(); } //}}} //{{{ getVisibleBuffers() method private static Collection<Buffer> getVisibleBuffers() { final Set<Buffer> buffers = new HashSet<Buffer>(); jEdit.visit(new JEditVisitorAdapter() { @Override public void visit(EditPane editPane) { buffers.add(editPane.getBuffer()); } }); return buffers; } //}}} //{{{ getCompletions() method private static Completion[] getCompletions(final Buffer buffer, final String word, final int caret) { // build a list of unique words in all buffers, or visible buffers, // depending on completeFromAllBuffers final Set<Completion> completions = new TreeSet<Completion>(new StandardUtilities .StringCompare<Completion>()); // only complete current buffer's keyword map final KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret); final String noWordSep = getNonAlphaNumericWordChars( buffer,keywordMap); final Collection<Buffer> sourceBuffers = jEdit.getBooleanProperty("completeFromAllBuffers") ? Arrays.asList(jEdit.getBuffers()) : getVisibleBuffers(); for (Buffer b : sourceBuffers) { // only complete current buffer's keyword map KeywordMap _keywordMap; if(b == buffer) _keywordMap = keywordMap; else _keywordMap = null; int offset = (b == buffer ? caret : 0); getCompletions(b,word,keywordMap,noWordSep, offset,completions); } Completion[] completionArray = completions .toArray(new Completion[completions.size()]); return completionArray; } //}}} //{{{ getCompletions() method private static void getCompletions(Buffer buffer, String word, KeywordMap keywordMap, String noWordSep, int caret, Set<Completion> completions) { int wordLen = word.length(); //{{{ try to find matching keywords if(keywordMap != null) { String[] keywords = keywordMap.getKeywords(); for (String _keyword : keywords) { if (_keyword.regionMatches(keywordMap.getIgnoreCase(), 0, word, 0, wordLen)) { Completion keyword = new Completion(_keyword, true); if (!completions.contains(keyword)) completions.add(keyword); } } } //}}} //{{{ loop through all lines of current buffer for(int i = 0; i < buffer.getLineCount(); i++) { CharSequence line = buffer.getLineSegment(i); int start = buffer.getLineStartOffset(i); // check for match at start of line if (StandardUtilities.startsWith(line, word) && caret != start + word.length()) { String _word = completeWord(line,0,noWordSep); Completion comp = new Completion(_word,false); // remove duplicates if(!completions.contains(comp)) { completions.add(comp); } } // check for match inside line int len = line.length() - word.length(); for(int j = 0; j < len; j++) { char c = line.charAt(j); if(!Character.isLetterOrDigit(c) && noWordSep.indexOf(c) == -1) { if (StandardUtilities.regionMatches(line,j + 1,word,0,wordLen) && caret != start + j + word.length() + 1) { String _word = completeWord(line,j + 1,noWordSep); Completion comp = new Completion(_word,false); // remove duplicates if(!completions.contains(comp)) { completions.add(comp); } } } } } //}}} } //}}} //{{{ completeWord() method private static String completeWord(CharSequence line, int offset, String noWordSep) { // '+ 1' so that findWordEnd() doesn't pick up the space at the start int wordEnd = TextUtilities.findWordEnd(line,offset + 1,noWordSep); return line.subSequence(offset,wordEnd).toString(); } //}}} //{{{ Instance variables private View view; private JEditTextArea textArea; private Buffer buffer; private String word; private String noWordSep; //}}} //{{{ Completion class private static class Completion { final String text; final boolean keyword; Completion(String text, boolean keyword) { this.text = text; this.keyword = keyword; } public String toString() { return text; } public int hashCode() { return text.hashCode(); } public boolean equals(Object obj) { if(obj instanceof Completion) return ((Completion)obj).text.equals(text); else return false; } } //}}} //{{{ Words class private class Words implements Candidates { private final DefaultListCellRenderer renderer; private final Completion[] completions; public Words(Completion[] completions) { this.renderer = new DefaultListCellRenderer(); this.completions = completions; } public int getSize() { return completions.length; } public boolean isValid() { return true; } public void complete(int index) { String insertion = completions[index].toString().substring(word.length()); textArea.replaceSelection(insertion); } public Component getCellRenderer(JList list, int index, boolean isSelected, boolean cellHasFocus) { renderer.getListCellRendererComponent(list, null, index, isSelected, cellHasFocus); Completion comp = completions[index]; String text = comp.text; Font font = list.getFont(); if(index < 9) text = (index + 1) + ": " + text; else if(index == 9) text = "0: " + text; if(comp.keyword) font = font.deriveFont(Font.BOLD); renderer.setText(text); renderer.setFont(font); return renderer; } public String getDescription(int index) { return null; } } //}}} //{{{ resetWords() method private void resetWords(String newWord) { int caret = textArea.getCaretPosition(); Completion[] completions = getCompletions( buffer,newWord,caret); if(completions.length > 0) { word = newWord; reset(new Words(completions), true); } else { dispose(); } } //}}} //}}} //{{{ keyPressed() medhod protected void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { textArea.backspace(); e.consume(); if(word.length() == 1) { dispose(); } else { resetWords(word.substring(0,word.length() - 1)); } } } //}}} //{{{ keyTyped() medhod protected void keyTyped(KeyEvent e) { char ch = e.getKeyChar(); if(jEdit.getBooleanProperty("insertCompletionWithDigit") && Character.isDigit(ch)) { int index = ch - '0'; if(index == 0) index = 9; else index--; if(index < getCandidates().getSize()) { setSelectedIndex(index); if(doSelectedCompletion()) { e.consume(); dispose(); } return; } else /* fall through */; } // \t handled above if(ch != '\b' && ch != '\t') { /* eg, foo<C+b>, will insert foobar, */ if(!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1) { doSelectedCompletion(); textArea.userInput(ch); e.consume(); dispose(); return; } textArea.userInput(ch); e.consume(); resetWords(word + ch); } } //}}} }