001: /*
002: * TclFormatter.java
003: *
004: * Copyright (C) 2002 Peter Graves
005: * $Id: TclFormatter.java,v 1.1.1.1 2002/09/24 16:09:09 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: public final class TclFormatter extends Formatter implements Constants {
025: private static final int TCL_STATE_NEUTRAL = 0;
026: private static final int TCL_STATE_COMMENT = 1;
027: private static final int TCL_STATE_QUOTE = 2;
028: private static final int TCL_STATE_IDENTIFIER = 3;
029: private static final int TCL_STATE_OPERATOR = 4;
030: private static final int TCL_STATE_BRACE = 5;
031: private static final int TCL_STATE_BRACKET = 6;
032: private static final int TCL_STATE_NUMBER = 7;
033:
034: private static final int TCL_FORMAT_TEXT = 0;
035: private static final int TCL_FORMAT_COMMENT = 1;
036: private static final int TCL_FORMAT_STRING = 2;
037: private static final int TCL_FORMAT_IDENTIFIER = 3;
038: private static final int TCL_FORMAT_KEYWORD = 4;
039: private static final int TCL_FORMAT_ARRAY = 5;
040: private static final int TCL_FORMAT_OPERATOR = 6;
041: private static final int TCL_FORMAT_BRACE = 7;
042: private static final int TCL_FORMAT_BRACKET = 8;
043: private static final int TCL_FORMAT_NUMBER = 9;
044:
045: private static final TclMode mode = TclMode.getMode();
046:
047: public TclFormatter(Buffer buffer) {
048: this .buffer = buffer;
049: }
050:
051: private int tokenBegin = 0;
052:
053: private void endToken(String text, int tokenEnd, int state) {
054: if (tokenEnd - tokenBegin > 0) {
055: int format = TCL_FORMAT_TEXT;
056: switch (state) {
057: case TCL_STATE_NEUTRAL:
058: format = TCL_FORMAT_TEXT;
059: break;
060: case TCL_STATE_QUOTE:
061: format = TCL_FORMAT_STRING;
062: break;
063: case TCL_STATE_IDENTIFIER:
064: format = TCL_FORMAT_IDENTIFIER;
065: break;
066: case TCL_STATE_COMMENT:
067: format = TCL_FORMAT_COMMENT;
068: break;
069: case TCL_STATE_OPERATOR:
070: format = TCL_FORMAT_OPERATOR;
071: break;
072: case TCL_STATE_BRACE:
073: format = TCL_FORMAT_BRACE;
074: break;
075: case TCL_STATE_BRACKET:
076: format = TCL_FORMAT_BRACKET;
077: break;
078: case TCL_STATE_NUMBER:
079: format = TCL_FORMAT_NUMBER;
080: break;
081: }
082: addSegment(text, tokenBegin, tokenEnd, format);
083: tokenBegin = tokenEnd;
084: }
085: }
086:
087: private void parseLine(Line line) {
088: if (line == null) {
089: addSegment("", TCL_FORMAT_TEXT);
090: return;
091: }
092: String text;
093: if (Editor.tabsAreVisible())
094: text = Utilities.makeTabsVisible(line.getText(), buffer
095: .getTabWidth());
096: else
097: text = Utilities
098: .detab(line.getText(), buffer.getTabWidth());
099: tokenBegin = 0;
100: int state = TCL_STATE_NEUTRAL;
101: int i = 0;
102: final int limit = text.length();
103:
104: // Skip whitespace at start of line.
105: while (i < limit) {
106: if (Character.isWhitespace(text.charAt(i))) {
107: ++i;
108: } else {
109: endToken(text, i, state);
110: break;
111: }
112: }
113:
114: while (i < limit) {
115: char c = text.charAt(i);
116: if (c == '\\' && i < limit - 1) {
117: i += 2;
118: continue;
119: }
120: if (state == TCL_STATE_QUOTE) {
121: if (c == '"') {
122: endToken(text, i + 1, state);
123: state = TCL_STATE_NEUTRAL;
124: }
125: ++i;
126: continue;
127: }
128: if (c == '"') {
129: endToken(text, i, state);
130: state = TCL_STATE_QUOTE;
131: ++i;
132: continue;
133: }
134: if (c == '#') {
135: // It's only a real comment if '#' is the first non-whitespace
136: // character on the line or if it is immediately preceded by a
137: // semicolon, opening brace, or opening bracket.
138: boolean isComment = true;
139: for (int j = i - 1; j >= 0; j--) {
140: char cc = text.charAt(j);
141: if (Character.isWhitespace(cc))
142: continue;
143: if (";{[".indexOf(cc) >= 0)
144: break;
145: // Otherwise...
146: isComment = false;
147: break;
148: }
149: if (isComment) {
150: endToken(text, i, state);
151: endToken(text, limit, TCL_STATE_COMMENT);
152: return;
153: } else {
154: ++i;
155: continue;
156: }
157: }
158: if (c == '{' || c == '}') {
159: if (state != TCL_STATE_BRACE) {
160: endToken(text, i, state);
161: state = TCL_STATE_BRACE;
162: }
163: ++i;
164: continue;
165: }
166: if (c == '[' || c == ']') {
167: if (state != TCL_STATE_BRACKET) {
168: endToken(text, i, state);
169: state = TCL_STATE_BRACKET;
170: }
171: ++i;
172: continue;
173: }
174: if (state == TCL_STATE_BRACE || state == TCL_STATE_BRACKET) {
175: if (mode.isIdentifierStart(c)) {
176: endToken(text, i, state);
177: state = TCL_STATE_IDENTIFIER;
178: } else if (Character.isDigit(c)) {
179: endToken(text, i, state);
180: state = TCL_STATE_NUMBER;
181: } else {
182: endToken(text, i, state);
183: state = TCL_STATE_NEUTRAL;
184: }
185: ++i;
186: continue;
187: }
188: if (state == TCL_STATE_IDENTIFIER) {
189: if (!mode.isIdentifierPart(c)) {
190: endToken(text, i, state);
191: final LineSegment segment = getLastSegment();
192: if (segment != null) {
193: final String segmentText = segment.getText();
194: if (isKeyword(segmentText))
195: segment.setFormat(TCL_FORMAT_KEYWORD);
196: else if (isOperator(segmentText))
197: segment.setFormat(TCL_FORMAT_OPERATOR);
198: else if (c == '(')
199: segment.setFormat(TCL_FORMAT_ARRAY);
200: }
201: state = TCL_STATE_NEUTRAL;
202: }
203: ++i;
204: continue;
205: }
206: if (state == TCL_STATE_NUMBER) {
207: if (!Character.isDigit(c)) {
208: endToken(text, i, state);
209: if (mode.isIdentifierStart(c))
210: state = TCL_STATE_IDENTIFIER;
211: else
212: state = TCL_STATE_NEUTRAL;
213: }
214: ++i;
215: continue;
216: }
217: if (state == TCL_STATE_NEUTRAL) {
218: if (mode.isIdentifierStart(c)) {
219: endToken(text, i, state);
220: state = TCL_STATE_IDENTIFIER;
221: } else if (Character.isDigit(c)) {
222: endToken(text, i, state);
223: state = TCL_STATE_NUMBER;
224: }
225: }
226: ++i;
227: }
228:
229: // Reached end of line.
230: endToken(text, i, state);
231: if (state == TCL_STATE_IDENTIFIER) {
232: final LineSegment segment = getLastSegment();
233: if (segment != null) {
234: final String segmentText = segment.getText();
235: if (isKeyword(segmentText))
236: segment.setFormat(TCL_FORMAT_KEYWORD);
237: else if (isOperator(segmentText))
238: segment.setFormat(TCL_FORMAT_OPERATOR);
239: }
240: }
241: }
242:
243: public LineSegmentList formatLine(Line line) {
244: clearSegmentList();
245: parseLine(line);
246: return segmentList;
247: }
248:
249: private static final boolean isOperator(String s) {
250: for (int i = s.length() - 1; i >= 0; i--) {
251: if (!isOperatorChar(s.charAt(i)))
252: return false;
253: }
254: return true;
255: }
256:
257: private static final boolean isOperatorChar(char c) {
258: return "!&|<>=+/*-".indexOf(c) >= 0;
259: }
260:
261: public FormatTable getFormatTable() {
262: if (formatTable == null) {
263: formatTable = new FormatTable("TclMode");
264: formatTable.addEntryFromPrefs(TCL_FORMAT_TEXT, "text");
265: formatTable
266: .addEntryFromPrefs(TCL_FORMAT_COMMENT, "comment");
267: formatTable.addEntryFromPrefs(TCL_FORMAT_STRING, "string");
268: formatTable.addEntryFromPrefs(TCL_FORMAT_IDENTIFIER,
269: "identifier", "text");
270: formatTable
271: .addEntryFromPrefs(TCL_FORMAT_KEYWORD, "keyword");
272: formatTable.addEntryFromPrefs(TCL_FORMAT_ARRAY, "text");
273: formatTable.addEntryFromPrefs(TCL_FORMAT_OPERATOR,
274: "operator");
275: formatTable.addEntryFromPrefs(TCL_FORMAT_BRACE, "brace");
276: formatTable.addEntryFromPrefs(TCL_FORMAT_BRACKET,
277: "bracket", "brace");
278: formatTable.addEntryFromPrefs(TCL_FORMAT_NUMBER, "number");
279: }
280: return formatTable;
281: }
282: }
|