Add JVM compiler support.
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * If you wish your version of this file to be governed by only the CDDL
25 * or only the GPL Version 2, indicate your decision by adding
26 * "[Contributor] elects to include this software in this distribution
27 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28 * single choice of license, a recipient has the option to distribute
29 * your version of this file under either the CDDL, the GPL Version 2 or
30 * to extend the choice of license to its licensees as provided above.
31 * However, if you add GPL Version 2 code and therefore, elected the GPL
32 * Version 2 license, then the option applies only if the new code is
33 * made subject to such option by the copyright holder.
37 * Portions Copyrighted 2008 Sun Microsystems, Inc.
39 package org.netbeans.modules.ada.editor.lexer;
41 import java.util.HashSet;
44 import javax.swing.text.BadLocationException;
45 import javax.swing.text.Document;
46 import org.netbeans.api.lexer.Token;
47 import org.netbeans.api.lexer.TokenHierarchy;
48 import org.netbeans.api.lexer.TokenId;
49 import org.netbeans.api.lexer.TokenSequence;
50 import org.netbeans.editor.BaseDocument;
51 import org.netbeans.editor.Utilities;
52 import org.netbeans.modules.ada.editor.AdaMimeResolver;
53 import org.netbeans.modules.csl.api.OffsetRange;
54 import org.netbeans.modules.parsing.spi.Parser;
55 import org.openide.filesystems.FileUtil;
56 import org.openide.loaders.DataObject;
57 import org.openide.util.Exceptions;
62 * Based on org.netbeans.modules.ruby.lexer.LexUtilities (Tor Norbye)
64 * Utilities associated with lexing or analyzing the document at the
65 * lexical level, unlike AstUtilities which is contains utilities
66 * to analyze parsed information about a document.
68 * @author Andrea Lucarelli
70 public class AdaLexUtilities {
72 * Tokens that match a corresponding END statement. Even though while, unless etc.
73 * can be statement modifiers, those luckily have different token ids so are not a problem
76 private static final Set<TokenId> END_PAIRS = new HashSet<TokenId>();
79 * Tokens that should cause indentation of the next line. This is true for all {@link #END_PAIRS},
80 * but also includes tokens like "else" that are not themselves matched with end but also contribute
81 * structure for indentation.
84 private static final Set<TokenId> INDENT_WORDS = new HashSet<TokenId>();
87 END_PAIRS.add(AdaTokenId.PROCEDURE);
88 END_PAIRS.add(AdaTokenId.FUNCTION);
89 END_PAIRS.add(AdaTokenId.DECLARE);
90 END_PAIRS.add(AdaTokenId.FOR);
91 END_PAIRS.add(AdaTokenId.WHILE);
92 END_PAIRS.add(AdaTokenId.IF);
93 END_PAIRS.add(AdaTokenId.PACKAGE);
94 END_PAIRS.add(AdaTokenId.CASE);
95 END_PAIRS.add(AdaTokenId.LOOP);
97 INDENT_WORDS.addAll(END_PAIRS);
98 // Add words that are not matched themselves with an "end",
99 // but which also provide block structure to indented content
100 // (usually part of a multi-keyword structure such as if-then-elsif-else-end if
101 // where only the "if" is considered an end-pair.)
102 INDENT_WORDS.add(AdaTokenId.ELSE);
103 INDENT_WORDS.add(AdaTokenId.ELSIF);
104 INDENT_WORDS.add(AdaTokenId.BEGIN);
107 private AdaLexUtilities() {
111 * For a possibly generated offset in an AST, return the corresponding lexing/true document offset
113 public static int getLexerOffset(Parser.Result result, int astOffset) {
114 return result.getSnapshot().getOriginalOffset(astOffset);
117 public static OffsetRange getLexerOffsets(Parser.Result result, OffsetRange astRange) {
118 int rangeStart = astRange.getStart();
119 int start = result.getSnapshot().getOriginalOffset(rangeStart);
120 if (start == rangeStart) {
122 } else if (start == -1) {
123 return OffsetRange.NONE;
125 // Assumes the translated range maintains size
126 return new OffsetRange(start, start + astRange.getLength());
131 * Find the Ada token sequence (in case it's embedded in something else at the top level
133 @SuppressWarnings("unchecked")
134 public static TokenSequence<?extends AdaTokenId> getAdaTokenSequence(BaseDocument doc, int offset) {
135 TokenHierarchy<Document> th = TokenHierarchy.get((Document)doc);
136 return getAdaTokenSequence(th, offset);
139 @SuppressWarnings("unchecked")
140 public static TokenSequence<?extends AdaTokenId> getAdaTokenSequence(TokenHierarchy<Document> th, int offset) {
141 TokenSequence<?extends AdaTokenId> ts = th.tokenSequence(AdaTokenId.language());
145 public static TokenSequence<?extends AdaTokenId> getPositionedSequence(BaseDocument doc, int offset) {
146 TokenSequence<?extends AdaTokenId> ts = getAdaTokenSequence(doc, offset);
151 } catch (AssertionError e) {
152 DataObject dobj = (DataObject)doc.getProperty(Document.StreamDescriptionProperty);
155 Exceptions.attachMessage(e, FileUtil.getFileDisplayName(dobj.getPrimaryFile()));
161 if (!ts.moveNext() && !ts.movePrevious()) {
172 public static Token<?extends AdaTokenId> getToken(BaseDocument doc, int offset) {
173 TokenSequence<?extends AdaTokenId> ts = getPositionedSequence(doc, offset);
182 public static char getTokenChar(BaseDocument doc, int offset) {
183 Token<?extends AdaTokenId> token = getToken(doc, offset);
186 String text = token.text().toString();
188 if (text.length() > 0) { // Usually true, but I could have gotten EOF right?
190 return text.charAt(0);
197 /** Search forwards in the token sequence until a token of type <code>down</code> is found */
198 public static OffsetRange findHeredocEnd(TokenSequence<?extends AdaTokenId> ts, Token<?extends AdaTokenId> startToken) {
199 // // Look for the end of the given heredoc
200 // String text = startToken.text().toString();
201 // assert text.startsWith("<<");
202 // text = text.substring(2);
203 // if (text.startsWith("-")) {
204 // text = text.substring(1);
206 // if ((text.startsWith("\"") && text.endsWith("\"")) || (text.startsWith("'") && text.endsWith("'"))) {
207 // text = text.substring(0, text.length()-2);
209 // String textn = text+"\n";
211 // while (ts.moveNext()) {
212 // Token<?extends AdaTokenId> token = ts.token();
213 // TokenId id = token.id();
215 // if (id == AdaTokenId.STRING_END || id == AdaTokenId.QUOTED_STRING_END) {
216 // String t = token.text().toString();
217 // if (text.equals(t) || textn.equals(t)) {
218 // return new OffsetRange(ts.offset(), ts.offset() + token.length());
223 return OffsetRange.NONE;
226 /** Search forwards in the token sequence until a token of type <code>down</code> is found */
227 public static OffsetRange findHeredocBegin(TokenSequence<?extends AdaTokenId> ts, Token<?extends AdaTokenId> endToken) {
228 // // Look for the end of the given heredoc
229 // String text = endToken.text().toString();
230 // if (text.endsWith("\n")) {
231 // text = text.substring(0, text.length()-1);
233 // String textQuotes = "\"" + text + "\"";
234 // String textSQuotes = "'" + text + "'";
236 // while (ts.movePrevious()) {
237 // Token<?extends AdaTokenId> token = ts.token();
238 // TokenId id = token.id();
240 // if (id == AdaTokenId.STRING_BEGIN || id == AdaTokenId.QUOTED_STRING_BEGIN) {
241 // String t = token.text().toString();
242 // String marker = null;
243 // if (t.startsWith("<<-")) {
244 // marker = t.substring(3);
245 // } else if (t.startsWith("<<")) {
246 // marker = t.substring(2);
248 // if (marker != null && (text.equals(marker) || textQuotes.equals(marker) || textSQuotes.equals(marker))) {
249 // return new OffsetRange(ts.offset(), ts.offset() + token.length());
254 return OffsetRange.NONE;
258 * Search forwards in the token sequence until a token of type <code>down</code> is found
260 public static OffsetRange findFwd(BaseDocument doc, TokenSequence<?extends AdaTokenId> ts, TokenId up,
264 while (ts.moveNext()) {
265 Token<?extends AdaTokenId> token = ts.token();
266 TokenId id = token.id();
270 } else if (id == down) {
272 return new OffsetRange(ts.offset(), ts.offset() + token.length());
279 return OffsetRange.NONE;
283 * Search backwards in the token sequence until a token of type <code>up</code> is found
285 public static OffsetRange findBwd(BaseDocument doc, TokenSequence<?extends AdaTokenId> ts, TokenId up,
289 while (ts.movePrevious()) {
290 Token<?extends AdaTokenId> token = ts.token();
291 TokenId id = token.id();
295 return new OffsetRange(ts.offset(), ts.offset() + token.length());
299 } else if (id == down) {
304 return OffsetRange.NONE;
308 * Find the token that begins a block terminated by "end". This is a token
309 * in the END_PAIRS array. Walk backwards and find the corresponding token.
310 * It does not use indentation for clues since this could be wrong and be
311 * precisely the reason why the user is using pair matching to see what's wrong.
313 public static OffsetRange findBegin(BaseDocument doc, TokenSequence<?extends AdaTokenId> ts) {
316 while (ts.movePrevious()) {
317 Token<?extends AdaTokenId> token = ts.token();
318 TokenId id = token.id();
320 if (isBeginToken(id, doc, ts)) {
321 // No matching dot for "do" used in conditionals etc.)) {
323 return new OffsetRange(ts.offset(), ts.offset() + token.length());
327 } else if (id == AdaTokenId.END || id == AdaTokenId.END_CASE || id == AdaTokenId.END_IF || id == AdaTokenId.END_LOOP) {
332 return OffsetRange.NONE;
335 public static OffsetRange findEnd(BaseDocument doc, TokenSequence<?extends AdaTokenId> ts) {
338 while (ts.moveNext()) {
339 Token<?extends AdaTokenId> token = ts.token();
340 TokenId id = token.id();
342 if (isBeginToken(id, doc, ts)) {
344 } else if (id == AdaTokenId.END || id == AdaTokenId.END_CASE || id == AdaTokenId.END_IF || id == AdaTokenId.END_LOOP) {
346 return new OffsetRange(ts.offset(), ts.offset() + token.length());
353 return OffsetRange.NONE;
357 * Determine whether "loop" is an indent-token (e.g. matches an end) or if
358 * it's simply a separator in while, for expressions)
360 public static boolean isEndmatchingLoop(BaseDocument doc, int offset) {
361 // In the following case, loop is dominant:
366 // However, not here:
371 // In the second case, the end matches the while, but in the first case
372 // the end matches the loop
374 // Look at the first token of the current line
376 int first = Utilities.getRowFirstNonWhite(doc, offset);
378 Token<? extends AdaTokenId> token = getToken(doc, first);
380 TokenId id = token.id();
381 if (id == AdaTokenId.WHILE || id == AdaTokenId.FOR) {
386 } catch (BadLocationException ble) {
387 Exceptions.printStackTrace(ble);
394 * Return true iff the given token is a token that should be matched
395 * with a corresponding "end" token, such as "begin", "package"
398 public static boolean isBeginToken(TokenId id, BaseDocument doc, int offset) {
399 if (id == AdaTokenId.LOOP) {
400 return isEndmatchingLoop(doc, offset);
402 return END_PAIRS.contains(id);
406 * Return true iff the given token is a token that should be matched
407 * with a corresponding "end" token, such as "begin", "package"
410 public static boolean isBeginToken(TokenId id, BaseDocument doc, TokenSequence<?extends AdaTokenId> ts) {
411 if (id == AdaTokenId.LOOP) {
412 return isEndmatchingLoop(doc, ts.offset());
414 return END_PAIRS.contains(id);
418 * Return true iff the given token is a token that indents its content,
419 * such as the various begin tokens as well as "else", "when", etc.
421 public static boolean isIndentToken(TokenId id) {
422 return INDENT_WORDS.contains(id);
425 /** Compute the balance of begin/end tokens on the line.
426 * @param doc the document
427 * @param offset The offset somewhere on the line
428 * @param upToOffset If true, only compute the line balance up to the given offset (inclusive),
429 * and if false compute the balance for the whole line
431 public static int getBeginEndLineBalance(BaseDocument doc, int offset, boolean upToOffset) {
433 int begin = Utilities.getRowStart(doc, offset);
434 int end = upToOffset ? offset : Utilities.getRowEnd(doc, offset);
436 TokenSequence<?extends AdaTokenId> ts = AdaLexUtilities.getAdaTokenSequence(doc, begin);
443 if (!ts.moveNext()) {
450 Token<?extends AdaTokenId> token = ts.token();
451 TokenId id = token.id();
453 if (isBeginToken(id, doc, ts)) {
455 } else if (id == AdaTokenId.END || id == AdaTokenId.END_CASE || id == AdaTokenId.END_IF || id == AdaTokenId.END_LOOP) {
458 } while (ts.moveNext() && (ts.offset() <= end));
461 } catch (BadLocationException ble) {
462 Exceptions.printStackTrace(ble);
468 /** Compute the balance of begin/end tokens on the line */
469 public static int getLineBalance(BaseDocument doc, int offset, TokenId up, TokenId down) {
471 int begin = Utilities.getRowStart(doc, offset);
472 int end = Utilities.getRowEnd(doc, offset);
474 TokenSequence<?extends AdaTokenId> ts = AdaLexUtilities.getAdaTokenSequence(doc, begin);
481 if (!ts.moveNext()) {
488 Token<?extends AdaTokenId> token = ts.token();
489 TokenId id = token.id();
493 } else if (id == down) {
496 } while (ts.moveNext() && (ts.offset() <= end));
499 } catch (BadLocationException ble) {
500 Exceptions.printStackTrace(ble);
507 * The same as braceBalance but generalized to any pair of matching
509 * @param open the token that increses the count
510 * @param close the token that decreses the count
512 public static int getTokenBalance(BaseDocument doc, TokenId open, TokenId close, int offset)
513 throws BadLocationException {
514 TokenSequence<?extends AdaTokenId> ts = AdaLexUtilities.getAdaTokenSequence(doc, 0);
519 // XXX Why 0? Why not offset?
522 if (!ts.moveNext()) {
529 Token t = ts.token();
531 if (t.id() == open) {
533 } else if (t.id() == close) {
536 } while (ts.moveNext());
542 * Return true iff the line for the given offset is a Ada comment line.
543 * This will return false for lines that contain comments (even when the
544 * offset is within the comment portion) but also contain code.
546 public static boolean isCommentOnlyLine(BaseDocument doc, int offset)
547 throws BadLocationException {
548 int begin = Utilities.getRowFirstNonWhite(doc, offset);
551 return false; // whitespace only
554 Token<? extends AdaTokenId> token = AdaLexUtilities.getToken(doc, begin);
556 return token.id() == AdaTokenId.COMMENT;
563 * Return the string at the given position, or null if none
565 @SuppressWarnings("unchecked")
566 public static String getStringAt(int caretOffset, TokenHierarchy<Document> th) {
567 TokenSequence<?extends AdaTokenId> ts = getAdaTokenSequence(th, caretOffset);
573 ts.move(caretOffset);
575 if (!ts.moveNext() && !ts.movePrevious()) {
579 if (ts.offset() == caretOffset) {
580 // We're looking at the offset to the RIGHT of the caret
581 // and here I care about what's on the left
582 if (!ts.movePrevious()) {
587 Token<?extends AdaTokenId> token = ts.token();
590 TokenId id = token.id();
592 String string = null;
594 // Skip over embedded Ada segments and literal strings until you find the beginning
597 while ((id == AdaTokenId.UNKNOWN_TOKEN) || (id == AdaTokenId.STRING_LITERAL)) {
598 string = token.text().toString();
600 if (!ts.movePrevious()) {
607 if (id == AdaTokenId.STRING_LITERAL) {
611 // Build up the String from the sequence
612 StringBuilder sb = new StringBuilder();
614 while (ts.moveNext()) {
618 if ((id == AdaTokenId.UNKNOWN_TOKEN) || (id == AdaTokenId.STRING_LITERAL)) {
619 sb.append(token.text());
625 return sb.toString();
634 * Check if the caret is inside a literal string that is associated with
635 * a require statement.
637 * @return The offset of the beginning of the require string, or -1
638 * if the offset is not inside a require string.
640 public static int getRequireStringOffset(int caretOffset, TokenHierarchy<Document> th) {
641 TokenSequence<?extends AdaTokenId> ts = getAdaTokenSequence(th, caretOffset);
647 ts.move(caretOffset);
649 if (!ts.moveNext() && !ts.movePrevious()) {
653 if (ts.offset() == caretOffset) {
654 // We're looking at the offset to the RIGHT of the caret
655 // and here I care about what's on the left
656 if (!ts.movePrevious()) {
661 Token<?extends AdaTokenId> token = ts.token();
664 TokenId id = token.id();
666 // Skip over embedded Ada segments and literal strings until you find the beginning
667 while ((id == AdaTokenId.UNKNOWN_TOKEN) || (id == AdaTokenId.STRING_LITERAL)) {
668 if (!ts.movePrevious()) {
675 int stringStart = ts.offset() + token.length();
677 if (id == AdaTokenId.STRING_LITERAL) {
678 // Completion of literal strings within require calls
679 while (ts.movePrevious()) {
684 if ((id == AdaTokenId.WHITESPACE) || (id == AdaTokenId.LPAREN) ||
685 (id == AdaTokenId.STRING_LITERAL)) {
689 if (id == AdaTokenId.IDENTIFIER) {
690 String text = token.text().toString();
692 if (text.equals("require") || text.equals("load")) {
707 public static int getSingleQuotedStringOffset(int caretOffset, TokenHierarchy<Document> th) {
708 return getLiteralStringOffset(caretOffset, th, AdaTokenId.STRING_LITERAL);
711 public static int getDoubleQuotedStringOffset(int caretOffset, TokenHierarchy<Document> th) {
712 return getLiteralStringOffset(caretOffset, th, AdaTokenId.STRING_LITERAL);
716 * Determine if the caret is inside a literal string, and if so, return its starting
717 * offset. Return -1 otherwise.
719 @SuppressWarnings("unchecked")
720 private static int getLiteralStringOffset(int caretOffset, TokenHierarchy<Document> th,
722 TokenSequence<?extends AdaTokenId> ts = getAdaTokenSequence(th, caretOffset);
728 ts.move(caretOffset);
730 if (!ts.moveNext() && !ts.movePrevious()) {
734 if (ts.offset() == caretOffset) {
735 // We're looking at the offset to the RIGHT of the caret
736 // and here I care about what's on the left
737 if (!ts.movePrevious()) {
742 Token<?extends AdaTokenId> token = ts.token();
745 TokenId id = token.id();
747 // Skip over embedded Ada segments and literal strings until you find the beginning
748 while ((id == AdaTokenId.UNKNOWN_TOKEN) || (id == AdaTokenId.STRING_LITERAL)) {
749 if (!ts.movePrevious()) {
757 if (!ts.moveNext()) {
768 public static boolean isInsideQuotedString(BaseDocument doc, int offset) {
769 TokenSequence<?extends AdaTokenId> ts = AdaLexUtilities.getAdaTokenSequence(doc, offset);
778 Token<?extends AdaTokenId> token = ts.token();
779 TokenId id = token.id();
780 if (id == AdaTokenId.STRING_LITERAL) {
784 if (ts.movePrevious()) {
785 Token<?extends AdaTokenId> token = ts.token();
786 TokenId id = token.id();
787 if (id == AdaTokenId.STRING_LITERAL) {
796 public static OffsetRange getCommentBlock(BaseDocument doc, int caretOffset) {
797 // Check if the caret is within a comment, and if so insert a new
798 // leaf "node" which contains the comment line and then comment block
800 Token<?extends AdaTokenId> token = AdaLexUtilities.getToken(doc, caretOffset);
802 if ((token != null) && (token.id() == AdaTokenId.COMMENT)) {
803 // First add a range for the current line
804 int begin = Utilities.getRowStart(doc, caretOffset);
805 int end = Utilities.getRowEnd(doc, caretOffset);
807 if (AdaLexUtilities.isCommentOnlyLine(doc, caretOffset)) {
810 int newBegin = Utilities.getRowStart(doc, begin - 1);
812 if ((newBegin < 0) || !AdaLexUtilities.isCommentOnlyLine(doc, newBegin)) {
813 begin = Utilities.getRowFirstNonWhite(doc, begin);
820 int length = doc.getLength();
823 int newEnd = Utilities.getRowEnd(doc, end + 1);
825 if ((newEnd >= length) || !AdaLexUtilities.isCommentOnlyLine(doc, newEnd)) {
826 end = Utilities.getRowLastNonWhite(doc, end)+1;
834 return new OffsetRange(begin, end);
837 // It's just a line comment next to some code
838 TokenHierarchy<Document> th = TokenHierarchy.get((Document)doc);
839 int offset = token.offset(th);
840 return new OffsetRange(offset, offset + token.length());
843 //else if (token != null && token.id() == AdaTokenId.DOCUMENTATION) {
844 // // Select the whole token block
845 // TokenHierarchy<BaseDocument> th = TokenHierarchy.get(doc);
846 // int begin = token.offset(th);
847 // int end = begin + token.length();
848 // return new OffsetRange(begin, end);
850 } catch (BadLocationException ble) {
851 Exceptions.printStackTrace(ble);
854 return OffsetRange.NONE;
858 * Back up to the first space character prior to the given offset - as long as
859 * it's on the same line! If there's only leading whitespace on the line up
860 * to the lex offset, return the offset itself
862 public static int findSpaceBegin(BaseDocument doc, int lexOffset) {
863 TokenSequence ts = AdaLexUtilities.getAdaTokenSequence(doc, lexOffset);
867 boolean allowPrevLine = false;
870 lineStart = Utilities.getRowStart(doc, Math.min(lexOffset, doc.getLength()));
871 int prevLast = lineStart-1;
873 prevLast = Utilities.getRowLastNonWhite(doc, lineStart-1);
874 if (prevLast != -1) {
875 char c = doc.getText(prevLast, 1).charAt(0);
877 // Arglist continuation? // TODO : check lexing
878 allowPrevLine = true;
882 if (!allowPrevLine) {
883 int firstNonWhite = Utilities.getRowFirstNonWhite(doc, lineStart);
884 if (lexOffset <= firstNonWhite || firstNonWhite == -1) {
888 // Make lineStart so small that Math.max won't cause any problems
889 int firstNonWhite = Utilities.getRowFirstNonWhite(doc, lineStart);
890 if (prevLast >= 0 && (lexOffset <= firstNonWhite || firstNonWhite == -1)) {
895 } catch (BadLocationException ble) {
896 Exceptions.printStackTrace(ble);
901 if (lexOffset > ts.offset()) {
902 // We're in the middle of a token
903 return Math.max((ts.token().id() == AdaTokenId.WHITESPACE) ?
904 ts.offset() : lexOffset, lineStart);
906 while (ts.movePrevious()) {
907 Token token = ts.token();
908 if (token.id() != AdaTokenId.WHITESPACE) {
909 return Math.max(ts.offset() + token.length(), lineStart);
918 * Get the rdoc documentation associated with the given node in the given document.
919 * The node must have position information that matches the source in the document.
921 public static OffsetRange findRDocRange(BaseDocument baseDoc, int methodBegin) {
922 int begin = methodBegin;
924 if (methodBegin >= baseDoc.getLength()) {
925 return OffsetRange.NONE;
928 // Search to previous lines, locate comments. Once we have a non-whitespace line that isn't
929 // a comment, we're done
931 int offset = Utilities.getRowStart(baseDoc, methodBegin);
934 // Skip empty and whitespace lines
935 while (offset >= 0) {
936 // Find beginning of line
937 offset = Utilities.getRowStart(baseDoc, offset);
939 if (!Utilities.isRowEmpty(baseDoc, offset) &&
940 !Utilities.isRowWhite(baseDoc, offset)) {
948 return OffsetRange.NONE;
951 while (offset >= 0) {
952 // Find beginning of line
953 offset = Utilities.getRowStart(baseDoc, offset);
955 if (Utilities.isRowEmpty(baseDoc, offset) || Utilities.isRowWhite(baseDoc, offset)) {
956 // Empty lines not allowed within an rdoc
960 // This is a comment line we should include
961 int lineBegin = Utilities.getRowFirstNonWhite(baseDoc, offset);
962 int lineEnd = Utilities.getRowLastNonWhite(baseDoc, offset) + 1;
963 String line = baseDoc.getText(lineBegin, lineEnd - lineBegin);
965 // TODO: should be changed
966 // Tolerate "public", "private" and "protected" here --
967 // Test::Unit::Assertions likes to put these in front of each
969 if (line.startsWith("#")) {
971 } else if (line.startsWith("=end") &&
972 (lineBegin == Utilities.getRowStart(baseDoc, offset))) {
973 // It could be a =begin,=end document - see scanf.rb in Ada lib for example. Treat this differently.
974 int docBegin = findInlineDocStart(baseDoc, offset);
975 if (docBegin != -1) {
978 return OffsetRange.NONE;
980 } else if (line.equals("public") || line.equals("private") ||
981 line.equals("protected")) { // NOI18N
982 // Skip newlines back up to the comment
985 while (offset >= 0) {
986 // Find beginning of line
987 offset = Utilities.getRowStart(baseDoc, offset);
989 if (!Utilities.isRowEmpty(baseDoc, offset) &&
990 !Utilities.isRowWhite(baseDoc, offset)) {
999 // No longer in a comment
1006 } catch (BadLocationException ble) {
1007 Exceptions.printStackTrace(ble);
1010 if (methodBegin > begin) {
1011 return new OffsetRange(begin, methodBegin);
1013 return OffsetRange.NONE;
1017 private static int findInlineDocStart(BaseDocument baseDoc, int offset) throws BadLocationException {
1018 // offset points to a line containing =end
1019 // Skip the =end list
1020 offset = Utilities.getRowStart(baseDoc, offset);
1023 // Search backwards in the document for the =begin (if any) and add all lines in reverse
1024 // order in between.
1025 while (offset >= 0) {
1026 // Find beginning of line
1027 offset = Utilities.getRowStart(baseDoc, offset);
1029 // This is a comment line we should include
1030 int lineBegin = offset;
1031 int lineEnd = Utilities.getRowEnd(baseDoc, offset);
1032 String line = baseDoc.getText(lineBegin, lineEnd - lineBegin);
1034 if (line.startsWith("=begin")) {