001: /*
002: * Expansion.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: * $Id: Expansion.java,v 1.4 2003/10/13 12:13:19 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.util.ArrayList;
025: import java.util.List;
026: import javax.swing.undo.CompoundEdit;
027:
028: public class Expansion implements Constants {
029: protected String prefix;
030: protected int prefixOffset; // Offset of prefix on current line.
031:
032: protected List candidates;
033: protected int last = -1;
034:
035: private static Expansion lastExpansion;
036:
037: protected Position savedDot;
038: protected String savedText;
039:
040: private final Mode mode;
041:
042: private String current;
043: private boolean forceLowerCase;
044:
045: // For MailAddressExpansion.
046: protected Expansion() {
047: mode = Editor.getModeList().getMode(PLAIN_TEXT_MODE);
048: }
049:
050: public Expansion(Position dot, Mode mode) {
051: savedDot = new Position(dot);
052: this .mode = mode;
053: final Line dotLine = dot.getLine();
054: final int dotOffset = dot.getOffset();
055: savedText = dotLine.getText();
056: // Get word before caret.
057: int begin = dotOffset - 1;
058: if (begin < 0)
059: return;
060: final int end = dotOffset;
061: while (begin > 0
062: && mode.isIdentifierPart(dotLine.charAt(begin)))
063: --begin;
064: if (!mode.isIdentifierPart(dotLine.charAt(begin)))
065: ++begin;
066: if (begin == end)
067: return;
068: prefix = dotLine.substring(begin, end);
069: prefixOffset = begin;
070: Position pos = new Position(dotLine, begin);
071: current = pos.getIdentifier(mode);
072: int modeId = mode.getId();
073: if (modeId == LISP_MODE || modeId == LISP_SHELL_MODE)
074: if (Utilities.isLowerCase(prefix))
075: forceLowerCase = true;
076: candidates = list(pos);
077: }
078:
079: public Expansion(Buffer buffer, String prefix, String current) {
080: this (buffer, prefix, current, null);
081: }
082:
083: public Expansion(Buffer buffer, String prefix, String current,
084: Position pos) {
085: mode = buffer.getMode();
086: this .prefix = prefix;
087: this .current = current;
088: if (buffer.getFirstLine() == null)
089: return;
090: if (pos == null)
091: pos = new Position(buffer.getFirstLine(), 0);
092: candidates = list(pos);
093: }
094:
095: private List list(Position pos) {
096: List list = new ArrayList();
097: if (prefix != null) {
098: final boolean ignoreCase = Utilities.isLowerCase(prefix);
099: String s = null;
100: // Search backwards on current line.
101: Line line = pos.getLine();
102: final int begin = pos.getOffset();
103: int index = begin - 1;
104: if (index >= 0) {
105: s = line.substring(0, begin);
106: if (ignoreCase)
107: s = s.toLowerCase();
108: while ((index = s.lastIndexOf(prefix, index)) >= 0) {
109: maybeAddCandidate(list, new Position(line, index));
110: --index;
111: }
112: }
113: // Search backwards to start of buffer.
114: for (line = pos.getLine().previous(); line != null; line = line
115: .previous()) {
116: index = line.length();
117: s = ignoreCase ? line.getText().toLowerCase() : line
118: .getText();
119: while ((index = s.lastIndexOf(prefix, index)) >= 0) {
120: maybeAddCandidate(list, new Position(line, index));
121: --index;
122: }
123: }
124: // Search forwards from current line to end of buffer. Search current
125: // line again to pick up possible matches to right of dot.
126: for (line = pos.getLine(); line != null; line = line.next()) {
127: index = 0;
128: s = ignoreCase ? line.getText().toLowerCase() : line
129: .getText();
130: while ((index = s.indexOf(prefix, index)) >= 0) {
131: maybeAddCandidate(list, new Position(line, index));
132: ++index;
133: }
134: }
135: }
136: return list;
137: }
138:
139: private void maybeAddCandidate(List list, Position where) {
140: final Line line = where.getLine();
141: final int offset = where.getOffset();
142: if (offset == 0
143: || !mode.isIdentifierPart(line.charAt(offset - 1))) {
144: final String candidate = where.getIdentifier(mode);
145: maybeAddCandidate(list, candidate);
146: }
147: }
148:
149: private void maybeAddCandidate(List list, String candidate) {
150: // We don't want what we started with.
151: if (candidate.equals(current))
152: return;
153: if (forceLowerCase)
154: candidate = candidate.toLowerCase();
155: for (int i = list.size(); i-- > 0;) {
156: if (candidate.equals(list.get(i))) {
157: // It's already in the list.
158: return;
159: }
160: }
161: // Not found.
162: list.add(candidate);
163: }
164:
165: public void appendCandidates(List list) {
166: final int size = list.size();
167: for (int i = 0; i < size; i++)
168: maybeAddCandidate(candidates, (String) list.get(i));
169: }
170:
171: public String getNextCandidate() {
172: if (candidates == null || candidates.size() == 0)
173: return null;
174: int index = last + 1;
175: if (index == candidates.size())
176: index = 0;
177: last = index;
178: return (String) candidates.get(index);
179: }
180:
181: private final int getPrefixOffset() {
182: return prefixOffset;
183: }
184:
185: public final String getPrefix() {
186: return prefix;
187: }
188:
189: public final String getCurrent() {
190: return current;
191: }
192:
193: public final List getCandidates() {
194: return candidates;
195: }
196:
197: public void undo(Editor editor) {
198: final Buffer buffer = editor.getBuffer();
199: try {
200: buffer.lockWrite();
201: } catch (InterruptedException e) {
202: Log.error(e);
203: return;
204: }
205: try {
206: editor.addUndo(SimpleEdit.LINE_EDIT);
207: editor.getDotLine().setText(savedText);
208: editor.getBuffer().modified();
209: editor.getDot().moveTo(savedDot);
210: editor.getDisplay().moveCaretToDotCol();
211: Editor.updateInAllEditors(editor.getDotLine());
212: } finally {
213: buffer.unlockWrite();
214: }
215: }
216:
217: public static Expansion getLastExpansion() {
218: return lastExpansion;
219: }
220:
221: public static void setLastExpansion(Expansion expansion) {
222: lastExpansion = expansion;
223: }
224:
225: public static void expand() {
226: final Editor editor = Editor.currentEditor();
227: if (editor.getLastCommand() == COMMAND_EXPAND)
228: expand(editor, Expansion.getLastExpansion(), true);
229: else {
230: Expansion e = editor.getBuffer().getExpansion(
231: editor.getDot());
232: Expansion.setLastExpansion(e);
233: expand(editor, e, false);
234: }
235: }
236:
237: private static void expand(Editor editor, Expansion expansion,
238: boolean again) {
239: final Buffer buffer = editor.getBuffer();
240: final String candidate = expansion.getNextCandidate();
241: if (candidate == null)
242: return;
243: try {
244: buffer.lockWrite();
245: } catch (InterruptedException e) {
246: Log.error(e);
247: return;
248: }
249: try {
250: if (again)
251: editor.undo();
252:
253: CompoundEdit compoundEdit = buffer.beginCompoundEdit();
254: editor.addUndo(SimpleEdit.MOVE);
255: editor.getDot().setOffset(expansion.getPrefixOffset());
256:
257: // Remove prefix from line.
258: final Line line = editor.getDotLine();
259: final int offset = editor.getDotOffset();
260: String head = line.substring(0, offset);
261: String tail = line.substring(offset
262: + expansion.getPrefix().length());
263: editor.addUndo(SimpleEdit.LINE_EDIT);
264: line.setText(head.concat(tail));
265:
266: editor.addUndo(SimpleEdit.INSERT_STRING);
267: editor.insertStringInternal(candidate);
268:
269: editor.moveCaretToDotCol();
270: buffer.endCompoundEdit(compoundEdit);
271: Editor.updateInAllEditors(line);
272: editor.setCurrentCommand(COMMAND_EXPAND);
273: } finally {
274: buffer.unlockWrite();
275: }
276: }
277: }
|