Source Code Cross Referenced for Formatter.java in  » IDE-Netbeans » ruby » org » netbeans » modules » ruby » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » ruby » org.netbeans.modules.ruby 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003:         *
004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005:         *
006:         * The contents of this file are subject to the terms of either the GNU
007:         * General Public License Version 2 only ("GPL") or the Common
008:         * Development and Distribution License("CDDL") (collectively, the
009:         * "License"). You may not use this file except in compliance with the
010:         * License. You can obtain a copy of the License at
011:         * http://www.netbeans.org/cddl-gplv2.html
012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013:         * specific language governing permissions and limitations under the
014:         * License.  When distributing the software, include this License Header
015:         * Notice in each file and include the License file at
016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
017:         * particular file as subject to the "Classpath" exception as provided
018:         * by Sun in the GPL Version 2 section of the License file that
019:         * accompanied this code. If applicable, add the following below the
020:         * License Header, with the fields enclosed by brackets [] replaced by
021:         * your own identifying information:
022:         * "Portions Copyrighted [year] [name of copyright owner]"
023:         *
024:         * Contributor(s):
025:         *
026:         * The Original Software is NetBeans. The Initial Developer of the Original
027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028:         * Microsystems, Inc. All Rights Reserved.
029:         *
030:         * If you wish your version of this file to be governed by only the CDDL
031:         * or only the GPL Version 2, indicate your decision by adding
032:         * "[Contributor] elects to include this software in this distribution
033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
034:         * single choice of license, a recipient has the option to distribute
035:         * your version of this file under either the CDDL, the GPL Version 2 or
036:         * to extend the choice of license to its licensees as provided above.
037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
038:         * Version 2 license, then the option applies only if the new code is
039:         * made subject to such option by the copyright holder.
040:         */
041:        package org.netbeans.modules.ruby;
042:
043:        import java.util.ArrayList;
044:        import java.util.List;
045:
046:        import javax.swing.text.BadLocationException;
047:        import javax.swing.text.Document;
048:
049:        import org.netbeans.modules.gsf.api.OffsetRange;
050:        import org.netbeans.api.lexer.Token;
051:        import org.netbeans.api.lexer.TokenHierarchy;
052:        import org.netbeans.api.lexer.TokenId;
053:        import org.netbeans.api.lexer.TokenSequence;
054:        import org.netbeans.editor.BaseDocument;
055:        import org.netbeans.editor.Utilities;
056:        import org.netbeans.modules.gsf.api.CompilationInfo;
057:        import org.netbeans.modules.ruby.lexer.LexUtilities;
058:        import org.netbeans.modules.ruby.lexer.RubyTokenId;
059:        import org.netbeans.modules.ruby.options.CodeStyle;
060:        import org.openide.util.Exceptions;
061:
062:        /**
063:         * Formatting and indentation for Ruby.
064:         * 
065:         * @todo Handle RHTML! 
066:         *      - 4 space indents
067:         *      - conflicts with HTML formatter (HtmlIndentTask)
068:         *      - The Ruby indentation should be indented into the HTML level as well!
069:         *          (so I should look at the previous HTML level for my initial context
070:         *           whenever the balance is 0.)
071:         * @todo Use configuration object to pass in Ruby conventions
072:         * @todo Use the provided parse tree, if any, to for example check heredoc nodes
073:         *   and see if they are indentable.
074:         * @todo If you select a complete line, the endOffset is on a new line; adjust it back
075:         * @todo If line ends with \ I definitely have a line continuation!
076:         * @todo Use the Context.modifyIndent() method to change line indents instead of
077:         *   the current document/formatter method
078:         * @todo This line screws up formatting:
079:         *        alias __class__ class #:nodoc:
080:         * @todo Why doesn't this format correctly?
081:         * <pre>
082:         class Module
083:         alias_method :class?, :===
084:         end
085:         * </pre>
086:         *
087:         * @author Tor Norbye
088:         */
089:        public class Formatter implements 
090:                org.netbeans.modules.gsf.api.Formatter {
091:            private boolean isRhtmlDocument;
092:            private final CodeStyle codeStyle;
093:            private int rightMarginOverride = -1;
094:
095:            public Formatter() {
096:                this .codeStyle = CodeStyle.getDefault(null);
097:            }
098:
099:            public Formatter(CodeStyle codeStyle, int rightMarginOverride) {
100:                assert codeStyle != null;
101:                this .codeStyle = codeStyle;
102:                this .rightMarginOverride = rightMarginOverride;
103:            }
104:
105:            public boolean needsParserResult() {
106:                return false;
107:            }
108:
109:            public void reindent(Document document, int startOffset,
110:                    int endOffset) {
111:                // Make sure we're not reindenting HTML content
112:                reindent(document, startOffset, endOffset, null, true);
113:            }
114:
115:            public void reformat(Document document, int startOffset,
116:                    int endOffset, CompilationInfo info) {
117:                reindent(document, startOffset, endOffset, info, false);
118:            }
119:
120:            public int indentSize() {
121:                return codeStyle.getIndentSize();
122:            }
123:
124:            public int hangingIndentSize() {
125:                return codeStyle.getContinuationIndentSize();
126:            }
127:
128:            /** Compute the initial balance of brackets at the given offset. */
129:            private int getFormatStableStart(BaseDocument doc, int offset) {
130:                TokenSequence<? extends RubyTokenId> ts = LexUtilities
131:                        .getRubyTokenSequence(doc, offset);
132:                if (ts == null) {
133:                    return 0;
134:                }
135:
136:                ts.move(offset);
137:
138:                if (!ts.movePrevious()) {
139:                    return 0;
140:                }
141:
142:                // Look backwards to find a suitable context - a class, module or method definition
143:                // which we will assume is properly indented and balanced
144:                do {
145:                    Token<? extends RubyTokenId> token = ts.token();
146:                    TokenId id = token.id();
147:
148:                    if (id == RubyTokenId.CLASS || id == RubyTokenId.MODULE
149:                            || id == RubyTokenId.DEF) {
150:                        return ts.offset();
151:                    }
152:                } while (ts.movePrevious());
153:
154:                return ts.offset();
155:            }
156:
157:            public static int getTokenBalanceDelta(TokenId id,
158:                    Token<? extends RubyTokenId> token, BaseDocument doc,
159:                    TokenSequence<? extends RubyTokenId> ts,
160:                    boolean includeKeywords) {
161:                if (id == RubyTokenId.IDENTIFIER) {
162:                    // In some cases, the [ shows up as an identifier, for example in this expression:
163:                    //  for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
164:                    if (token.length() == 1) {
165:                        char c = token.text().charAt(0);
166:                        if (c == '[') {
167:                            return 1;
168:                        } else if (c == ']') {
169:                            // I've seen "]" come instead of a RBRACKET too - for example in RHTML:
170:                            // <%if session[:user]%>
171:                            return -1;
172:                        }
173:                    }
174:                } else if (id == RubyTokenId.LPAREN
175:                        || id == RubyTokenId.LBRACKET
176:                        || id == RubyTokenId.LBRACE) {
177:                    return 1;
178:                } else if (id == RubyTokenId.RPAREN
179:                        || id == RubyTokenId.RBRACKET
180:                        || id == RubyTokenId.RBRACE) {
181:                    return -1;
182:                } else if (includeKeywords) {
183:                    if (LexUtilities.isBeginToken(id, doc, ts)) {
184:                        return 1;
185:                    } else if (id == RubyTokenId.END) {
186:                        return -1;
187:                    }
188:                }
189:
190:                return 0;
191:            }
192:
193:            // TODO RHTML - there can be many discontiguous sections, I've gotta process all of them on the given line
194:            public static int getTokenBalance(BaseDocument doc, int begin,
195:                    int end, boolean includeKeywords, boolean rhtml) {
196:                int balance = 0;
197:
198:                if (rhtml) {
199:                    TokenHierarchy<Document> th = TokenHierarchy
200:                            .get((Document) doc);
201:                    // Probably an RHTML file - gotta process it in sections since I can have lines
202:                    // made up of both whitespace, ruby, html and delimiters and all ruby sections
203:                    // can affect the token balance
204:                    TokenSequence<?> t = th.tokenSequence();
205:                    if (t == null) {
206:                        return 0;
207:                    }
208:                    t.move(begin);
209:                    if (!t.moveNext()) {
210:                        return 0;
211:                    }
212:
213:                    do {
214:                        Token<?> token = t.token();
215:                        TokenId id = token.id();
216:
217:                        if (id.primaryCategory().equals("ruby")) { // NOI18N
218:                            TokenSequence<? extends RubyTokenId> ts = t
219:                                    .embedded(RubyTokenId.language());
220:                            ts.move(begin);
221:                            ts.moveNext();
222:                            do {
223:                                Token<? extends RubyTokenId> rubyToken = ts
224:                                        .token();
225:                                if (rubyToken == null) {
226:                                    break;
227:                                }
228:                                TokenId rubyId = rubyToken.id();
229:
230:                                balance += getTokenBalanceDelta(rubyId,
231:                                        rubyToken, doc, ts, includeKeywords);
232:                            } while (ts.moveNext() && (ts.offset() < end));
233:                        }
234:
235:                    } while (t.moveNext() && (t.offset() < end));
236:                } else {
237:                    TokenSequence<? extends RubyTokenId> ts = LexUtilities
238:                            .getRubyTokenSequence(doc, begin);
239:                    if (ts == null) {
240:                        return 0;
241:                    }
242:
243:                    ts.move(begin);
244:
245:                    if (!ts.moveNext()) {
246:                        return 0;
247:                    }
248:
249:                    do {
250:                        Token<? extends RubyTokenId> token = ts.token();
251:                        TokenId id = token.id();
252:
253:                        balance += getTokenBalanceDelta(id, token, doc, ts,
254:                                includeKeywords);
255:                    } while (ts.moveNext() && (ts.offset() < end));
256:                }
257:
258:                return balance;
259:            }
260:
261:            private boolean isInLiteral(BaseDocument doc, int offset)
262:                    throws BadLocationException {
263:                // TODO: Handle arrays better
264:                // %w(January February March April May June July
265:                //    August September October November December)
266:                // I should indent to the same level
267:
268:                // Can't reformat these at the moment because reindenting a line
269:                // that is a continued string array causes incremental lexing errors
270:                // (which further screw up formatting)
271:                int pos = Utilities.getRowFirstNonWhite(doc, offset);
272:                //int pos = offset;
273:
274:                if (pos != -1) {
275:                    // I can't look at the first position on the line, since
276:                    // for a string array that is indented, the indentation portion
277:                    // is recorded as a blank identifier
278:                    Token<? extends RubyTokenId> token = LexUtilities.getToken(
279:                            doc, pos);
280:
281:                    if (token != null) {
282:                        TokenId id = token.id();
283:                        // If we're in a string literal (or regexp or documentation) leave
284:                        // indentation alone!
285:                        if ((id == RubyTokenId.STRING_LITERAL)
286:                                || id == RubyTokenId.DOCUMENTATION
287:                                || (id == RubyTokenId.QUOTED_STRING_LITERAL)
288:                                || (id == RubyTokenId.REGEXP_LITERAL)) {
289:                            // No indentation for literal strings in Ruby, since they can
290:                            // contain newlines. Leave it as is.
291:                            return true;
292:                        }
293:
294:                        if (id == RubyTokenId.STRING_END
295:                                || id == RubyTokenId.QUOTED_STRING_END) {
296:                            // Possibly a heredoc
297:                            TokenSequence<? extends RubyTokenId> ts = LexUtilities
298:                                    .getRubyTokenSequence(doc, pos);
299:                            ts.move(pos);
300:                            OffsetRange range = LexUtilities.findHeredocBegin(
301:                                    ts, token);
302:                            if (range != OffsetRange.NONE) {
303:                                String text = doc.getText(range.getStart(),
304:                                        range.getLength());
305:                                if (text.startsWith("<<-")) { // NOI18N
306:                                    return false;
307:                                } else {
308:                                    return true;
309:                                }
310:                            }
311:                        }
312:                    } else {
313:                        // No ruby token -- leave the formatting alone!
314:                        // (Probably in an RHTML file on a line with no Ruby)
315:                        return true;
316:                    }
317:                } else {
318:                    // Empty line inside a string, documentation etc. literal?
319:                    Token<? extends RubyTokenId> token = LexUtilities.getToken(
320:                            doc, offset);
321:
322:                    if (token != null) {
323:                        TokenId id = token.id();
324:                        // If we're in a string literal (or regexp or documentation) leave
325:                        // indentation alone!
326:                        if ((id == RubyTokenId.STRING_LITERAL)
327:                                || id == RubyTokenId.DOCUMENTATION
328:                                || (id == RubyTokenId.QUOTED_STRING_LITERAL)
329:                                || (id == RubyTokenId.REGEXP_LITERAL)) {
330:                            // No indentation for literal strings in Ruby, since they can
331:                            // contain newlines. Leave it as is.
332:                            return true;
333:                        }
334:                    }
335:                }
336:
337:                return false;
338:            }
339:
340:            /** 
341:             * Get the first token on the given line. Similar to LexUtilities.getToken(doc, lineBegin)
342:             * except (a) it computes the line begin from the offset itself, and more importantly,
343:             * (b) it handles RHTML tokens specially; e.g. if a line begins with
344:             * {@code
345:             *    <% if %>
346:             * }
347:             * then the "if" embedded token will be returned rather than the RHTML delimiter, or even
348:             * the whitespace token (which is the first Ruby token in the embedded sequence).
349:             *    
350:             * </pre>   
351:             */
352:            private Token<? extends RubyTokenId> getFirstToken(
353:                    BaseDocument doc, int offset) throws BadLocationException {
354:                int lineBegin = Utilities.getRowFirstNonWhite(doc, offset);
355:
356:                if (lineBegin != -1) {
357:                    if (isRhtmlDocument) {
358:                        TokenSequence<? extends RubyTokenId> ts = LexUtilities
359:                                .getRubyTokenSequence(doc, lineBegin);
360:                        if (ts != null) {
361:                            ts.moveNext();
362:                            Token<? extends RubyTokenId> token = ts.token();
363:                            while (token != null
364:                                    && token.id() == RubyTokenId.WHITESPACE) {
365:                                if (!ts.moveNext()) {
366:                                    return null;
367:                                }
368:                                token = ts.token();
369:                            }
370:                            return token;
371:                        }
372:                    } else {
373:                        return LexUtilities.getToken(doc, lineBegin);
374:                    }
375:                }
376:
377:                return null;
378:            }
379:
380:            private boolean isEndIndent(BaseDocument doc, int offset)
381:                    throws BadLocationException {
382:                int lineBegin = Utilities.getRowFirstNonWhite(doc, offset);
383:
384:                if (lineBegin != -1) {
385:                    Token<? extends RubyTokenId> token = getFirstToken(doc,
386:                            offset);
387:
388:                    if (token == null) {
389:                        if (isRhtmlDocument) {
390:                            // Could be the END of a Ruby section - line begins with "%>"
391:                            if (lineBegin < doc.getLength() - 2) {
392:                                String lineBeginStr = doc.getText(lineBegin, 2);
393:                                if (lineBeginStr.equals("-%")
394:                                        && lineBegin < doc.getLength() - 3) { // NOI18N
395:                                    lineBeginStr = doc.getText(lineBegin, 3);
396:                                    if (lineBeginStr.equals("-%>")) { // NOI18N
397:                                        return true;
398:                                    }
399:                                } else if (lineBeginStr.equals("%>")) { // NOI18N
400:                                    return true;
401:                                }
402:                            }
403:
404:                        }
405:                        return false;
406:                    }
407:
408:                    TokenId id = token.id();
409:
410:                    // If the line starts with an end-marker, such as "end", "}", "]", etc.,
411:                    // find the corresponding opening marker, and indent the line to the same
412:                    // offset as the beginning of that line.
413:                    return (LexUtilities.isIndentToken(id) && !LexUtilities
414:                            .isBeginToken(id, doc, offset))
415:                            || id == RubyTokenId.END
416:                            || id == RubyTokenId.RBRACE
417:                            || id == RubyTokenId.RBRACKET
418:                            || id == RubyTokenId.RPAREN;
419:                }
420:
421:                return false;
422:            }
423:
424:            private boolean isLineContinued(BaseDocument doc, int offset,
425:                    int bracketBalance) throws BadLocationException {
426:                // TODO RHTML - this isn't going to work for rhtml embedded strings...
427:                offset = Utilities.getRowLastNonWhite(doc, offset);
428:                if (offset == -1) {
429:                    return false;
430:                }
431:
432:                TokenSequence<? extends RubyTokenId> ts = LexUtilities
433:                        .getRubyTokenSequence(doc, offset);
434:
435:                if (ts == null) {
436:                    return false;
437:                }
438:                ts.move(offset);
439:
440:                if (!ts.moveNext() && !ts.movePrevious()) {
441:                    return false;
442:                }
443:
444:                Token<? extends RubyTokenId> token = ts.token();
445:
446:                if (token != null) {
447:                    TokenId id = token.id();
448:
449:                    // http://www.netbeans.org/issues/show_bug.cgi?id=115279
450:                    boolean isContinuationOperator = (id == RubyTokenId.NONUNARY_OP || id == RubyTokenId.DOT);
451:
452:                    if (ts.offset() == offset && token.length() > 1
453:                            && token.text().toString().startsWith("\\")) {
454:                        // Continued lines have different token types
455:                        isContinuationOperator = true;
456:                    }
457:
458:                    if (token.length() == 1 && id == RubyTokenId.IDENTIFIER
459:                            && token.text().toString().equals(",")) {
460:                        // If there's a comma it's a continuation operator, but inside arrays, hashes or parentheses
461:                        // parameter lists we should not treat it as such since we'd "double indent" the items, and
462:                        // NOT the first item (where there's no comma, e.g. you'd have
463:                        //  foo(
464:                        //    firstarg,
465:                        //      secondarg,  # indented both by ( and hanging indent ,
466:                        //      thirdarg)
467:                        if (bracketBalance == 0) {
468:                            isContinuationOperator = true;
469:                        }
470:                    }
471:
472:                    if (isContinuationOperator) {
473:                        // Make sure it's not a case like this:
474:                        //    alias eql? ==
475:                        // or
476:                        //    def ==
477:                        token = LexUtilities.getToken(doc, Utilities
478:                                .getRowFirstNonWhite(doc, offset));
479:                        if (token != null) {
480:                            id = token.id();
481:                            if (id == RubyTokenId.DEF
482:                                    || id == RubyTokenId.ANY_KEYWORD
483:                                    && token.text().toString().equals("alias")) { // NOI18N
484:                                return false;
485:                            }
486:                        }
487:
488:                        return true;
489:                    } else if (id == RubyTokenId.ANY_KEYWORD) {
490:                        String text = token.text().toString();
491:                        if ("or".equals(text) || "and".equals(text)) { // NOI18N
492:                            return true;
493:                        }
494:                    }
495:                }
496:
497:                return false;
498:            }
499:
500:            private void reindent(Document document, int startOffset,
501:                    int endOffset, CompilationInfo info, boolean indentOnly) {
502:                isRhtmlDocument = RubyUtils.isRhtmlDocument(document);
503:
504:                try {
505:                    BaseDocument doc = (BaseDocument) document; // document.getText(0, document.getLength())
506:
507:                    if (indentOnly && isRhtmlDocument) {
508:                        // Make sure we're not messing with indentation in HTML
509:                        Token<? extends RubyTokenId> token = LexUtilities
510:                                .getToken(doc, startOffset);
511:                        if (token == null) {
512:                            return;
513:                        }
514:                    }
515:
516:                    syncOptions(doc, codeStyle);
517:
518:                    if (endOffset > doc.getLength()) {
519:                        endOffset = doc.getLength();
520:                    }
521:
522:                    startOffset = Utilities.getRowStart(doc, startOffset);
523:                    int lineStart = startOffset;//Utilities.getRowStart(doc, startOffset);
524:                    int initialOffset = 0;
525:                    int initialIndent = 0;
526:                    if (startOffset > 0) {
527:                        int prevOffset = Utilities.getRowStart(doc,
528:                                startOffset - 1);
529:                        initialOffset = getFormatStableStart(doc, prevOffset);
530:                        initialIndent = LexUtilities.getLineIndent(doc,
531:                                initialOffset);
532:                    }
533:
534:                    // Build up a set of offsets and indents for lines where I know I need
535:                    // to adjust the offset. I will then go back over the document and adjust
536:                    // lines that are different from the intended indent. By doing piecemeal
537:                    // replacements in the document rather than replacing the whole thing,
538:                    // a lot of things will work better: breakpoints and other line annotations
539:                    // will be left in place, semantic coloring info will not be temporarily
540:                    // damaged, and the caret will stay roughly where it belongs.
541:                    List<Integer> offsets = new ArrayList<Integer>();
542:                    List<Integer> indents = new ArrayList<Integer>();
543:
544:                    // When we're formatting sections, include whitespace on empty lines; this
545:                    // is used during live code template insertions for example. However, when
546:                    // wholesale formatting a whole document, leave these lines alone.
547:                    boolean indentEmptyLines = (startOffset != 0 || endOffset != doc
548:                            .getLength());
549:
550:                    boolean includeEnd = endOffset == doc.getLength()
551:                            || indentOnly;
552:
553:                    // TODO - remove initialbalance etc.
554:                    computeIndents(doc, initialIndent, initialOffset,
555:                            endOffset, info, offsets, indents,
556:                            indentEmptyLines, includeEnd, indentOnly);
557:
558:                    try {
559:                        doc.atomicLock();
560:
561:                        // Iterate in reverse order such that offsets are not affected by our edits
562:                        assert indents.size() == offsets.size();
563:                        org.netbeans.editor.Formatter editorFormatter = doc
564:                                .getFormatter();
565:                        for (int i = indents.size() - 1; i >= 0; i--) {
566:                            int indent = indents.get(i);
567:                            int lineBegin = offsets.get(i);
568:
569:                            if (lineBegin < lineStart) {
570:                                // We're now outside the region that the user wanted reformatting;
571:                                // these offsets were computed to get the correct continuation context etc.
572:                                // for the formatter
573:                                break;
574:                            }
575:
576:                            if (lineBegin == lineStart && i > 0) {
577:                                // Look at the previous line, and see how it's indented
578:                                // in the buffer.  If it differs from the computed position,
579:                                // offset my computed position (thus, I'm only going to adjust
580:                                // the new line position relative to the existing editing.
581:                                // This avoids the situation where you're inserting a newline
582:                                // in the middle of "incorrectly" indented code (e.g. different
583:                                // size than the IDE is using) and the newline position ending
584:                                // up "out of sync"
585:                                int prevOffset = offsets.get(i - 1);
586:                                int prevIndent = indents.get(i - 1);
587:                                int actualPrevIndent = LexUtilities
588:                                        .getLineIndent(doc, prevOffset);
589:                                if (actualPrevIndent != prevIndent) {
590:                                    // For blank lines, indentation may be 0, so don't adjust in that case
591:                                    if (!(Utilities.isRowEmpty(doc, prevOffset) || Utilities
592:                                            .isRowWhite(doc, prevOffset))) {
593:                                        indent = actualPrevIndent
594:                                                + (indent - prevIndent);
595:                                    }
596:                                }
597:                            }
598:
599:                            // Adjust the indent at the given line (specified by offset) to the given indent
600:                            int currentIndent = LexUtilities.getLineIndent(doc,
601:                                    lineBegin);
602:
603:                            if (currentIndent != indent) {
604:                                editorFormatter.changeRowIndent(doc, lineBegin,
605:                                        indent);
606:                            }
607:                        }
608:
609:                        if (!indentOnly && codeStyle.reformatComments()) {
610:                            reformatComments(doc, startOffset, endOffset);
611:                        }
612:                    } finally {
613:                        doc.atomicUnlock();
614:                    }
615:                } catch (BadLocationException ble) {
616:                    Exceptions.printStackTrace(ble);
617:                }
618:            }
619:
620:            public void computeIndents(BaseDocument doc, int initialIndent,
621:                    int startOffset, int endOffset, CompilationInfo info,
622:                    List<Integer> offsets, List<Integer> indents,
623:                    boolean indentEmptyLines, boolean includeEnd,
624:                    boolean indentOnly) {
625:                // PENDING:
626:                // The reformatting APIs in NetBeans should be lexer based. They are still
627:                // based on the old TokenID apis. Once we get a lexer version, convert this over.
628:                // I just need -something- in place until that is provided.
629:
630:                try {
631:                    // Algorithm:
632:                    // Iterate over the range.
633:                    // Accumulate a token balance ( {,(,[, and keywords like class, case, etc. increases the balance, 
634:                    //      },),] and "end" decreases it
635:                    // If the line starts with an end marker, indent the line to the level AFTER the token
636:                    // else indent the line to the level BEFORE the token (the level being the balance * indentationSize)
637:                    // Compute the initial balance and indentation level and use that as a "base".
638:                    // If the previous line is not "done" (ends with a comma or a binary operator like "+" etc.
639:                    // add a "hanging indent" modifier.
640:                    // At the end of the day, we're recording a set of line offsets and indents.
641:                    // This can be used either to reformat the buffer, or indent a new line.
642:
643:                    // State:
644:                    int offset = Utilities.getRowStart(doc, startOffset); // The line's offset
645:                    int end = endOffset;
646:
647:                    int indentSize = codeStyle.getIndentSize();
648:                    int hangingIndentSize = codeStyle
649:                            .getContinuationIndentSize();
650:
651:                    // Pending - apply comment formatting too?
652:
653:                    // XXX Look up RHTML too
654:                    //int indentSize = EditorOptions.get(RubyInstallation.RUBY_MIME_TYPE).getSpacesPerTab();
655:                    //int hangingIndentSize = indentSize;
656:
657:                    // Build up a set of offsets and indents for lines where I know I need
658:                    // to adjust the offset. I will then go back over the document and adjust
659:                    // lines that are different from the intended indent. By doing piecemeal
660:                    // replacements in the document rather than replacing the whole thing,
661:                    // a lot of things will work better: breakpoints and other line annotations
662:                    // will be left in place, semantic coloring info will not be temporarily
663:                    // damaged, and the caret will stay roughly where it belongs.
664:
665:                    // The token balance at the offset
666:                    int balance = 0;
667:                    // The bracket balance at the offset ( parens, bracket, brace )
668:                    int bracketBalance = 0;
669:                    boolean continued = false;
670:                    boolean indentHtml = false;
671:                    if (isRhtmlDocument) {
672:                        indentHtml = codeStyle.indentHtml();
673:                    }
674:
675:                    while ((!includeEnd && offset < end)
676:                            || (includeEnd && offset <= end)) {
677:                        int indent; // The indentation to be used for the current line
678:
679:                        int hangingIndent = continued ? (hangingIndentSize) : 0;
680:
681:                        if (isRhtmlDocument && !indentOnly) {
682:                            // Pick up the indentation level assigned by the HTML indenter; gets HTML structure
683:                            initialIndent = LexUtilities.getLineIndent(doc,
684:                                    offset);
685:                        }
686:
687:                        if (isInLiteral(doc, offset)) {
688:                            // Skip this line - leave formatting as it is prior to reformatting 
689:                            indent = LexUtilities.getLineIndent(doc, offset);
690:
691:                            if (isRhtmlDocument && indentHtml && balance > 0) {
692:                                indent += balance * indentSize;
693:                            }
694:                        } else if (isEndIndent(doc, offset)) {
695:                            indent = (balance - 1) * indentSize + hangingIndent
696:                                    + initialIndent;
697:                        } else {
698:                            indent = balance * indentSize + hangingIndent
699:                                    + initialIndent;
700:                        }
701:
702:                        if (indent < 0) {
703:                            indent = 0;
704:                        }
705:
706:                        int lineBegin = Utilities.getRowFirstNonWhite(doc,
707:                                offset);
708:
709:                        // Insert whitespace on empty lines too -- needed for abbreviations expansion
710:                        if (lineBegin != -1 || indentEmptyLines) {
711:                            // Don't do a hanging indent if we're already indenting beyond the parent level?
712:
713:                            indents.add(Integer.valueOf(indent));
714:                            offsets.add(Integer.valueOf(offset));
715:                        }
716:
717:                        int endOfLine = Utilities.getRowEnd(doc, offset) + 1;
718:
719:                        if (lineBegin != -1) {
720:                            balance += getTokenBalance(doc, lineBegin,
721:                                    endOfLine, true, isRhtmlDocument);
722:                            bracketBalance += getTokenBalance(doc, lineBegin,
723:                                    endOfLine, false, isRhtmlDocument);
724:                            continued = isLineContinued(doc, offset,
725:                                    bracketBalance);
726:                        }
727:
728:                        offset = endOfLine;
729:                    }
730:                } catch (BadLocationException ble) {
731:                    Exceptions.printStackTrace(ble);
732:                }
733:            }
734:
735:            void reformatComments(BaseDocument doc, int start, int end) {
736:                int rightMargin = rightMarginOverride;
737:                if (rightMargin == -1) {
738:                    CodeStyle style = codeStyle;
739:                    if (style == null) {
740:                        style = CodeStyle.getDefault(null);
741:                    }
742:
743:                    rightMargin = style.getRightMargin();
744:                }
745:
746:                ReflowParagraphAction action = new ReflowParagraphAction();
747:                action.reflowComments(doc, start, end, rightMargin);
748:            }
749:
750:            /**
751:             * Ensure that the editor-settings for tabs match our code style, since the
752:             * primitive "doc.getFormatter().changeRowIndent" calls will be using
753:             * those settings
754:             */
755:            private static void syncOptions(BaseDocument doc, CodeStyle style) {
756:                org.netbeans.editor.Formatter formatter = doc.getFormatter();
757:                if (formatter.getSpacesPerTab() != style.getIndentSize()) {
758:                    formatter.setSpacesPerTab(style.getIndentSize());
759:                }
760:            }
761:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.