PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/gui/CompleteWord.java

#
Java | 610 lines | 455 code | 86 blank | 69 comment | 88 complexity | e7ff71d3e8987881aed3667aff40b28c MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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. package org.gjt.sp.jedit.gui;
  23. //{{{ Imports
  24. import javax.swing.*;
  25. import java.awt.*;
  26. import java.awt.event.*;
  27. import java.util.HashSet;
  28. import java.util.TreeSet;
  29. import java.util.Set;
  30. import org.gjt.sp.jedit.syntax.*;
  31. import org.gjt.sp.jedit.textarea.*;
  32. import org.gjt.sp.jedit.*;
  33. //}}}
  34. public class CompleteWord extends JWindow
  35. {
  36. //{{{ completeWord() method
  37. public static void completeWord(View view)
  38. {
  39. JEditTextArea textArea = view.getTextArea();
  40. Buffer buffer = view.getBuffer();
  41. int caretLine = textArea.getCaretLine();
  42. int caret = textArea.getCaretPosition();
  43. if(!buffer.isEditable())
  44. {
  45. textArea.getToolkit().beep();
  46. return;
  47. }
  48. KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
  49. String noWordSep = getNonAlphaNumericWordChars(
  50. buffer,keywordMap);
  51. String word = getWordToComplete(buffer,caretLine,
  52. caret,noWordSep);
  53. if(word == null)
  54. {
  55. textArea.getToolkit().beep();
  56. return;
  57. }
  58. Completion[] completions = getCompletions(buffer,word,caret);
  59. if(completions.length == 0)
  60. {
  61. textArea.getToolkit().beep();
  62. }
  63. //{{{ if there is only one competion, insert in buffer
  64. else if(completions.length == 1)
  65. {
  66. Completion c = completions[0];
  67. if(c.text.equals(word))
  68. {
  69. textArea.getToolkit().beep();
  70. }
  71. else
  72. {
  73. textArea.setSelectedText(c.text.substring(
  74. word.length()));
  75. }
  76. } //}}}
  77. //{{{ show popup if > 1
  78. else
  79. {
  80. String longestPrefix = MiscUtilities.getLongestPrefix(
  81. completions,
  82. keywordMap != null
  83. ? keywordMap.getIgnoreCase()
  84. : false);
  85. if (word.length() < longestPrefix.length())
  86. {
  87. buffer.insert(caret,longestPrefix.substring(
  88. word.length()));
  89. }
  90. textArea.scrollToCaret(false);
  91. Point location = textArea.offsetToXY(
  92. caret - word.length());
  93. location.y += textArea.getPainter().getFontMetrics()
  94. .getHeight();
  95. SwingUtilities.convertPointToScreen(location,
  96. textArea.getPainter());
  97. new CompleteWord(view,longestPrefix,
  98. completions,location,noWordSep);
  99. } //}}}
  100. } //}}}
  101. //{{{ fitInScreen() method
  102. public static Point fitInScreen(Point p, Window w, int lineHeight)
  103. {
  104. Rectangle screenSize = w.getGraphicsConfiguration().getBounds();
  105. if(p.y + w.getHeight() >= screenSize.height)
  106. p.y = p.y - w.getHeight() - lineHeight;
  107. return p;
  108. } //}}}
  109. //{{{ CompleteWord constructor
  110. public CompleteWord(View view, String word, Completion[] completions,
  111. Point location, String noWordSep)
  112. {
  113. super(view);
  114. this.noWordSep = noWordSep;
  115. setContentPane(new JPanel(new BorderLayout())
  116. {
  117. /**
  118. * Returns if this component can be traversed by pressing the
  119. * Tab key. This returns false.
  120. */
  121. public boolean isManagingFocus()
  122. {
  123. return false;
  124. }
  125. /**
  126. * Makes the tab key work in Java 1.4.
  127. */
  128. public boolean getFocusTraversalKeysEnabled()
  129. {
  130. return false;
  131. }
  132. });
  133. this.view = view;
  134. this.textArea = view.getTextArea();
  135. this.buffer = view.getBuffer();
  136. this.word = word;
  137. words = new JList(completions);
  138. words.setVisibleRowCount(Math.min(completions.length,8));
  139. words.addMouseListener(new MouseHandler());
  140. words.setSelectedIndex(0);
  141. words.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  142. words.setCellRenderer(new Renderer());
  143. // stupid scrollbar policy is an attempt to work around
  144. // bugs people have been seeing with IBM's JDK -- 7 Sep 2000
  145. JScrollPane scroller = new JScrollPane(words,
  146. JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
  147. JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  148. getContentPane().add(scroller, BorderLayout.CENTER);
  149. GUIUtilities.requestFocus(this,words);
  150. pack();
  151. setLocation(fitInScreen(location,this,
  152. textArea.getPainter().getFontMetrics()
  153. .getHeight()));
  154. setVisible(true);
  155. KeyHandler keyHandler = new KeyHandler();
  156. addKeyListener(keyHandler);
  157. words.addKeyListener(keyHandler);
  158. view.setKeyEventInterceptor(keyHandler);
  159. } //}}}
  160. //{{{ dispose() method
  161. public void dispose()
  162. {
  163. view.setKeyEventInterceptor(null);
  164. super.dispose();
  165. SwingUtilities.invokeLater(new Runnable()
  166. {
  167. public void run()
  168. {
  169. textArea.requestFocus();
  170. }
  171. });
  172. } //}}}
  173. //{{{ Private members
  174. //{{{ getNonAlphaNumericWordChars() method
  175. private static String getNonAlphaNumericWordChars(Buffer buffer,
  176. KeywordMap keywordMap)
  177. {
  178. // figure out what constitutes a word character and what
  179. // doesn't
  180. String noWordSep = buffer.getStringProperty("noWordSep");
  181. if(noWordSep == null)
  182. noWordSep = "";
  183. if(keywordMap != null)
  184. {
  185. String keywordNoWordSep = keywordMap.getNonAlphaNumericChars();
  186. if(keywordNoWordSep != null)
  187. noWordSep = noWordSep + keywordNoWordSep;
  188. }
  189. return noWordSep;
  190. } //}}}
  191. //{{{ getWordToComplete() method
  192. private static String getWordToComplete(Buffer buffer, int caretLine,
  193. int caret, String noWordSep)
  194. {
  195. String line = buffer.getLineText(caretLine);
  196. int dot = caret - buffer.getLineStartOffset(caretLine);
  197. if(dot == 0)
  198. return null;
  199. char ch = line.charAt(dot-1);
  200. if(!Character.isLetterOrDigit(ch)
  201. && noWordSep.indexOf(ch) == -1)
  202. {
  203. // attempting to expand non-word char
  204. return null;
  205. }
  206. int wordStart = TextUtilities.findWordStart(line,dot-1,noWordSep);
  207. String word = line.substring(wordStart,dot);
  208. if(word.length() == 0)
  209. return null;
  210. return word;
  211. } //}}}
  212. //{{{ getCompletions() method
  213. private static Completion[] getCompletions(Buffer buffer, String word,
  214. int caret)
  215. {
  216. // build a list of unique words in all visible buffers
  217. Set completions = new TreeSet(new MiscUtilities
  218. .StringCompare());
  219. Set buffers = new HashSet();
  220. // only complete current buffer's keyword map
  221. KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
  222. String noWordSep = getNonAlphaNumericWordChars(
  223. buffer,keywordMap);
  224. View views = jEdit.getFirstView();
  225. while(views != null)
  226. {
  227. EditPane[] panes = views.getEditPanes();
  228. for(int i = 0; i < panes.length; i++)
  229. {
  230. Buffer b = panes[i].getBuffer();
  231. if(buffers.contains(b))
  232. continue;
  233. buffers.add(b);
  234. // only complete current buffer's keyword map
  235. KeywordMap _keywordMap;
  236. if(b == buffer)
  237. _keywordMap = keywordMap;
  238. else
  239. _keywordMap = null;
  240. int offset = (b == buffer ? caret : 0);
  241. getCompletions(b,word,keywordMap,noWordSep,
  242. offset,completions);
  243. }
  244. views = views.getNext();
  245. }
  246. Completion[] completionArray = (Completion[])completions
  247. .toArray(new Completion[completions.size()]);
  248. return completionArray;
  249. } //}}}
  250. //{{{ getCompletions() method
  251. private static void getCompletions(Buffer buffer, String word,
  252. KeywordMap keywordMap, String noWordSep, int caret,
  253. Set completions)
  254. {
  255. int wordLen = word.length();
  256. //{{{ try to find matching keywords
  257. if(keywordMap != null)
  258. {
  259. String[] keywords = keywordMap.getKeywords();
  260. for(int i = 0; i < keywords.length; i++)
  261. {
  262. String _keyword = keywords[i];
  263. if(_keyword.regionMatches(keywordMap.getIgnoreCase(),
  264. 0,word,0,wordLen))
  265. {
  266. Completion keyword = new Completion(_keyword,true);
  267. if(!completions.contains(keyword))
  268. {
  269. completions.add(keyword);
  270. }
  271. }
  272. }
  273. } //}}}
  274. //{{{ loop through all lines of current buffer
  275. for(int i = 0; i < buffer.getLineCount(); i++)
  276. {
  277. String line = buffer.getLineText(i);
  278. int start = buffer.getLineStartOffset(i);
  279. // check for match at start of line
  280. if(line.startsWith(word) && caret != start + word.length())
  281. {
  282. String _word = completeWord(line,0,noWordSep);
  283. Completion comp = new Completion(_word,false);
  284. // remove duplicates
  285. if(!completions.contains(comp))
  286. {
  287. completions.add(comp);
  288. }
  289. }
  290. // check for match inside line
  291. int len = line.length() - word.length();
  292. for(int j = 0; j < len; j++)
  293. {
  294. char c = line.charAt(j);
  295. if(!Character.isLetterOrDigit(c) && noWordSep.indexOf(c) == -1)
  296. {
  297. if(line.regionMatches(j + 1,word,0,wordLen)
  298. && caret != start + j + word.length() + 1)
  299. {
  300. String _word = completeWord(line,j + 1,noWordSep);
  301. Completion comp = new Completion(_word,false);
  302. // remove duplicates
  303. if(!completions.contains(comp))
  304. {
  305. completions.add(comp);
  306. }
  307. }
  308. }
  309. }
  310. } //}}}
  311. } //}}}
  312. //{{{ completeWord() method
  313. private static String completeWord(String line, int offset, String noWordSep)
  314. {
  315. // '+ 1' so that findWordEnd() doesn't pick up the space at the start
  316. int wordEnd = TextUtilities.findWordEnd(line,offset + 1,noWordSep);
  317. return line.substring(offset,wordEnd);
  318. } //}}}
  319. //{{{ Instance variables
  320. private View view;
  321. private JEditTextArea textArea;
  322. private Buffer buffer;
  323. private String word;
  324. private JList words;
  325. private String noWordSep;
  326. //}}}
  327. //{{{ insertSelected() method
  328. private void insertSelected()
  329. {
  330. textArea.setSelectedText(words.getSelectedValue().toString()
  331. .substring(word.length()));
  332. dispose();
  333. } //}}}
  334. //}}}
  335. //{{{ Completion class
  336. static class Completion
  337. {
  338. String text;
  339. boolean keyword;
  340. Completion(String text, boolean keyword)
  341. {
  342. this.text = text;
  343. this.keyword = keyword;
  344. }
  345. public String toString()
  346. {
  347. return text;
  348. }
  349. public int hashCode()
  350. {
  351. return text.hashCode();
  352. }
  353. public boolean equals(Object obj)
  354. {
  355. if(obj instanceof Completion)
  356. return ((Completion)obj).text.equals(text);
  357. else
  358. return false;
  359. }
  360. } //}}}
  361. //{{{ Renderer class
  362. static class Renderer extends DefaultListCellRenderer
  363. {
  364. public Component getListCellRendererComponent(JList list, Object value,
  365. int index, boolean isSelected, boolean cellHasFocus)
  366. {
  367. super.getListCellRendererComponent(list,null,index,
  368. isSelected,cellHasFocus);
  369. Completion comp = (Completion)value;
  370. if(index < 9)
  371. setText((index + 1) + ": " + comp.text);
  372. else if(index == 9)
  373. setText("0: " + comp.text);
  374. else
  375. setText(comp.text);
  376. if(comp.keyword)
  377. setFont(list.getFont().deriveFont(Font.BOLD));
  378. else
  379. setFont(list.getFont());
  380. return this;
  381. }
  382. } //}}}
  383. //{{{ KeyHandler class
  384. class KeyHandler extends KeyAdapter
  385. {
  386. //{{{ keyPressed() method
  387. public void keyPressed(KeyEvent evt)
  388. {
  389. switch(evt.getKeyCode())
  390. {
  391. case KeyEvent.VK_TAB:
  392. case KeyEvent.VK_ENTER:
  393. insertSelected();
  394. evt.consume();
  395. break;
  396. case KeyEvent.VK_ESCAPE:
  397. dispose();
  398. evt.consume();
  399. break;
  400. case KeyEvent.VK_UP:
  401. int selected = words.getSelectedIndex();
  402. if(selected == 0)
  403. selected = words.getModel().getSize() - 1;
  404. else if(getFocusOwner() == words)
  405. return;
  406. else
  407. selected = selected - 1;
  408. words.setSelectedIndex(selected);
  409. words.ensureIndexIsVisible(selected);
  410. evt.consume();
  411. break;
  412. case KeyEvent.VK_DOWN:
  413. /* int */ selected = words.getSelectedIndex();
  414. if(selected == words.getModel().getSize() - 1)
  415. selected = 0;
  416. else if(getFocusOwner() == words)
  417. return;
  418. else
  419. selected = selected + 1;
  420. words.setSelectedIndex(selected);
  421. words.ensureIndexIsVisible(selected);
  422. evt.consume();
  423. break;
  424. case KeyEvent.VK_BACK_SPACE:
  425. if(word.length() == 1)
  426. {
  427. textArea.backspace();
  428. evt.consume();
  429. dispose();
  430. }
  431. else
  432. {
  433. word = word.substring(0,word.length() - 1);
  434. textArea.backspace();
  435. int caret = textArea.getCaretPosition();
  436. Completion[] completions
  437. = getCompletions(buffer,word,
  438. caret);
  439. if(completions.length == 0)
  440. {
  441. dispose();
  442. return;
  443. }
  444. words.setListData(completions);
  445. words.setSelectedIndex(0);
  446. words.setVisibleRowCount(Math.min(completions.length,8));
  447. pack();
  448. evt.consume();
  449. }
  450. break;
  451. default:
  452. if(evt.isActionKey()
  453. || evt.isControlDown()
  454. || evt.isAltDown()
  455. || evt.isMetaDown())
  456. {
  457. dispose();
  458. view.processKeyEvent(evt);
  459. }
  460. break;
  461. }
  462. } //}}}
  463. //{{{ keyTyped() method
  464. public void keyTyped(KeyEvent evt)
  465. {
  466. char ch = evt.getKeyChar();
  467. evt = KeyEventWorkaround.processKeyEvent(evt);
  468. if(evt == null)
  469. return;
  470. if(Character.isDigit(ch))
  471. {
  472. int index = ch - '0';
  473. if(index == 0)
  474. index = 9;
  475. else
  476. index--;
  477. if(index < words.getModel().getSize())
  478. {
  479. words.setSelectedIndex(index);
  480. textArea.setSelectedText(words.getModel()
  481. .getElementAt(index).toString()
  482. .substring(word.length()));
  483. dispose();
  484. return;
  485. }
  486. else
  487. /* fall through */;
  488. }
  489. // \t handled above
  490. if(ch != '\b' && ch != '\t')
  491. {
  492. /* eg, foo<C+b>, will insert foobar, */
  493. if(!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)
  494. {
  495. insertSelected();
  496. textArea.userInput(ch);
  497. dispose();
  498. return;
  499. }
  500. textArea.userInput(ch);
  501. word = word + ch;
  502. int caret = textArea.getCaretPosition();
  503. Completion[] completions = getCompletions(
  504. buffer,word,caret);
  505. if(completions.length == 0)
  506. {
  507. dispose();
  508. return;
  509. }
  510. words.setListData(completions);
  511. words.setSelectedIndex(0);
  512. words.setVisibleRowCount(Math.min(completions.length,8));
  513. }
  514. } //}}}
  515. } //}}}
  516. //{{{ MouseHandler class
  517. class MouseHandler extends MouseAdapter
  518. {
  519. public void mouseClicked(MouseEvent evt)
  520. {
  521. insertSelected();
  522. }
  523. } //}}}
  524. }