/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 */
20package org.jedit.ruby.completion;
21
22import org.gjt.sp.jedit.Buffer;
23import org.gjt.sp.jedit.textarea.Selection;
24import org.gjt.sp.jedit.textarea.JEditTextArea;
25import org.jedit.ruby.ast.*;
26import org.jedit.ruby.ast.Error;
27import org.jedit.ruby.ri.RDocViewer;
28import org.jedit.ruby.RubyPlugin;
29import org.jedit.ruby.utils.EditorView;
30import sidekick.SideKickCompletion;
31
32import javax.swing.*;
33import javax.swing.text.html.HTMLEditorKit;
34import java.awt.*;
35import java.util.List;
36import java.util.Set;
37
38/**
39 * @author robmckinnon at users.sourceforge.net
40 */
41public final class RubyCompletion extends SideKickCompletion {
42
43 private static final CompletionNameRenderer COMPLETION_NAME_RENDERER = new CompletionNameRenderer();
44 private static final CompletorCreatorVisitor COMPLETOR_CREATOR = new CompletorCreatorVisitor();
45 private static final String NO_DOT_METHOD_STARTS = "=<>%*-+/|~&^";
46 private static boolean CONTINUE_COMPLETING = false;
47
48 private final List<? extends Member> members;
49 private final String partialMethod;
50 private final String partialClass;
51 private JWindow frame;
52 private final EditorView editorView;
53
54 public RubyCompletion(EditorView view, String partialClass, String partialMethod, List<? extends Member> members) {
55 super(view.getView(), "", members);
56 this.members = members;
57 this.partialMethod = partialMethod;
58 this.partialClass = partialClass;
59 frame = new JWindow((Frame)null);
60 frame.setFocusable(false);
61 JTextPane textPane = new JTextPane();
62 textPane.setEditorKit(new HTMLEditorKit());
63 JScrollPane scroller = new JScrollPane(textPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
64 frame.getContentPane().add(scroller, BorderLayout.CENTER);
65 frame.setSize(400, 400);
66 editorView = view;
67 }
68
69 public ListCellRenderer getRenderer() {
70 return COMPLETION_NAME_RENDERER;
71 }
72
73 public static boolean continueCompleting() {
74 return CONTINUE_COMPLETING;
75 }
76
77 private void setContinueCompleting(boolean continueCompleting) {
78 CONTINUE_COMPLETING = continueCompleting;
79 }
80
81 /**
82 * Returns true if should continue completing.
83 */
84 public final boolean handleKeystroke(int selectedIndex, char keyChar) {
85 RubyPlugin.log("handle keystroke", getClass());
86 final boolean emptyPopup = selectedIndex == -1;
87 final boolean backspace = keyChar == '\b';
88 final boolean space = keyChar == ' ';
89 final boolean dot = keyChar == '.';
90 boolean continueCompleting = !space && !dot && keyChar != '\t' && keyChar != '\n';
91
92 if (backspace) {
93 continueCompleting = handleBackspace();
94
95 } else if (continueCompleting || emptyPopup) {
96 textArea.userInput(keyChar);
97
98 } else {
99 Member member = members.get(selectedIndex);
100 continueCompleting = insert(member, space, dot);
101 }
102
103 RubyPlugin.log("continue completing: " + continueCompleting, getClass());
104 if (!continueCompleting) {
105 CodeAnalyzer.setLastReturnTypes(null);
106 }
107
108 setContinueCompleting(continueCompleting);
109 return continueCompleting;
110 }
111
112 public final void insert(int index) {
113 setContinueCompleting(insert(members.get(index), false, false));
114 }
115
116 private boolean insert(Member member, boolean space, boolean dot) {
117 Buffer buffer = view.getBuffer();
118 RubyPlugin.log("insert member: " + member.getName(), getClass());
119 Completor completor = COMPLETOR_CREATOR.getCompletor(partialMethod, partialClass, space, dot, member);
120 super.text = completor.partialName;
121 completor.completeCode(buffer, textArea, dot, space);
122
123 frame.setVisible(false);
124 frame.dispose();
125 frame = null;
126 return completor.continueCompleting;
127 }
128
129 private boolean handleBackspace() {
130 RubyPlugin.log("handle backspace", getClass());
131 String text = textArea.getLineText(textArea.getCaretLine());
132 char deletedChar = (char)-1;
133 if (text.length() > 0) {
134 int caretPositionInLine = editorView.getCaretOffsetInLine();
135 deletedChar = text.charAt(caretPositionInLine - 1);
136// String upToCaret = text.substring(0, caretPositionInLine - 1);
137// String afterCaret = (caretPositionInLine == text.length()) ? "" : text.substring(caretPositionInLine, text.length());
138// RubyPlugin.log("text: " + text, getClass());
139// RubyPlugin.log("upToCaret: " + upToCaret, getClass());
140// RubyPlugin.log("deletedChar: " + deletedChar, getClass());
141// RubyPlugin.log("afterCaret: " + afterCaret, getClass());
142// textArea.selectLine();
143// textArea.setSelectedText(upToCaret + afterCaret);
144// textArea.setCaretPosition(textArea.getCaretPosition() - 2);
145 textArea.backspace();
146 }
147 boolean dotInsertionPoint = CodeAnalyzer.isDotInsertionPoint(editorView) && deletedChar != '.' && deletedChar != ':';
148 boolean classCompletionPoint = CodeAnalyzer.isClassCompletionPoint(editorView);
149 RubyPlugin.log("dot? " + dotInsertionPoint + " class? " + classCompletionPoint + " " + deletedChar, getClass());
150 return dotInsertionPoint || classCompletionPoint;
151 }
152
153 /**
154 * Overriden super class method in order
155 * to set Ruby docs in Ruby doc viewer
156 */
157 public final String getCompletionDescription(int index) {
158 RDocViewer.setMemberInViewer(members.get(index));
159 return null;
160 }
161
162 private static final class CompletorCreatorVisitor extends MemberVisitorAdapter {
163 private String partialMethod;
164 private String partialClass;
165 private Completor completor;
166 private boolean space;
167 private boolean dot;
168
169 private Completor getCompletor(String partialMethod, String partialClass, boolean space, boolean dot, Member member) {
170 this.partialMethod = partialMethod;
171 this.partialClass = partialClass;
172 this.space = space;
173 this.dot = dot;
174 member.accept(this);
175 return completor;
176 }
177
178 public final void handleDefault(Member member) {
179 completor = new Completor(member, member.getFullName(), 0, true, partialClass);
180 CodeCompletor.setLastCompleted(partialClass, member);
181 }
182
183 public void handleKeyword(KeywordMember keyword) {
184 String partial = partialMethod != null ? partialMethod : partialClass;
185 completor = new Completor(keyword, keyword.getFullName(), 0, true, partial);
186 CodeCompletor.setLastCompleted(partial, keyword);
187 }
188
189 public final void handleMethod(Method method) {
190 String name = method.getName();
191
192 if (name.equals("each")) {
193 completor = new Completor(method, "each do ||", -1, true, partialMethod);
194
195 } else if (name.startsWith("[")) {
196 completor = new Completor(method, name, -1, false, partialMethod);
197
198 } else if (NO_DOT_METHOD_STARTS.indexOf(name.charAt(0)) != -1) {
199 completor = new Completor(method, name + " ", 0, false, partialMethod);
200
201 } else if (name.endsWith("=") && name.length() > 1) {
202 name = name.substring(0, name.length() - 1) + " = ";
203 completor = new Completor(method, name, 0, true, partialMethod);
204
205 } else if (name.endsWith("?") && name.length() > 1) {
206 completor = new Completor(method, name + " ", 0, true, partialMethod);
207
208 } else if (!dot && !space && method.hasParameters()) {
209// completor = new Completor(method, name + "()", -1, true, partialMethod);
210 completor = new Completor(method, name, 0, true, partialMethod);
211
212 } else {
213 completor = new Completor(method, name, 0, true, partialMethod);
214 }
215 CodeCompletor.setLastCompleted(partialMethod, method);
216 }
217
218 }
219
220 private static final class Completor extends MemberVisitorAdapter {
221 final Member member;
222 final String text;
223 final String partialName;
224 final boolean showDot;
225 final int caretAdjustment;
226 boolean continueCompleting;
227
228 public Completor(Member member, String text, int caretPositionAdjustment, boolean showDot, String partialName) {
229 this.caretAdjustment = caretPositionAdjustment;
230 this.member = member;
231 this.text = text;
232 this.showDot = showDot;
233 this.partialName = partialName;
234 continueCompleting = false;
235 }
236
237 public void completeCode(Buffer buffer, JEditTextArea textArea, boolean dot, boolean space) {
238 int caretPosition = textArea.getCaretPosition();
239 int offset = removePartialName(buffer, caretPosition);
240
241 if (!showDot) {
242 offset = removeDot(textArea, offset);
243 }
244
245 buffer.insert(offset, text);
246 textArea.setCaretPosition(textArea.getCaretPosition() + caretAdjustment);
247
248 if (space) {
249 textArea.userInput(' ');
250 } else if (dot) {
251 textArea.userInput('.');
252 member.accept(this);
253 }
254 }
255
256 public void handleMethod(Method method) {
257 this.continueCompleting = true;
258 Set<Member> returnTypes = method.getReturnTypes();
259 CodeAnalyzer.setLastReturnTypes(returnTypes);
260 }
261
262 private int removeDot(JEditTextArea textArea, int offset) {
263 int caretPosition;
264 caretPosition = textArea.getCaretPosition();
265 Selection.Range range = new Selection.Range(caretPosition - 1, caretPosition);
266 textArea.setSelection(range);
267 if (caretAdjustment == 0) {
268 textArea.setSelectedText(" ");
269 } else {
270 textArea.setSelectedText("");
271 offset--;
272 }
273 return offset;
274 }
275
276 public int removePartialName(Buffer buffer, int offset) {
277 if (partialName != null) {
278 offset -= partialName.length();
279 buffer.remove(offset, partialName.length());
280 }
281 return offset;
282 }
283 }
284
285 private static class CompletionNameRenderer extends DefaultListCellRenderer implements MemberVisitor {
286 String text;
287
288 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
289 if (value instanceof Member) {
290 Member member = (Member)value;
291 member.accept(this);
292 } else {
293 text = String.valueOf(value);
294 }
295 return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus);
296 }
297
298 public void handleModule(Module module) {
299 text = module.getFullName();
300 }
301
302 public void handleClass(ClassMember classMember) {
303 text = classMember.getFullName();
304 }
305
306 public void handleMethod(Method method) {
307 if (method.getNamespace() != null) {
308 text = method.getName() + " (" + method.getNamespace() + ")";
309 } else {
310 text = method.getName();
311 }
312 }
313
314 public void handleMethodCallWithSelfAsAnImplicitReceiver(MethodCallWithSelfAsAnImplicitReceiver methodCall) {
315 }
316
317 public void handleKeyword(KeywordMember keywordMember) {
318 text = keywordMember.getName();
319 }
320
321 public void handleWarning(Warning warning) {
322 }
323
324 public void handleError(Error warning) {
325 }
326
327 public void handleRoot(Root root) {
328 }
329 }
330}