PageRenderTime 305ms CodeModel.GetById 283ms app.highlight 17ms RepoModel.GetById 1ms 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
  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}