001: /*
002: * PHPFormatter.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: * $Id: PHPFormatter.java,v 1.2 2003/05/15 15:34:01 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 PHPFormatter extends Formatter implements Constants {
025: private static final int PHP_STATE_IDENTIFIER = STATE_LAST + 1;
026: private static final int PHP_STATE_OPERATOR = STATE_LAST + 2;
027: private static final int PHP_STATE_BRACE = STATE_LAST + 3;
028: private static final int PHP_STATE_NUMBER = STATE_LAST + 4;
029: private static final int PHP_STATE_HEXNUMBER = STATE_LAST + 5;
030: private static final int PHP_STATE_IGNORE = STATE_LAST + 6;
031: private static final int PHP_STATE_KEYWORD = STATE_LAST + 7;
032: private static final int PHP_STATE_TAG_STARTING = STATE_LAST + 8;
033: private static final int PHP_STATE_TAG = STATE_LAST + 9;
034: private static final int PHP_STATE_ATTNAME = STATE_LAST + 10;
035: private static final int PHP_STATE_EQUALS = STATE_LAST + 11;
036: private static final int PHP_STATE_ATTVALUE = STATE_LAST + 12;
037: private static final int PHP_STATE_ATTVALUE_QUOTE = STATE_LAST + 13;
038: private static final int PHP_STATE_ATTVALUE_SINGLEQUOTE = STATE_LAST + 14;
039: private static final int PHP_STATE_TAG_ENDING = STATE_LAST + 15;
040: private static final int PHP_STATE_HTML_COMMENT = STATE_LAST + 16;
041:
042: private static final int PHP_FORMAT_TEXT = 0;
043: private static final int PHP_FORMAT_COMMENT = 1;
044: private static final int PHP_FORMAT_STRING = 2;
045: private static final int PHP_FORMAT_IDENTIFIER = 3;
046: private static final int PHP_FORMAT_KEYWORD = 4;
047: private static final int PHP_FORMAT_FUNCTION = 5;
048: private static final int PHP_FORMAT_OPERATOR = 6;
049: private static final int PHP_FORMAT_BRACE = 7;
050: private static final int PHP_FORMAT_NUMBER = 8;
051: private static final int PHP_FORMAT_VAR = 9;
052: private static final int PHP_FORMAT_DELIMITER = 10;
053: private static final int PHP_FORMAT_TAG = 11;
054: private static final int PHP_FORMAT_ATTRIBUTE = 12;
055: private static final int PHP_FORMAT_EQUALS = 13;
056:
057: private static PHPMode mode;
058:
059: public PHPFormatter(Buffer buffer) {
060: this .buffer = buffer;
061: if (mode == null)
062: mode = (PHPMode) PHPMode.getMode();
063: }
064:
065: private int tokenBegin = 0;
066:
067: private final void endToken(StringPosition pos, int state) {
068: endToken(pos.getText(), pos.getOffset(), state);
069: }
070:
071: private void endToken(String text, int tokenEnd, int state) {
072: if (tokenEnd - tokenBegin > 0) {
073: int format;
074: switch (state) {
075: case STATE_NEUTRAL:
076: case PHP_STATE_IGNORE:
077: format = PHP_FORMAT_TEXT;
078: break;
079: case STATE_QUOTE:
080: case STATE_SINGLEQUOTE:
081: format = PHP_FORMAT_STRING;
082: break;
083: case PHP_STATE_IDENTIFIER:
084: format = PHP_FORMAT_IDENTIFIER;
085: break;
086: case STATE_COMMENT:
087: case PHP_STATE_HTML_COMMENT:
088: format = PHP_FORMAT_COMMENT;
089: break;
090: case PHP_STATE_OPERATOR:
091: format = PHP_FORMAT_OPERATOR;
092: break;
093: case PHP_STATE_BRACE:
094: format = PHP_FORMAT_BRACE;
095: break;
096: case PHP_STATE_NUMBER:
097: case PHP_STATE_HEXNUMBER:
098: format = PHP_FORMAT_NUMBER;
099: break;
100: case PHP_STATE_KEYWORD:
101: format = PHP_FORMAT_KEYWORD;
102: break;
103: case PHP_STATE_TAG_STARTING:
104: case PHP_STATE_TAG_ENDING:
105: format = PHP_FORMAT_DELIMITER;
106: break;
107: case PHP_STATE_TAG:
108: format = PHP_FORMAT_TAG;
109: break;
110: case PHP_STATE_ATTNAME:
111: format = PHP_FORMAT_ATTRIBUTE;
112: break;
113: case PHP_STATE_EQUALS:
114: format = PHP_FORMAT_EQUALS;
115: break;
116: case PHP_STATE_ATTVALUE:
117: case PHP_STATE_ATTVALUE_QUOTE:
118: case PHP_STATE_ATTVALUE_SINGLEQUOTE:
119: format = PHP_FORMAT_STRING;
120: break;
121: default:
122: format = PHP_FORMAT_TEXT;
123: break;
124: }
125: addSegment(text, tokenBegin, tokenEnd, format);
126: tokenBegin = tokenEnd;
127: }
128: }
129:
130: private void parseLine(Line line) {
131: String text;
132: if (Editor.tabsAreVisible())
133: text = Utilities.makeTabsVisible(line.getText(), buffer
134: .getTabWidth());
135: else
136: text = Utilities
137: .detab(line.getText(), buffer.getTabWidth());
138: tokenBegin = 0;
139:
140: char quoteChar = '\0';
141: int state = getState(line.flags());
142: final int htmlState = getHtmlState(line.flags());
143: if (state == PHP_STATE_TAG) {
144: // End of tag name at end of previous line.
145: state = PHP_STATE_ATTNAME;
146: } else if (state == STATE_QUOTE)
147: quoteChar = '"';
148: else if (state == STATE_SINGLEQUOTE)
149: quoteChar = '\'';
150:
151: StringPosition pos = new StringPosition(text);
152: // Skip whitespace at start of line.
153: while (!pos.atEnd() && pos.charIsWhitespace())
154: pos.next();
155: endToken(pos, state);
156:
157: if (pos.atEnd())
158: return;
159:
160: switch (state) {
161: case STATE_NEUTRAL:
162: case STATE_COMMENT:
163: case STATE_QUOTE:
164: case STATE_SINGLEQUOTE:
165: case PHP_STATE_IDENTIFIER:
166: case PHP_STATE_OPERATOR:
167: case PHP_STATE_BRACE:
168: case PHP_STATE_NUMBER:
169: case PHP_STATE_HEXNUMBER:
170: case PHP_STATE_KEYWORD:
171: parseLinePHP(pos, state, htmlState);
172: default:
173: parseLineHTML(pos, state);
174: }
175: }
176:
177: private final void parseLinePHP(StringPosition pos, int state) {
178: parseLinePHP(pos, state, PHP_STATE_IGNORE);
179: }
180:
181: private void parseLinePHP(StringPosition pos, int state,
182: int htmlState) {
183: while (!pos.atEnd()) {
184: char c = pos.getChar();
185: if (c == '\\') {
186: // Escape.
187: pos.skip(1);
188: pos.next();
189: continue;
190: }
191: if (state == STATE_COMMENT) {
192: if (pos.lookingAt("*/")) {
193: pos.skip(2);
194: endToken(pos, state);
195: state = STATE_NEUTRAL;
196: } else
197: pos.next();
198: continue;
199: }
200: if (state == STATE_QUOTE) {
201: if (c == '"') {
202: pos.next();
203: endToken(pos, state);
204: state = STATE_NEUTRAL;
205: continue;
206: }
207: pos.next();
208: continue;
209: }
210: if (state == STATE_SINGLEQUOTE) {
211: if (c == '\'') {
212: pos.next();
213: endToken(pos, state);
214: state = STATE_NEUTRAL;
215: continue;
216: }
217: pos.next();
218: continue;
219: }
220: // Reaching here, we're not in a comment or a quoted string.
221: if (c == '"' || c == '\'') {
222: endToken(pos, state);
223: state = c == '"' ? STATE_QUOTE : STATE_SINGLEQUOTE;
224: pos.next();
225: continue;
226: }
227: if (pos.lookingAt("?>")) {
228: endToken(pos, state);
229: pos.skip(2);
230: endToken(pos, PHP_STATE_TAG_ENDING);
231: parseLineHTML(pos, htmlState);
232: return;
233: }
234: if (c == '/') {
235: if (pos.lookingAt("/*")) {
236: endToken(pos, state);
237: checkLastSegment();
238: state = STATE_COMMENT;
239: pos.skip(2);
240: } else if (pos.lookingAt("//")) {
241: endToken(pos, state);
242: checkLastSegment();
243: pos.setOffset(pos.getText().length());
244: endToken(pos, STATE_COMMENT);
245: return;
246: } else
247: pos.next();
248: continue;
249: }
250: if (c == '#') {
251: endToken(pos, state);
252: checkLastSegment();
253: pos.setOffset(pos.getText().length());
254: endToken(pos, STATE_COMMENT);
255: return;
256: }
257: if (isOperatorChar(c)) {
258: if (state != PHP_STATE_OPERATOR) {
259: endToken(pos, state);
260: // Check for keyword (as in e.g. "char*") or var.
261: checkLastSegment();
262: state = PHP_STATE_OPERATOR;
263: }
264: pos.next();
265: continue;
266: }
267: if (c == '{' || c == '}') {
268: if (state != PHP_STATE_BRACE) {
269: endToken(pos, state);
270: // Check for keyword (e.g. "try") or var.
271: checkLastSegment();
272: state = PHP_STATE_BRACE;
273: }
274: pos.next();
275: continue;
276: }
277: if (state == PHP_STATE_OPERATOR || state == PHP_STATE_BRACE) {
278: if (mode.isIdentifierStart(c)) {
279: endToken(pos, state);
280: state = PHP_STATE_IDENTIFIER;
281: } else if (Character.isDigit(c)) {
282: endToken(pos, state);
283: state = PHP_STATE_NUMBER;
284: } else {
285: endToken(pos, state);
286: state = STATE_NEUTRAL;
287: }
288: pos.next();
289: continue;
290: }
291: if (state == PHP_STATE_IDENTIFIER) {
292: if (!mode.isIdentifierPart(c)) {
293: endToken(pos, state);
294: // Check for keyword or function.
295: LineSegment segment = getLastSegment();
296: if (segment != null) {
297: String segmentText = segment.getText();
298: if (isVar(segment.getText()))
299: segment.setFormat(PHP_FORMAT_VAR);
300: else if (isKeyword(segment.getText()))
301: segment.setFormat(PHP_FORMAT_KEYWORD);
302: else if (c == '(')
303: segment.setFormat(PHP_FORMAT_FUNCTION);
304: else if (Character.isWhitespace(c)) {
305: // Look ahead to see if next non-whitespace char is '('.
306: int j = pos.getOffset() + 1;
307: final String text = pos.getText();
308: final int limit = text.length();
309: while (j < limit
310: && Character.isWhitespace(c = text
311: .charAt(j)))
312: ++j;
313: if (c == '(')
314: segment.setFormat(PHP_FORMAT_FUNCTION);
315: }
316: }
317: state = STATE_NEUTRAL;
318: }
319: pos.next();
320: continue;
321: }
322: if (state == PHP_STATE_NUMBER) {
323: if (Character.isDigit(c))
324: ;
325: else if (c == 'u' || c == 'U' || c == 'l' || c == 'L')
326: ;
327: else if (pos.getOffset() - tokenBegin == 1 && c == 'x'
328: || c == 'X')
329: state = PHP_STATE_HEXNUMBER;
330: else {
331: endToken(pos, state);
332: if (mode.isIdentifierStart(c))
333: state = PHP_STATE_IDENTIFIER;
334: else
335: state = STATE_NEUTRAL;
336: }
337: pos.next();
338: continue;
339: }
340: if (state == PHP_STATE_HEXNUMBER) {
341: if (Character.isDigit(c))
342: ;
343: else if ((c >= 'a' && c <= 'f')
344: || (c >= 'A' && c <= 'F'))
345: ;
346: else if (c == 'u' || c == 'U' || c == 'l' || c == 'L')
347: ;
348: else {
349: endToken(pos, state);
350: if (mode.isIdentifierStart(c))
351: state = PHP_STATE_IDENTIFIER;
352: else
353: state = STATE_NEUTRAL;
354: }
355: pos.next();
356: continue;
357: }
358: if (state == STATE_NEUTRAL) {
359: if (c == '$' || mode.isIdentifierStart(c)) {
360: endToken(pos, state);
361: state = PHP_STATE_IDENTIFIER;
362: } else if (Character.isDigit(c)) {
363: endToken(pos, state);
364: state = PHP_STATE_NUMBER;
365: }
366: }
367: pos.next();
368: }
369: // Reached end of line.
370: endToken(pos, state);
371: if (state == PHP_STATE_IDENTIFIER) {
372: // Last token might be a keyword.
373: checkLastSegment();
374: }
375: }
376:
377: private void parseLineHTML(StringPosition pos, int state) {
378: if (state == 0)
379: state = PHP_STATE_IGNORE;
380: while (!pos.atEnd()) {
381: char c = pos.getChar();
382: switch (state) {
383: case PHP_STATE_HTML_COMMENT:
384: if (pos.lookingAt("-->")) {
385: pos.skip(3);
386: endToken(pos, state);
387: state = PHP_STATE_IGNORE;
388: } else
389: pos.next();
390: continue;
391: case PHP_STATE_IGNORE:
392: if (c == '<') {
393: endToken(pos, state);
394: if (pos.lookingAt("<?php")) {
395: pos.skip(2);
396: endToken(pos, PHP_STATE_TAG_STARTING);
397: pos.skip(3);
398: endToken(pos, PHP_STATE_TAG);
399: parseLinePHP(pos, STATE_NEUTRAL);
400: return;
401: }
402: if (pos.lookingAt("<?")) {
403: pos.skip(2);
404: endToken(pos, PHP_STATE_TAG_STARTING);
405: parseLinePHP(pos, STATE_NEUTRAL);
406: return;
407: }
408: if (pos.lookingAt("<!--")) {
409: pos.skip(4);
410: state = PHP_STATE_HTML_COMMENT;
411: continue;
412: }
413: // Otherwise it's the opening '<' of a normal HTML tag.
414: state = PHP_STATE_TAG_STARTING;
415: }
416: pos.next();
417: continue;
418: case PHP_STATE_TAG_STARTING:
419: if (!Character.isWhitespace(c)) {
420: endToken(pos, state);
421: state = PHP_STATE_TAG;
422: }
423: pos.next();
424: continue;
425: case PHP_STATE_TAG:
426: if (Character.isWhitespace(c)) {
427: endToken(pos, state);
428: state = PHP_STATE_ATTNAME;
429: pos.next();
430: } else if (c == '>') {
431: endToken(pos, state);
432: pos.next();
433: endToken(pos, PHP_STATE_TAG_ENDING);
434: state = PHP_STATE_IGNORE;
435: } else
436: pos.next();
437: continue;
438: case PHP_STATE_ATTNAME:
439: if (c == '=') {
440: endToken(pos, state);
441: pos.next();
442: state = PHP_STATE_EQUALS;
443: } else if (c == '>') {
444: endToken(pos, state);
445: pos.next();
446: endToken(pos, PHP_STATE_TAG_ENDING);
447: state = PHP_STATE_IGNORE;
448: } else
449: pos.next();
450: continue;
451: case PHP_STATE_EQUALS:
452: if (!Character.isWhitespace(c)) {
453: endToken(pos, state);
454: if (c == '"')
455: state = PHP_STATE_ATTVALUE_QUOTE;
456: else if (c == '\'')
457: state = PHP_STATE_ATTVALUE_SINGLEQUOTE;
458: else if (pos.lookingAt("<?php")) {
459: pos.skip(2);
460: endToken(pos, PHP_STATE_TAG_STARTING);
461: pos.skip(3);
462: endToken(pos, PHP_STATE_TAG);
463: parseLinePHP(pos, STATE_NEUTRAL,
464: PHP_STATE_ATTVALUE);
465: } else
466: state = PHP_STATE_ATTVALUE;
467: }
468: pos.next();
469: continue;
470: case PHP_STATE_ATTVALUE:
471: if (Character.isWhitespace(c)) {
472: endToken(pos, state);
473: pos.next();
474: state = PHP_STATE_ATTNAME;
475: } else if (c == '>') {
476: endToken(pos, state);
477: pos.next();
478: endToken(pos, PHP_STATE_TAG_ENDING);
479: state = PHP_STATE_IGNORE;
480: } else
481: pos.next();
482: continue;
483: case PHP_STATE_ATTVALUE_QUOTE:
484: if (pos.lookingAt("<?php")) {
485: endToken(pos, state);
486: pos.skip(2);
487: endToken(pos, PHP_STATE_TAG_STARTING);
488: pos.skip(3);
489: endToken(pos, PHP_STATE_TAG);
490: parseLinePHP(pos, STATE_NEUTRAL,
491: PHP_STATE_ATTVALUE_QUOTE);
492: } else {
493: pos.next();
494: if (c == '"') {
495: endToken(pos, state);
496: state = PHP_STATE_ATTNAME;
497: }
498: }
499: continue;
500: case PHP_STATE_ATTVALUE_SINGLEQUOTE:
501: if (pos.lookingAt("<?php")) {
502: endToken(pos, state);
503: pos.skip(2);
504: endToken(pos, PHP_STATE_TAG_STARTING);
505: pos.skip(3);
506: endToken(pos, PHP_STATE_TAG);
507: parseLinePHP(pos, STATE_NEUTRAL,
508: PHP_STATE_ATTVALUE_QUOTE);
509: } else {
510: pos.next();
511: if (c == '\'') {
512: endToken(pos, state);
513: state = PHP_STATE_ATTNAME;
514: }
515: }
516: continue;
517: default:
518: Log.error("state = " + state);
519: Debug.assertTrue(false);
520: break;
521: }
522: }
523: // Reached end of line.
524: endToken(pos, state);
525: }
526:
527: private void checkLastSegment() {
528: final LineSegment segment = getLastSegment();
529: if (segment != null) {
530: if (isVar(segment.getText()))
531: segment.setFormat(PHP_FORMAT_VAR);
532: else if (isKeyword(segment.getText()))
533: segment.setFormat(PHP_FORMAT_KEYWORD);
534: }
535: }
536:
537: public LineSegmentList formatLine(Line line) {
538: clearSegmentList();
539: if (line == null) {
540: addSegment("", PHP_FORMAT_TEXT);
541: return segmentList;
542: }
543: parseLine(line);
544: return segmentList;
545: }
546:
547: public boolean parseBuffer() {
548: Line line = buffer.getFirstLine();
549: if (line == null)
550: return false;
551: boolean changed = false;
552: final int newFlags = makeFlags(PHP_STATE_IGNORE, 0);
553: if (newFlags != line.flags()) {
554: line.setFlags(newFlags);
555: changed = true;
556: }
557: changed = parseBufferHTML(new Position(line, 0),
558: PHP_STATE_IGNORE, changed);
559: buffer.setNeedsParsing(false);
560: return changed;
561: }
562:
563: private boolean parseBufferPHP(Position pos, int state,
564: int htmlState, boolean changed) {
565: char quoteChar = '\0';
566: while (!pos.atEnd()) {
567: char c = pos.getChar();
568: if (c == EOL) {
569: if (pos.next()) {
570: final int newFlags = makeFlags(state, htmlState);
571: if (newFlags != pos.getLine().flags()) {
572: pos.getLine().setFlags(newFlags);
573: changed = true;
574: }
575: continue;
576: } else
577: break;
578: }
579: if (c == '\\') {
580: // Escape.
581: pos.skip(1);
582: pos.next();
583: continue;
584: }
585: if (state == STATE_COMMENT) {
586: if (c == '*' && pos.lookingAt("*/")) {
587: state = STATE_NEUTRAL;
588: pos.skip(2);
589: } else
590: pos.next();
591: continue;
592: }
593: if (state == STATE_QUOTE || state == STATE_SINGLEQUOTE) {
594: if (c == quoteChar) {
595: state = STATE_NEUTRAL;
596: quoteChar = '\0';
597: }
598: pos.next();
599: continue;
600: }
601: // Not in comment or quoted string.
602: if (c == '/') {
603: if (pos.lookingAt("//")) {
604: // Beginning of comment. Ignore rest of line.
605: pos.setOffset(pos.getLineLength());
606: } else if (pos.lookingAt("/*")) {
607: state = STATE_COMMENT;
608: pos.skip(2);
609: } else
610: pos.next();
611: continue;
612: }
613: if (c == '#') {
614: // Beginning of comment. Ignore rest of line.
615: Line next = pos.getNextLine();
616: if (next != null) {
617: pos.moveTo(next, 0);
618: continue;
619: } else
620: break;
621: }
622: if (c == '"') {
623: state = STATE_QUOTE;
624: quoteChar = c;
625: pos.next();
626: continue;
627: }
628: if (c == '\'') {
629: state = STATE_SINGLEQUOTE;
630: quoteChar = c;
631: pos.next();
632: continue;
633: }
634: if (c == '?' && pos.lookingAt("?>")) {
635: state = PHP_STATE_IGNORE;
636: pos.skip(2);
637: return parseBufferHTML(pos, state, changed);
638: }
639: // Otherwise...
640: pos.next();
641: }
642: return changed;
643: }
644:
645: private boolean parseBufferHTML(Position pos, int state,
646: boolean changed) {
647: while (!pos.atEnd()) {
648: char c = pos.getChar();
649: if (c == EOL) {
650: if (pos.next()) {
651: final int newFlags = makeFlags(state, 0);
652: if (newFlags != pos.getLine().flags()) {
653: pos.getLine().setFlags(newFlags);
654: changed = true;
655: }
656: continue;
657: } else
658: break;
659: }
660: if (c == '\\') {
661: // Escape.
662: pos.skip(1);
663: pos.next();
664: continue;
665: }
666: switch (state) {
667: case PHP_STATE_HTML_COMMENT:
668: if (c == '-' && pos.lookingAt("-->")) {
669: state = PHP_STATE_IGNORE;
670: pos.skip(3);
671: } else
672: pos.next();
673: continue;
674: case PHP_STATE_IGNORE:
675: if (c == '<') {
676: if (pos.lookingAt("<?php")) {
677: state = STATE_NEUTRAL;
678: pos.skip(5);
679: return parseBufferPHP(pos, state, 0, changed);
680: } else if (pos.lookingAt("<?")) {
681: state = STATE_NEUTRAL;
682: pos.skip(2);
683: return parseBufferPHP(pos, state, 0, changed);
684: } else if (pos.lookingAt("<!--")) {
685: state = PHP_STATE_HTML_COMMENT;
686: pos.skip(4);
687: } else {
688: // Otherwise it's the start of an HTML tag.
689: state = PHP_STATE_TAG;
690: pos.next();
691: }
692: } else
693: pos.next();
694: continue;
695: case PHP_STATE_TAG:
696: if (Character.isWhitespace(c))
697: state = PHP_STATE_ATTNAME;
698: else if (c == '>')
699: state = PHP_STATE_IGNORE;
700: pos.next();
701: continue;
702: case PHP_STATE_ATTNAME:
703: if (c == '=')
704: state = PHP_STATE_EQUALS;
705: else if (c == '>')
706: state = PHP_STATE_IGNORE;
707: pos.next();
708: continue;
709: case PHP_STATE_EQUALS:
710: if (!Character.isWhitespace(c)) {
711: if (c == '"')
712: state = PHP_STATE_ATTVALUE_QUOTE;
713: else if (c == '\'')
714: state = PHP_STATE_ATTVALUE_SINGLEQUOTE;
715: else if (pos.lookingAt("<?php")) {
716: pos.skip(5);
717: changed = parseBufferPHP(pos, STATE_NEUTRAL,
718: PHP_STATE_ATTNAME, changed);
719: state = PHP_STATE_ATTNAME;
720: continue;
721: } else
722: state = PHP_STATE_ATTVALUE;
723: }
724: pos.next();
725: continue;
726: case PHP_STATE_ATTVALUE:
727: pos.next();
728: if (Character.isWhitespace(c)) {
729: state = PHP_STATE_ATTNAME;
730: } else if (c == '>') {
731: state = PHP_STATE_IGNORE;
732: }
733: continue;
734: case PHP_STATE_ATTVALUE_QUOTE:
735: if (pos.lookingAt("<?php")) {
736: pos.skip(5);
737: changed = parseBufferPHP(pos, STATE_NEUTRAL,
738: PHP_STATE_ATTVALUE_QUOTE, changed);
739: } else {
740: pos.next();
741: if (c == '"')
742: state = PHP_STATE_ATTNAME;
743: }
744: continue;
745: case PHP_STATE_ATTVALUE_SINGLEQUOTE:
746: if (pos.lookingAt("<?php")) {
747: pos.skip(5);
748: changed = parseBufferPHP(pos, STATE_NEUTRAL,
749: PHP_STATE_ATTVALUE_SINGLEQUOTE, changed);
750: } else {
751: pos.next();
752: if (c == '\'')
753: state = PHP_STATE_ATTNAME;
754: }
755: continue;
756: default:
757: Debug.assertTrue(false);
758: break;
759: }
760: }
761: return changed;
762: }
763:
764: private static final int makeFlags(int state, int htmlState) {
765: return (htmlState << 8) + state;
766: }
767:
768: public static final int getState(int flags) {
769: return flags & 0xff;
770: }
771:
772: private static final int getHtmlState(int flags) {
773: return (flags & 0xff00) >> 8;
774: }
775:
776: private final boolean isVar(String s) {
777: return s.length() > 0 && s.charAt(0) == '$';
778: }
779:
780: private static final String opchars = "!&|<>=+/*-";
781:
782: private static final boolean isOperatorChar(char c) {
783: return opchars.indexOf(c) >= 0;
784: }
785:
786: public FormatTable getFormatTable() {
787: if (formatTable == null) {
788: formatTable = new FormatTable("PHPMode");
789: formatTable.addEntryFromPrefs(PHP_FORMAT_TEXT, "text");
790: formatTable
791: .addEntryFromPrefs(PHP_FORMAT_COMMENT, "comment");
792: formatTable.addEntryFromPrefs(PHP_FORMAT_STRING, "string");
793: formatTable.addEntryFromPrefs(PHP_FORMAT_IDENTIFIER,
794: "identifier", "text");
795: formatTable
796: .addEntryFromPrefs(PHP_FORMAT_KEYWORD, "keyword");
797: formatTable.addEntryFromPrefs(PHP_FORMAT_FUNCTION,
798: "function");
799: formatTable.addEntryFromPrefs(PHP_FORMAT_OPERATOR,
800: "operator");
801: formatTable.addEntryFromPrefs(PHP_FORMAT_BRACE, "brace");
802: formatTable.addEntryFromPrefs(PHP_FORMAT_NUMBER, "number");
803: formatTable.addEntryFromPrefs(PHP_FORMAT_VAR, "var");
804: formatTable.addEntryFromPrefs(PHP_FORMAT_DELIMITER,
805: "delimiter");
806: formatTable.addEntryFromPrefs(PHP_FORMAT_TAG, "tag");
807: formatTable.addEntryFromPrefs(PHP_FORMAT_ATTRIBUTE,
808: "attribute");
809: formatTable.addEntryFromPrefs(PHP_FORMAT_EQUALS, "equals");
810: }
811: return formatTable;
812: }
813: }
|