1 19 package org.netbeans.modules.ruby; 20 21 import java.io.IOException ; 22 import java.io.Reader ; 23 import java.io.StringReader ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 27 import javax.swing.text.BadLocationException ; 28 29 import org.jruby.ast.Node; 30 import org.jruby.ast.RootNode; 31 import org.jruby.common.IRubyWarnings; 32 import org.jruby.lexer.yacc.ISourcePosition; 33 import org.jruby.lexer.yacc.LexerSource; 34 import org.jruby.lexer.yacc.SyntaxException; 35 import org.jruby.parser.DefaultRubyParser; 36 import org.jruby.parser.RubyParserConfiguration; 37 import org.jruby.parser.RubyParserResult; 38 import org.netbeans.api.gsf.CompilationInfo; 39 import org.netbeans.api.gsf.Element; 40 import org.netbeans.api.gsf.Element; 41 import org.netbeans.api.gsf.ElementHandle; 42 import org.netbeans.api.gsf.Error; 43 import org.netbeans.api.gsf.ParseEvent; 44 import org.netbeans.api.gsf.ParseListener; 45 import org.netbeans.api.gsf.Parser; 46 import org.netbeans.api.gsf.ParserFile; 47 import org.netbeans.api.gsf.ParserResult; 48 import org.netbeans.api.gsf.PositionManager; 49 import org.netbeans.api.gsf.SemanticAnalyzer; 50 import org.netbeans.api.gsf.Severity; 51 import org.netbeans.api.gsf.Severity; 52 import org.netbeans.api.gsf.SourceFileReader; 53 import org.netbeans.editor.BaseDocument; 54 import org.netbeans.editor.Utilities; 55 import org.netbeans.modules.ruby.elements.AstElement; 56 import org.netbeans.modules.ruby.elements.AstRootElement; 57 import org.netbeans.modules.ruby.elements.IndexedElement; 58 import org.netbeans.modules.ruby.elements.KeywordElement; 59 import org.netbeans.spi.gsf.DefaultError; 60 import org.netbeans.spi.gsf.DefaultPosition; 61 import org.openide.filesystems.FileObject; 62 import org.openide.util.Exceptions; 63 64 65 72 public class RubyParser implements Parser { 73 private PositionManager positions = new RubyPositionManager(); 74 75 78 public RubyParser() { 79 } 80 81 private static String asString(CharSequence sequence) { 82 if (sequence instanceof String ) { 83 return (String )sequence; 84 } else { 85 return sequence.toString(); 86 } 87 } 88 89 92 public void parseFiles(List <ParserFile> files, ParseListener listener, SourceFileReader reader) { 93 for (ParserFile file : files) { 94 ParseEvent beginEvent = new ParseEvent(ParseEvent.Kind.PARSE, file, null); 95 listener.started(beginEvent); 96 97 ParserResult result = null; 98 99 try { 100 CharSequence buffer = reader.read(file); 101 int offset = reader.getCaretOffset(file); 102 result = parseBuffer(file, offset, buffer, listener, false); 103 } catch (IOException ioe) { 104 listener.exception(ioe); 105 } 106 107 ParseEvent doneEvent = new ParseEvent(ParseEvent.Kind.PARSE, file, result); 108 listener.finished(doneEvent); 109 } 110 } 111 112 122 private String getSanitizedSource(ParserFile file, int caretOffset, String buffer) { 123 FileObject fileObject = file.getFileObject(); 127 128 if (fileObject == null) { 129 return buffer; 130 } 131 132 BaseDocument doc = AstUtilities.getBaseDocument(fileObject, false); 133 134 if (doc == null) { 135 return buffer; 136 } 137 138 if (caretOffset > doc.getLength()) { 139 return buffer; 140 } 141 142 try { 143 if (!(Utilities.isRowEmpty(doc, caretOffset) || Utilities.isRowWhite(doc, caretOffset))) { 144 int lineEnd = Utilities.getRowLastNonWhite(doc, caretOffset); 146 147 if (lineEnd != -1) { 148 StringBuilder sb = new StringBuilder (doc.getLength()); 149 int lineStart = Utilities.getRowStart(doc, caretOffset); 150 int rest = lineStart + 1; 151 152 sb.append(doc.getText(0, lineStart)); 153 sb.append('#'); 154 155 if (rest < doc.getLength()) { 156 sb.append(doc.getText(rest, doc.getLength() - rest)); 157 } 158 assert sb.length() == doc.getLength(); 159 160 return sb.toString(); 161 } 162 } 163 } catch (BadLocationException ble) { 164 Exceptions.printStackTrace(ble); 165 } 166 167 return buffer; 168 } 169 170 private void notifyError(ParseListener listener, ParserFile file, String key, 171 Severity severity, String description, String details, int offset) { 172 Error error = 173 new DefaultError(key, description, details, file.getFileObject(), 174 new DefaultPosition(offset), new DefaultPosition(offset), severity); 175 listener.error(error); 176 } 177 178 public ParserResult parseBuffer(final ParserFile file, int caretOffset, 179 final CharSequence sequence, final ParseListener listener, boolean trySanitizing) { 180 String source = asString(sequence); 181 boolean sanitizedSource = false; 182 183 if (trySanitizing && (caretOffset != -1)) { 184 String s = getSanitizedSource(file, caretOffset, source); 185 186 if (s != source) { 187 source = s; 188 sanitizedSource = true; 189 } 190 } 191 192 Reader content = new StringReader (source); 193 194 RubyParserResult result = null; 195 196 final boolean ignoreErrors = sanitizedSource; 197 198 try { 199 IRubyWarnings warnings = 200 new IRubyWarnings() { 201 public void warn(ISourcePosition position, String message) { 202 if (!ignoreErrors) { 203 notifyError(listener, file, null, Severity.WARNING, message, null, 204 position.getStartOffset()); 205 } 206 } 207 208 public boolean isVerbose() { 209 return false; 210 } 211 212 public void warn(String message) { 213 if (!ignoreErrors) { 214 notifyError(listener, file, null, Severity.WARNING, message, null, -1); 215 } 216 } 217 218 public void warning(String message) { 219 if (!ignoreErrors) { 220 notifyError(listener, file, null, Severity.WARNING, message, null, -1); 221 } 222 } 223 224 public void warning(ISourcePosition position, String message) { 225 if (!ignoreErrors) { 226 notifyError(listener, file, null, Severity.WARNING, message, null, 227 position.getStartOffset()); 228 } 229 } 230 }; 231 232 DefaultRubyParser parser = new DefaultRubyParser(); 234 parser.setWarnings(warnings); 235 236 String fileName = ""; 237 238 if (file != null) { 239 fileName = file.getFileObject().getNameExt(); 240 } 241 242 LexerSource lexerSource = new LexerSource(fileName, content); 243 RubyParserConfiguration configuration = new RubyParserConfiguration(); 244 result = parser.parse(configuration, lexerSource); 245 } catch (SyntaxException e) { 246 int offset = e.getPosition().getStartOffset(); 247 248 if (offset >= source.length()) { 250 offset = source.length() - 1; 251 252 if (offset < 0) { 253 offset = 0; 254 } 255 } 256 257 if (!ignoreErrors) { 258 notifyError(listener, file, null, Severity.ERROR, e.getMessage(), 259 e.getLocalizedMessage(), offset); 260 } 261 } 262 263 Node root = (result != null) ? result.getAST() : null; 264 265 RootNode realRoot = null; 266 267 if (root instanceof RootNode) { 268 realRoot = (RootNode)root; 272 root = realRoot.getBodyNode(); 273 } 274 275 if (root != null) { 276 AstRootElement rootElement = new AstRootElement(file.getFileObject(), root, result); 277 AstNodeAdapter ast = new AstNodeAdapter(null, root); 278 RubyParseResult r = new RubyParseResult(file, rootElement, ast, root, realRoot, result); 279 r.setSanitizedSource(sanitizedSource); 280 r.setSource(source); 281 282 return r; 283 } else { 284 if (!trySanitizing) { 287 return parseBuffer(file, caretOffset, source, listener, true); 288 } 289 290 return new RubyParseResult(file); 291 } 292 } 293 294 public PositionManager getPositionManager() { 295 return positions; 296 } 297 298 public SemanticAnalyzer getSemanticAnalysisTask() { 299 return new SemanticAnalysis(); 300 } 301 302 public org.netbeans.api.gsf.OccurrencesFinder getMarkOccurrencesTask(int caretPosition) { 303 OccurrencesFinder finder = new OccurrencesFinder(); 304 finder.setCaretPosition(caretPosition); 305 306 return finder; 307 } 308 309 @SuppressWarnings ("unchecked") 310 public <T extends Element> ElementHandle<T> createHandle(CompilationInfo info, final T object) { 311 if (object instanceof KeywordElement) { 312 return new RubyElementHandle(null, object); 314 } 315 316 if (object instanceof IndexedElement) { 318 return new RubyElementHandle(null, object); 321 } 322 323 if (!(object instanceof AstElement)) { 324 return null; 325 } 326 327 ParserResult result = info.getParserResult(); 328 329 if (result == null) { 330 return null; 331 } 332 333 ParserResult.AstTreeNode ast = result.getAst(); 334 335 if (ast == null) { 336 return null; 337 } 338 339 Node root = AstUtilities.getRoot(info); 340 341 return new RubyElementHandle(root, object); 342 } 343 344 @SuppressWarnings ("unchecked") 345 public <T extends Element> T resolveHandle(CompilationInfo info, ElementHandle<T> handle) { 346 RubyElementHandle h = (RubyElementHandle)handle; 347 Node oldRoot = h.root; 348 Node oldNode; 349 350 if (h.object instanceof KeywordElement || h.object instanceof IndexedElement) { 351 return (T)h.object; 353 } 354 355 if (h.object instanceof AstElement) { 356 oldNode = ((AstElement)h.object).getNode(); } else { 358 return null; 359 } 360 361 Node newRoot = AstUtilities.getRoot(info); 362 363 Node newNode = find(oldRoot, oldNode, newRoot); 365 366 if (newNode != null) { 367 Element co = AstElement.create(newNode); 368 369 return (T)co; 370 } 371 372 return null; 373 } 374 375 private Node find(Node oldRoot, Node oldObject, Node newRoot) { 376 @SuppressWarnings ("unchecked") 378 List <?extends Node> oldChildren = oldRoot.childNodes(); 379 @SuppressWarnings ("unchecked") 380 List <?extends Node> newChildren = newRoot.childNodes(); 381 Iterator <?extends Node> itOld = oldChildren.iterator(); 382 Iterator <?extends Node> itNew = newChildren.iterator(); 383 384 while (itOld.hasNext()) { 385 if (!itNew.hasNext()) { 386 return null; } 388 389 Node o = itOld.next(); 390 Node n = itNew.next(); 391 392 if (o == oldObject) { 393 return n; 395 } 396 397 Node match = find(o, oldObject, n); 399 400 if (match != null) { 401 return match; 402 } 403 } 404 405 if (itNew.hasNext()) { 406 return null; } 408 409 return null; 410 } 411 412 private class RubyElementHandle<T extends Element> extends ElementHandle<T> { 413 private Node root; 414 private T object; 415 416 private RubyElementHandle(Node root, T object) { 417 this.root = root; 418 this.object = object; 419 } 420 421 public boolean signatureEquals(ElementHandle handle) { 422 return false; 424 } 425 426 public FileObject getFileObject() { 427 if (object instanceof IndexedElement) { 428 return ((IndexedElement)object).getFileObject(); 429 } 430 431 return null; 432 } 433 } 434 } 435 | Popular Tags |