1 19 package org.netbeans.modules.ruby; 20 21 import java.io.PrintWriter ; 22 import java.io.StringWriter ; 23 import java.util.Iterator ; 24 25 import javax.swing.text.BadLocationException ; 26 import javax.swing.text.Document ; 27 28 import org.jruby.ast.ArgumentNode; 29 import org.jruby.ast.CommentNode; 30 import org.jruby.ast.Node; 31 import org.jruby.ast.visitor.rewriter.DefaultFormatHelper; 32 import org.jruby.ast.visitor.rewriter.FormatHelper; 33 import org.jruby.ast.visitor.rewriter.ReWriteVisitor; 34 import org.jruby.ast.visitor.rewriter.ReWriterFactory; 35 import org.jruby.ast.visitor.rewriter.utils.ReWriterContext; 36 import org.netbeans.api.gsf.FormattingPreferences; 37 import org.netbeans.api.gsf.GsfTokenId; 38 import org.netbeans.api.gsf.ParserResult; 39 import org.netbeans.api.lexer.Token; 40 import org.netbeans.api.lexer.TokenId; 41 import org.netbeans.editor.BaseDocument; 42 import org.netbeans.editor.Utilities; 43 import org.netbeans.modules.ruby.lexer.LexUtilities; 44 import org.netbeans.modules.ruby.lexer.RubyTokenId; 45 import org.openide.util.Exceptions; 46 47 48 64 public class Formatter implements org.netbeans.api.gsf.Formatter { 65 private static final int KEEP_INDENT = -1; 66 67 public Formatter() { 68 } 69 70 public int getLineIndent(Document document, int offset, FormattingPreferences preferences) { 71 BaseDocument doc = (BaseDocument)document; 72 73 Token<?extends GsfTokenId> token = LexUtilities.getToken(doc, offset); 74 75 if ((token.id() == RubyTokenId.STRING_LITERAL) || 76 (token.id() == RubyTokenId.QUOTED_STRING_LITERAL) || 77 (token.id() == RubyTokenId.REGEXP_LITERAL)) { 78 return 0; 81 } 82 83 try { 99 int begin = Utilities.getRowStart(doc, offset); 100 101 begin--; 103 104 while ((begin >= 0) && 105 (LexUtilities.isCommentOnlyLine(doc, begin) || 106 Utilities.isRowWhite(doc, begin))) { 107 begin--; 108 } 109 110 if (begin < 0) { 111 return 0; } 113 114 int indent = LexUtilities.getLineIndent(doc, begin); 115 indent = computeIndent(doc, offset, begin, indent, preferences.getIndentation()); 116 117 if (indent == KEEP_INDENT) { 118 indent = LexUtilities.getLineIndent(doc, offset); 119 } 120 121 return indent; 122 } catch (BadLocationException ex) { 123 Exceptions.printStackTrace(ex); 124 } 125 126 return 0; 127 } 128 129 public void reformat(Document document, ParserResult result, FormattingPreferences preferences) { 130 RubyParseResult parseResult = (RubyParseResult)result; 131 132 if ((parseResult == null) || (parseResult.getRealRoot() == null)) { 133 reindent(document, 0, document.getLength(), result, preferences); 135 136 return; 137 } 138 139 if (!parseResult.isCommentsAdded()) { 140 new StructureAnalyzer().addComments(parseResult); 141 } 142 143 BaseDocument doc = (BaseDocument)document; 144 145 StringWriter stringWriter = new StringWriter (document.getLength() * 2); 146 PrintWriter output = new PrintWriter (stringWriter); 147 String source = parseResult.getSource(); 148 FormatHelper formatHelper = new DefaultFormatHelper(); 149 ReWriterContext context = new ReWriterContext(output, source, formatHelper); 150 ReWriterFactory factory = new ReWriterFactory(context); 151 152 ReWriteVisitor visitor = new CommentRewriter(context); 154 155 Node root = parseResult.getRealRoot(); 156 root.accept(visitor); 157 visitor.flushStream(); 158 159 String reformatted = stringWriter.toString(); 160 161 try { 162 doc.atomicLock(); 163 doc.replace(0, doc.getLength(), reformatted, null); 164 } catch (BadLocationException ble) { 165 Exceptions.printStackTrace(ble); 166 } finally { 167 doc.atomicUnlock(); 168 } 169 } 170 171 public void reindent(Document document, int startOffset, int endOffset, ParserResult result, 172 FormattingPreferences preferences) { 173 178 BaseDocument doc = (BaseDocument)document; 182 183 try { 184 int offset = Utilities.getRowStart(doc, startOffset); 185 int end = Utilities.getRowEnd(doc, endOffset); 186 int replaceBegin = offset; 187 int replaceEnd = end; 188 189 int prevIndent = 0; 190 int prevOffset = -1; 191 192 if (offset > 0) { 193 prevOffset = offset - 1; 194 prevIndent = LexUtilities.getLineIndent(doc, prevOffset); 195 } 196 197 int indentSize = preferences.getIndentation(); 198 StringBuilder sb = new StringBuilder (2 * doc.getLength()); 199 200 while (offset < end) { 201 int indent; 217 218 if (prevOffset == -1) { 219 indent = 0; 220 } else { 221 indent = computeIndent(doc, offset, prevOffset, prevIndent, indentSize); 222 } 223 224 if (indent == KEEP_INDENT) { 225 indent = LexUtilities.getLineIndent(doc, offset); 226 } else { 227 prevIndent = indent; 228 prevOffset = offset; 229 } 230 231 int lineBegin = Utilities.getRowFirstNonWhite(doc, offset); 233 int lineEnd = Utilities.getRowLastNonWhite(doc, offset) + 1; 234 235 if ((lineBegin != -1) && (lineEnd > lineBegin)) { 236 for (int i = 0; i < indent; i++) { 237 sb.append(' '); 238 } 239 240 String text = doc.getText(lineBegin, lineEnd - lineBegin); 241 sb.append(text); 242 } 243 244 sb.append("\n"); 245 246 offset = Utilities.getRowEnd(doc, offset) + 1; 247 } 248 249 String reindented = sb.toString(); 250 251 try { 252 doc.atomicLock(); 253 doc.replace(replaceBegin, replaceEnd - replaceBegin, reindented, null); 254 } finally { 255 doc.atomicUnlock(); 256 } 257 } catch (BadLocationException ble) { 258 Exceptions.printStackTrace(ble); 259 } 260 } 261 262 private int computeIndent(BaseDocument doc, int offset, int prevLineOffset, int prevIndent, 263 int indentSize) throws BadLocationException { 264 int indent = prevIndent; 265 int lineBegin = Utilities.getRowFirstNonWhite(doc, prevLineOffset); 266 Token<?extends GsfTokenId> token = LexUtilities.getToken(doc, lineBegin); 267 268 if (token == null) { 269 return indent; 270 } 271 272 TokenId id = token.id(); 277 278 if ((LexUtilities.getBeginEndLineBalance(doc, prevLineOffset) > 0) || (LexUtilities.isIndentToken(id) && !LexUtilities.isBeginToken(id))) { indent += indentSize; 281 } else { 282 int balance = 287 LexUtilities.getLineBalance(doc, prevLineOffset, RubyTokenId.LBRACE, 288 RubyTokenId.RBRACE); 289 290 if (balance > 0) { 291 indent += indentSize; 292 } 293 } 294 295 lineBegin = Utilities.getRowFirstNonWhite(doc, offset); 297 298 if (lineBegin != -1) { 299 token = LexUtilities.getToken(doc, lineBegin); 300 301 if ((token.id() == RubyTokenId.STRING_LITERAL) || 302 (token.id() == RubyTokenId.QUOTED_STRING_LITERAL) || 303 (token.id() == RubyTokenId.REGEXP_LITERAL)) { 304 return KEEP_INDENT; 307 } 308 309 if (token != null) { 310 id = token.id(); 311 312 if ((LexUtilities.isIndentToken(id) && !LexUtilities.isBeginToken(id)) || 313 (id == RubyTokenId.END) || (id == RubyTokenId.RBRACE)) { 314 indent -= indentSize; 315 } 316 } 317 } 318 319 return indent; 320 } 321 322 public int indentSize() { 323 return 2; 324 } 325 326 private static class CommentRewriter extends ReWriteVisitor { 327 public CommentRewriter(ReWriterContext config) { 328 super(config); 329 } 330 331 public void visitNode(Node iVisited) { 332 if (iVisited == null) { 333 return; 334 } 335 336 printCommentsBefore(iVisited); 337 338 if (iVisited instanceof ArgumentNode) { 339 print(((ArgumentNode)iVisited).getName()); 340 } else { 341 iVisited.accept(this); 342 } 343 344 printCommentsAfter(iVisited); 345 config.setLastPosition(iVisited.getPosition()); 346 } 347 348 private static int getEndLine(Node n) { 349 return n.getPosition().getEndLine(); 350 } 351 352 private static int getStartLine(Node n) { 353 return n.getPosition().getStartLine(); 354 } 355 356 private void printCommentsBefore(Node iVisited) { 363 for (Iterator it = iVisited.getComments().iterator(); it.hasNext();) { 364 CommentNode n = (CommentNode)it.next(); 365 366 if (getStartLine(n) < getStartLine(iVisited)) { 367 String comment = n.getContent(); 368 visitNode(n); 369 print(comment); 370 printNewlineAndIndentation(); 371 } 372 } 373 } 374 375 protected boolean printCommentsAfter(Node iVisited) { 376 boolean hasComment = false; 377 378 for (Iterator it = iVisited.getComments().iterator(); it.hasNext();) { 379 CommentNode n = (CommentNode)it.next(); 380 381 if (getStartLine(n) >= getEndLine(iVisited)) { 382 print(' '); 383 visitNode(n); 384 385 String comment = n.getContent(); 386 print(comment); 387 hasComment = true; 388 } 389 } 390 391 return hasComment; 392 } 393 } 394 } 395 | Popular Tags |