Source Code Cross Referenced for ConvertBlockType.java in  » IDE-Netbeans » ruby » org » netbeans » modules » ruby » hints » 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.hints 
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:         * Portions Copyrighted 2007 Sun Microsystems, Inc.
027:         */
028:        package org.netbeans.modules.ruby.hints;
029:
030:        import java.io.IOException;
031:        import java.util.ArrayList;
032:        import java.util.Collections;
033:        import java.util.HashSet;
034:        import java.util.List;
035:        import java.util.Set;
036:        import java.util.prefs.Preferences;
037:        import javax.swing.JComponent;
038:        import javax.swing.text.BadLocationException;
039:        import org.jruby.ast.IArgumentNode;
040:        import org.jruby.ast.Node;
041:        import org.jruby.ast.NodeTypes;
042:        import org.jruby.ast.YieldNode;
043:        import org.jruby.lexer.yacc.ISourcePosition;
044:        import org.netbeans.modules.gsf.api.CompilationInfo;
045:        import org.netbeans.modules.gsf.api.OffsetRange;
046:        import org.netbeans.api.lexer.Token;
047:        import org.netbeans.api.lexer.TokenId;
048:        import org.netbeans.api.lexer.TokenSequence;
049:        import org.netbeans.editor.BaseDocument;
050:        import org.netbeans.editor.Utilities;
051:        import org.netbeans.modules.ruby.AstPath;
052:        import org.netbeans.modules.ruby.AstUtilities;
053:        import org.netbeans.modules.ruby.hints.spi.AstRule;
054:        import org.netbeans.modules.ruby.hints.spi.Description;
055:        import org.netbeans.modules.ruby.hints.spi.EditList;
056:        import org.netbeans.modules.ruby.hints.spi.Fix;
057:        import org.netbeans.modules.ruby.hints.spi.HintSeverity;
058:        import org.netbeans.modules.ruby.hints.spi.PreviewableFix;
059:        import org.netbeans.modules.ruby.hints.spi.RuleContext;
060:        import org.netbeans.modules.ruby.lexer.LexUtilities;
061:        import org.netbeans.modules.ruby.lexer.RubyTokenId;
062:        import org.openide.util.Exceptions;
063:        import org.openide.util.NbBundle;
064:
065:        /**
066:         * Offer to convert a {}-style block into do-end, or vice versa
067:         *
068:         * @author Tor Norbye
069:         */
070:        public class ConvertBlockType implements  AstRule {
071:
072:            public ConvertBlockType() {
073:            }
074:
075:            public boolean appliesTo(CompilationInfo info) {
076:                // Skip for RHTML files for now - isn't implemented properly
077:                return info.getFileObject().getMIMEType().equals("text/x-ruby");
078:            }
079:
080:            public Set<Integer> getKinds() {
081:                return Collections.singleton(NodeTypes.ITERNODE);
082:            }
083:
084:            public void run(RuleContext context, List<Description> result) {
085:                Node node = context.node;
086:                CompilationInfo info = context.compilationInfo;
087:                int caretOffset = context.caretOffset;
088:                BaseDocument doc = context.doc;
089:
090:                assert (node.nodeId == NodeTypes.ITERNODE);
091:                try {
092:                    int astOffset = node.getPosition().getStartOffset();
093:                    int lexOffset = LexUtilities
094:                            .getLexerOffset(info, astOffset);
095:                    if (lexOffset == -1 || lexOffset > doc.getLength() - 1) {
096:                        return;
097:                    }
098:
099:                    // Limit the hint to the -opening- line of the block
100:                    boolean caretOnStart = true;
101:                    final int beginRowEnd = Utilities.getRowEnd(doc, lexOffset);
102:                    final int caretRowEnd = Utilities.getRowEnd(doc,
103:                            caretOffset);
104:                    boolean caretLine = beginRowEnd == caretRowEnd;
105:                    int endLexOffset = -1;
106:                    if (!caretLine) {
107:                        // ...or the -ending- line of the block
108:                        int endAstOffset = node.getPosition().getEndOffset();
109:                        endLexOffset = LexUtilities.getLexerOffset(info,
110:                                endAstOffset);
111:                        if (endLexOffset == -1) {
112:                            return;
113:                        }
114:                        int endRowEnd = endLexOffset;
115:                        if (endRowEnd < doc.getLength()) {
116:                            endRowEnd = Utilities.getRowEnd(doc, endLexOffset);
117:                        }
118:                        caretLine = endRowEnd == caretRowEnd;
119:                        if (!caretLine) {
120:                            return;
121:                        }
122:                        if (endRowEnd != beginRowEnd) {
123:                            caretOnStart = false;
124:                        }
125:                    }
126:
127:                    Token<? extends RubyTokenId> token = LexUtilities.getToken(
128:                            doc, lexOffset);
129:                    if (token == null) {
130:                        return;
131:                    }
132:
133:                    TokenId id = token.id();
134:                    if (id == RubyTokenId.LBRACE || id == RubyTokenId.DO) {
135:                        OffsetRange range;
136:                        if (caretOnStart) {
137:                            range = new OffsetRange(lexOffset, lexOffset
138:                                    + token.length());
139:                        } else {
140:                            assert endLexOffset != -1;
141:                            int len = (id == RubyTokenId.LBRACE) ? 1 : 3; // }=1, end=3
142:                            range = new OffsetRange(endLexOffset - len,
143:                                    endLexOffset);
144:                        }
145:                        List<Fix> fixList = new ArrayList<Fix>(1);
146:                        boolean convertFromBrace = id == RubyTokenId.LBRACE;
147:
148:                        int endOffset = node.getPosition().getEndOffset();
149:                        if (endOffset > doc.getLength()) {
150:                            endOffset = doc.getLength();
151:                        }
152:
153:                        // See if we should offer to collapse
154:                        String text = doc.getText(lexOffset, endOffset
155:                                - lexOffset);
156:                        int nonspaceChars = 0;
157:                        for (int i = 0; i < text.length(); i++) {
158:                            char c = text.charAt(i);
159:                            if (!Character.isWhitespace(c)) {
160:                                nonspaceChars++;
161:                            }
162:                        }
163:                        int startColumn = lexOffset
164:                                - Utilities.getRowStart(doc, lexOffset);
165:                        // Not yet exposed from the Ruby module
166:                        //int rightMargin = org.netbeans.modules.ruby.options.CodeStyle.getDefault(null).getRightMargin();
167:                        // #119151: This should be available for a lot of hints that don't neatly fit.
168:                        // So only suppress it for -really- large blocks.
169:                        int rightMargin = 350;
170:                        boolean offerCollapse = rightMargin > startColumn
171:                                + nonspaceChars;
172:
173:                        // TODO - in an RHTML page, make sure there are no "gaps" (non Ruby code) between the do and the end,
174:                        // since we can't handle those for collapse
175:                        // TODO
176:
177:                        boolean sameLine = Utilities.getRowEnd(doc, lexOffset) == Utilities
178:                                .getRowEnd(doc, endOffset);
179:                        if (sameLine && convertFromBrace) {
180:                            fixList.add(new ConvertTypeFix(info, node,
181:                                    convertFromBrace, !convertFromBrace, true,
182:                                    false));
183:                        } else if (!sameLine && !convertFromBrace
184:                                && offerCollapse) {
185:                            fixList.add(new ConvertTypeFix(info, node,
186:                                    convertFromBrace, !convertFromBrace, false,
187:                                    true));
188:                        } // else: Should I let you expand a single line do-end to a multiline {}, or vice versa? Naeh,
189:                        // they can do this in two steps; it's not common
190:                        fixList.add(new ConvertTypeFix(info, node,
191:                                convertFromBrace, !convertFromBrace, false,
192:                                false));
193:                        if (sameLine || (!sameLine && offerCollapse)) {
194:                            fixList.add(new ConvertTypeFix(info, node, false,
195:                                    false, sameLine, !sameLine));
196:                        }
197:                        Description desc = new Description(this ,
198:                                getDisplayName(), info.getFileObject(), range,
199:                                fixList, 500);
200:                        result.add(desc);
201:                    }
202:                } catch (BadLocationException ex) {
203:                    Exceptions.printStackTrace(ex);
204:                }
205:            }
206:
207:            public String getId() {
208:                return "Convert_Blocktype"; // NOI18N
209:            }
210:
211:            public String getDisplayName() {
212:                return NbBundle.getMessage(ConvertBlockType.class,
213:                        "ConvertBlockType");
214:            }
215:
216:            public String getDescription() {
217:                return NbBundle.getMessage(ConvertBlockType.class,
218:                        "ConvertBlockTypeDesc");
219:            }
220:
221:            public boolean getDefaultEnabled() {
222:                return true;
223:            }
224:
225:            public HintSeverity getDefaultSeverity() {
226:                return HintSeverity.CURRENT_LINE_WARNING;
227:            }
228:
229:            public JComponent getCustomizer(Preferences node) {
230:                return null;
231:            }
232:
233:            public boolean showInTasklist() {
234:                return false;
235:            }
236:
237:            private static class ConvertTypeFix implements  PreviewableFix {
238:
239:                private final CompilationInfo info;
240:                private final boolean convertToDo;
241:                private final boolean convertToBrace;
242:                private final Node node;
243:                private final boolean expand;
244:                private final boolean collapse;
245:
246:                ConvertTypeFix(CompilationInfo info, Node node,
247:                        boolean convertToDo, boolean convertToBrace,
248:                        boolean expand, boolean collapse) {
249:                    this .info = info;
250:                    this .node = node;
251:                    this .convertToDo = convertToDo;
252:                    this .convertToBrace = convertToBrace;
253:                    this .expand = expand;
254:                    this .collapse = collapse;
255:                }
256:
257:                public String getDescription() {
258:                    String key;
259:                    if (convertToDo) {
260:                        if (expand) {
261:                            key = "ConvertBraceToDoMulti"; // NOI18N
262:                        } else if (collapse) {
263:                            key = "ConvertBraceToDoSingle"; // NOI18N
264:                        } else {
265:                            key = "ConvertBraceToDo"; // NOI18N
266:                        }
267:                    } else if (convertToBrace) {
268:                        if (expand) {
269:                            key = "ConvertDoToBraceMulti"; // NOI18N
270:                        } else if (collapse) {
271:                            key = "ConvertDoToBraceSingle"; // NOI18N
272:                        } else {
273:                            key = "ConvertDoToBrace"; // NOI18N
274:                        }
275:                    } else {
276:                        if (expand) {
277:                            key = "ChangeBlockToMulti"; // NOI18N
278:                        } else {
279:                            assert collapse;
280:                            key = "ChangeBlockToSingle"; // NOI18N
281:                        }
282:                    }
283:                    return NbBundle.getMessage(ConvertBlockType.class, key);
284:                }
285:
286:                public boolean canPreview() {
287:                    return true;
288:                }
289:
290:                public void implement() throws Exception {
291:                    getEditList().apply();
292:                }
293:
294:                public EditList getEditList() throws Exception {
295:                    BaseDocument doc = (BaseDocument) info.getDocument();
296:                    EditList edits = new EditList(doc);
297:
298:                    ISourcePosition pos = node.getPosition();
299:                    int startOffset = pos.getStartOffset();
300:                    int originalEnd = pos.getEndOffset();
301:                    int endOffset;
302:                    if (convertToDo) {
303:                        endOffset = originalEnd - 1;
304:                    } else if (convertToBrace) {
305:                        endOffset = originalEnd - 3;
306:                    } else {
307:                        endOffset = originalEnd;
308:                    }
309:                    if (startOffset > doc.getLength() - 1
310:                            || endOffset > doc.getLength()) {
311:                        return edits;
312:                    }
313:
314:                    if (convertToDo) {
315:                        if (doc.getText(startOffset, 1).charAt(0) == '{'
316:                                && doc.getText(endOffset, 1).charAt(0) == '}') {
317:                            String end;
318:                            if (endOffset > 0
319:                                    && !Character.isWhitespace(doc.getText(
320:                                            endOffset - 1, 1).charAt(0))) {
321:                                end = " end"; // NOI18N
322:                            } else {
323:                                end = "end"; // NOI18N
324:                            }
325:                            edits.replace(endOffset, 1, end, false, 0); // NOI18N
326:
327:                            boolean spaceBefore = true;
328:                            boolean spaceAfter = true;
329:                            if (startOffset > 0) {
330:                                String s = doc.getText(startOffset - 1, 3);
331:                                spaceBefore = Character.isWhitespace(s
332:                                        .charAt(0));
333:                                spaceAfter = Character
334:                                        .isWhitespace(s.charAt(2));
335:                            }
336:                            String insert = "do";
337:                            if (!spaceAfter) {
338:                                insert = insert + " ";
339:                            }
340:                            if (!spaceBefore) {
341:                                insert = " " + insert;
342:                            }
343:                            edits.replace(startOffset, 1, insert, false, 1); // NOI18N
344:
345:                            if (expand) {
346:                                expand(edits, doc, node, startOffset,
347:                                        originalEnd);
348:                            } else if (collapse) {
349:                                collapse(edits, doc, node, startOffset,
350:                                        originalEnd);
351:                            }
352:                        }
353:                    } else if (convertToBrace) {
354:                        if (doc.getText(startOffset, 2).equals("do")
355:                                && endOffset <= doc.getLength() - 3 && // NOI18N
356:                                doc.getText(endOffset, 3).equals("end")) { // NOI18N
357:                            // TODO - make sure there is whitespace next to these tokens!!!
358:                            // They are optional around {} but not around do/end!
359:                            AstPath path = new AstPath(AstUtilities
360:                                    .getRoot(info), node);
361:                            assert path.leaf() == node;
362:                            boolean parenIsNecessary = isArgParenNecessary(
363:                                    path, doc);
364:
365:                            edits.replace(endOffset, 3, "}", false, 0); // NOI18N
366:                            edits.replace(startOffset, 2, "{", false, 0); // NOI18N
367:
368:                            if (parenIsNecessary) {
369:                                // Insert parentheses
370:                                assert AstUtilities.isCall(path.leafParent());
371:                                OffsetRange range = AstUtilities
372:                                        .getCallRange(path.leafParent());
373:                                int insertPos = range.getEnd();
374:                                // Check if I should remove a space; e.g. replace "foo arg" with "foo(arg"
375:                                if (Character.isWhitespace(doc.getText(
376:                                        insertPos, 1).charAt(0))) {
377:                                    edits.replace(insertPos, 1, "(", false, 1); // NOI18N
378:                                } else {
379:                                    edits.replace(insertPos, 0, "(", false, 1); // NOI18N
380:                                }
381:
382:                                // Insert )
383:                                edits
384:                                        .replace(startOffset - 1, 0, ")",
385:                                                false, 2); // NOI18N
386:
387:                                if (!Character.isWhitespace(doc.getText(
388:                                        startOffset - 1, 1).charAt(0))) {
389:                                    edits.replace(startOffset - 1, 0, " ",
390:                                            false, 3); // NOI18N
391:                                }
392:                            }
393:
394:                            if (expand) {
395:                                expand(edits, doc, node, startOffset,
396:                                        originalEnd);
397:                            } else if (collapse) {
398:                                collapse(edits, doc, node, startOffset,
399:                                        originalEnd);
400:                            }
401:                        }
402:                    } else {
403:                        assert collapse || expand;
404:
405:                        if (expand) {
406:                            expand(edits, doc, node, startOffset, endOffset);
407:                        } else {
408:                            collapse(edits, doc, node, startOffset, endOffset);
409:                        }
410:                    }
411:
412:                    return edits;
413:                }
414:
415:                /** JRuby sometimes has wrong AST offsets. For example, for 
416:                 * this IterNode
417:                 * sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
418:                 * the NewlineNode inside the iter will be here: a1^[0] instead of ^a1[0].
419:                 * To work around this problem, look at the left most children of a NewlineNode
420:                 * and find the TRUE starting range of the newline node.
421:                 * @todo File JRuby issue
422:                 */
423:                private int findRealStart(Node node) {
424:                    int min = Integer.MAX_VALUE;
425:                    while (true) {
426:                        int start = node.getPosition().getStartOffset();
427:                        if (start < min) {
428:                            min = start;
429:                        }
430:
431:                        @SuppressWarnings(value="unchecked")
432:                        List<Node> list = node.childNodes();
433:
434:                        if (list != null && list.size() > 0) {
435:                            node = list.get(0);
436:                        } else {
437:                            return min;
438:                        }
439:                    }
440:                }
441:
442:                private void findLineBreaks(Node node, Set<Integer> offsets) {
443:                    if (node.nodeId == NodeTypes.NEWLINENODE) {
444:                        // Doesn't work, need above workaround
445:                        //int start = node.getPosition().getStartOffset();
446:                        int start = findRealStart(node);
447:                        offsets.add(start);
448:                    }
449:
450:                    @SuppressWarnings(value="unchecked")
451:                    List<Node> list = node.childNodes();
452:
453:                    for (Node child : list) {
454:                        if (child.nodeId == NodeTypes.EVSTRNODE) {
455:                            // Don't linebreak inside a #{} expression
456:                            continue;
457:                        }
458:                        findLineBreaks(child, offsets);
459:                    }
460:                }
461:
462:                /** NOTE - document should be under atomic lock when this is called */
463:                private void expand(EditList edits, BaseDocument doc,
464:                        Node node, int startOffset, int endOffset) {
465:                    assert endOffset <= doc.getLength();
466:
467:                    // Look through the document and find the statement separators (;);
468:                    // at these locations I'll replace the ; with a newline and then
469:                    // apply a formatter
470:                    Set<Integer> offsetSet = new HashSet<Integer>();
471:                    findLineBreaks(node, offsetSet);
472:
473:                    // Add in ; replacements
474:                    TokenSequence<? extends RubyTokenId> ts = LexUtilities
475:                            .getRubyTokenSequence(doc, endOffset);
476:                    if (ts != null) {
477:                        // Traverse sequence in reverse order such that my offset list is in decreasing order
478:                        ts.move(endOffset);
479:                        while (ts.movePrevious() && ts.offset() > startOffset) {
480:                            Token<? extends RubyTokenId> token = ts.token();
481:                            TokenId id = token.id();
482:
483:                            if (id == RubyTokenId.IDENTIFIER
484:                                    && ";".equals(token.text().toString())) { // NOI18N
485:                                //offsetSet.add(ts.offset());
486:                            } else if (id == RubyTokenId.END
487:                                    || id == RubyTokenId.RBRACE) {
488:                                offsetSet.add(ts.offset());
489:                            }
490:                        }
491:                    }
492:
493:                    List<Integer> offsets = new ArrayList<Integer>(offsetSet);
494:                    Collections.sort(offsets);
495:                    // Ensure that we go in high to lower order such that I edit the
496:                    // document from bottom to top (so offsets don't have to be adjusted
497:                    // to account for our own edits along the way)
498:                    Collections.reverse(offsets);
499:
500:                    if (offsets.size() > 0) {
501:                        // TODO: Create a ModificationResult here and process it
502:                        // The following is the WRONG way to do it...
503:                        // I've gotta use a ModificationResult instead!
504:                        try {
505:                            // Process offsets from back to front such that I can
506:                            // modify the document without worrying that the other offsets
507:                            // need to be adjusted
508:                            int prev = -1;
509:                            int added = 0;
510:                            for (int offset : offsets) {
511:                                // We might get some dupes since we add offsets from both
512:                                // the AST newline nodes and semicolons discovered in the lexical token hierarchy
513:                                if (offset == prev) {
514:                                    continue;
515:                                }
516:                                prev = offset;
517:
518:                                // Back up over any whitespace
519:                                int whitespaces = 0;
520:                                for (int i = 1; i < 5 && offset - i > 0; i++) {
521:                                    char c = doc.getText(offset - i, 1).charAt(
522:                                            0);
523:                                    if (Character.isWhitespace(c)) {
524:                                        whitespaces++;
525:                                    } else {
526:                                        break;
527:                                    }
528:                                }
529:
530:                                if (whitespaces > 0) {
531:                                    edits.replace(offset - whitespaces,
532:                                            whitespaces, "\n", false, 4); // NOI18N
533:                                } else {
534:                                    edits.replace(offset, 0, "\n", false, 4); // NOI18N
535:                                }
536:                                added++;
537:                            }
538:
539:                            // Remove trailing semicolons
540:                            for (int offset : offsets) {
541:                                char c = doc.getText(offset - 1, 1).charAt(0);
542:                                if (c == ';') {
543:                                    edits
544:                                            .replace(offset - 1, 1, null,
545:                                                    false, 5);
546:                                } else if (Character.isWhitespace(c)) {
547:                                    c = doc.getText(offset - 2, 1).charAt(0);
548:                                    if (c == ';') {
549:                                        edits.replace(offset - 2, 1, null,
550:                                                false, 5);
551:                                    }
552:                                }
553:                            }
554:                            int newEnd = endOffset + added;
555:
556:                            // Remove trailing whitespace
557:                            // TODO
558:
559:                        } catch (BadLocationException ble) {
560:                            Exceptions.printStackTrace(ble);
561:                        }
562:                    }
563:                    edits.format();
564:                }
565:
566:                private void collapse(EditList edits, BaseDocument doc,
567:                        Node node, int startOffset, int endOffset) {
568:                    assert endOffset <= doc.getLength();
569:
570:                    // Look through the document and find the statement separators (;);
571:                    // at these locations I'll replace the ; with a newline and then
572:                    // apply a formatter
573:                    Set<Integer> offsetSet = new HashSet<Integer>();
574:                    findLineBreaks(node, offsetSet);
575:
576:                    Token<? extends TokenId> t = LexUtilities.getToken(doc,
577:                            startOffset);
578:                    TokenId tid = t.id();
579:                    assert tid == RubyTokenId.LBRACE || tid == RubyTokenId.DO;
580:                    boolean isDoBlock = tid == RubyTokenId.DO;
581:
582:                    // Add in ; replacements
583:                    TokenSequence<? extends RubyTokenId> ts = LexUtilities
584:                            .getRubyTokenSequence(doc, endOffset);
585:                    if (ts != null) {
586:                        // Traverse sequence in reverse order such that my offset list is in decreasing order
587:                        ts.move(endOffset);
588:                        while (ts.movePrevious() && ts.offset() > startOffset) {
589:                            Token<? extends RubyTokenId> token = ts.token();
590:                            TokenId id = token.id();
591:
592:                            if (id == RubyTokenId.END
593:                                    || id == RubyTokenId.RBRACE) {
594:                                offsetSet.add(ts.offset());
595:                            }
596:                        }
597:                    }
598:
599:                    List<Integer> offsets = new ArrayList<Integer>(offsetSet);
600:                    Collections.sort(offsets);
601:                    // Ensure that we go in high to lower order such that I edit the
602:                    // document from bottom to top (so offsets don't have to be adjusted
603:                    // to account for our own edits along the way)
604:                    //Collections.reverse(offsets);
605:                    if (offsets.size() > 0) {
606:                        // TODO: Create a ModificationResult here and process it
607:                        // The following is the WRONG way to do it...
608:                        // I've gotta use a ModificationResult instead!
609:                        try {
610:                            // Process offsets from back to front such that I can
611:                            // modify the document without worrying that the other offsets
612:                            // need to be adjusted
613:                            int prev = -1;
614:                            //int posDelta; // Amount to add to offsets to account for our
615:                            for (int i = offsets.size() - 1; i >= 0; i--) {
616:                                int offset = offsets.get(i);
617:                                // We might get some dupes since we add offsets from both
618:                                // the AST newline nodes and semicolons discovered in the lexical token hierarchy
619:                                if (offset == prev) {
620:                                    continue;
621:                                }
622:                                prev = offset;
623:                                int prevOffset = i > 0 ? offsets.get(i - 1) : 0;
624:
625:                                int segmentOffset = offset;
626:                                // TODO - use an editor-finder which can do this efficiently
627:                                // See also DocumentUtilities.getText() which can do it efficiently
628:                                int s = segmentOffset;
629:                                while (s > prevOffset) {
630:                                    s--;
631:                                    char c = doc.getText(s, 1).charAt(0);
632:                                    if (Character.isWhitespace(c)) {
633:                                        segmentOffset = s;
634:                                    } else {
635:                                        break;
636:                                    }
637:                                }
638:                                int segmentLength = offset - segmentOffset;
639:                                s = offset - 1;
640:                                while (s < doc.getLength()) {
641:                                    s++;
642:                                    char c = doc.getText(s, 1).charAt(0);
643:                                    if (Character.isWhitespace(c)) {
644:                                        segmentLength++;
645:                                    } else {
646:                                        break;
647:                                    }
648:                                }
649:
650:                                // Collapse all whitespace around this offset and replace with a single "; "
651:                                char prevChar = '?';
652:                                if (segmentOffset > 0) {
653:                                    prevChar = doc
654:                                            .getText(segmentOffset - 1, 1)
655:                                            .charAt(0);
656:                                }
657:                                if (prevChar == '|'
658:                                        || (isDoBlock
659:                                                && (segmentOffset <= startOffset + 3) || (!isDoBlock && (segmentOffset <= startOffset + 1)))) {
660:                                    edits.replace(segmentOffset, segmentLength,
661:                                            " ", false, 4);
662:                                } else {
663:                                    // Don't insert semicolons before "end" or around parens in "if (true)" etc.
664:                                    boolean skipSemicolon = false;
665:                                    //if (segmentOffset > 0) {
666:                                    //    Token tkr = LexUtilities.getToken(doc, segmentOffset-1);
667:                                    //    if (tkr != null && tkr.id() == RubyTokenId.RPAREN) {
668:                                    //        skipSemicolon = true;
669:                                    //    }
670:                                    //}
671:                                    TokenSequence<? extends TokenId> rts = LexUtilities
672:                                            .getRubyTokenSequence(doc,
673:                                                    segmentOffset);
674:                                    rts.move(segmentOffset);
675:                                    while (rts.moveNext()) {
676:                                        Token tk = rts.token();
677:                                        TokenId tkid = tk.id();
678:                                        if (tkid == RubyTokenId.END
679:                                                || tkid == RubyTokenId.RBRACE
680:                                                || tkid == RubyTokenId.LPAREN) {
681:                                            skipSemicolon = true;
682:                                            break;
683:                                        } else if (tkid != RubyTokenId.WHITESPACE) {
684:                                            break;
685:                                        }
686:                                    }
687:                                    if (skipSemicolon) {
688:                                        edits.replace(segmentOffset,
689:                                                segmentLength, " ", false, 4);
690:                                    } else {
691:                                        edits.replace(segmentOffset,
692:                                                segmentLength, "; ", false, 4);
693:                                    }
694:                                }
695:                            }
696:                        } catch (BadLocationException ble) {
697:                            Exceptions.printStackTrace(ble);
698:                        }
699:                    }
700:                    edits.format();
701:                }
702:
703:                /** Determine whether parentheses are necessary around the call
704:                 * corresponding to a block call.
705:                 * For example, in 
706:                 * <pre>
707:                 *  b.create_menu :name => 'default_menu' do |d| ...
708:                 * </pre>
709:                 * parens are necessary if you want to switch to a brace block.
710:                 */
711:                private boolean isArgParenNecessary(AstPath path,
712:                        BaseDocument doc) throws BadLocationException {
713:                    // Look at the surrounding CallNode and see if it has arguments.
714:                    // If so, see if it has parens. If not, return true.
715:                    assert path.leaf().nodeId == NodeTypes.ITERNODE;
716:                    Node n = path.leafParent();
717:                    if (n != null && AstUtilities.isCall(n)
718:                            && n instanceof  IArgumentNode
719:                            && ((IArgumentNode) n).getArgsNode() != null) {
720:                        // Yes, call has args - check parens
721:                        int end = node.getPosition().getStartOffset(); // Start of do/{ - end of args
722:                        for (int i = end - 1; i >= 0 && i < doc.getLength(); i--) {
723:                            // XXX Use a more performant document content iterator!
724:                            char c = doc.getText(i, 1).charAt(0);
725:                            if (Character.isWhitespace(c)) {
726:                                continue;
727:                            }
728:                            if (c == ')') {
729:                                return false;
730:                            } else {
731:                                return true;
732:                            }
733:                        }
734:                    }
735:
736:                    return false;
737:                }
738:
739:                public boolean isSafe() {
740:                    // Different precedence rules apply for do and {}
741:                    return !convertToBrace && !convertToDo;
742:                }
743:
744:                public boolean isInteractive() {
745:                    return false;
746:                }
747:            }
748:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.