001: /*
002: * PythonIndenter.java
003: *
004: * Copyright (C) 2002-2003 Peter Graves
005: * $Id: PythonIndenter.java,v 1.3 2003/10/30 19:25:30 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 PythonIndenter {
025: private static final PythonMode mode = PythonMode.getMode();
026:
027: private final Line line;
028: private final Buffer buffer;
029: private final int indentSize;
030:
031: public PythonIndenter(Line line, Buffer buffer) {
032: this .line = line;
033: this .buffer = buffer;
034: indentSize = buffer.getIndentSize();
035: }
036:
037: public int getCorrectIndentation() {
038: final Line model = findModel(line);
039: if (model == null)
040: return 0;
041: final int modelIndent = buffer.getIndentation(model);
042: // Keep same indentation if model is comment or docstring. Model is
043: // guaranteed not to be blank so charAt(0) is safe.
044: switch (model.getText().trim().charAt(0)) {
045: case '#':
046: case '\'':
047: case '"':
048: return modelIndent;
049: default:
050: break;
051: }
052: final String modelText = trimSyntacticWhitespace(model
053: .getText());
054: if (modelText.length() == 0)
055: return 0; // Shouldn't happen.
056: // Indent after '{', '(' or '['.
057: char c = modelText.charAt(modelText.length() - 1);
058: if (c == '{' || c == '(' || c == '[')
059: return modelIndent + indentSize;
060: final String modelFirst = getFirstIdentifier(modelText);
061: if (c == ':') {
062: final String[] indentAfter = { "class", "def", "if",
063: "else", "elif", "for", "while", "try", "except",
064: "finally" };
065: if (Utilities.isOneOf(modelFirst, indentAfter))
066: return modelIndent + indentSize;
067: }
068: final String lineText = trimSyntacticWhitespace(line.getText());
069: final String lineFirst = getFirstIdentifier(lineText);
070: if (lineFirst.equals("class"))
071: return 0;
072: if (lineFirst.equals("def"))
073: return indentDef();
074: // Unindent after "break", "continue", "return" and "pass".
075: final String[] unindentAfter = { "break", "continue", "return",
076: "pass" };
077: if (Utilities.isOneOf(modelFirst, unindentAfter))
078: return Math.max(0, modelIndent - indentSize);
079: // Unindent if the current line starts with "else", "elif", "except" or
080: // "finally".
081: final String[] unindent = { "else", "elif", "except", "finally" };
082: if (Utilities.isOneOf(lineFirst, unindent))
083: return Math.max(0, modelIndent - indentSize);
084: return modelIndent;
085: }
086:
087: // Scan backwards for line starting with "def" or "class" and indent
088: // accordingly.
089: private int indentDef() {
090: for (Line model = line.previous(); model != null; model = model
091: .previous()) {
092: String modelFirst = getFirstIdentifier(model);
093: if (modelFirst.equals("def"))
094: return buffer.getIndentation(model);
095: if (modelFirst.equals("class"))
096: return buffer.getIndentation(model)
097: + buffer.getIndentSize();
098: }
099: return 0;
100: }
101:
102: // Return last non-blank line before this one.
103: private static Line findModel(Line line) {
104: for (Line model = line.previous(); model != null; model = model
105: .previous()) {
106: if (!model.isBlank())
107: return model;
108: }
109: return null;
110: }
111:
112: // Replace syntactic whitespace (quotes and comments) with actual space
113: // characters and return trimmed string.
114: private static String trimSyntacticWhitespace(String s) {
115: PythonSyntaxIterator it = new PythonSyntaxIterator(null);
116: return new String(it.hideSyntacticWhitespace(s)).trim();
117: }
118:
119: // Never returns null.
120: private static String getFirstIdentifier(Line line) {
121: return getFirstIdentifier(trimSyntacticWhitespace(line
122: .getText()));
123: }
124:
125: // Never returns null.
126: private static String getFirstIdentifier(String s) {
127: FastStringBuffer sb = new FastStringBuffer();
128: final int length = s.length();
129: int i = 0;
130: while (i < length && !mode.isIdentifierStart(s.charAt(i)))
131: ++i;
132: char c;
133: while (i < length && mode.isIdentifierPart(c = s.charAt(i))) {
134: sb.append(c);
135: ++i;
136: }
137: return sb.toString();
138: }
139: }
|