source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rsyntaxtextarea/AbstractJFlexCTokenMaker.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: 5.8 KB
Line 
1/*
2 * 01/25/2009
3 *
4 * AbstractJFlexCTokenMaker.java - Base class for token makers that use curly
5 * braces to denote code blocks, such as C, C++, Java, Perl, etc.
6 *
7 * This library is distributed under a modified BSD license. See the included
8 * RSyntaxTextArea.License.txt file for details.
9 */
10package org.fife.ui.rsyntaxtextarea;
11
12import java.awt.event.ActionEvent;
13import java.util.regex.Matcher;
14import java.util.regex.Pattern;
15import javax.swing.Action;
16import javax.swing.UIManager;
17import javax.swing.text.BadLocationException;
18
19import org.fife.ui.rtextarea.RTextArea;
20
21
22
23/**
24 * Base class for JFlex-based token makers using C-style syntax. This class
25 * knows how to auto-indent after opening braces and parens.
26 *
27 * @author Robert Futrell
28 * @version 1.0
29 */
30public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
31
32 protected static final Action INSERT_BREAK_ACTION = new InsertBreakAction();
33
34
35 /**
36 * Returns <code>true</code> always as C-style languages use curly braces
37 * to denote code blocks.
38 *
39 * @return <code>true</code> always.
40 */
41 public boolean getCurlyBracesDenoteCodeBlocks() {
42 return true;
43 }
44
45
46 /**
47 * Returns an action to handle "insert break" key presses (i.e. Enter).
48 * An action is returned that handles newlines differently in multi-line
49 * comments.
50 *
51 * @return The action.
52 */
53 public Action getInsertBreakAction() {
54 return INSERT_BREAK_ACTION;
55 }
56
57
58 /**
59 * {@inheritDoc}
60 */
61 public boolean getMarkOccurrencesOfTokenType(int type) {
62 return type==Token.IDENTIFIER || type==Token.FUNCTION;
63 }
64
65
66 /**
67 * {@inheritDoc}
68 */
69 public boolean getShouldIndentNextLineAfter(Token t) {
70 if (t!=null && t.textCount==1) {
71 char ch = t.text[t.textOffset];
72 return ch=='{' || ch=='(';
73 }
74 return false;
75 }
76
77
78 /**
79 * Action that knows how to special-case inserting a newline in a
80 * multi-line comment for languages like C and Java.
81 */
82 private static class InsertBreakAction extends
83 RSyntaxTextAreaEditorKit.InsertBreakAction {
84
85 private static final Pattern p =
86 Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
87
88 public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
89
90 if (!textArea.isEditable() || !textArea.isEnabled()) {
91 UIManager.getLookAndFeel().provideErrorFeedback(textArea);
92 return;
93 }
94
95 RSyntaxTextArea rsta = (RSyntaxTextArea)getTextComponent(e);
96 RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
97
98 int line = textArea.getCaretLineNumber();
99 int type = doc.getLastTokenTypeOnLine(line);
100
101 // Only in MLC's should we try this
102 if (type==Token.COMMENT_DOCUMENTATION ||
103 type==Token.COMMENT_MULTILINE) {
104 insertBreakInMLC(e, rsta, line);
105 }
106 else {
107 handleInsertBreak(rsta, true);
108 }
109
110 }
111
112
113 /**
114 * Returns whether the MLC token containing <code>offs</code> appears
115 * to have a "nested" comment (i.e., contains "<code>/*</code>"
116 * somewhere inside of it). This implies that it is likely a "new" MLC
117 * and needs to be closed. While not foolproof, this is usually good
118 * enough of a sign.
119 *
120 * @param textArea
121 * @param line
122 * @param offs
123 * @return Whether a comment appears to be nested inside this one.
124 */
125 private boolean appearsNested(RSyntaxTextArea textArea,
126 int line, int offs) {
127
128 final int firstLine = line; // Remember the line we start at.
129
130 while (line<textArea.getLineCount()) {
131 Token t = textArea.getTokenListForLine(line);
132 int i = 0;
133 // If examining the first line, start at offs.
134 if (line++==firstLine) {
135 t = RSyntaxUtilities.getTokenAtOffset(t, offs);
136 if (t==null) { // offs was at end of the line
137 continue;
138 }
139 i = t.documentToToken(offs);
140 }
141 else {
142 i = t.textOffset;
143 }
144 while (i<t.textOffset+t.textCount-1) {
145 if (t.text[i]=='/' && t.text[i+1]=='*') {
146 return true;
147 }
148 i++;
149 }
150 // If tokens come after this one on this line, our MLC ended.
151 if (t.getNextToken()!=null) {
152 return false;
153 }
154 }
155
156 return true; // No match - MLC goes to the end of the file
157
158 }
159
160 private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea,
161 int line) {
162
163 Matcher m = null;
164 int start = -1;
165 int end = -1;
166 try {
167 start = textArea.getLineStartOffset(line);
168 end = textArea.getLineEndOffset(line);
169 String text = textArea.getText(start, end-start);
170 m = p.matcher(text);
171 } catch (BadLocationException ble) { // Never happens
172 UIManager.getLookAndFeel().provideErrorFeedback(textArea);
173 ble.printStackTrace();
174 return;
175 }
176
177 if (m.lookingAt()) {
178
179 String leadingWS = m.group(1);
180 String mlcMarker = m.group(2);
181
182 // If the caret is "inside" any leading whitespace or MLC
183 // marker, move it to the end of the line.
184 int dot = textArea.getCaretPosition();
185 if (dot>=start &&
186 dot<start+leadingWS.length()+mlcMarker.length()) {
187 // If we're in the whitespace before the very start of the
188 // MLC though, just insert a normal newline
189 if (mlcMarker.charAt(0)=='/') {
190 handleInsertBreak(textArea, true);
191 return;
192 }
193 textArea.setCaretPosition(end-1);
194 }
195
196 boolean firstMlcLine = mlcMarker.charAt(0)=='/';
197 boolean nested = appearsNested(textArea, line,
198 start+leadingWS.length()+2);
199 String header = leadingWS +
200 (firstMlcLine ? " * " : "*") +
201 m.group(3);
202 textArea.replaceSelection("\n" + header);
203 if (nested) {
204 dot = textArea.getCaretPosition(); // Has changed
205 textArea.insert("\n" + leadingWS + " */", dot);
206 textArea.setCaretPosition(dot);
207 }
208
209 }
210 else {
211 handleInsertBreak(textArea, true);
212 }
213
214 }
215
216 }
217
218
219}
Note: See TracBrowser for help on using the repository browser.