0001: /*
0002: * LispMode.java
0003: *
0004: * Copyright (C) 1998-2004 Peter Graves
0005: * $Id: LispMode.java,v 1.82 2004/09/13 13:49:09 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.j;
0023:
0024: import java.awt.event.KeyEvent;
0025: import java.util.HashMap;
0026: import java.util.StringTokenizer;
0027: import org.armedbear.lisp.Interpreter;
0028: import org.armedbear.lisp.Lisp;
0029: import org.armedbear.lisp.LispObject;
0030:
0031: public class LispMode extends AbstractMode implements Constants, Mode {
0032: private static final LispMode mode = new LispMode();
0033:
0034: private LispMode() {
0035: super (LISP_MODE, LISP_MODE_NAME);
0036: keywords = new Keywords(this );
0037: setProperty(Property.INDENT_SIZE, 2);
0038: setProperty(Property.HIGHLIGHT_BRACKETS, true);
0039: }
0040:
0041: protected LispMode(int id, String displayName) {
0042: super (id, displayName);
0043: }
0044:
0045: public static Mode getMode() {
0046: return mode;
0047: }
0048:
0049: public String getCommentStart() {
0050: return ";; ";
0051: }
0052:
0053: public final SyntaxIterator getSyntaxIterator(Position pos) {
0054: return new LispSyntaxIterator(pos);
0055: }
0056:
0057: public Formatter getFormatter(Buffer buffer) {
0058: return new LispFormatter(buffer);
0059: }
0060:
0061: protected void setKeyMapDefaults(KeyMap km) {
0062: km.mapKey(KeyEvent.VK_TAB, 0, "tab");
0063: km.mapKey(KeyEvent.VK_TAB, CTRL_MASK, "insertTab");
0064: km.mapKey(KeyEvent.VK_F12, 0, "wrapComment");
0065: km.mapKey(KeyEvent.VK_ENTER, 0, "newlineAndIndent");
0066: km.mapKey(KeyEvent.VK_T, CTRL_MASK, "findTag");
0067: km.mapKey(KeyEvent.VK_PERIOD, ALT_MASK, "findTagAtDot");
0068: km.mapKey(KeyEvent.VK_COMMA, ALT_MASK, "listMatchingTagsAtDot");
0069: km.mapKey(KeyEvent.VK_PERIOD, CTRL_MASK | ALT_MASK,
0070: "findTagAtDotOtherWindow");
0071: km.mapKey(')', "closeParen");
0072: km.mapKey(KeyEvent.VK_F1, ALT_MASK, "hyperspec");
0073: km.mapKey(KeyEvent.VK_F, CTRL_MASK | ALT_MASK, "forwardSexp");
0074: km.mapKey(KeyEvent.VK_B, CTRL_MASK | ALT_MASK, "backwardSexp");
0075: km.mapKey(KeyEvent.VK_D, CTRL_MASK | ALT_MASK, "downList");
0076: km
0077: .mapKey(KeyEvent.VK_U, CTRL_MASK | ALT_MASK,
0078: "backwardUpList");
0079: km.mapKey(KeyEvent.VK_X, CTRL_MASK | ALT_MASK, "evalDefunLisp");
0080: km.mapKey(KeyEvent.VK_C, CTRL_MASK | ALT_MASK,
0081: "compileDefunLisp");
0082: km
0083: .mapKey(KeyEvent.VK_R, CTRL_MASK | ALT_MASK,
0084: "evalRegionLisp");
0085: km.mapKey(KeyEvent.VK_M, CTRL_MASK, "lispFindMatchingChar");
0086: km.mapKey(KeyEvent.VK_M, CTRL_MASK | SHIFT_MASK,
0087: "lispSelectSyntax");
0088: km.mapKey(KeyEvent.VK_9, CTRL_MASK | SHIFT_MASK,
0089: "insertParentheses");
0090: km.mapKey(KeyEvent.VK_0, CTRL_MASK | SHIFT_MASK,
0091: "movePastCloseAndReindent");
0092: }
0093:
0094: public void populateModeMenu(Editor editor, Menu menu) {
0095: boolean enabled = LispShell.findLisp(null) != null;
0096: if (isSlimeLoaded()) {
0097: menu.add(editor, "Eval Region", 'R',
0098: "(slime:slime-eval-region)", enabled);
0099: menu.add(editor, "Eval Defun", 'D',
0100: "(slime:slime-eval-defun)", enabled);
0101: menu.add(editor, "Compile Defun", 'C',
0102: "(slime:slime-compile-defun)", enabled);
0103: menu.add(editor, "Load File", 'L',
0104: "(slime:slime-load-file)", enabled);
0105: menu.add(editor, "Compile File", 'F',
0106: "(slime:slime-compile-file)", enabled);
0107: menu.add(editor, "Compile and Load File", 'A',
0108: "(slime:slime-compile-and-load-file)", enabled);
0109: } else {
0110: menu.add(editor, "Eval Region", 'R', "evalRegionLisp",
0111: enabled);
0112: menu.add(editor, "Eval Defun", 'D', "evalDefunLisp",
0113: enabled);
0114: menu.add(editor, "Compile Defun", 'C', "compileDefunLisp",
0115: enabled);
0116: menu.add(editor, "Load File", 'L', "loadLispFile", enabled);
0117: menu.add(editor, "Compile File", 'F', "compileLispFile",
0118: enabled);
0119: menu.add(editor, "Compile and Load File", 'A',
0120: "compileAndLoadLispFile", enabled);
0121: }
0122: }
0123:
0124: private static final boolean isSlimeLoaded() {
0125: if (Editor.isLispInitialized()) {
0126: try {
0127: LispObject result = Interpreter
0128: .evaluate("(sys:featurep :slime)");
0129: return (result != Lisp.NIL) ? true : false;
0130: } catch (Throwable t) {
0131: Log.debug(t);
0132: }
0133: }
0134: return false;
0135: }
0136:
0137: public boolean isTaggable() {
0138: return true;
0139: }
0140:
0141: public Tagger getTagger(SystemBuffer buffer) {
0142: return new LispTagger(buffer);
0143: }
0144:
0145: private static final String validChars = "!$%&*+-./0123456789<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{}~";
0146:
0147: public final boolean isIdentifierStart(char c) {
0148: return validChars.indexOf(c) >= 0;
0149: }
0150:
0151: public final boolean isIdentifierPart(char c) {
0152: return validChars.indexOf(c) >= 0;
0153: }
0154:
0155: private static final HashMap definers = new HashMap();
0156:
0157: static {
0158: String[] strings = new String[] { "defclass", "defconstant",
0159: "defgeneric", "define-condition", "defmacro",
0160: "defmethod", "defparameter", "defstruct", "deftype",
0161: "defun", "defvar" };
0162: for (int i = strings.length; i-- > 0;)
0163: definers.put(strings[i], strings[i]);
0164: // SBCL
0165: definers.put("def!struct", "defstruct");
0166: definers.put("defmacro-mundanely", "defmacro");
0167: definers.put("def!macro", "defmacro");
0168: // Slime
0169: definers.put("defslimefun", "defun");
0170: definers.put("definterface", "defgeneric");
0171: definers.put("defimplementation", "defmethod");
0172: }
0173:
0174: public static final String translateDefiner(String s) {
0175: if (s.length() >= 5 && s.startsWith("def"))
0176: return (String) definers.get(s);
0177: return null;
0178: }
0179:
0180: public boolean isInQuote(Buffer buffer, Position pos) {
0181: // This implementation only considers the current line.
0182: final Line line = pos.getLine();
0183: final int offset = pos.getOffset();
0184: boolean inQuote = false;
0185: for (int i = 0; i < offset; i++) {
0186: char c = line.charAt(i);
0187: if (c == '\\') {
0188: // Escape.
0189: ++i;
0190: } else if (inQuote) {
0191: if (c == '"')
0192: inQuote = false;
0193: } else if (c == '"') {
0194: inQuote = true;
0195: }
0196: }
0197: return inQuote;
0198: }
0199:
0200: public boolean canIndent() {
0201: return true;
0202: }
0203:
0204: private final String[] specials = new String[] { "block", "case",
0205: "catch", "do-all-symbols", "do-external-symbols",
0206: "do-symbols", "dolist", "dotimes", "ecase", "etypecase",
0207: "eval-when", "flet", "handler-bind", "labels", "lambda",
0208: "let", "let*", "locally", "loop", "macrolet",
0209: "multiple-value-bind", "progn", "typecase", "unless",
0210: "when" };
0211:
0212: private final String[] elispSpecials = new String[] { "while" };
0213:
0214: private final String[] hemlockSpecials = new String[] { "frob",
0215: "with-mark" };
0216:
0217: public int getCorrectIndentation(Line line, Buffer buffer) {
0218: final Line model = findModel(line);
0219: if (model == null)
0220: return 0;
0221: final int modelIndent = buffer.getIndentation(model);
0222: final String modelTrim = model.trim();
0223: if (line.flags() == STATE_QUOTE) {
0224: if (modelTrim.length() > 0 && modelTrim.charAt(0) == '"')
0225: return modelIndent + 1;
0226: else
0227: return modelIndent;
0228: }
0229: if (modelTrim.length() == 0)
0230: return 0;
0231: if (modelTrim.charAt(0) == ';')
0232: return modelIndent;
0233: final int indentSize = buffer.getIndentSize();
0234: Position here = new Position(line, 0);
0235: Position pos = findContainingSexp(here);
0236: if (pos == null) // Top level.
0237: return 0;
0238: Debug.bugIfNot(pos.getChar() == '(');
0239: int offset = pos.getOffset();
0240: if (offset > 1) {
0241: if (new Position(pos.getLine(), offset - 2)
0242: .lookingAt("'#("))
0243: return buffer.getCol(pos) + 1;
0244: }
0245: if (offset > 0) {
0246: if (pos.getLine().charAt(offset - 1) == '\'')
0247: return buffer.getCol(pos) + 1;
0248: }
0249: Position posFirst = downList(pos);
0250: if (posFirst != null) {
0251: if (posFirst.equals(here))
0252: return buffer.getCol(pos) + 1;
0253: char firstChar = posFirst.getChar();
0254: if (firstChar == '(' || firstChar == ',') {
0255: // First element of containing sexp is a list or backquote
0256: // expansion.
0257: return buffer.getCol(posFirst);
0258: }
0259: // Otherwise...
0260: String token = gatherToken(posFirst);
0261: if (token.equals("do") || token.equals("do*")) {
0262: // Skip DO/DO*.
0263: Position p1 = forwardSexp(posFirst);
0264: if (p1 != null) {
0265: // Skip whitespace to get to opening '(' of variable list.
0266: p1.skipWhitespace();
0267: // Skip past variable list.
0268: Position p2 = forwardSexp(p1);
0269: if (p2 != null) {
0270: // Skip past end test form.
0271: p2 = forwardSexp(p2);
0272: // Make sure line numbers are right for isBefore().
0273: if (buffer.needsRenumbering())
0274: buffer.renumber();
0275: if (p2 != null && here.isBefore(p2)) {
0276: // This is the end test form. Indent it under the
0277: // opening '(' of the variable list
0278: return buffer.getCol(p1);
0279: }
0280: }
0281: }
0282: return buffer.getCol(pos) + indentSize;
0283: }
0284: if (token.equals("handler-case")) {
0285: Position p1 = forwardSexp(posFirst);
0286: if (p1 != null) {
0287: // Skip whitespace to get to opening '(' of form to be
0288: // evaluated.
0289: p1.skipWhitespace();
0290: // Make sure line numbers are right for isBefore().
0291: if (buffer.needsRenumbering())
0292: buffer.renumber();
0293: if (here.isBefore(p1))
0294: return buffer.getCol(pos) + indentSize * 2;
0295: }
0296: return buffer.getCol(pos) + indentSize;
0297: }
0298: if (token.startsWith("def")
0299: || Utilities.isOneOf(token, specials)
0300: || Utilities.isOneOf(token, elispSpecials)
0301: || Utilities.isOneOf(token, hemlockSpecials)
0302: || token.startsWith("with-"))
0303: return buffer.getCol(pos) + indentSize;
0304: // Not special. Indent under the second element of the containing
0305: // list, if the second element is on the same line as the first.
0306: Position posSecond = forwardSexp(posFirst);
0307: if (posSecond != null) {
0308: posSecond.skipWhitespace();
0309: if (posSecond.getChar() != ';')
0310: if (posSecond.getLine() == pos.getLine())
0311: return buffer.getCol(posSecond);
0312: }
0313: }
0314: return buffer.getCol(pos) + 1;
0315: }
0316:
0317: private static Line findModel(Line line) {
0318: Line model = line.previous();
0319: if (line.flags() == STATE_COMMENT) {
0320: // Any non-blank line is an acceptable model.
0321: while (model != null && model.isBlank())
0322: model = model.previous();
0323: } else {
0324: while (model != null) {
0325: if (isAcceptableModel(model))
0326: break; // Found an acceptable model.
0327: else
0328: model = model.previous();
0329: }
0330: }
0331: return model;
0332: }
0333:
0334: private static boolean isAcceptableModel(Line model) {
0335: String trim = model.trim();
0336: if (trim.length() == 0)
0337: return false;
0338: if (trim.charAt(0) == ';')
0339: return false;
0340: return true;
0341: }
0342:
0343: private String gatherToken(Position start) {
0344: Position pos = start.copy();
0345: FastStringBuffer sb = new FastStringBuffer();
0346: while (true) {
0347: char c = pos.getChar();
0348: if (Character.isWhitespace(c))
0349: break;
0350: sb.append(c);
0351: if (!pos.next())
0352: break;
0353: }
0354: return sb.toString();
0355: }
0356:
0357: private static Position findStartOfDefun(Position pos) {
0358: Line line = pos.getLine();
0359: while (true) {
0360: if (line.getText().startsWith("(def"))
0361: return new Position(line, 0);
0362: Line prev = line.previous();
0363: if (prev == null)
0364: return new Position(line, 0);
0365: line = prev;
0366: }
0367: }
0368:
0369: public static Position findContainingSexp(Position start) {
0370: LispSyntaxIterator it = new LispSyntaxIterator(start);
0371: int parenCount = 0;
0372: while (true) {
0373: switch (it.prevChar()) {
0374: case ')':
0375: ++parenCount;
0376: break;
0377: case '(':
0378: if (parenCount == 0) {
0379: // Found unmatched '('.
0380: return it.getPosition();
0381: }
0382: --parenCount;
0383: break;
0384: case SyntaxIterator.DONE:
0385: return null;
0386: default:
0387: break;
0388: }
0389: }
0390: }
0391:
0392: private Position downList(Position start) {
0393: if (start == null)
0394: return null;
0395: Position pos = start.copy();
0396: // Skip whitespace and comments.
0397: char c;
0398: while (true) {
0399: pos.skipWhitespace();
0400: c = pos.getChar();
0401: if (c == ';')
0402: skipComment(pos);
0403: else
0404: break;
0405: }
0406: // Reached non-whitespace char.
0407: while (true) {
0408: if (c == ')')
0409: return null; // "Containing expression ends prematurely."
0410: if (c == '(') {
0411: // List starting.
0412: if (!pos.next())
0413: return null;
0414: // Skip whitespace and comments.
0415: while (true) {
0416: pos.skipWhitespace();
0417: c = pos.getChar();
0418: if (c == ';')
0419: skipComment(pos);
0420: else
0421: break;
0422: }
0423: if (pos.atEnd())
0424: return null;
0425: return pos;
0426: }
0427: if (c == '"') {
0428: // Skip string.
0429: skipString(pos);
0430: if (pos.atEnd())
0431: return null;
0432: continue;
0433: }
0434: if (c == ';') {
0435: skipComment(pos);
0436: if (pos.atEnd())
0437: return null;
0438: continue;
0439: }
0440: if (!pos.next())
0441: return null;
0442: c = pos.getChar();
0443: }
0444: }
0445:
0446: public static void downList() {
0447: final Editor editor = Editor.currentEditor();
0448: if (editor.getMode() instanceof LispMode) {
0449: Position pos = mode.downList(editor.getDot());
0450: if (pos != null)
0451: editor.moveDotTo(pos);
0452: }
0453: }
0454:
0455: public static void backwardUpList() {
0456: final Editor editor = Editor.currentEditor();
0457: if (editor.getMode() instanceof LispMode) {
0458: Position pos = findContainingSexp(editor.getDot());
0459: if (pos != null)
0460: editor.moveDotTo(pos);
0461: }
0462: }
0463:
0464: private void skipString(Position pos) {
0465: while (true) {
0466: if (!pos.next())
0467: return;
0468: switch (pos.getChar()) {
0469: case '\\':
0470: if (!pos.next())
0471: return;
0472: break;
0473: case '"':
0474: pos.next();
0475: return;
0476: }
0477: }
0478: }
0479:
0480: private Position forwardSexp(Position start) {
0481: if (start == null)
0482: return null;
0483: Position pos = start.copy();
0484: // Skip whitespace and comments.
0485: char c;
0486: while (true) {
0487: pos.skipWhitespace();
0488: c = pos.getChar();
0489: if (c == ';')
0490: skipComment(pos);
0491: else
0492: break;
0493: }
0494: // Reached non-whitespace char.
0495: if (c == ')')
0496: return null; // "Containing expression ends prematurely."
0497: if (c == '(') {
0498: // List starting.
0499: int parenCount = 1;
0500: while (true) {
0501: if (!pos.next())
0502: return null;
0503: switch (pos.getChar()) {
0504: case ';':
0505: skipComment(pos);
0506: break;
0507: case ')':
0508: --parenCount;
0509: if (parenCount == 0) {
0510: if (pos.next())
0511: return pos;
0512: else
0513: return null;
0514: }
0515: break;
0516: case '(':
0517: ++parenCount;
0518: break;
0519: default:
0520: break;
0521: }
0522: }
0523: }
0524: if (c == '"') {
0525: while (true) {
0526: if (!pos.next())
0527: return null;
0528: switch (pos.getChar()) {
0529: case '\\':
0530: if (!pos.next())
0531: return null;
0532: break;
0533: case '"':
0534: if (!pos.next())
0535: return null;
0536: return pos;
0537: default:
0538: break;
0539: }
0540: }
0541: }
0542: // Otherwise...
0543: while (true) {
0544: if (!pos.next())
0545: return null;
0546: c = pos.getChar();
0547: if (Character.isWhitespace(c) || c == '(' || c == ')')
0548: return pos;
0549: }
0550: }
0551:
0552: private Position backwardSexp(Position start) {
0553: Position pos = findContainingSexp(start);
0554: if (pos == null) {
0555: // Top level.
0556: LispSyntaxIterator it = new LispSyntaxIterator(start);
0557: while (true) {
0558: char c = it.prevChar();
0559: if (c == SyntaxIterator.DONE)
0560: return null;
0561: if (!Character.isWhitespace(c)) {
0562: pos = it.getPosition();
0563: break;
0564: }
0565: }
0566: if (pos.getChar() == ')')
0567: return findContainingSexp(pos);
0568: while (true) {
0569: if (!pos.prev())
0570: return pos;
0571: if (Character.isWhitespace(pos.getChar())) {
0572: pos.next();
0573: return pos;
0574: }
0575: }
0576: }
0577: pos = downList(pos);
0578: if (pos == null)
0579: return null;
0580: while (true) {
0581: Position last = pos;
0582: pos = forwardSexp(pos);
0583: if (pos == null)
0584: return last;
0585: // Skip whitespace and comments.
0586: char c;
0587: while (true) {
0588: pos.skipWhitespace();
0589: c = pos.getChar();
0590: if (c == ';')
0591: skipComment(pos);
0592: else
0593: break;
0594: }
0595: if (c == ')')
0596: return last;
0597: if (pos.equals(start))
0598: return last;
0599: if (pos.isAfter(start))
0600: return last;
0601: }
0602: }
0603:
0604: // Advances pos to start of next line.
0605: private static void skipComment(Position pos) {
0606: Line nextLine = pos.getNextLine();
0607: if (nextLine != null)
0608: pos.moveTo(nextLine, 0);
0609: }
0610:
0611: public static void forwardSexp() {
0612: final Editor editor = Editor.currentEditor();
0613: if (editor.getMode() instanceof LispMode) {
0614: Position pos = mode.forwardSexp(editor.getDot());
0615: if (pos != null)
0616: editor.moveDotTo(pos);
0617: }
0618: }
0619:
0620: public static void backwardSexp() {
0621: final Editor editor = Editor.currentEditor();
0622: if (editor.getMode() instanceof LispMode) {
0623: Position pos = mode.backwardSexp(editor.getDot());
0624: if (pos != null)
0625: editor.moveDotTo(pos);
0626: }
0627: }
0628:
0629: public static void lispFindMatchingChar() {
0630: final Editor editor = Editor.currentEditor();
0631: Position dot = editor.getDotCopy();
0632: if (dot == null)
0633: return;
0634: Position pos = findDelimiterNear(dot);
0635: editor.setWaitCursor();
0636: Position match = editor.findMatchInternal(pos, 0);
0637: editor.setDefaultCursor();
0638: if (match != null) {
0639: // Move past closing parenthesis.
0640: if (match.getChar() == ')')
0641: match.next();
0642: editor.addUndo(SimpleEdit.MOVE);
0643: editor.unmark();
0644: editor.updateDotLine();
0645: editor.getDot().moveTo(match);
0646: editor.updateDotLine();
0647: editor.moveCaretToDotCol();
0648: } else
0649: editor.status("No match");
0650: }
0651:
0652: public static void lispSelectSyntax() {
0653: final Editor editor = Editor.currentEditor();
0654: Position dot = editor.getDotCopy();
0655: if (dot == null)
0656: return;
0657: Position pos;
0658: if (editor.getMark() != null) {
0659: pos = findContainingSexp(dot);
0660: } else {
0661: pos = findDelimiterNear(dot);
0662: if (pos == null)
0663: pos = findContainingSexp(dot);
0664: }
0665: if (pos == null)
0666: return;
0667: editor.setWaitCursor();
0668: Position match = editor.findMatchInternal(pos, 0);
0669: if (match != null) {
0670: if (pos.getChar() == ')')
0671: pos.next();
0672: else if (match.getChar() == ')')
0673: match.next();
0674: if (pos.getLine() != match.getLine()) {
0675: // Extend selection to full lines if possible.
0676: Region r = new Region(editor.getBuffer(), pos, match);
0677: Position begin = r.getBegin();
0678: if (begin.getLine().substring(0, begin.getOffset())
0679: .trim().length() == 0) {
0680: Position end = r.getEnd();
0681: String trim = end.getLine().substring(
0682: end.getOffset()).trim();
0683: if (trim.length() == 0 || trim.charAt(0) == ';') {
0684: // Extend selection to complete lines.
0685: begin.setOffset(0);
0686: if (end.getNextLine() != null)
0687: end.moveTo(end.getNextLine(), 0);
0688: else
0689: end.setOffset(end.getLineLength());
0690: if (pos.isBefore(match)) {
0691: pos = begin;
0692: match = end;
0693: } else {
0694: match = begin;
0695: pos = end;
0696: }
0697: }
0698: }
0699: }
0700: editor.addUndo(SimpleEdit.MOVE);
0701: editor.unmark();
0702: editor.getDot().moveTo(pos);
0703: editor.setMarkAtDot();
0704: editor.updateDotLine();
0705: editor.getDot().moveTo(match);
0706: editor.updateDotLine();
0707: editor.moveCaretToDotCol();
0708: if (editor.getDotLine() != editor.getMarkLine())
0709: editor.setUpdateFlag(REPAINT);
0710: } else
0711: editor.status("No match");
0712: editor.setDefaultCursor();
0713: }
0714:
0715: private static Position findDelimiterNear(Position pos) {
0716: Position saved = pos.copy();
0717: if (pos.getChar() == '(')
0718: return pos;
0719: if (pos.getOffset() > 0) {
0720: pos.prev();
0721: if (pos.getChar() == ')')
0722: return pos;
0723: }
0724: // Go back to original starting point.
0725: pos.moveTo(saved);
0726: while (pos.getOffset() > 0) {
0727: // Look at previous char.
0728: pos.prev();
0729: char c = pos.getChar();
0730: if (c == '(' || c == ')')
0731: return pos;
0732: }
0733: // Go back to original starting point.
0734: pos.moveTo(saved);
0735: final int limit = pos.getLineLength() - 1;
0736: while (pos.getOffset() < limit) {
0737: // Look at next char.
0738: pos.next();
0739: char c = pos.getChar();
0740: if (c == '(' || c == ')')
0741: return pos;
0742: if (c == ';')
0743: return null; // The rest of the line is a comment.
0744: }
0745: return null;
0746: }
0747:
0748: private static Editor getLispShellEditor(Editor editor) {
0749: Editor ed = editor.getOtherEditor();
0750: if (ed != null) {
0751: Buffer b = ed.getBuffer();
0752: if (b instanceof CommandInterpreter) {
0753: CommandInterpreter comint = (CommandInterpreter) b;
0754: if (comint.isLisp())
0755: return ed;
0756: }
0757: }
0758: CommandInterpreter lisp = LispShell.findLisp(null);
0759: if (lisp == null) {
0760: MessageDialog.showMessageDialog("No Lisp shell is running",
0761: "Error");
0762: return null;
0763: }
0764: ed = findEditor(lisp);
0765: if (ed != null)
0766: return ed;
0767: return editor.displayInOtherWindow(lisp);
0768: }
0769:
0770: private static Editor findEditor(Buffer buf) {
0771: Editor ed = null;
0772: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0773: ed = it.nextEditor();
0774: if (ed.getBuffer() == buf)
0775: return ed;
0776: }
0777: return null;
0778: }
0779:
0780: public static String getCurrentDefun(Editor editor) {
0781: Position begin = findStartOfDefun(editor.getDot());
0782: if (begin != null && begin.lookingAt("(def")) {
0783: Position end = mode.forwardSexp(begin);
0784: if (end != null) {
0785: Region r = new Region(editor.getBuffer(), begin, end);
0786: return r.toString().trim();
0787: }
0788: }
0789: return null;
0790: }
0791:
0792: private static String getDefunName(String s) {
0793: StringTokenizer st = new StringTokenizer(s);
0794: int count = st.countTokens();
0795: if (count >= 2) {
0796: // Skip first token.
0797: st.nextToken();
0798: // Return second token.
0799: return st.nextToken().toUpperCase();
0800: }
0801: return "";
0802: }
0803:
0804: public static void evalDefunLisp() {
0805: final Editor editor = Editor.currentEditor();
0806: if (editor.getMode() != mode)
0807: return;
0808: Editor ed = getLispShellEditor(editor);
0809: if (ed != null) {
0810: CommandInterpreter lisp = (CommandInterpreter) ed
0811: .getBuffer();
0812: String defun = getCurrentDefun(editor);
0813: if (defun != null) {
0814: String name = getDefunName(defun);
0815: if (name != null) {
0816: Position end = lisp.getEnd();
0817: end.getLine().setFlags(STATE_INPUT);
0818: lisp.insertString(end, ";;; Evaluating defun "
0819: + name + " ...\n");
0820: lisp.renumber();
0821: ed.eob();
0822: ed.getDotLine().setFlags(0);
0823: lisp.send(defun);
0824: }
0825: }
0826: }
0827: }
0828:
0829: public static void compileDefunLisp() {
0830: final Editor editor = Editor.currentEditor();
0831: if (editor.getMode() != mode)
0832: return;
0833: Editor ed = getLispShellEditor(editor);
0834: if (ed != null) {
0835: CommandInterpreter lisp = (CommandInterpreter) ed
0836: .getBuffer();
0837: String defun = getCurrentDefun(editor);
0838: if (defun != null) {
0839: String name = getDefunName(defun);
0840: if (name != null) {
0841: Position end = lisp.getEnd();
0842: end.getLine().setFlags(STATE_INPUT);
0843: lisp.insertString(end, ";;; Compiling defun "
0844: + name + " ...\n");
0845: lisp.renumber();
0846: ed.eob();
0847: ed.getDotLine().setFlags(0);
0848: lisp.send("(CL:PROGN " + defun + " (CL:COMPILE '"
0849: + name + "))\n");
0850: }
0851: }
0852: }
0853: }
0854:
0855: public static void evalRegionLisp() {
0856: final Editor editor = Editor.currentEditor();
0857: if (editor.getMode() != mode)
0858: return;
0859: if (editor.getMark() == null)
0860: return;
0861: if (editor.isColumnSelection()) {
0862: editor.notSupportedForColumnSelections();
0863: return;
0864: }
0865: Editor ed = getLispShellEditor(editor);
0866: if (ed != null) {
0867: CommandInterpreter lisp = (CommandInterpreter) ed
0868: .getBuffer();
0869: Position bufEnd = lisp.getEnd();
0870: bufEnd.getLine().setFlags(STATE_INPUT);
0871: lisp.insertString(bufEnd, ";;; Evaluating region ...\n");
0872: lisp.renumber();
0873: ed.eob();
0874: ed.getDotLine().setFlags(0);
0875: lisp.send(new Region(editor).toString().trim());
0876: }
0877: }
0878:
0879: public static void loadLispFile() {
0880: final Editor editor = Editor.currentEditor();
0881: if (editor.getMode() != mode)
0882: return;
0883: Editor ed = getLispShellEditor(editor);
0884: if (ed != null) {
0885: Buffer buffer = editor.getBuffer();
0886: boolean save = false;
0887: if (buffer.isModified()) {
0888: int response = ConfirmDialog
0889: .showConfirmDialogWithCancelButton(editor,
0890: CHECK_SAVE_PROMPT, "Load File");
0891: switch (response) {
0892: case RESPONSE_YES:
0893: save = true;
0894: break;
0895: case RESPONSE_NO:
0896: break;
0897: case RESPONSE_CANCEL:
0898: return;
0899: }
0900: editor.repaintNow();
0901: }
0902: if (!save || buffer.save()) {
0903: CommandInterpreter lisp = (CommandInterpreter) ed
0904: .getBuffer();
0905: String path = editor.getBuffer().getFile()
0906: .canonicalPath();
0907: if (path != null) {
0908: Position end = lisp.getEnd();
0909: end.getLine().setFlags(STATE_INPUT);
0910: lisp.insertString(end, ";;; Loading file " + path
0911: + " ...\n");
0912: lisp.renumber();
0913: ed.eob();
0914: ed.getDotLine().setFlags(0);
0915: lisp.send("(CL:LOAD \"" + path + "\")\n");
0916: }
0917: }
0918: }
0919: }
0920:
0921: public static void compileLispFile() {
0922: final Editor editor = Editor.currentEditor();
0923: if (editor.getMode() != mode)
0924: return;
0925: Editor ed = getLispShellEditor(editor);
0926: if (ed != null) {
0927: Buffer buffer = editor.getBuffer();
0928: boolean save = false;
0929: if (buffer.isModified()) {
0930: int response = ConfirmDialog
0931: .showConfirmDialogWithCancelButton(editor,
0932: CHECK_SAVE_PROMPT, "Compile File");
0933: switch (response) {
0934: case RESPONSE_YES:
0935: save = true;
0936: break;
0937: case RESPONSE_NO:
0938: break;
0939: case RESPONSE_CANCEL:
0940: return;
0941: }
0942: editor.repaintNow();
0943: }
0944: if (!save || buffer.save()) {
0945: CommandInterpreter lisp = (CommandInterpreter) ed
0946: .getBuffer();
0947: String path = editor.getBuffer().getFile()
0948: .canonicalPath();
0949: if (path != null) {
0950: Position end = lisp.getEnd();
0951: end.getLine().setFlags(STATE_INPUT);
0952: lisp.insertString(end, ";;; Compiling " + path
0953: + " ...\n");
0954: lisp.renumber();
0955: ed.eob();
0956: ed.getDotLine().setFlags(0);
0957: lisp.send("(CL:COMPILE-FILE \"" + path + "\")\n");
0958: }
0959: }
0960: }
0961: }
0962:
0963: public static void compileAndLoadLispFile() {
0964: final Editor editor = Editor.currentEditor();
0965: if (editor.getMode() != mode)
0966: return;
0967: Editor ed = getLispShellEditor(editor);
0968: if (ed != null) {
0969: Buffer buffer = editor.getBuffer();
0970: boolean save = false;
0971: if (buffer.isModified()) {
0972: int response = ConfirmDialog
0973: .showConfirmDialogWithCancelButton(editor,
0974: CHECK_SAVE_PROMPT,
0975: "Compile and Load File");
0976: switch (response) {
0977: case RESPONSE_YES:
0978: save = true;
0979: break;
0980: case RESPONSE_NO:
0981: break;
0982: case RESPONSE_CANCEL:
0983: return;
0984: }
0985: editor.repaintNow();
0986: }
0987: if (!save || buffer.save()) {
0988: CommandInterpreter lisp = (CommandInterpreter) ed
0989: .getBuffer();
0990: String path = editor.getBuffer().getFile()
0991: .canonicalPath();
0992: if (path != null) {
0993: Position end = lisp.getEnd();
0994: end.getLine().setFlags(STATE_INPUT);
0995: lisp.insertString(end, ";;; Compiling and loading "
0996: + path + " ...\n");
0997: lisp.renumber();
0998: ed.eob();
0999: ed.getDotLine().setFlags(0);
1000: lisp.send("(CL:LOAD (CL:COMPILE-FILE \"" + path
1001: + "\"))\n");
1002: }
1003: }
1004: }
1005: }
1006:
1007: private static HashMap map;
1008:
1009: public static void hyperspec() {
1010: hyperspec(null);
1011: }
1012:
1013: public static void hyperspec(String s) {
1014: final Editor editor = Editor.currentEditor();
1015: if (s == null) {
1016: if (editor.getDot() == null)
1017: return;
1018: char c = editor.getDotChar();
1019: if (c == ')' || Character.isWhitespace(c)) {
1020: final Line dotLine = editor.getDotLine();
1021: final String text = dotLine.getText();
1022: for (int offset = editor.getDotOffset(); offset-- > 0;) {
1023: c = text.charAt(offset);
1024: if (mode.isIdentifierPart(c)) {
1025: s = mode.getIdentifier(dotLine, offset);
1026: break;
1027: }
1028: }
1029: } else
1030: s = mode.getIdentifier(editor.getDot());
1031: if (s == null)
1032: return;
1033: }
1034: if (s.length() == 0)
1035: return;
1036: final Buffer buffer = editor.getBuffer();
1037: String clhsRoot = buffer.getStringProperty(Property.CLHS_ROOT);
1038: File rootDir = File.getInstance(clhsRoot);
1039: if (rootDir == null || rootDir.isRemote()
1040: || !rootDir.isDirectory())
1041: return;
1042: if (map == null) {
1043: File file = File.getInstance(rootDir, "Data/Map_Sym.txt");
1044: if (!file.isFile())
1045: return;
1046: SystemBuffer buf = new SystemBuffer(file);
1047: buf.load();
1048: if (!buf.isLoaded())
1049: return;
1050: map = new HashMap();
1051: Line line = buf.getFirstLine();
1052: while (true) {
1053: String key = line.trim().toLowerCase();
1054: line = line.next();
1055: if (line == null)
1056: break;
1057: if (line != null) {
1058: String value = line.trim();
1059: if (key.length() > 0 && value.length() > 0)
1060: map.put(key, value);
1061: }
1062: line = line.next();
1063: if (line == null)
1064: break;
1065: }
1066: }
1067: String filename = (String) map.get(s.toLowerCase());
1068: if (filename == null) {
1069: editor.status("No entry for \"" + s + '"');
1070: return;
1071: }
1072: String rootPath = rootDir.canonicalPath();
1073: File dataDir = File.getInstance(rootDir, "Data");
1074: File file = File.getInstance(dataDir, filename);
1075: WebBuffer buf = null;
1076: // Look for existing buffer.
1077: if (buffer instanceof WebBuffer) {
1078: if (buffer.getFile().canonicalPath().startsWith(rootPath))
1079: buf = (WebBuffer) buffer;
1080: }
1081: if (buf == null) {
1082: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
1083: Buffer b = it.nextBuffer();
1084: if (b instanceof WebBuffer) {
1085: if (b.getFile().canonicalPath()
1086: .startsWith(rootPath)) {
1087: buf = (WebBuffer) b;
1088: break;
1089: }
1090: }
1091: }
1092: }
1093: if (buf != null)
1094: buf.go(file, 0, "text/html");
1095: else {
1096: buf = WebBuffer.createWebBuffer(file, null, null);
1097: buf.setTransient(true);
1098: }
1099: if (editor.getBuffer() != buf) {
1100: Editor otherEditor = editor.getOtherEditor();
1101: if (otherEditor != null) {
1102: buf.setUnsplitOnClose(false);
1103: otherEditor.makeNext(buf);
1104: } else
1105: editor.makeNext(buf);
1106: editor.displayInOtherWindow(buf);
1107: }
1108: }
1109: }
|