001: /*
002: * MakefileFormatter.java
003: *
004: * Copyright (C) 2000-2002 Peter Graves
005: * $Id: MakefileFormatter.java,v 1.1.1.1 2002/09/24 16:07:55 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: import gnu.regexp.RE;
025: import gnu.regexp.REMatch;
026: import gnu.regexp.UncheckedRE;
027:
028: public final class MakefileFormatter extends Formatter {
029: private static final int MAKEFILE_FORMAT_TEXT = 0;
030: private static final int MAKEFILE_FORMAT_COMMENT = 1;
031: private static final int MAKEFILE_FORMAT_STRING = 2;
032: private static final int MAKEFILE_FORMAT_KEYWORD = 3;
033: private static final int MAKEFILE_FORMAT_TARGET = 4;
034:
035: private static final int STATE_BACKQUOTE = STATE_LAST + 1;
036:
037: private FastStringBuffer sb = new FastStringBuffer();
038: private int tokStart;
039:
040: private static final RE targetRE = new UncheckedRE("^\\S+.*:");
041: private static final RE assignmentRE = new UncheckedRE(
042: "^\\S+\\s*:?=");
043: private static final RE conditionalRE = new UncheckedRE(
044: "^(ifn?(eq|def)\\s)|^(else\\s*)|^(endif\\s*)");
045:
046: public MakefileFormatter(Buffer buffer) {
047: this .buffer = buffer;
048: }
049:
050: private void endToken(int state) {
051: if (sb.length() > 0) {
052: int format;
053: switch (state) {
054: case STATE_QUOTE:
055: case STATE_SINGLEQUOTE:
056: case STATE_BACKQUOTE:
057: format = MAKEFILE_FORMAT_STRING;
058: break;
059: case STATE_COMMENT:
060: format = MAKEFILE_FORMAT_COMMENT;
061: break;
062: default:
063: format = MAKEFILE_FORMAT_TEXT;
064: break;
065: }
066: addSegment(sb.toString(), format);
067: tokStart += sb.length();
068: sb.setLength(0);
069: }
070: }
071:
072: private void addToken(String s, int format) {
073: int length = s.length();
074: if (length > 0) {
075: addSegment(s, format);
076: tokStart += length;
077: }
078: }
079:
080: private void parseLine(String text, int state) {
081: if (Editor.tabsAreVisible())
082: text = Utilities
083: .makeTabsVisible(text, buffer.getTabWidth());
084: else
085: text = Utilities.detab(text, buffer.getTabWidth());
086: clearSegmentList();
087: int braceCount = 0;
088: sb.setLength(0);
089: int i = 0;
090: tokStart = 0;
091: if (text.trim().startsWith("#")) {
092: addToken(text, MAKEFILE_FORMAT_COMMENT);
093: return;
094: }
095: // Try some regexps at the beginning of the line.
096: REMatch match = conditionalRE.getMatch(text);
097: if (match != null) {
098: addToken(match.toString(), MAKEFILE_FORMAT_KEYWORD);
099: i += match.toString().length();
100: } else {
101: match = assignmentRE.getMatch(text);
102: if (match != null) {
103: addToken(match.toString(), MAKEFILE_FORMAT_TEXT);
104: i += match.toString().length();
105: } else {
106: match = targetRE.getMatch(text);
107: if (match != null) {
108: addToken(match.toString(), MAKEFILE_FORMAT_TARGET);
109: i += match.toString().length();
110: }
111: }
112: }
113: final int limit = text.length();
114: // Skip whitespace at start of line.
115: while (i < limit) {
116: char c = text.charAt(i);
117: if (Character.isWhitespace(c)) {
118: sb.append(c);
119: ++i;
120: } else {
121: endToken(state);
122: break;
123: }
124: }
125: while (i < limit) {
126: char c = text.charAt(i);
127: if (state == STATE_QUOTE) {
128: sb.append(c);
129: if (c == '"') {
130: endToken(state);
131: state = STATE_NEUTRAL;
132: } else if (c == '\\' && i < limit - 1) {
133: // Escape char.
134: sb.append(text.charAt(++i));
135: }
136: ++i;
137: continue;
138: }
139: if (state == STATE_SINGLEQUOTE) {
140: sb.append(c);
141: if (c == '\'') {
142: endToken(state);
143: state = STATE_NEUTRAL;
144: }
145: ++i;
146: continue;
147: }
148: if (state == STATE_BACKQUOTE) {
149: sb.append(c);
150: if (c == '`') {
151: endToken(state);
152: state = STATE_NEUTRAL;
153: }
154: ++i;
155: continue;
156: }
157: // Reaching here, we're not in a quoted string.
158: if (c == '"') {
159: endToken(state);
160: sb.append(c);
161: state = STATE_QUOTE;
162: ++i;
163: continue;
164: }
165: if (c == '\'') {
166: endToken(state);
167: sb.append(c);
168: state = STATE_SINGLEQUOTE;
169: ++i;
170: continue;
171: }
172: if (c == '`') {
173: endToken(state);
174: sb.append(c);
175: state = STATE_BACKQUOTE;
176: ++i;
177: continue;
178: }
179: if (c == '#') {
180: endToken(state);
181: state = STATE_COMMENT;
182: sb.append(text.substring(i));
183: endToken(state);
184: return;
185: }
186: if (state == STATE_IDENTIFIER) {
187: if (buffer.mode.isIdentifierPart(c))
188: sb.append(c);
189: else {
190: endToken(state);
191: sb.append(c);
192: state = STATE_NEUTRAL;
193: }
194: ++i;
195: continue;
196: }
197: if (state == STATE_NUMBER) {
198: if (Character.isDigit(c))
199: sb.append(c);
200: else {
201: endToken(state);
202: sb.append(c);
203: if (buffer.mode.isIdentifierStart(c))
204: state = STATE_IDENTIFIER;
205: else
206: state = STATE_NEUTRAL;
207: }
208: ++i;
209: continue;
210: }
211: if (state == STATE_NEUTRAL) {
212: if (buffer.mode.isIdentifierStart(c)) {
213: endToken(state);
214: sb.append(c);
215: state = STATE_IDENTIFIER;
216: } else if (Character.isDigit(c)) {
217: endToken(state);
218: sb.append(c);
219: state = STATE_NUMBER;
220: } else
221: // Still neutral...
222: sb.append(c);
223: }
224: ++i;
225: }
226: endToken(state);
227: }
228:
229: public LineSegmentList formatLine(Line line) {
230: if (line == null) {
231: clearSegmentList();
232: addSegment("", MAKEFILE_FORMAT_TEXT);
233: return segmentList;
234: }
235: parseLine(line.getText(), line.flags());
236: for (int i = 0; i < segmentList.size(); i++) {
237: LineSegment segment = segmentList.getSegment(i);
238: if (segment.getFormat() > 0)
239: continue;
240: String token = segment.getText();
241: if (isKeyword(token))
242: segment.setFormat(MAKEFILE_FORMAT_KEYWORD);
243: else
244: segment.setFormat(MAKEFILE_FORMAT_TEXT);
245: }
246: return segmentList;
247: }
248:
249: public FormatTable getFormatTable() {
250: if (formatTable == null) {
251: formatTable = new FormatTable("MakefileMode");
252: formatTable.addEntryFromPrefs(MAKEFILE_FORMAT_TEXT, "text");
253: formatTable.addEntryFromPrefs(MAKEFILE_FORMAT_COMMENT,
254: "comment");
255: formatTable.addEntryFromPrefs(MAKEFILE_FORMAT_STRING,
256: "string");
257: formatTable.addEntryFromPrefs(MAKEFILE_FORMAT_KEYWORD,
258: "keyword");
259: formatTable.addEntryFromPrefs(MAKEFILE_FORMAT_TARGET,
260: "target");
261: }
262: return formatTable;
263: }
264: }
|