PageRenderTime 56ms CodeModel.GetById 33ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/structure/RubySideKickParser.java

#
Java | 221 lines | 147 code | 41 blank | 33 comment | 21 complexity | a892cb84d7ede9fc1e18f2bb41a96d77 MD5 | raw file
  1/*
  2 * RubySideKickParser.java - Side Kick Parser for Ruby
  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.structure;
 21
 22import sidekick.SideKickParser;
 23import sidekick.SideKickParsedData;
 24import sidekick.SideKickCompletion;
 25import org.gjt.sp.jedit.Buffer;
 26import org.gjt.sp.jedit.EditPane;
 27import org.jruby.lexer.yacc.ISourcePosition;
 28import org.jedit.ruby.ast.Member;
 29import org.jedit.ruby.parser.RubyParser;
 30import org.jedit.ruby.ast.RubyMembers;
 31import org.jedit.ruby.RubyPlugin;
 32import org.jedit.ruby.utils.EditorView;
 33import org.jedit.ruby.completion.CodeCompletor;
 34import org.jedit.ruby.completion.RubyCompletion;
 35import org.jedit.ruby.completion.CodeAnalyzer;
 36import errorlist.DefaultErrorSource;
 37import errorlist.ErrorSource;
 38
 39import javax.swing.tree.DefaultMutableTreeNode;
 40
 41/**
 42 * @author robmckinnon at users.sourceforge.net
 43 */
 44public final class RubySideKickParser extends SideKickParser {
 45
 46    private static final ErrorSource.Error[] EMPTY_ERROR_LIST = new ErrorSource.Error[0];
 47    private static DefaultErrorSource errorSource;
 48
 49    private final RubyTokenHandler tokenHandler;
 50
 51    public RubySideKickParser() {
 52        super("ruby");
 53        tokenHandler = new RubyTokenHandler();
 54    }
 55
 56    public final boolean supportsCompletion() {
 57        return true;
 58    }
 59
 60    public final boolean canHandleBackspace() {
 61        return true;
 62    }
 63
 64    public static ErrorSource.Error[] getErrors() {
 65        ErrorSource.Error[] errors = errorSource.getAllErrors();
 66        return errors != null ? errors : EMPTY_ERROR_LIST;
 67    }
 68
 69    public final SideKickParsedData parse(final Buffer buffer, final DefaultErrorSource errorSource) {
 70        String text = buffer.getText(0, buffer.getLength());
 71        RubySideKickParser.errorSource = errorSource;
 72
 73        RubyParser.WarningListener listener = new RubySideKickWarningListener(errorSource);
 74        SideKickParsedData data = new SideKickParsedData(buffer.getName());
 75        RubyMembers members = RubyParser.getMembers(text, buffer.getPath(), listener, false);
 76
 77        if (!members.containsErrors()) {
 78            addNodes(data.root, members.getMembers(), buffer);
 79
 80        } else if (RubyParser.hasLastGoodMembers(buffer)) {
 81            members = RubyParser.getLastGoodMembers(buffer);
 82            addNodes(data.root, members.combineMembersAndProblems(0), buffer);
 83
 84        } else {
 85            addNodes(data.root, members.getProblems(), buffer);
 86        }
 87
 88        return data;
 89    }
 90
 91    /**
 92     * True if caret is at a method insertion
 93     * point, else false.
 94     * This means that if the user types a dot or
 95     * equivalent, this method will return true
 96     * permitting completion to automatically
 97     * occur after a delay. In other cases
 98     * there won't be automatic completion
 99     * after a delay, the user will have to
100     * manually hit the completion shortcut.
101     */
102    public boolean canCompleteAnywhere() {
103        EditorView view = RubyPlugin.getActiveView();
104        return CodeAnalyzer.isDotInsertionPoint(view) || RubyCompletion.continueCompleting();
105    }
106
107    public final SideKickCompletion complete(EditPane editPane, int caret) {
108        Buffer buffer = editPane.getBuffer();
109        RubyToken syntaxType = tokenHandler.getTokenAtCaret(buffer, caret);
110        RubyCompletion completion = null;
111
112        if (!ignore(syntaxType)) {
113            CodeCompletor completor = new CodeCompletor(RubyPlugin.getActiveView());
114
115            if (completor.isDotInsertionPoint()) {
116                completion = completor.getDotCompletion();
117
118            } else if (completor.hasCompletion()) {
119                completion = completor.getCompletion();
120
121            } else {
122                completion = completor.getEmptyCompletion();
123                clearLastCompletion();
124            }
125        } else {
126            clearLastCompletion();
127        }
128
129        return completion;
130    }
131
132    private static void clearLastCompletion() {
133        CodeAnalyzer.setLastReturnTypes(null);
134        CodeAnalyzer.setLastCompleted(null);
135    }
136
137    private boolean ignore(RubyToken token) {
138        if (token.isComment() || token.isLiteral()) {
139            RubyPlugin.log("ignoring: " + token, getClass());
140            return true;
141        } else {
142            RubyPlugin.log("not ignoring: " + token, getClass());
143            return false;
144        }
145    }
146
147    private void addWarning(String message, ISourcePosition position, DefaultErrorSource errorSource) {
148        addToErrorList(ErrorSource.WARNING, position, errorSource, message);
149    }
150
151    private void addError(String message, ISourcePosition position, DefaultErrorSource errorSource) {
152        addToErrorList(ErrorSource.ERROR, position, errorSource, message);
153    }
154
155    private void addToErrorList(int type, ISourcePosition position, DefaultErrorSource errorSource, String message) {
156        int line = position == null ? 0 : position.getEndLine();
157        String file = position == null ? null : position.getFile();
158
159        int startOffset = RubyPlugin.getStartOffset(line);
160        int nonSpaceStartOffset = RubyPlugin.getNonSpaceStartOffset(line);
161        int endOffset = RubyPlugin.getEndOffset(line);
162
163        int startOffsetInLine = nonSpaceStartOffset - startOffset;
164        int endOffsetInLine = endOffset - startOffset;
165
166        RubyPlugin.log("start " + startOffsetInLine + ", end " + endOffsetInLine, getClass());
167        errorSource.addError(type, file, line, startOffsetInLine, endOffsetInLine, message);
168    }
169
170    private void addNodes(DefaultMutableTreeNode parentNode, Member[] members, Buffer buffer) {
171        if (members != null) {
172            for (Member member : members) {
173                MemberNode node = new MemberNode(member);
174                node.setStart(buffer.createPosition(Math.min(buffer.getLength(), member.getStartOffset())));
175                node.setEnd(buffer.createPosition(Math.min(buffer.getLength(), member.getEndOffset())));
176                DefaultMutableTreeNode treeNode = node.createTreeNode();
177                if (member.hasChildMembers()) {
178                    Member[] childMembers = member.getChildMembers();
179                    addNodes(treeNode, childMembers, buffer);
180                }
181                parentNode.add(treeNode);
182            }
183        }
184    }
185
186    private final class RubySideKickWarningListener implements RubyParser.WarningListener {
187
188        private final DefaultErrorSource errorSource;
189
190        public RubySideKickWarningListener(DefaultErrorSource errorSource) {
191            this.errorSource = errorSource;
192        }
193
194        public boolean isVerbose() {
195            return false;
196        }
197
198        public final void warn(ISourcePosition position, String message) {
199            addWarning(message, position, errorSource);
200        }
201
202        public final void warn(String message) {
203            addWarning(message, null, errorSource);
204        }
205
206        public final void warning(ISourcePosition position, String message) {
207            addWarning(message, position, errorSource);
208        }
209
210        public final void warning(String message) {
211            addWarning(message, null, errorSource);
212        }
213
214        public final void error(ISourcePosition position, String message) {
215            addError(message, position, errorSource);
216        }
217
218        public final void clear() {
219        }
220    }
221}