tor@1
|
1 |
/*
|
phrebejk@559
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
tor@1
|
3 |
*
|
jglick@4117
|
4 |
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
|
jglick@4117
|
5 |
*
|
jglick@4117
|
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
|
jglick@4117
|
7 |
* Other names may be trademarks of their respective owners.
|
tor@1
|
8 |
*
|
phrebejk@559
|
9 |
* The contents of this file are subject to the terms of either the GNU
|
phrebejk@559
|
10 |
* General Public License Version 2 only ("GPL") or the Common
|
phrebejk@559
|
11 |
* Development and Distribution License("CDDL") (collectively, the
|
phrebejk@559
|
12 |
* "License"). You may not use this file except in compliance with the
|
phrebejk@559
|
13 |
* License. You can obtain a copy of the License at
|
phrebejk@559
|
14 |
* http://www.netbeans.org/cddl-gplv2.html
|
phrebejk@559
|
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
|
phrebejk@559
|
16 |
* specific language governing permissions and limitations under the
|
phrebejk@559
|
17 |
* License. When distributing the software, include this License Header
|
phrebejk@559
|
18 |
* Notice in each file and include the License file at
|
jglick@4117
|
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
|
phrebejk@559
|
20 |
* particular file as subject to the "Classpath" exception as provided
|
jglick@4117
|
21 |
* by Oracle in the GPL Version 2 section of the License file that
|
phrebejk@559
|
22 |
* accompanied this code. If applicable, add the following below the
|
phrebejk@559
|
23 |
* License Header, with the fields enclosed by brackets [] replaced by
|
phrebejk@559
|
24 |
* your own identifying information:
|
tor@1
|
25 |
* "Portions Copyrighted [year] [name of copyright owner]"
|
tor@1
|
26 |
*
|
phrebejk@559
|
27 |
* Contributor(s):
|
phrebejk@559
|
28 |
*
|
tor@1
|
29 |
* The Original Software is NetBeans. The Initial Developer of the Original
|
tor@1
|
30 |
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
|
tor@1
|
31 |
* Microsystems, Inc. All Rights Reserved.
|
phrebejk@559
|
32 |
*
|
phrebejk@559
|
33 |
* If you wish your version of this file to be governed by only the CDDL
|
phrebejk@559
|
34 |
* or only the GPL Version 2, indicate your decision by adding
|
phrebejk@559
|
35 |
* "[Contributor] elects to include this software in this distribution
|
phrebejk@559
|
36 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a
|
phrebejk@559
|
37 |
* single choice of license, a recipient has the option to distribute
|
phrebejk@559
|
38 |
* your version of this file under either the CDDL, the GPL Version 2 or
|
phrebejk@559
|
39 |
* to extend the choice of license to its licensees as provided above.
|
phrebejk@559
|
40 |
* However, if you add GPL Version 2 code and therefore, elected the GPL
|
phrebejk@559
|
41 |
* Version 2 license, then the option applies only if the new code is
|
phrebejk@559
|
42 |
* made subject to such option by the copyright holder.
|
tor@1
|
43 |
*/
|
tor@1
|
44 |
package org.netbeans.modules.ruby;
|
tor@1
|
45 |
|
enebo@4515
|
46 |
import java.io.IOException;
|
emononen@3533
|
47 |
import java.io.StringReader;
|
mkrauskopf@3030
|
48 |
import java.util.ArrayList;
|
mkrauskopf@3030
|
49 |
import java.util.Collection;
|
tor@1
|
50 |
import java.util.Iterator;
|
tor@1
|
51 |
import java.util.List;
|
mkrauskopf@3030
|
52 |
import javax.swing.event.ChangeListener;
|
tor@1
|
53 |
import javax.swing.text.BadLocationException;
|
emononen@3259
|
54 |
import org.jrubyparser.ast.Node;
|
emononen@3259
|
55 |
import org.jrubyparser.ast.RootNode;
|
emononen@3259
|
56 |
import org.jrubyparser.IRubyWarnings;
|
emononen@3259
|
57 |
import org.jrubyparser.IRubyWarnings.ID;
|
emononen@3259
|
58 |
import org.jrubyparser.SourcePosition;
|
emononen@3259
|
59 |
import org.jrubyparser.lexer.LexerSource;
|
emononen@3259
|
60 |
import org.jrubyparser.lexer.SyntaxException;
|
emononen@3259
|
61 |
import org.jrubyparser.parser.ParserConfiguration;
|
emononen@3259
|
62 |
import org.jrubyparser.parser.ParserResult;
|
emononen@3259
|
63 |
import org.jrubyparser.parser.Ruby18Parser;
|
emononen@3264
|
64 |
import org.jrubyparser.parser.Ruby19Parser;
|
enebo@4542
|
65 |
import org.jrubyparser.parser.Ruby20Parser;
|
emononen@3264
|
66 |
import org.netbeans.api.project.FileOwnerQuery;
|
emononen@3264
|
67 |
import org.netbeans.api.project.Project;
|
emononen@3264
|
68 |
import org.netbeans.api.ruby.platform.RubyPlatform;
|
mkrauskopf@3030
|
69 |
import org.netbeans.modules.csl.api.ElementHandle;
|
mkrauskopf@3030
|
70 |
import org.netbeans.modules.csl.api.Error;
|
mkrauskopf@3030
|
71 |
import org.netbeans.modules.csl.api.OffsetRange;
|
mkrauskopf@3030
|
72 |
import org.netbeans.modules.csl.api.Severity;
|
mkrauskopf@3030
|
73 |
import org.netbeans.modules.csl.spi.GsfUtilities;
|
mkrauskopf@3030
|
74 |
import org.netbeans.modules.parsing.api.Snapshot;
|
mkrauskopf@3030
|
75 |
import org.netbeans.modules.parsing.api.Task;
|
mkrauskopf@3030
|
76 |
import org.netbeans.modules.parsing.spi.ParseException;
|
mkrauskopf@3030
|
77 |
import org.netbeans.modules.parsing.spi.Parser;
|
mkrauskopf@3030
|
78 |
import org.netbeans.modules.parsing.spi.ParserFactory;
|
mkrauskopf@3030
|
79 |
import org.netbeans.modules.parsing.spi.SourceModificationEvent;
|
tor@1
|
80 |
import org.netbeans.modules.ruby.elements.AstElement;
|
tor@1205
|
81 |
import org.netbeans.modules.ruby.elements.RubyElement;
|
emononen@3264
|
82 |
import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator;
|
tor@1291
|
83 |
import org.openide.filesystems.FileObject;
|
tor@1
|
84 |
import org.openide.util.NbBundle;
|
tor@1
|
85 |
|
tor@1
|
86 |
|
tor@1
|
87 |
/**
|
tor@1
|
88 |
* Wrapper around JRuby to parse a buffer into an AST.
|
tor@1
|
89 |
*
|
tor@1
|
90 |
* @todo Rename to RubyParser for symmetry with RubyLexer
|
tor@1
|
91 |
* @todo Idea: If you get a syntax error on the last line, it's probably a missing
|
tor@1
|
92 |
* "end" much earlier. Go back and look for a method inside a method, and the outer
|
tor@1
|
93 |
* method is probably missing an end (can use indentation to look for this as well).
|
tor@1
|
94 |
* Create a quickfix to insert it.
|
tor@468
|
95 |
* @todo Only look for missing-end if there's an unexpected end
|
tor@468
|
96 |
* @todo If you get a "class definition in method body" error, there's a missing
|
tor@468
|
97 |
* end - prior to the class!
|
tor@468
|
98 |
* @todo "syntax error, unexpected tRCURLY" means that I also have a missing end,
|
tor@468
|
99 |
* but we encountered a } before we got to it. I need to be bracketing this stuff.
|
tor@371
|
100 |
*
|
tor@1
|
101 |
* @author Tor Norbye
|
tor@1
|
102 |
*/
|
mkrauskopf@3030
|
103 |
public final class RubyParser extends Parser {
|
mkrauskopf@3030
|
104 |
|
emononen@4184
|
105 |
/**
|
enebo@4515
|
106 |
* System property for defaulting to 1.8 parser.
|
emononen@4184
|
107 |
*/
|
enebo@4515
|
108 |
private static boolean DEFAULT_TO_RUBY18 = Boolean.getBoolean("ruby.parser.default18"); //NOI18N
|
emononen@4184
|
109 |
|
mkrauskopf@3030
|
110 |
private RubyParseResult lastResult;
|
tor@1
|
111 |
|
tor@1
|
112 |
/**
|
tor@1
|
113 |
* Creates a new instance of RubyParser
|
tor@1
|
114 |
*/
|
tor@1
|
115 |
public RubyParser() {
|
tor@1
|
116 |
}
|
tor@1
|
117 |
|
mkrauskopf@3030
|
118 |
// ------------------------------------------------------------------------
|
mkrauskopf@3030
|
119 |
// o.n.m.p.spi.Parser implementation
|
mkrauskopf@3030
|
120 |
// ------------------------------------------------------------------------
|
mkrauskopf@3030
|
121 |
|
mkrauskopf@3030
|
122 |
public @Override void parse(Snapshot snapshot, Task task, SourceModificationEvent event) throws ParseException {
|
mkrauskopf@3030
|
123 |
Context context = new Context(snapshot, event);
|
mkrauskopf@3030
|
124 |
final List<Error> errors = new ArrayList<Error>();
|
mkrauskopf@3030
|
125 |
context.errorHandler = new ParseErrorHandler() {
|
enebo@4515
|
126 |
@Override
|
mkrauskopf@3030
|
127 |
public void error(Error error) {
|
mkrauskopf@3030
|
128 |
errors.add(error);
|
mkrauskopf@3030
|
129 |
}
|
mkrauskopf@3030
|
130 |
};
|
mkrauskopf@3030
|
131 |
lastResult = parseBuffer(context, Sanitize.NONE);
|
mkrauskopf@3030
|
132 |
lastResult.setErrors(errors);
|
mkrauskopf@3030
|
133 |
}
|
mkrauskopf@3030
|
134 |
|
mkrauskopf@3030
|
135 |
public @Override Result getResult(Task task) throws ParseException {
|
mkrauskopf@3030
|
136 |
assert lastResult != null : "getResult() called prior parse()"; //NOI18N
|
mkrauskopf@3030
|
137 |
return lastResult;
|
mkrauskopf@3030
|
138 |
}
|
mkrauskopf@3030
|
139 |
|
mkrauskopf@3030
|
140 |
public @Override void addChangeListener(ChangeListener changeListener) {
|
mkrauskopf@3030
|
141 |
// no-op, we don't support state changes
|
mkrauskopf@3030
|
142 |
}
|
mkrauskopf@3030
|
143 |
|
mkrauskopf@3030
|
144 |
public @Override void removeChangeListener(ChangeListener changeListener) {
|
mkrauskopf@3030
|
145 |
// no-op, we don't support state changes
|
mkrauskopf@3030
|
146 |
}
|
mkrauskopf@3030
|
147 |
|
mkrauskopf@3030
|
148 |
|
mkrauskopf@3030
|
149 |
// ------------------------------------------------------------------------
|
mkrauskopf@3030
|
150 |
// o.n.m.p.spi.ParserFactory implementation
|
mkrauskopf@3030
|
151 |
// ------------------------------------------------------------------------
|
mkrauskopf@3030
|
152 |
|
mkrauskopf@3030
|
153 |
private static final class Factory extends ParserFactory {
|
mkrauskopf@3030
|
154 |
|
mkrauskopf@3030
|
155 |
@Override
|
mkrauskopf@3030
|
156 |
public Parser createParser(Collection<Snapshot> snapshots) {
|
mkrauskopf@3030
|
157 |
return new RubyParser();
|
mkrauskopf@3030
|
158 |
}
|
mkrauskopf@3030
|
159 |
|
mkrauskopf@3030
|
160 |
} // End of Factory class
|
mkrauskopf@3030
|
161 |
|
mkrauskopf@3030
|
162 |
|
tor@1
|
163 |
private static String asString(CharSequence sequence) {
|
tor@1
|
164 |
if (sequence instanceof String) {
|
tor@1
|
165 |
return (String)sequence;
|
tor@1
|
166 |
} else {
|
tor@1
|
167 |
return sequence.toString();
|
tor@1
|
168 |
}
|
tor@1
|
169 |
}
|
tor@1
|
170 |
|
tor@1
|
171 |
/**
|
tor@1
|
172 |
* Try cleaning up the source buffer around the current offset to increase
|
tor@1
|
173 |
* likelihood of parse success. Initially this method had a lot of
|
tor@1
|
174 |
* logic to determine whether a parse was likely to fail (e.g. invoking
|
tor@1
|
175 |
* the isEndMissing method from bracket completion etc.).
|
tor@1
|
176 |
* However, I am now trying a parse with the real source first, and then
|
tor@1
|
177 |
* only if that fails do I try parsing with sanitized source. Therefore,
|
tor@1
|
178 |
* this method has to be less conservative in ripping out code since it
|
tor@1
|
179 |
* will only be used when the regular source is failing.
|
tor@1
|
180 |
*/
|
tor@364
|
181 |
private boolean sanitizeSource(Context context, Sanitize sanitizing) {
|
tor@364
|
182 |
|
tor@364
|
183 |
if (sanitizing == Sanitize.MISSING_END) {
|
tor@364
|
184 |
context.sanitizedSource = context.source + ";end";
|
tor@364
|
185 |
int start = context.source.length();
|
tor@364
|
186 |
context.sanitizedRange = new OffsetRange(start, start+4);
|
tor@581
|
187 |
context.sanitizedContents = "";
|
tor@364
|
188 |
return true;
|
tor@364
|
189 |
}
|
tor@364
|
190 |
|
tor@364
|
191 |
int offset = context.caretOffset;
|
tor@364
|
192 |
|
tor@1
|
193 |
// Let caretOffset represent the offset of the portion of the buffer we'll be operating on
|
tor@1
|
194 |
if ((sanitizing == Sanitize.ERROR_DOT) || (sanitizing == Sanitize.ERROR_LINE)) {
|
tor@364
|
195 |
offset = context.errorOffset;
|
tor@1
|
196 |
}
|
tor@1
|
197 |
|
tor@1
|
198 |
// Don't attempt cleaning up the source if we don't have the buffer position we need
|
tor@364
|
199 |
if (offset == -1) {
|
tor@364
|
200 |
return false;
|
tor@1
|
201 |
}
|
tor@1
|
202 |
|
tor@1
|
203 |
// The user might be editing around the given caretOffset.
|
tor@1
|
204 |
// See if it looks modified
|
tor@1
|
205 |
// Insert an end statement? Insert a } marker?
|
tor@478
|
206 |
String doc = context.source;
|
tor@478
|
207 |
if (offset > doc.length()) {
|
tor@364
|
208 |
return false;
|
tor@1
|
209 |
}
|
tor@1
|
210 |
|
tor@797
|
211 |
if (sanitizing == Sanitize.BLOCK_START) {
|
tor@797
|
212 |
try {
|
tor@2469
|
213 |
int start = GsfUtilities.getRowFirstNonWhite(doc, offset);
|
tor@797
|
214 |
if (start != -1 &&
|
tor@797
|
215 |
start+2 < doc.length() &&
|
tor@797
|
216 |
doc.regionMatches(start, "if", 0, 2)) {
|
tor@797
|
217 |
// TODO - check lexer
|
tor@797
|
218 |
char c = 0;
|
tor@797
|
219 |
if (start+2 < doc.length()) {
|
tor@797
|
220 |
c = doc.charAt(start+2);
|
tor@797
|
221 |
}
|
tor@797
|
222 |
if (!Character.isLetter(c)) {
|
tor@797
|
223 |
int removeStart = start;
|
tor@797
|
224 |
int removeEnd = removeStart+2;
|
tor@797
|
225 |
StringBuilder sb = new StringBuilder(doc.length());
|
tor@797
|
226 |
sb.append(doc.substring(0, removeStart));
|
tor@797
|
227 |
for (int i = removeStart; i < removeEnd; i++) {
|
tor@797
|
228 |
sb.append(' ');
|
tor@797
|
229 |
}
|
tor@797
|
230 |
if (removeEnd < doc.length()) {
|
tor@797
|
231 |
sb.append(doc.substring(removeEnd, doc.length()));
|
tor@797
|
232 |
}
|
tor@797
|
233 |
assert sb.length() == doc.length();
|
tor@797
|
234 |
context.sanitizedRange = new OffsetRange(removeStart, removeEnd);
|
tor@797
|
235 |
context.sanitizedSource = sb.toString();
|
tor@797
|
236 |
context.sanitizedContents = doc.substring(removeStart, removeEnd);
|
tor@797
|
237 |
return true;
|
tor@797
|
238 |
}
|
tor@797
|
239 |
}
|
tor@797
|
240 |
|
tor@797
|
241 |
return false;
|
tor@797
|
242 |
} catch (BadLocationException ble) {
|
tor@797
|
243 |
return false;
|
tor@797
|
244 |
}
|
tor@797
|
245 |
}
|
tor@797
|
246 |
|
tor@1
|
247 |
try {
|
tor@1
|
248 |
// Sometimes the offset shows up on the next line
|
tor@2469
|
249 |
if (GsfUtilities.isRowEmpty(doc, offset) || GsfUtilities.isRowWhite(doc, offset)) {
|
tor@2469
|
250 |
offset = GsfUtilities.getRowStart(doc, offset)-1;
|
tor@364
|
251 |
if (offset < 0) {
|
tor@364
|
252 |
offset = 0;
|
tor@1
|
253 |
}
|
tor@1
|
254 |
}
|
tor@1
|
255 |
|
tor@2469
|
256 |
if (!(GsfUtilities.isRowEmpty(doc, offset) || GsfUtilities.isRowWhite(doc, offset))) {
|
tor@1
|
257 |
if ((sanitizing == Sanitize.EDITED_LINE) || (sanitizing == Sanitize.ERROR_LINE)) {
|
tor@1
|
258 |
// See if I should try to remove the current line, since it has text on it.
|
tor@2469
|
259 |
int lineEnd = GsfUtilities.getRowLastNonWhite(doc, offset);
|
tor@1
|
260 |
|
tor@1
|
261 |
if (lineEnd != -1) {
|
tor@478
|
262 |
StringBuilder sb = new StringBuilder(doc.length());
|
tor@2469
|
263 |
int lineStart = GsfUtilities.getRowStart(doc, offset);
|
tor@1
|
264 |
int rest = lineStart + 1;
|
tor@1
|
265 |
|
tor@478
|
266 |
sb.append(doc.substring(0, lineStart));
|
tor@1
|
267 |
sb.append('#');
|
tor@1
|
268 |
|
tor@478
|
269 |
if (rest < doc.length()) {
|
tor@478
|
270 |
sb.append(doc.substring(rest, doc.length()));
|
tor@1
|
271 |
}
|
tor@478
|
272 |
assert sb.length() == doc.length();
|
tor@1
|
273 |
|
tor@364
|
274 |
context.sanitizedRange = new OffsetRange(lineStart, lineEnd);
|
tor@364
|
275 |
context.sanitizedSource = sb.toString();
|
tor@581
|
276 |
context.sanitizedContents = doc.substring(lineStart, lineEnd);
|
tor@364
|
277 |
return true;
|
tor@1
|
278 |
}
|
tor@1
|
279 |
} else {
|
tor@364
|
280 |
assert sanitizing == Sanitize.ERROR_DOT || sanitizing == Sanitize.EDITED_DOT;
|
tor@1
|
281 |
// Try nuking dots/colons from this line
|
tor@1
|
282 |
// See if I should try to remove the current line, since it has text on it.
|
tor@2469
|
283 |
int lineStart = GsfUtilities.getRowStart(doc, offset);
|
tor@581
|
284 |
int lineEnd = offset-1;
|
tor@581
|
285 |
while (lineEnd >= lineStart && lineEnd < doc.length()) {
|
tor@581
|
286 |
if (!Character.isWhitespace(doc.charAt(lineEnd))) {
|
tor@581
|
287 |
break;
|
tor@581
|
288 |
}
|
tor@581
|
289 |
lineEnd--;
|
tor@581
|
290 |
}
|
tor@581
|
291 |
if (lineEnd > lineStart) {
|
tor@478
|
292 |
StringBuilder sb = new StringBuilder(doc.length());
|
tor@478
|
293 |
String line = doc.substring(lineStart, lineEnd + 1);
|
tor@1
|
294 |
int removeChars = 0;
|
tor@581
|
295 |
int removeEnd = lineEnd+1;
|
tor@1
|
296 |
|
tor@478
|
297 |
if (line.endsWith(".") || line.endsWith("(")) { // NOI18N
|
tor@1
|
298 |
removeChars = 1;
|
tor@581
|
299 |
} else if (line.endsWith(",")) { // NOI18N removeChars = 1;
|
tor@581
|
300 |
removeChars = 1;
|
tor@581
|
301 |
} else if (line.endsWith(",:")) { // NOI18N
|
tor@581
|
302 |
removeChars = 2;
|
tor@581
|
303 |
} else if (line.endsWith(", :")) { // NOI18N
|
tor@581
|
304 |
removeChars = 3;
|
tor@581
|
305 |
} else if (line.endsWith(", ")) { // NOI18N
|
tor@581
|
306 |
removeChars = 2;
|
tor@581
|
307 |
} else if (line.endsWith("=> :")) { // NOI18N
|
tor@581
|
308 |
removeChars = 4;
|
tor@581
|
309 |
} else if (line.endsWith("=>:")) { // NOI18N
|
tor@581
|
310 |
removeChars = 3;
|
tor@581
|
311 |
} else if (line.endsWith("=>")) { // NOI18N
|
tor@581
|
312 |
removeChars = 2;
|
tor@478
|
313 |
} else if (line.endsWith("::")) { // NOI18N
|
tor@1
|
314 |
removeChars = 2;
|
tor@544
|
315 |
} else if (line.endsWith(":")) { // NOI18N
|
tor@544
|
316 |
removeChars = 1;
|
tor@478
|
317 |
} else if (line.endsWith("@@")) { // NOI18N
|
tor@478
|
318 |
removeChars = 2;
|
tor@2324
|
319 |
} else if (line.endsWith("@") || line.endsWith("$")) { // NOI18N
|
tor@478
|
320 |
removeChars = 1;
|
tor@478
|
321 |
} else if (line.endsWith(",)")) { // NOI18N
|
tor@1
|
322 |
// Handle lone comma in parameter list - e.g.
|
tor@1
|
323 |
// type "foo(a," -> you end up with "foo(a,|)" which doesn't parse - but
|
tor@1
|
324 |
// the line ends with ")", not "," !
|
tor@1
|
325 |
// Just remove the comma
|
tor@1
|
326 |
removeChars = 1;
|
tor@581
|
327 |
removeEnd--;
|
tor@478
|
328 |
} else if (line.endsWith(", )")) { // NOI18N
|
tor@1
|
329 |
// Just remove the comma
|
tor@1
|
330 |
removeChars = 1;
|
tor@581
|
331 |
removeEnd -= 2;
|
tor@1
|
332 |
}
|
tor@364
|
333 |
|
tor@364
|
334 |
if (removeChars == 0) {
|
tor@364
|
335 |
return false;
|
tor@364
|
336 |
}
|
tor@1
|
337 |
|
tor@581
|
338 |
int removeStart = removeEnd-removeChars;
|
tor@1
|
339 |
|
tor@581
|
340 |
sb.append(doc.substring(0, removeStart));
|
tor@1
|
341 |
|
tor@1
|
342 |
for (int i = 0; i < removeChars; i++) {
|
tor@1
|
343 |
sb.append(' ');
|
tor@1
|
344 |
}
|
tor@1
|
345 |
|
tor@581
|
346 |
if (removeEnd < doc.length()) {
|
tor@581
|
347 |
sb.append(doc.substring(removeEnd, doc.length()));
|
tor@1
|
348 |
}
|
tor@478
|
349 |
assert sb.length() == doc.length();
|
tor@1
|
350 |
|
tor@581
|
351 |
context.sanitizedRange = new OffsetRange(removeStart, removeEnd);
|
tor@364
|
352 |
context.sanitizedSource = sb.toString();
|
tor@581
|
353 |
context.sanitizedContents = doc.substring(removeStart, removeEnd);
|
tor@364
|
354 |
return true;
|
tor@1
|
355 |
}
|
tor@1
|
356 |
}
|
tor@1
|
357 |
}
|
tor@1
|
358 |
} catch (BadLocationException ble) {
|
emononen@3695
|
359 |
// do nothing - see #154991
|
tor@1
|
360 |
}
|
tor@1
|
361 |
|
tor@364
|
362 |
return false;
|
tor@1
|
363 |
}
|
tor@478
|
364 |
|
tor@364
|
365 |
@SuppressWarnings("fallthrough")
|
mkrauskopf@3030
|
366 |
private RubyParseResult sanitize(final Context context, final Sanitize sanitizing) {
|
tor@364
|
367 |
|
tor@364
|
368 |
switch (sanitizing) {
|
tor@364
|
369 |
case NEVER:
|
mkrauskopf@3030
|
370 |
return createParseResult(context.snapshot, null);
|
tor@364
|
371 |
|
tor@364
|
372 |
case NONE:
|
tor@364
|
373 |
|
tor@364
|
374 |
// We've currently tried with no sanitization: try first level
|
tor@364
|
375 |
// of sanitization - removing dots/colons at the edited offset.
|
tor@364
|
376 |
// First try removing the dots or double colons around the failing position
|
tor@364
|
377 |
if (context.caretOffset != -1) {
|
tor@364
|
378 |
return parseBuffer(context, Sanitize.EDITED_DOT);
|
tor@364
|
379 |
}
|
tor@364
|
380 |
|
tor@364
|
381 |
// Fall through to try the next trick
|
tor@364
|
382 |
case EDITED_DOT:
|
tor@364
|
383 |
|
tor@364
|
384 |
// We've tried editing the caret location - now try editing the error location
|
tor@478
|
385 |
// (Don't bother doing this if errorOffset==caretOffset since that would try the same
|
tor@478
|
386 |
// source as EDITED_DOT which has no better chance of succeeding...)
|
tor@478
|
387 |
if (context.errorOffset != -1 && context.errorOffset != context.caretOffset) {
|
tor@364
|
388 |
return parseBuffer(context, Sanitize.ERROR_DOT);
|
tor@364
|
389 |
}
|
tor@364
|
390 |
|
tor@364
|
391 |
// Fall through to try the next trick
|
tor@364
|
392 |
case ERROR_DOT:
|
tor@364
|
393 |
|
tor@364
|
394 |
// We've tried removing dots - now try removing the whole line at the error position
|
tor@797
|
395 |
if (context.caretOffset != -1) {
|
tor@797
|
396 |
return parseBuffer(context, Sanitize.BLOCK_START);
|
tor@797
|
397 |
}
|
tor@797
|
398 |
|
tor@797
|
399 |
// Fall through to try the next trick
|
tor@797
|
400 |
case BLOCK_START:
|
tor@797
|
401 |
|
tor@797
|
402 |
// We've tried removing dots - now try removing the whole line at the error position
|
tor@364
|
403 |
if (context.errorOffset != -1) {
|
tor@364
|
404 |
return parseBuffer(context, Sanitize.ERROR_LINE);
|
tor@364
|
405 |
}
|
tor@364
|
406 |
|
tor@364
|
407 |
// Fall through to try the next trick
|
tor@364
|
408 |
case ERROR_LINE:
|
tor@364
|
409 |
|
tor@364
|
410 |
// Messing with the error line didn't work - we could try "around" the error line
|
tor@364
|
411 |
// but I'm not attempting that now.
|
tor@364
|
412 |
// Finally try removing the whole line around the user editing position
|
tor@364
|
413 |
// (which could be far from where the error is showing up - but if you're typing
|
tor@364
|
414 |
// say a new "def" statement in a class, this will show up as an error on a mismatched
|
tor@364
|
415 |
// "end" statement rather than here
|
tor@364
|
416 |
if (context.caretOffset != -1) {
|
tor@364
|
417 |
return parseBuffer(context, Sanitize.EDITED_LINE);
|
tor@364
|
418 |
}
|
tor@364
|
419 |
|
tor@364
|
420 |
// Fall through to try the next trick
|
tor@364
|
421 |
case EDITED_LINE:
|
tor@364
|
422 |
return parseBuffer(context, Sanitize.MISSING_END);
|
tor@364
|
423 |
|
tor@364
|
424 |
// Fall through for default handling
|
tor@364
|
425 |
case MISSING_END:
|
tor@364
|
426 |
default:
|
tor@364
|
427 |
// We're out of tricks - just return the failed parse result
|
mkrauskopf@3030
|
428 |
return createParseResult(context.snapshot, null);
|
tor@364
|
429 |
}
|
tor@364
|
430 |
}
|
tor@1291
|
431 |
|
tor@1291
|
432 |
protected void notifyError(Context context, ID id,
|
tor@1291
|
433 |
Severity severity, String description, int offset, Sanitize sanitizing, Object[] data) {
|
enebo@4529
|
434 |
if (description == null) description = "";
|
enebo@4518
|
435 |
if (description.startsWith("syntax error, ")) description = description.substring(14);
|
enebo@4518
|
436 |
|
tor@2501
|
437 |
if (description.startsWith("unexpected k")) {
|
tor@2501
|
438 |
description = "Unexpected keyword " + description.substring(12);
|
tor@2501
|
439 |
}
|
tor@1
|
440 |
// Replace a common but unwieldy JRuby error message with a shorter one
|
tor@1
|
441 |
if (description.startsWith("syntax error, expecting ")) { // NOI18N
|
tor@1
|
442 |
int start = description.indexOf(" but found "); // NOI18N
|
tor@1
|
443 |
assert start != -1;
|
tor@1
|
444 |
start += 11;
|
tor@1
|
445 |
int end = description.indexOf("instead", start); // NOI18N
|
tor@1
|
446 |
assert end != -1;
|
tor@1
|
447 |
String found = description.substring(start, end);
|
tor@1291
|
448 |
description = NbBundle.getMessage(RubyParser.class, "UnexpectedError", found);
|
tor@1
|
449 |
}
|
tor@2501
|
450 |
|
tor@2501
|
451 |
if (description.length() > 0) {
|
tor@2501
|
452 |
// Capitalize sentences
|
tor@2501
|
453 |
char firstChar = description.charAt(0);
|
tor@2501
|
454 |
char upcasedChar = Character.toUpperCase(firstChar);
|
tor@2501
|
455 |
if (firstChar != upcasedChar) {
|
tor@2501
|
456 |
description = upcasedChar + description.substring(1);
|
tor@2501
|
457 |
}
|
tor@2501
|
458 |
}
|
tor@1
|
459 |
|
mkrauskopf@3030
|
460 |
Error error = new RubyError(description, id, context.snapshot.getSource().getFileObject(), offset, offset, severity, data);
|
emononen@3102
|
461 |
context.errorHandler.error(error);
|
tor@1
|
462 |
if (sanitizing == Sanitize.NONE) {
|
tor@364
|
463 |
context.errorOffset = offset;
|
tor@1
|
464 |
}
|
tor@1
|
465 |
}
|
tor@1
|
466 |
|
tor@544
|
467 |
protected RubyParseResult parseBuffer(final Context context, final Sanitize sanitizing) {
|
tor@1
|
468 |
boolean sanitizedSource = false;
|
tor@364
|
469 |
String source = context.source;
|
tor@364
|
470 |
if (!((sanitizing == Sanitize.NONE) || (sanitizing == Sanitize.NEVER))) {
|
tor@364
|
471 |
boolean ok = sanitizeSource(context, sanitizing);
|
tor@1
|
472 |
|
tor@364
|
473 |
if (ok) {
|
tor@364
|
474 |
assert context.sanitizedSource != null;
|
tor@1
|
475 |
sanitizedSource = true;
|
tor@364
|
476 |
source = context.sanitizedSource;
|
tor@364
|
477 |
} else {
|
tor@364
|
478 |
// Try next trick
|
tor@364
|
479 |
return sanitize(context, sanitizing);
|
tor@1
|
480 |
}
|
tor@1
|
481 |
}
|
tor@1
|
482 |
|
emononen@3259
|
483 |
ParserResult result = null;
|
tor@1
|
484 |
|
tor@1
|
485 |
final boolean ignoreErrors = sanitizedSource;
|
tor@1
|
486 |
|
tor@1
|
487 |
try {
|
tor@1
|
488 |
IRubyWarnings warnings =
|
tor@1
|
489 |
new IRubyWarnings() {
|
enebo@4515
|
490 |
@Override
|
tor@1
|
491 |
public boolean isVerbose() {
|
tor@1
|
492 |
return false;
|
tor@1
|
493 |
}
|
tor@1
|
494 |
|
enebo@4515
|
495 |
@Override
|
emononen@3259
|
496 |
public void warn(ID id, SourcePosition position, String message, Object... data) {
|
tor@1
|
497 |
if (!ignoreErrors) {
|
tor@1291
|
498 |
notifyError(context, id, Severity.WARNING, message, position.getStartOffset(),
|
tor@1291
|
499 |
sanitizing, data);
|
tor@1
|
500 |
}
|
tor@1
|
501 |
}
|
tor@1
|
502 |
|
enebo@4515
|
503 |
@Override
|
tor@1291
|
504 |
public void warn(ID id, String fileName, int lineNumber, String message, Object... data) {
|
tor@1291
|
505 |
// XXX What about a the position? Compute from fileName+lineNumber?
|
tor@1
|
506 |
if (!ignoreErrors) {
|
tor@1291
|
507 |
notifyError(context, id, Severity.WARNING, message, -1,
|
tor@1291
|
508 |
sanitizing, data);
|
tor@1
|
509 |
}
|
tor@1
|
510 |
}
|
tor@1
|
511 |
|
enebo@4515
|
512 |
@Override
|
tor@1291
|
513 |
public void warn(ID id, String message, Object... data) {
|
tor@1
|
514 |
if (!ignoreErrors) {
|
tor@1291
|
515 |
notifyError(context, id, Severity.WARNING, message, -1,
|
tor@1291
|
516 |
sanitizing, data);
|
tor@1291
|
517 |
}
|
tor@1291
|
518 |
}
|
tor@1291
|
519 |
|
enebo@4515
|
520 |
@Override
|
tor@1291
|
521 |
public void warning(ID id, String message, Object... data) {
|
tor@1291
|
522 |
if (!ignoreErrors) {
|
tor@1291
|
523 |
notifyError(context, id, Severity.WARNING, message, -1,
|
tor@1291
|
524 |
sanitizing, data);
|
tor@1291
|
525 |
}
|
tor@1291
|
526 |
}
|
tor@1291
|
527 |
|
enebo@4515
|
528 |
@Override
|
emononen@3259
|
529 |
public void warning(ID id, SourcePosition position, String message, Object... data) {
|
tor@1291
|
530 |
if (!ignoreErrors) {
|
tor@1291
|
531 |
notifyError(context, id, Severity.WARNING, message, position.getStartOffset(),
|
tor@1291
|
532 |
sanitizing, data);
|
tor@1291
|
533 |
}
|
tor@1291
|
534 |
}
|
tor@1291
|
535 |
|
enebo@4515
|
536 |
@Override
|
tor@1291
|
537 |
public void warning(ID id, String fileName, int lineNumber, String message, Object... data) {
|
tor@1291
|
538 |
// XXX What about a the position? Compute from fileName+lineNumber?
|
tor@1291
|
539 |
if (!ignoreErrors) {
|
tor@1291
|
540 |
notifyError(context, id, Severity.WARNING, message, -1,
|
tor@1291
|
541 |
sanitizing, data);
|
tor@1
|
542 |
}
|
tor@1
|
543 |
}
|
tor@1
|
544 |
};
|
tor@1
|
545 |
|
tor@1
|
546 |
//warnings.setFile(file);
|
emononen@3264
|
547 |
org.jrubyparser.parser.RubyParser parser = getParserFor(context);
|
tor@1
|
548 |
parser.setWarnings(warnings);
|
tor@1
|
549 |
|
tor@1
|
550 |
if (sanitizing == Sanitize.NONE) {
|
tor@364
|
551 |
context.errorOffset = -1;
|
tor@1
|
552 |
}
|
tor@1
|
553 |
|
tor@1
|
554 |
String fileName = "";
|
tor@1
|
555 |
|
mkrauskopf@3030
|
556 |
final FileObject fo = context.snapshot.getSource().getFileObject();
|
mkrauskopf@3030
|
557 |
if (fo != null) {
|
mkrauskopf@3030
|
558 |
fileName = fo.getNameExt();
|
tor@1
|
559 |
}
|
tor@1
|
560 |
|
emononen@3259
|
561 |
ParserConfiguration configuration = new ParserConfiguration();
|
emononen@3533
|
562 |
LexerSource lexerSource =
|
emononen@3533
|
563 |
LexerSource.getSource(fileName, new StringReader(source), configuration);
|
tor@1
|
564 |
result = parser.parse(configuration, lexerSource);
|
enebo@4515
|
565 |
} catch (IOException e) {
|
enebo@4515
|
566 |
// FIXME: This should do something other than ignore?
|
tor@1
|
567 |
} catch (SyntaxException e) {
|
tor@1
|
568 |
int offset = e.getPosition().getStartOffset();
|
tor@1
|
569 |
|
tor@1
|
570 |
// XXX should this be >, and = length?
|
tor@1
|
571 |
if (offset >= source.length()) {
|
tor@1
|
572 |
offset = source.length() - 1;
|
tor@1
|
573 |
|
tor@1
|
574 |
if (offset < 0) {
|
tor@1
|
575 |
offset = 0;
|
tor@1
|
576 |
}
|
tor@1
|
577 |
}
|
tor@1
|
578 |
|
tor@1
|
579 |
if (!ignoreErrors) {
|
emononen@3259
|
580 |
//XXX: jruby-parser
|
tor@1291
|
581 |
notifyError(context, ID.SYNTAX_ERROR, Severity.ERROR, e.getMessage(),
|
tor@1291
|
582 |
offset, sanitizing, new Object[] { e.getPid(), e });
|
tor@1
|
583 |
}
|
tor@1
|
584 |
}
|
tor@1
|
585 |
|
tor@1
|
586 |
Node root = (result != null) ? result.getAST() : null;
|
tor@1
|
587 |
|
enebo@4542
|
588 |
// FIXME: Change all code to not mind searching from RootNode
|
tor@1
|
589 |
if (root instanceof RootNode) {
|
enebo@4542
|
590 |
Node body = ((RootNode) root).getBody();
|
enebo@4542
|
591 |
|
enebo@4542
|
592 |
if (body != null) root = body;
|
tor@1
|
593 |
}
|
tor@1
|
594 |
|
enebo@4542
|
595 |
// FIXME: Is can a non-null result really have a null root node?
|
tor@1
|
596 |
if (root != null) {
|
tor@364
|
597 |
context.sanitized = sanitizing;
|
mkrauskopf@3030
|
598 |
RubyParseResult r = createParseResult(context.snapshot, root);
|
tor@581
|
599 |
r.setSanitized(context.sanitized, context.sanitizedRange, context.sanitizedContents);
|
tor@1
|
600 |
r.setSource(source);
|
tor@1
|
601 |
return r;
|
tor@1
|
602 |
}
|
enebo@4542
|
603 |
|
enebo@4542
|
604 |
return sanitize(context, sanitizing);
|
tor@1
|
605 |
}
|
emononen@3259
|
606 |
|
emononen@3264
|
607 |
|
emononen@3264
|
608 |
/**
|
emononen@3264
|
609 |
* Gets the parser for the given context. If the context is owned by
|
emononen@3264
|
610 |
* a project that uses Ruby 1.9 or JRuby with 1.9 turned on, this method
|
emononen@3264
|
611 |
* will return a 1.9 compatible parser; otherwise a 1.8 compatible parser.
|
emononen@3264
|
612 |
*
|
emononen@3264
|
613 |
* @param context
|
emononen@3264
|
614 |
* @return
|
emononen@3264
|
615 |
*/
|
emononen@3264
|
616 |
private static org.jrubyparser.parser.RubyParser getParserFor(Context context) {
|
emononen@3264
|
617 |
// currently there is no way to specify a source level for the project
|
emononen@3264
|
618 |
// using a UI. instead the source version is determined by the platform the project
|
emononen@3264
|
619 |
// uses, or in case JRuby that can support both 1.8 and 1.9 we check for the
|
emononen@3264
|
620 |
// specified compat level
|
emononen@3264
|
621 |
FileObject fo = context.snapshot.getSource().getFileObject();
|
enebo@4515
|
622 |
if (fo == null) return getDefaultParser();
|
enebo@4515
|
623 |
|
emononen@3264
|
624 |
Project owner = FileOwnerQuery.getOwner(fo);
|
enebo@4515
|
625 |
if (owner == null) return getDefaultParser();
|
enebo@4515
|
626 |
|
emononen@3264
|
627 |
RubyPlatform platform = RubyPlatform.platformFor(owner);
|
enebo@4515
|
628 |
|
enebo@4515
|
629 |
if (platform == null) return getDefaultParser();
|
enebo@4515
|
630 |
if (platform.isJRuby()) return getParserForJRuby(owner);
|
enebo@4542
|
631 |
|
enebo@4542
|
632 |
return getParserFromProject(owner);
|
enebo@4542
|
633 |
}
|
enebo@4542
|
634 |
|
enebo@4542
|
635 |
private static org.jrubyparser.parser.RubyParser getParserFromProject(Project owner) {
|
enebo@4542
|
636 |
RubyPlatform platform = RubyPlatform.platformFor(owner);
|
enebo@4542
|
637 |
|
enebo@4542
|
638 |
if (platform != null) {
|
enebo@4542
|
639 |
if (platform.is18()) return new Ruby18Parser();
|
enebo@4542
|
640 |
if (platform.is19()) return new Ruby19Parser();
|
enebo@4542
|
641 |
if (platform.is20()) return new Ruby20Parser();
|
enebo@4542
|
642 |
}
|
enebo@4542
|
643 |
|
enebo@4542
|
644 |
return getDefaultParser();
|
enebo@4542
|
645 |
}
|
enebo@4542
|
646 |
|
enebo@4542
|
647 |
private static org.jrubyparser.parser.RubyParser getDefaultParser() {
|
enebo@4542
|
648 |
return DEFAULT_TO_RUBY18 ? new Ruby18Parser() : new Ruby20Parser();
|
emononen@3264
|
649 |
}
|
emononen@3264
|
650 |
|
emononen@3264
|
651 |
private static org.jrubyparser.parser.RubyParser getParserForJRuby(Project project) {
|
emononen@3264
|
652 |
PropertyEvaluator evaluator = project.getLookup().lookup(PropertyEvaluator.class);
|
emononen@3264
|
653 |
if (evaluator != null) {
|
emononen@3264
|
654 |
// specified in SharedRubyProjectProperties, but don't want add a dep to it.
|
emononen@3264
|
655 |
String jvmArgs = evaluator.getProperty("jvm.args"); //NOI18N
|
emononen@3264
|
656 |
if (jvmArgs != null) {
|
enebo@4542
|
657 |
if (jvmArgs.contains("jruby.compat.version=RUBY1_8")) return new Ruby18Parser();
|
enebo@4542
|
658 |
if (jvmArgs.contains("jruby.compat.version=RUBY1_9")) return new Ruby19Parser();
|
enebo@4542
|
659 |
if (jvmArgs.contains("jruby.compat.version=RUBY2_0")) return new Ruby20Parser();
|
emononen@3264
|
660 |
}
|
emononen@3264
|
661 |
}
|
enebo@4542
|
662 |
return getParserFromProject(project);
|
emononen@3264
|
663 |
}
|
emononen@3264
|
664 |
|
mkrauskopf@3030
|
665 |
protected RubyParseResult createParseResult(Snapshot snapshots, Node rootNode) {
|
enebo@4554
|
666 |
return new RubyParseResult(snapshots, rootNode);
|
tor@544
|
667 |
}
|
tor@544
|
668 |
|
emononen@3259
|
669 |
public static RubyElement resolveHandle(org.netbeans.modules.csl.spi.ParserResult info, ElementHandle handle) {
|
enebo@4542
|
670 |
if (handle instanceof RubyElement) return (RubyElement) handle;
|
tor@1205
|
671 |
if (handle instanceof AstElement) {
|
tor@1205
|
672 |
AstElement element = (AstElement)handle;
|
enebo@4542
|
673 |
|
emononen@3259
|
674 |
org.netbeans.modules.csl.spi.ParserResult oldInfo = element.getInfo();
|
enebo@4542
|
675 |
if (oldInfo == info) return element;
|
enebo@4542
|
676 |
|
tor@1238
|
677 |
Node oldNode = element.getNode();
|
tor@1205
|
678 |
Node oldRoot = AstUtilities.getRoot(oldInfo);
|
tor@1205
|
679 |
|
tor@1205
|
680 |
Node newRoot = AstUtilities.getRoot(info);
|
enebo@4542
|
681 |
if (newRoot == null) return null;
|
tor@1
|
682 |
|
tor@1205
|
683 |
// Find newNode
|
tor@1205
|
684 |
Node newNode = find(oldRoot, oldNode, newRoot);
|
enebo@4542
|
685 |
if (newNode != null) return AstElement.create(info, newNode);
|
tor@1
|
686 |
}
|
tor@1
|
687 |
return null;
|
tor@1
|
688 |
}
|
tor@1
|
689 |
|
enebo@4542
|
690 |
/**
|
enebo@4542
|
691 |
* Assumes nearly or completely identical ASTs. Finds same point in new tree where oldObject
|
enebo@4542
|
692 |
* was in old tree.
|
enebo@4542
|
693 |
*/
|
tor@1198
|
694 |
private static Node find(Node oldRoot, Node oldObject, Node newRoot) {
|
tor@1
|
695 |
// Walk down the tree to locate oldObject, and in the process, pick the same child for newRoot
|
enebo@4542
|
696 |
Iterator<Node> itNew = newRoot.childNodes().iterator();
|
enebo@4542
|
697 |
|
enebo@4542
|
698 |
for (Node o: oldRoot.childNodes()) {
|
enebo@4542
|
699 |
if (!itNew.hasNext()) return null; // different number of nodes between trees. mismatch
|
enebo@4542
|
700 |
|
enebo@4542
|
701 |
if (o == oldObject) return itNew.next(); // found location in old tree return new equiv.
|
enebo@4542
|
702 |
|
enebo@4542
|
703 |
Node match = find(o, oldObject, itNew.next()); // depth-first search
|
enebo@4542
|
704 |
if (match != null) return match;
|
tor@1
|
705 |
}
|
enebo@4542
|
706 |
|
tor@1
|
707 |
return null;
|
tor@1
|
708 |
}
|
tor@1
|
709 |
|
tor@1
|
710 |
/** Attempts to sanitize the input buffer */
|
tor@364
|
711 |
public static enum Sanitize {
|
tor@1
|
712 |
/** Only parse the current file accurately, don't try heuristics */
|
tor@1
|
713 |
NEVER,
|
tor@1
|
714 |
/** Perform no sanitization */
|
tor@1
|
715 |
NONE,
|
tor@364
|
716 |
/** Try to remove the trailing . or :: at the caret line */
|
tor@364
|
717 |
EDITED_DOT,
|
tor@1
|
718 |
/** Try to remove the trailing . or :: at the error position, or the prior
|
tor@1
|
719 |
* line, or the caret line */
|
tor@1
|
720 |
ERROR_DOT,
|
tor@797
|
721 |
/** Try to remove the initial "if" or "unless" on the block
|
tor@797
|
722 |
* in case it's not terminated
|
tor@797
|
723 |
*/
|
tor@797
|
724 |
BLOCK_START,
|
tor@1
|
725 |
/** Try to cut out the error line */
|
tor@1
|
726 |
ERROR_LINE,
|
tor@1
|
727 |
/** Try to cut out the current edited line, if known */
|
tor@364
|
728 |
EDITED_LINE,
|
tor@364
|
729 |
/** Attempt to add an "end" to the end of the buffer to make it compile */
|
tor@364
|
730 |
MISSING_END,
|
tor@364
|
731 |
}
|
tor@364
|
732 |
|
tor@364
|
733 |
/** Parsing context */
|
tor@364
|
734 |
public static class Context {
|
mkrauskopf@3030
|
735 |
private final Snapshot snapshot;
|
mkrauskopf@3030
|
736 |
private final SourceModificationEvent event;
|
tor@364
|
737 |
private int errorOffset;
|
tor@364
|
738 |
private String source;
|
tor@364
|
739 |
private String sanitizedSource;
|
tor@364
|
740 |
private OffsetRange sanitizedRange = OffsetRange.NONE;
|
tor@581
|
741 |
private String sanitizedContents;
|
tor@364
|
742 |
private int caretOffset;
|
tor@364
|
743 |
private Sanitize sanitized = Sanitize.NONE;
|
mkrauskopf@3030
|
744 |
private ParseErrorHandler errorHandler;
|
tor@364
|
745 |
|
mkrauskopf@3030
|
746 |
public Context(Snapshot snapshot, SourceModificationEvent event) {
|
mkrauskopf@3030
|
747 |
this.snapshot = snapshot;
|
mkrauskopf@3030
|
748 |
this.event = event;
|
mkrauskopf@3030
|
749 |
this.source = asString(snapshot.getText());
|
mkrauskopf@3030
|
750 |
this.caretOffset = GsfUtilities.getLastKnownCaretOffset(snapshot, event);
|
tor@364
|
751 |
}
|
tor@364
|
752 |
|
tor@364
|
753 |
@Override
|
tor@364
|
754 |
public String toString() {
|
mkrauskopf@3030
|
755 |
return "RubyParser.Context(" + snapshot.getSource().getFileObject() + ")"; // NOI18N
|
tor@364
|
756 |
}
|
tor@364
|
757 |
|
tor@364
|
758 |
public OffsetRange getSanitizedRange() {
|
tor@364
|
759 |
return sanitizedRange;
|
tor@364
|
760 |
}
|
tor@364
|
761 |
|
tor@364
|
762 |
public Sanitize getSanitized() {
|
tor@364
|
763 |
return sanitized;
|
tor@364
|
764 |
}
|
tor@364
|
765 |
|
tor@364
|
766 |
public String getSanitizedSource() {
|
tor@364
|
767 |
return sanitizedSource;
|
tor@364
|
768 |
}
|
tor@364
|
769 |
|
tor@364
|
770 |
public int getErrorOffset() {
|
tor@364
|
771 |
return errorOffset;
|
tor@364
|
772 |
}
|
tor@1
|
773 |
}
|
mkrauskopf@3030
|
774 |
|
mkrauskopf@3030
|
775 |
private static interface ParseErrorHandler {
|
mkrauskopf@3030
|
776 |
void error(Error error);
|
mkrauskopf@3030
|
777 |
}
|
mkrauskopf@3030
|
778 |
|
emononen@3932
|
779 |
public static class RubyError implements Error.Badging {
|
mkrauskopf@3030
|
780 |
|
tor@1291
|
781 |
private final String displayName;
|
tor@1291
|
782 |
private final ID id;
|
tor@1291
|
783 |
private final FileObject file;
|
tor@1291
|
784 |
private final int startPosition;
|
tor@1291
|
785 |
private final int endPosition;
|
tor@1291
|
786 |
private final Severity severity;
|
tor@1291
|
787 |
private final Object[] parameters;
|
tor@1291
|
788 |
|
tor@1291
|
789 |
public RubyError(String displayName, ID id, FileObject file, int startPosition, int endPosition, Severity severity, Object[] parameters) {
|
tor@1291
|
790 |
this.displayName = displayName;
|
tor@1291
|
791 |
this.id = id;
|
tor@1291
|
792 |
this.file = file;
|
tor@1291
|
793 |
this.startPosition = startPosition;
|
tor@1291
|
794 |
this.endPosition = endPosition;
|
tor@1291
|
795 |
this.severity = severity;
|
tor@1291
|
796 |
this.parameters = parameters;
|
tor@1291
|
797 |
}
|
tor@1291
|
798 |
|
emononen@3932
|
799 |
@Override
|
tor@1291
|
800 |
public String getDisplayName() {
|
tor@1291
|
801 |
return displayName;
|
tor@1291
|
802 |
}
|
tor@1291
|
803 |
|
emononen@3932
|
804 |
@Override
|
tor@1291
|
805 |
public int getStartPosition() {
|
tor@1291
|
806 |
return startPosition;
|
tor@1291
|
807 |
}
|
tor@1291
|
808 |
|
emononen@3932
|
809 |
@Override
|
tor@1291
|
810 |
public int getEndPosition() {
|
tor@1291
|
811 |
return endPosition;
|
tor@1291
|
812 |
}
|
tor@1291
|
813 |
|
emononen@3932
|
814 |
@Override
|
tor@1291
|
815 |
public FileObject getFile() {
|
tor@1291
|
816 |
return file;
|
tor@1291
|
817 |
}
|
tor@1291
|
818 |
|
emononen@3932
|
819 |
@Override
|
tor@1291
|
820 |
public String getKey() {
|
tor@1291
|
821 |
return id != null ? id.name() : "";
|
tor@1291
|
822 |
}
|
tor@1291
|
823 |
|
tor@1291
|
824 |
public ID getId() {
|
tor@1291
|
825 |
return id;
|
tor@1291
|
826 |
}
|
tor@1291
|
827 |
|
emononen@3932
|
828 |
@Override
|
tor@1291
|
829 |
public Object[] getParameters() {
|
tor@1291
|
830 |
return parameters;
|
tor@1291
|
831 |
}
|
tor@1291
|
832 |
|
emononen@3932
|
833 |
@Override
|
tor@1291
|
834 |
public Severity getSeverity() {
|
tor@1291
|
835 |
return severity;
|
tor@1291
|
836 |
}
|
tor@1291
|
837 |
|
tor@1291
|
838 |
@Override
|
tor@1291
|
839 |
public String toString() {
|
tor@1291
|
840 |
return "RubyError:" + displayName;
|
tor@1291
|
841 |
}
|
tor@1291
|
842 |
|
emononen@3932
|
843 |
@Override
|
tor@1291
|
844 |
public String getDescription() {
|
tor@1291
|
845 |
return null;
|
tor@1291
|
846 |
}
|
mfukala@3312
|
847 |
|
emononen@3932
|
848 |
@Override
|
mfukala@3312
|
849 |
public boolean isLineError() {
|
mfukala@3312
|
850 |
return true;
|
mfukala@3312
|
851 |
}
|
emononen@3932
|
852 |
|
emononen@3932
|
853 |
@Override
|
emononen@3932
|
854 |
public boolean showExplorerBadge() {
|
emononen@4081
|
855 |
// don't show explored badges for rhtml files,
|
emononen@4081
|
856 |
// see #183453
|
emononen@4081
|
857 |
return !RubyUtils.isRhtmlFile(file);
|
emononen@3932
|
858 |
}
|
tor@1291
|
859 |
}
|
tor@1
|
860 |
}
|