001: /*
002: * TclMode.java
003: *
004: * Copyright (C) 2002 Peter Graves
005: * $Id: TclMode.java,v 1.1.1.1 2002/09/24 16:08: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: import java.awt.event.KeyEvent;
025:
026: public final class TclMode extends AbstractMode implements Constants,
027: Mode {
028: private static final int STATE_NEUTRAL = 0;
029: private static final int STATE_SINGLEQUOTE = 1;
030: private static final int STATE_DOUBLEQUOTE = 2;
031:
032: private static final TclMode mode = new TclMode();
033:
034: private TclMode() {
035: super (TCL_MODE, TCL_MODE_NAME);
036: keywords = new Keywords(this );
037: }
038:
039: public static TclMode getMode() {
040: return mode;
041: }
042:
043: public String getCommentStart() {
044: return "# ";
045: }
046:
047: public Formatter getFormatter(Buffer buffer) {
048: return new TclFormatter(buffer);
049: }
050:
051: protected void setKeyMapDefaults(KeyMap km) {
052: km.mapKey('{', "electricOpenBrace");
053: km.mapKey('}', "electricCloseBrace");
054: km.mapKey(KeyEvent.VK_TAB, CTRL_MASK, "insertTab");
055: km.mapKey(KeyEvent.VK_TAB, 0, "tab");
056: km.mapKey(KeyEvent.VK_ENTER, 0, "newlineAndIndent");
057: km.mapKey(KeyEvent.VK_T, CTRL_MASK, "findTag");
058: km.mapKey(KeyEvent.VK_PERIOD, ALT_MASK, "findTagAtDot");
059: km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK | SHIFT_MASK,
060: "insertBraces");
061: // Duplicate mapping for 1.4.
062: km.mapKey(KeyEvent.VK_BRACELEFT, CTRL_MASK | SHIFT_MASK,
063: "insertBraces");
064: km.mapKey(KeyEvent.VK_F12, 0, "wrapComment");
065: // Duplicate mapping to support IBM 1.3 for Linux.
066: km.mapKey(0xffc9, 0, "wrapComment"); // F12
067: }
068:
069: public boolean isTaggable() {
070: return true;
071: }
072:
073: public Tagger getTagger(SystemBuffer buffer) {
074: return new TclTagger(buffer);
075: }
076:
077: public boolean canIndent() {
078: return true;
079: }
080:
081: public int getCorrectIndentation(Line line, Buffer buffer) {
082: final int indentSize = buffer.getIndentSize();
083:
084: // Is the current line a continuation line?
085: Line previous = line.previous();
086: if (previous == null)
087: return 0;
088: if (isContinued(previous)) {
089: // The current line is a continuation line. We need to find the
090: // first line in the sequence of continued lines.
091: while (true) {
092: Line maybe = previous.previous();
093: if (maybe != null && isContinued(maybe))
094: previous = maybe;
095: else
096: break;
097: }
098: return buffer.getIndentation(previous) + indentSize;
099: }
100:
101: // Current line is not a continuation line.
102: final String trim = line.getText().trim();
103: if (trim.startsWith("}")) {
104: Position pos = matchClosingBrace(new Position(line, 0));
105: return buffer.getIndentation(pos.getLine());
106: }
107: final Line model = findModel(line);
108: if (model == null)
109: return 0;
110: final int modelIndent = buffer.getIndentation(model);
111: final String modelTrim = trimSyntacticWhitespace(model);
112:
113: if (modelTrim.endsWith("{")) {
114: if (buffer.getBooleanProperty(Property.INDENT_AFTER_BRACE))
115: return modelIndent + indentSize;
116: else
117: return modelIndent;
118: }
119:
120: return modelIndent;
121: }
122:
123: private static Line findModel(Line line) {
124: Line model = line.previous();
125: if (model == null)
126: return null;
127: // Check for continuation line.
128: if (isContinued(model)) {
129: while (true) {
130: Line previous = model.previous();
131: if (previous != null && isContinued(previous))
132: model = previous;
133: else
134: break;
135: }
136: return model;
137: }
138: // Otherwise any non-blank line will do.
139: while (model != null && model.isBlank())
140: model = model.previous();
141: if (model == null)
142: return null;
143: // If the model we've found is itself a continuation line, we need to
144: // find the first line in the sequence of continued lines.
145: while (true) {
146: Line previous = model.previous();
147: if (previous != null && isContinued(previous))
148: model = previous;
149: else
150: break;
151: }
152: return model;
153: }
154:
155: private static boolean isContinued(Line line) {
156: return line.getText().endsWith("\\");
157: }
158:
159: private static Position matchClosingBrace(Position start) {
160: int count = 1;
161: TclSyntaxIterator it = new TclSyntaxIterator(start);
162: char c;
163: while ((c = it.prevChar()) != SyntaxIterator.DONE) {
164: if (c == '}')
165: ++count;
166: else if (c == '{') {
167: --count;
168: if (count == 0) // Found it!
169: break;
170: }
171: }
172: return it.getPosition();
173: }
174:
175: // Replaces syntactic whitespace (quotes and comments) with actual space
176: // characters and returns trimmed string.
177: private static String trimSyntacticWhitespace(Line line) {
178: TclSyntaxIterator it = new TclSyntaxIterator(null);
179: return new String(it.hideSyntacticWhitespace(line.getText()))
180: .trim();
181: }
182:
183: public boolean isIdentifierStart(char c) {
184: return !Character.isWhitespace(c);
185: }
186:
187: public boolean isIdentifierPart(char c) {
188: return !Character.isWhitespace(c);
189: }
190: }
|