source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rsyntaxtextarea/folding/CurlyFoldParser.java@ 25584

Last change on this file since 25584 was 25584, checked in by davidb, 12 years ago

Initial cut an a text edit area for GLI that supports color syntax highlighting

File size: 9.1 KB
Line 
1/*
2 * 10/08/2011
3 *
4 * CurlyFoldParser.java - Fold parser for languages with C-style syntax.
5 *
6 * This library is distributed under a modified BSD license. See the included
7 * RSyntaxTextArea.License.txt file for details.
8 */
9package org.fife.ui.rsyntaxtextarea.folding;
10
11import java.util.ArrayList;
12import java.util.List;
13import javax.swing.text.BadLocationException;
14
15import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
16import org.fife.ui.rsyntaxtextarea.Token;
17import org.fife.ui.rsyntaxtextarea.TokenMaker;
18
19
20/**
21 * A basic fold parser that can be used for languages such as C, that use
22 * curly braces to denote code blocks. This parser searches for curly brace
23 * pairs and creates code folds out of them. It can also optionally find
24 * C-style multi-line comments ("<code>/* ... *&#47;</code>") and make them
25 * foldable as well.<p>
26 *
27 * This parser knows nothing about language semantics; it uses
28 * <code>RSyntaxTextArea</code>'s syntax highlighting tokens to identify
29 * curly braces. By default, it looks for single-char tokens of type
30 * {@link Token#SEPARATOR}, with lexemes '<code>{</code>' or '<code>}</code>'.
31 * If your {@link TokenMaker} uses a different token type for curly braces, you
32 * should override the {@link #isLeftCurly(Token)} and
33 * {@link #isRightCurly(Token)} methods with your own definitions. In theory,
34 * you could extend this fold parser to parse languages that use completely
35 * different tokens than curly braces to denote foldable regions by overriding
36 * those two methods.<p>
37 *
38 * Note also that this class may impose somewhat of a performance penalty on
39 * large source files, since it re-parses the entire document each time folds
40 * are reevaluated.
41 *
42 * @author Robert Futrell
43 * @version 1.0
44 */
45public class CurlyFoldParser implements FoldParser {
46
47 /**
48 * Whether to scan for C-style multi-line comments and make them foldable.
49 */
50 private boolean foldableMultiLineComments;
51
52 /**
53 * Whether this parser is folding Java.
54 */
55 private boolean java;
56
57 /**
58 * Used to find import statements when folding Java code.
59 */
60 private static final char[] KEYWORD_IMPORT = "import".toCharArray();
61
62 /**
63 * Ending of a multi-line comment in C, C++, Java, etc.
64 */
65 protected static final char[] C_MLC_END = "*/".toCharArray();
66
67
68 /**
69 * Creates a fold parser that identifies foldable regions via curly braces
70 * as well as C-style multi-line comments.
71 */
72 public CurlyFoldParser() {
73 this(true, false);
74 }
75
76
77 /**
78 * Constructor.
79 *
80 * @param cStyleMultiLineComments Whether to scan for C-style multi-line
81 * comments and make them foldable.
82 * @param java Whether this parser is folding Java. This adds extra
83 * parsing rules, such as grouping all import statements into a
84 * fold section.
85 */
86 public CurlyFoldParser(boolean cStyleMultiLineComments, boolean java) {
87 this.foldableMultiLineComments = cStyleMultiLineComments;
88 this.java = java;
89 }
90
91
92 /**
93 * Returns whether multi-line comments are foldable with this parser.
94 *
95 * @return Whether multi-line comments are foldable.
96 * @see #setFoldableMultiLineComments(boolean)
97 */
98 public boolean getFoldableMultiLineComments() {
99 return foldableMultiLineComments;
100 }
101
102
103 /**
104 * {@inheritDoc}
105 */
106 public List getFolds(RSyntaxTextArea textArea) {
107
108 List folds = new ArrayList();
109
110 Fold currentFold = null;
111 int lineCount = textArea.getLineCount();
112 boolean inMLC = false;
113 int mlcStart = 0;
114 int importStartLine = -1;
115 int lastSeenImportLine = -1;
116 int importGroupStartOffs = -1;
117 int importGroupEndOffs = -1;
118
119 try {
120
121 for (int line=0; line<lineCount; line++) {
122
123 Token t = textArea.getTokenListForLine(line);
124 while (t!=null && t.isPaintable()) {
125
126 if (getFoldableMultiLineComments() && t.isComment()) {
127
128 // Java-specific stuff
129 if (java) {
130
131 if (importStartLine>-1) {
132 if (lastSeenImportLine>importStartLine) {
133 Fold fold = null;
134 // Any imports found *should* be a top-level fold,
135 // but we're extra lenient here and allow groups
136 // of them anywhere to keep our parser better-behaved
137 // if they have random "imports" throughout code.
138 if (currentFold==null) {
139 fold = new Fold(FoldType.IMPORTS,
140 textArea, importGroupStartOffs);
141 folds.add(fold);
142 }
143 else {
144 fold = currentFold.createChild(FoldType.IMPORTS,
145 importGroupStartOffs);
146 }
147 fold.setEndOffset(importGroupEndOffs);
148 }
149 importStartLine = lastSeenImportLine =
150 importGroupStartOffs = importGroupEndOffs = -1;
151 }
152
153 }
154
155 if (inMLC) {
156 // If we found the end of an MLC that started
157 // on a previous line...
158 if (t.endsWith(C_MLC_END)) {
159 int mlcEnd = t.offset + t.textCount - 1;
160 if (currentFold==null) {
161 currentFold = new Fold(FoldType.COMMENT, textArea, mlcStart);
162 currentFold.setEndOffset(mlcEnd);
163 folds.add(currentFold);
164 currentFold = null;
165 }
166 else {
167 currentFold = currentFold.createChild(FoldType.COMMENT, mlcStart);
168 currentFold.setEndOffset(mlcEnd);
169 currentFold = currentFold.getParent();
170 }
171 //System.out.println("Ending MLC at: " + mlcEnd + ", parent==" + currentFold);
172 inMLC = false;
173 mlcStart = 0;
174 }
175 // Otherwise, this MLC is continuing on to yet
176 // another line.
177 }
178 else {
179 // If we're an MLC that ends on a later line...
180 if (t.type!=Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
181 //System.out.println("Starting MLC at: " + t.offset);
182 inMLC = true;
183 mlcStart = t.offset;
184 }
185 }
186
187 }
188
189 else if (isLeftCurly(t)) {
190
191 // Java-specific stuff
192 if (java) {
193
194 if (importStartLine>-1) {
195 if (lastSeenImportLine>importStartLine) {
196 Fold fold = null;
197 // Any imports found *should* be a top-level fold,
198 // but we're extra lenient here and allow groups
199 // of them anywhere to keep our parser better-behaved
200 // if they have random "imports" throughout code.
201 if (currentFold==null) {
202 fold = new Fold(FoldType.IMPORTS,
203 textArea, importGroupStartOffs);
204 folds.add(fold);
205 }
206 else {
207 fold = currentFold.createChild(FoldType.IMPORTS,
208 importGroupStartOffs);
209 }
210 fold.setEndOffset(importGroupEndOffs);
211 }
212 importStartLine = lastSeenImportLine =
213 importGroupStartOffs = importGroupEndOffs = -1;
214 }
215
216 }
217
218 if (currentFold==null) {
219 currentFold = new Fold(FoldType.CODE, textArea, t.offset);
220 folds.add(currentFold);
221 }
222 else {
223 currentFold = currentFold.createChild(FoldType.CODE, t.offset);
224 }
225
226 }
227
228 else if (isRightCurly(t)) {
229
230 if (currentFold!=null) {
231 currentFold.setEndOffset(t.offset);
232 Fold parentFold = currentFold.getParent();
233 //System.out.println("... Adding regular fold at " + t.offset + ", parent==" + parentFold);
234 // Don't add fold markers for single-line blocks
235 if (currentFold.isOnSingleLine()) {
236 if (parentFold!=null) {
237 currentFold.removeFromParent();
238 }
239 else {
240 folds.remove(folds.size()-1);
241 }
242 }
243 currentFold = parentFold;
244 }
245
246 }
247
248 // Java-specific folding rules
249 else if (java) {
250
251 if (t.is(Token.RESERVED_WORD, KEYWORD_IMPORT)) {
252 if (importStartLine==-1) {
253 importStartLine = line;
254 importGroupStartOffs = t.offset;
255 importGroupEndOffs = t.offset;
256 }
257 lastSeenImportLine = line;
258 }
259
260 else if (importStartLine>-1 &&
261 t.isIdentifier() &&//SEPARATOR &&
262 t.isSingleChar(';')) {
263 importGroupEndOffs = t.offset;
264 }
265
266 }
267
268 t = t.getNextToken();
269
270 }
271
272 }
273
274 } catch (BadLocationException ble) { // Should never happen
275 ble.printStackTrace();
276 }
277
278 return folds;
279
280 }
281
282
283 /**
284 * Returns whether the token is a left curly brace. This method exists
285 * so subclasses can provide their own curly brace definition.
286 *
287 * @param t The token.
288 * @return Whether it is a left curly brace.
289 * @see #isRightCurly(Token)
290 */
291 public boolean isLeftCurly(Token t) {
292 return t.isLeftCurly();
293 }
294
295
296 /**
297 * Returns whether the token is a right curly brace. This method exists
298 * so subclasses can provide their own curly brace definition.
299 *
300 * @param t The token.
301 * @return Whether it is a right curly brace.
302 * @see #isLeftCurly(Token)
303 */
304 public boolean isRightCurly(Token t) {
305 return t.isRightCurly();
306 }
307
308
309 /**
310 * Sets whether multi-line comments are foldable with this parser.
311 *
312 * @param foldable Whether multi-line comments are foldable.
313 * @see #getFoldableMultiLineComments()
314 */
315 public void setFoldableMultiLineComments(boolean foldable) {
316 this.foldableMultiLineComments = foldable;
317 }
318
319
320}
Note: See TracBrowser for help on using the repository browser.