PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/completion/RubyCompletion.java

#
Java | 330 lines | 239 code | 52 blank | 39 comment | 53 complexity | 630fadd4cb77ed27baa8b4446df32152 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. * RubyCompletion.java -
  3. *
  4. * Copyright 2005 Robert McKinnon
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. package org.jedit.ruby.completion;
  21. import org.gjt.sp.jedit.Buffer;
  22. import org.gjt.sp.jedit.textarea.Selection;
  23. import org.gjt.sp.jedit.textarea.JEditTextArea;
  24. import org.jedit.ruby.ast.*;
  25. import org.jedit.ruby.ast.Error;
  26. import org.jedit.ruby.ri.RDocViewer;
  27. import org.jedit.ruby.RubyPlugin;
  28. import org.jedit.ruby.utils.EditorView;
  29. import sidekick.SideKickCompletion;
  30. import javax.swing.*;
  31. import javax.swing.text.html.HTMLEditorKit;
  32. import java.awt.*;
  33. import java.util.List;
  34. import java.util.Set;
  35. /**
  36. * @author robmckinnon at users.sourceforge.net
  37. */
  38. public final class RubyCompletion extends SideKickCompletion {
  39. private static final CompletionNameRenderer COMPLETION_NAME_RENDERER = new CompletionNameRenderer();
  40. private static final CompletorCreatorVisitor COMPLETOR_CREATOR = new CompletorCreatorVisitor();
  41. private static final String NO_DOT_METHOD_STARTS = "=<>%*-+/|~&^";
  42. private static boolean CONTINUE_COMPLETING = false;
  43. private final List<? extends Member> members;
  44. private final String partialMethod;
  45. private final String partialClass;
  46. private JWindow frame;
  47. private final EditorView editorView;
  48. public RubyCompletion(EditorView view, String partialClass, String partialMethod, List<? extends Member> members) {
  49. super(view.getView(), "", members);
  50. this.members = members;
  51. this.partialMethod = partialMethod;
  52. this.partialClass = partialClass;
  53. frame = new JWindow((Frame)null);
  54. frame.setFocusable(false);
  55. JTextPane textPane = new JTextPane();
  56. textPane.setEditorKit(new HTMLEditorKit());
  57. JScrollPane scroller = new JScrollPane(textPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  58. frame.getContentPane().add(scroller, BorderLayout.CENTER);
  59. frame.setSize(400, 400);
  60. editorView = view;
  61. }
  62. public ListCellRenderer getRenderer() {
  63. return COMPLETION_NAME_RENDERER;
  64. }
  65. public static boolean continueCompleting() {
  66. return CONTINUE_COMPLETING;
  67. }
  68. private void setContinueCompleting(boolean continueCompleting) {
  69. CONTINUE_COMPLETING = continueCompleting;
  70. }
  71. /**
  72. * Returns true if should continue completing.
  73. */
  74. public final boolean handleKeystroke(int selectedIndex, char keyChar) {
  75. RubyPlugin.log("handle keystroke", getClass());
  76. final boolean emptyPopup = selectedIndex == -1;
  77. final boolean backspace = keyChar == '\b';
  78. final boolean space = keyChar == ' ';
  79. final boolean dot = keyChar == '.';
  80. boolean continueCompleting = !space && !dot && keyChar != '\t' && keyChar != '\n';
  81. if (backspace) {
  82. continueCompleting = handleBackspace();
  83. } else if (continueCompleting || emptyPopup) {
  84. textArea.userInput(keyChar);
  85. } else {
  86. Member member = members.get(selectedIndex);
  87. continueCompleting = insert(member, space, dot);
  88. }
  89. RubyPlugin.log("continue completing: " + continueCompleting, getClass());
  90. if (!continueCompleting) {
  91. CodeAnalyzer.setLastReturnTypes(null);
  92. }
  93. setContinueCompleting(continueCompleting);
  94. return continueCompleting;
  95. }
  96. public final void insert(int index) {
  97. setContinueCompleting(insert(members.get(index), false, false));
  98. }
  99. private boolean insert(Member member, boolean space, boolean dot) {
  100. Buffer buffer = view.getBuffer();
  101. RubyPlugin.log("insert member: " + member.getName(), getClass());
  102. Completor completor = COMPLETOR_CREATOR.getCompletor(partialMethod, partialClass, space, dot, member);
  103. super.text = completor.partialName;
  104. completor.completeCode(buffer, textArea, dot, space);
  105. frame.setVisible(false);
  106. frame.dispose();
  107. frame = null;
  108. return completor.continueCompleting;
  109. }
  110. private boolean handleBackspace() {
  111. RubyPlugin.log("handle backspace", getClass());
  112. String text = textArea.getLineText(textArea.getCaretLine());
  113. char deletedChar = (char)-1;
  114. if (text.length() > 0) {
  115. int caretPositionInLine = editorView.getCaretOffsetInLine();
  116. deletedChar = text.charAt(caretPositionInLine - 1);
  117. // String upToCaret = text.substring(0, caretPositionInLine - 1);
  118. // String afterCaret = (caretPositionInLine == text.length()) ? "" : text.substring(caretPositionInLine, text.length());
  119. // RubyPlugin.log("text: " + text, getClass());
  120. // RubyPlugin.log("upToCaret: " + upToCaret, getClass());
  121. // RubyPlugin.log("deletedChar: " + deletedChar, getClass());
  122. // RubyPlugin.log("afterCaret: " + afterCaret, getClass());
  123. // textArea.selectLine();
  124. // textArea.setSelectedText(upToCaret + afterCaret);
  125. // textArea.setCaretPosition(textArea.getCaretPosition() - 2);
  126. textArea.backspace();
  127. }
  128. boolean dotInsertionPoint = CodeAnalyzer.isDotInsertionPoint(editorView) && deletedChar != '.' && deletedChar != ':';
  129. boolean classCompletionPoint = CodeAnalyzer.isClassCompletionPoint(editorView);
  130. RubyPlugin.log("dot? " + dotInsertionPoint + " class? " + classCompletionPoint + " " + deletedChar, getClass());
  131. return dotInsertionPoint || classCompletionPoint;
  132. }
  133. /**
  134. * Overriden super class method in order
  135. * to set Ruby docs in Ruby doc viewer
  136. */
  137. public final String getCompletionDescription(int index) {
  138. RDocViewer.setMemberInViewer(members.get(index));
  139. return null;
  140. }
  141. private static final class CompletorCreatorVisitor extends MemberVisitorAdapter {
  142. private String partialMethod;
  143. private String partialClass;
  144. private Completor completor;
  145. private boolean space;
  146. private boolean dot;
  147. private Completor getCompletor(String partialMethod, String partialClass, boolean space, boolean dot, Member member) {
  148. this.partialMethod = partialMethod;
  149. this.partialClass = partialClass;
  150. this.space = space;
  151. this.dot = dot;
  152. member.accept(this);
  153. return completor;
  154. }
  155. public final void handleDefault(Member member) {
  156. completor = new Completor(member, member.getFullName(), 0, true, partialClass);
  157. CodeCompletor.setLastCompleted(partialClass, member);
  158. }
  159. public void handleKeyword(KeywordMember keyword) {
  160. String partial = partialMethod != null ? partialMethod : partialClass;
  161. completor = new Completor(keyword, keyword.getFullName(), 0, true, partial);
  162. CodeCompletor.setLastCompleted(partial, keyword);
  163. }
  164. public final void handleMethod(Method method) {
  165. String name = method.getName();
  166. if (name.equals("each")) {
  167. completor = new Completor(method, "each do ||", -1, true, partialMethod);
  168. } else if (name.startsWith("[")) {
  169. completor = new Completor(method, name, -1, false, partialMethod);
  170. } else if (NO_DOT_METHOD_STARTS.indexOf(name.charAt(0)) != -1) {
  171. completor = new Completor(method, name + " ", 0, false, partialMethod);
  172. } else if (name.endsWith("=") && name.length() > 1) {
  173. name = name.substring(0, name.length() - 1) + " = ";
  174. completor = new Completor(method, name, 0, true, partialMethod);
  175. } else if (name.endsWith("?") && name.length() > 1) {
  176. completor = new Completor(method, name + " ", 0, true, partialMethod);
  177. } else if (!dot && !space && method.hasParameters()) {
  178. // completor = new Completor(method, name + "()", -1, true, partialMethod);
  179. completor = new Completor(method, name, 0, true, partialMethod);
  180. } else {
  181. completor = new Completor(method, name, 0, true, partialMethod);
  182. }
  183. CodeCompletor.setLastCompleted(partialMethod, method);
  184. }
  185. }
  186. private static final class Completor extends MemberVisitorAdapter {
  187. final Member member;
  188. final String text;
  189. final String partialName;
  190. final boolean showDot;
  191. final int caretAdjustment;
  192. boolean continueCompleting;
  193. public Completor(Member member, String text, int caretPositionAdjustment, boolean showDot, String partialName) {
  194. this.caretAdjustment = caretPositionAdjustment;
  195. this.member = member;
  196. this.text = text;
  197. this.showDot = showDot;
  198. this.partialName = partialName;
  199. continueCompleting = false;
  200. }
  201. public void completeCode(Buffer buffer, JEditTextArea textArea, boolean dot, boolean space) {
  202. int caretPosition = textArea.getCaretPosition();
  203. int offset = removePartialName(buffer, caretPosition);
  204. if (!showDot) {
  205. offset = removeDot(textArea, offset);
  206. }
  207. buffer.insert(offset, text);
  208. textArea.setCaretPosition(textArea.getCaretPosition() + caretAdjustment);
  209. if (space) {
  210. textArea.userInput(' ');
  211. } else if (dot) {
  212. textArea.userInput('.');
  213. member.accept(this);
  214. }
  215. }
  216. public void handleMethod(Method method) {
  217. this.continueCompleting = true;
  218. Set<Member> returnTypes = method.getReturnTypes();
  219. CodeAnalyzer.setLastReturnTypes(returnTypes);
  220. }
  221. private int removeDot(JEditTextArea textArea, int offset) {
  222. int caretPosition;
  223. caretPosition = textArea.getCaretPosition();
  224. Selection.Range range = new Selection.Range(caretPosition - 1, caretPosition);
  225. textArea.setSelection(range);
  226. if (caretAdjustment == 0) {
  227. textArea.setSelectedText(" ");
  228. } else {
  229. textArea.setSelectedText("");
  230. offset--;
  231. }
  232. return offset;
  233. }
  234. public int removePartialName(Buffer buffer, int offset) {
  235. if (partialName != null) {
  236. offset -= partialName.length();
  237. buffer.remove(offset, partialName.length());
  238. }
  239. return offset;
  240. }
  241. }
  242. private static class CompletionNameRenderer extends DefaultListCellRenderer implements MemberVisitor {
  243. String text;
  244. public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
  245. if (value instanceof Member) {
  246. Member member = (Member)value;
  247. member.accept(this);
  248. } else {
  249. text = String.valueOf(value);
  250. }
  251. return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus);
  252. }
  253. public void handleModule(Module module) {
  254. text = module.getFullName();
  255. }
  256. public void handleClass(ClassMember classMember) {
  257. text = classMember.getFullName();
  258. }
  259. public void handleMethod(Method method) {
  260. if (method.getNamespace() != null) {
  261. text = method.getName() + " (" + method.getNamespace() + ")";
  262. } else {
  263. text = method.getName();
  264. }
  265. }
  266. public void handleMethodCallWithSelfAsAnImplicitReceiver(MethodCallWithSelfAsAnImplicitReceiver methodCall) {
  267. }
  268. public void handleKeyword(KeywordMember keywordMember) {
  269. text = keywordMember.getName();
  270. }
  271. public void handleWarning(Warning warning) {
  272. }
  273. public void handleError(Error warning) {
  274. }
  275. public void handleRoot(Root root) {
  276. }
  277. }
  278. }