KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > LispShell


1 /*
2  * LispShell.java
3  *
4  * Copyright (C) 2002-2004 Peter Graves
5  * $Id: LispShell.java,v 1.78 2004/09/21 16:10:32 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import gnu.regexp.REMatch;
25 import java.io.IOException JavaDoc;
26 import java.io.OutputStreamWriter JavaDoc;
27 import java.util.List JavaDoc;
28 import javax.swing.SwingUtilities JavaDoc;
29 import org.armedbear.lisp.Site;
30
31 public class LispShell extends Shell
32 {
33     private static final String JavaDoc DEFAULT_PROMPT_PATTERN =
34         "^[^:>\\*\\]]*[:>\\*\\]] *";
35
36     private static final String JavaDoc ALLEGRO_PROMPT_PATTERN =
37         "^(\\[[0-9+][ci]?\\] )?[^ ]+\\([0-9]+\\): ";
38
39     private static final String JavaDoc ARMEDBEAR_PROMPT_PATTERN =
40         ALLEGRO_PROMPT_PATTERN;
41
42     private static final String JavaDoc CLISP_PROMPT_PATTERN =
43         "^[^>\\*\\]]*\\[[0-9]+\\]> ";
44
45     private static final String JavaDoc CMUCL_PROMPT_PATTERN =
46         "^\\* |^[0-9]+\\] ";
47
48     private static final String JavaDoc SBCL_PROMPT_PATTERN =
49         CMUCL_PROMPT_PATTERN + "|" + ALLEGRO_PROMPT_PATTERN;
50
51     private final boolean slime;
52
53     private String JavaDoc resetCommand = null;
54     private String JavaDoc exitCommand = "(exit)";
55
56     private Position posBeforeLastPrompt;
57     private Position posEndOfInput;
58
59     private File currentDirectory;
60
61     // For JLisp.java.
62
protected LispShell()
63     {
64         setPromptRE(ARMEDBEAR_PROMPT_PATTERN);
65         setResetCommand(":reset");
66         slime = false;
67     }
68
69     private LispShell(String JavaDoc shellCommand, String JavaDoc title)
70     {
71         super(shellCommand, LispShellMode.getMode());
72         this.title = title;
73         formatter = mode.getFormatter(this);
74         slime = title.startsWith("slime ");
75     }
76
77     public final boolean isLisp()
78     {
79         return true;
80     }
81
82     private void setResetCommand(String JavaDoc s)
83     {
84         resetCommand = s;
85     }
86
87     private void setExitCommand(String JavaDoc s)
88     {
89         exitCommand = s;
90     }
91
92     private static Shell createLispShell(String JavaDoc shellCommand, String JavaDoc title,
93                                          boolean startSlime)
94     {
95         if (startSlime) {
96             if (shellCommand.indexOf("sbcl") >= 0) {
97                 File lispHome = File.getInstance(Site.getLispHome());
98                 if (lispHome == null)
99                     return null; // FIXME Error message?
100
File swankLoader = File.getInstance(lispHome,
101                                                     "swank-loader.lisp");
102                 if (swankLoader == null)
103                     return null; // FIXME Error message?
104
shellCommand =
105                     shellCommand + " --load " + swankLoader.canonicalPath();
106             } else if (shellCommand.indexOf("abcl") >= 0 ||
107                        shellCommand.indexOf("org.armedbear.lisp") >= 0) {
108                 shellCommand =
109                     shellCommand.concat(" --load-system-file swank-loader.lisp");
110             }
111         }
112         LispShell lisp = new LispShell(shellCommand, title);
113         lisp.startProcess();
114         if (lisp.getProcess() == null) {
115             Editor.getBufferList().remove(lisp);
116             String JavaDoc message;
117             if (Utilities.haveJpty())
118                 message = "Unable to start process \"" + shellCommand + "\"";
119             else
120                 message = JPTY_NOT_FOUND;
121             MessageDialog.showMessageDialog(message, "Error");
122             return null;
123         }
124         if (shellCommand.equals("alisp") || shellCommand.equals("/usr/bin/alisp")) {
125             lisp.setPromptRE(ALLEGRO_PROMPT_PATTERN);
126             lisp.setResetCommand(":reset");
127         } else if (shellCommand.indexOf("clisp") >= 0) {
128             // clisp -I
129
lisp.setPromptRE(CLISP_PROMPT_PATTERN);
130             lisp.setResetCommand("(sys::debug-unwind)");
131         } else if (shellCommand.equals("/usr/bin/lisp")) {
132             lisp.setPromptRE(CMUCL_PROMPT_PATTERN);
133             lisp.setResetCommand(":q");
134             lisp.setExitCommand("(quit)");
135         } else if (shellCommand.indexOf("sbcl") >= 0) {
136             lisp.setPromptRE(SBCL_PROMPT_PATTERN);
137             lisp.setResetCommand(":abort");
138             lisp.setExitCommand("(quit)");
139         } else if (shellCommand.indexOf("org.armedbear.lisp") >= 0 ||
140                    shellCommand.indexOf("abcl") >= 0)
141         {
142             lisp.setPromptRE(ARMEDBEAR_PROMPT_PATTERN);
143             lisp.setResetCommand(":reset");
144         } else {
145             lisp.setPromptRE(DEFAULT_PROMPT_PATTERN);
146             if (shellCommand.equals("rep") || shellCommand.equals("/usr/bin/rep"))
147                 lisp.setExitCommand(",quit");
148         }
149         lisp.needsRenumbering(true);
150         if (Editor.isLispInitialized())
151             LispAPI.invokeLispShellStartupHook(lisp, shellCommand);
152         return lisp;
153     }
154
155     protected void startProcess()
156     {
157         if (shellCommand == null) {
158             Debug.bug();
159             return;
160         }
161         File initialDirectory = Editor.currentEditor().getCurrentDirectory();
162         if (initialDirectory == null || initialDirectory.isRemote())
163             initialDirectory = Directories.getUserHomeDirectory();
164         List JavaDoc tokens = Utilities.tokenize(shellCommand);
165         final int tokenCount = tokens.size();
166         String JavaDoc[] cmdArray;
167         int i = 0;
168         if (Utilities.haveJpty()) {
169             cmdArray = new String JavaDoc[tokenCount + 1];
170             cmdArray[i++] = "jpty";
171         } else
172             cmdArray = new String JavaDoc[tokenCount];
173         for (int j = 0; j < tokenCount; j++)
174             cmdArray[i++] = (String JavaDoc) tokens.get(j);
175         Process JavaDoc p = null;
176         try {
177             p = Runtime.getRuntime().exec(cmdArray, null,
178                                           new java.io.File JavaDoc(initialDirectory.canonicalPath()));
179             setProcess(p);
180         }
181         catch (Throwable JavaDoc t) {
182             setProcess(null);
183             return;
184         }
185         currentDirectory = initialDirectory;
186         startWatcherThread();
187         // See if the process exits right away (meaning jpty couldn't launch
188
// the shell command).
189
try {
190             Thread.sleep(100);
191         }
192         catch (InterruptedException JavaDoc e) {
193             Log.error(e);
194         }
195         // When the process exits, the watcher thread calls setProcess(null),
196
// so check the value of getProcess() here.
197
if (getProcess() == null)
198             return; // Process exited.
199
try {
200             stdin = new OutputStreamWriter JavaDoc(p.getOutputStream());
201             stdoutThread = new StdoutThread(p.getInputStream());
202             stderrThread = new StderrThread(p.getErrorStream());
203             stdoutThread.start();
204             stderrThread.start();
205             readOnly = false;
206         }
207         catch (Throwable JavaDoc t) {
208             Log.error(t);
209         }
210     }
211
212     protected void initializeHistory()
213     {
214         history = new History("lisp.history", 30);
215     }
216
217     public void enter()
218     {
219         if (!checkProcess())
220             return;
221         final Editor editor = Editor.currentEditor();
222         Position dot = editor.getDotCopy();
223         if (dot == null)
224             return;
225         if (needsRenumbering)
226             renumber();
227         final Line dotLine = dot.getLine();
228         final Position endOfOutput = getEndOfOutput();
229         if (endOfOutput == null) {
230             // Ignore input before first prompt is displayed.
231
dotLine.setText("");
232             return;
233         }
234         if (dot.isBefore(endOfOutput)) {
235             editor.newlineAndIndent();
236             return; // For now.
237
}
238         final Line promptLine = endOfOutput.getLine();
239         Annotation a = new Annotation(endOfOutput.getOffset());
240         promptLine.setAnnotation(a);
241         promptLine.setFlags(STATE_PROMPT);
242         Position end = getEnd();
243         Position pos = LispMode.findContainingSexp(end);
244         boolean isComplete = (pos == null || pos.isBefore(endOfOutput));
245         if (isComplete) {
246             // Complete sexp.
247
editor.eob();
248             editor.insertLineSeparator();
249             editor.getDotLine().setFlags(0);
250         } else {
251             // Not complete; multiline input.
252
editor.newline();
253             editor.getDotLine().setFlags(STATE_INPUT);
254         }
255         if (needsRenumbering)
256             renumber();
257         editor.moveCaretToDotCol();
258         editor.getDisplay().setReframe(-2);
259         resetUndo();
260         stripEcho = true;
261         if (isComplete) {
262             // No containing sexp. Send input to lisp process.
263
Position begin = endOfOutput;
264             end = editor.getDotCopy();
265             end.setOffset(end.getLineLength());
266             setEndOfOutput(end);
267             Line lineBeforeLastPrompt = promptLine.previous();
268             if (lineBeforeLastPrompt != null) {
269                 posBeforeLastPrompt =
270                     new Position(lineBeforeLastPrompt,
271                                  lineBeforeLastPrompt.length());
272             }
273             posEndOfInput = end.copy();
274             String JavaDoc s = new Region(this, begin, end).toString();
275             sendInputToLisp(s);
276         } else
277             indentLineAtDot(editor);
278     }
279
280     public void resetLisp()
281     {
282         if (resetCommand != null) {
283             Position pos = getEnd();
284             insertString(pos, resetCommand.concat("\n"));
285             if (needsRenumbering())
286                 renumber();
287             enforceOutputLimit(Property.SHELL_OUTPUT_LIMIT);
288             posEndOfInput = pos.copy();
289             send(resetCommand);
290         }
291     }
292
293     protected void stdOutUpdate(final String JavaDoc s)
294     {
295         String JavaDoc prompt;
296         int index = s.lastIndexOf('\n');
297         if (index >= 0)
298             prompt = s.substring(index + 1);
299         else
300             prompt = s;
301         final REMatch match = promptRE.getMatch(prompt);
302         if (match != null) {
303             // Last line of output looks like a prompt.
304
String JavaDoc m = match.toString();
305             if (prompt.startsWith(m)) {
306                 if (prompt.substring(m.length()).startsWith(m)) {
307                     // Double prompt. Remove one of them.
308
prompt = prompt.substring(m.length());
309                 }
310             }
311         }
312         final String JavaDoc output;
313         if (index >= 0)
314             output = s.substring(0, index + 1) + prompt;
315         else
316             output = prompt;
317         Runnable JavaDoc r = new Runnable JavaDoc() {
318             public void run()
319             {
320                 Position pos = getEnd();
321                 if (pos != null)
322                     pos.getLine().setFlags(0); // This value will propagate.
323
if (output.length() > 0) {
324                     appendString(output);
325                     if (match != null) {
326                         Line lineBeforeLastPrompt =
327                             getEnd().getLine().previous();
328                         if (lineBeforeLastPrompt != null) {
329                             posBeforeLastPrompt =
330                                 new Position(lineBeforeLastPrompt,
331                                              lineBeforeLastPrompt.length());
332                         }
333                         if (isBusy())
334                             setBusy(false);
335                     }
336                 }
337                 updateDisplayInAllFrames();
338                 resetUndo();
339             }
340         };
341         SwingUtilities.invokeLater(r);
342     }
343
344     protected void stdErrUpdate(final String JavaDoc s)
345     {
346         Runnable JavaDoc r = new Runnable JavaDoc() {
347             public void run()
348             {
349                 appendString(s);
350                 updateDisplayInAllFrames();
351                 resetUndo();
352             }
353         };
354         SwingUtilities.invokeLater(r);
355     }
356
357     protected void appendString(String JavaDoc s)
358     {
359         try {
360             lockWrite();
361         }
362         catch (InterruptedException JavaDoc e) {
363             Log.error(e);
364             return;
365         }
366         try {
367             if (slime) {
368                 // Slime.
369
if (posEndOfInput == null)
370                     posEndOfInput = new Position(getFirstLine(), 0);
371                 final Position pos;
372                 if (posBeforeLastPrompt != null && posEndOfInput != null) {
373                     Position posLastPrompt =
374                         new Position(posBeforeLastPrompt.getNextLine(), 0);
375                     if (posEndOfInput.isAfter(posLastPrompt)) {
376                         // There has been user input since the last prompt.
377
pos = getEnd();
378                     } else {
379                         pos = posBeforeLastPrompt;
380                     }
381                 } else
382                     pos = getEnd();
383                 if (pos != null) {
384                     if (pos == posBeforeLastPrompt) {
385                         if (s.length() > 0) {
386                             if (s.charAt(s.length() - 1) == '\n')
387                                 s = s.substring(0, s.length() - 1);
388                         }
389                         if (s.length() > 0 && s.charAt(0) != '\n')
390                             insertLineSeparator(pos);
391                     }
392                     insertString(pos, s);
393                     if (needsRenumbering())
394                         renumber();
395                     enforceOutputLimit(Property.SHELL_OUTPUT_LIMIT);
396                     if (pos != posBeforeLastPrompt)
397                         setEndOfOutput(pos.copy());
398                 } else {
399                     // Empty buffer.
400
setText(s);
401                     setEndOfOutput(getEnd().copy());
402                 }
403             } else {
404                 // No slime.
405
Position pos = getEnd();
406                 if (pos != null) {
407                     insertString(pos, s);
408                     if (needsRenumbering())
409                         renumber();
410                     enforceOutputLimit(Property.SHELL_OUTPUT_LIMIT);
411                     setEndOfOutput(pos.copy());
412                 } else {
413                     setText(s);
414                     setEndOfOutput(getEnd().copy());
415                 }
416             }
417         }
418         finally {
419             unlockWrite();
420         }
421     }
422
423     private void indentLineAtDot(Editor editor)
424     {
425         final Line dotLine = editor.getDotLine();
426         if (dotLine.length() > 0)
427             return;
428         try {
429             lockWrite();
430         }
431         catch (InterruptedException JavaDoc e) {
432             Log.error(e);
433             return;
434         }
435         try {
436             getFormatter().parseBuffer();
437             int indent = mode.getCorrectIndentation(dotLine, this);
438             if (indent != getIndentation(dotLine)) {
439                 editor.addUndo(SimpleEdit.LINE_EDIT);
440                 setIndentation(dotLine, indent);
441                 dotLine.setFlags(STATE_INPUT);
442                 modified();
443             }
444             if (dotLine.length() > 0) {
445                 editor.moveDotToIndentation();
446                 editor.moveCaretToDotCol();
447             } else {
448                 final Display display = editor.getDisplay();
449                 display.setCaretCol(indent - display.getShift());
450                 if (getBooleanProperty(Property.RESTRICT_CARET))
451                     editor.fillToCaret();
452             }
453             resetUndo(); // Why?
454
}
455         finally {
456             unlockWrite();
457         }
458     }
459
460     private void sendInputToLisp(String JavaDoc input)
461     {
462         // Save history unless input is very short (e.g. ":q"). Ignore
463
// whitespace at end of line.
464
String JavaDoc trim = input.trim();
465         if (trim.length() > 2) {
466             history.append(trim);
467             history.save();
468         }
469         send(input);
470     }
471
472     public void dispose()
473     {
474         if (!checkProcess()) {
475             Log.debug("checkProcess returned false");
476             return;
477         }
478         Thread JavaDoc t = new Thread JavaDoc("LispShell dispose") {
479             public void run()
480             {
481                 try {
482                     stdin.write(3);
483                     stdin.flush();
484                     stdin.write(exitCommand);
485                     stdin.write("\n");
486                     stdin.flush();
487                     stdin.close();
488                     final Process JavaDoc p = getProcess();
489                     if (p != null) {
490                         p.destroy();
491                         p.waitFor();
492                     }
493                 }
494                 catch (IOException JavaDoc e) {
495                     Log.error(e);
496                 }
497                 catch (InterruptedException JavaDoc e) {
498                     Log.error(e);
499                 }
500             }
501         };
502         t.setPriority(Thread.MIN_PRIORITY);
503         t.setDaemon(true);
504         t.start();
505     }
506
507     public File getCurrentDirectory()
508     {
509         return currentDirectory;
510     }
511
512     public File getCompletionDirectory()
513     {
514         return currentDirectory;
515     }
516
517     public String JavaDoc getFileNameForDisplay()
518     {
519         return title;
520     }
521
522     public String JavaDoc toString()
523     {
524         return title;
525     }
526
527     public static void slime()
528     {
529         _slime(getDefaultLispShellCommand(), "slime abcl", false);
530     }
531
532     public static void slime(String JavaDoc shellCommand)
533     {
534         _slime(shellCommand, "slime ".concat(shellCommand),
535                // Require jpty on Unix platforms.
536
Platform.isPlatformUnix());
537     }
538
539     private static final void _slime(String JavaDoc shellCommand, String JavaDoc title,
540                                      boolean requireJpty)
541     {
542         Buffer buffer = findSlime();
543         if (buffer != null) {
544             final Editor editor = Editor.currentEditor();
545             editor.makeNext(buffer);
546             Buffer b = editor.getBuffer();
547             if (b != null && b.isPaired())
548                 editor.switchToBuffer(buffer);
549             else
550                 editor.activate(buffer);
551             return;
552         }
553         lisp(shellCommand, title, requireJpty, true);
554     }
555
556     public static void lisp()
557     {
558         lisp(getDefaultLispShellCommand(), "abcl", false, false);
559     }
560
561     public static void lisp(String JavaDoc shellCommand)
562     {
563         // Require jpty on Unix platforms.
564
lisp(shellCommand, shellCommand, Platform.isPlatformUnix(), false);
565     }
566
567     private static void lisp(String JavaDoc shellCommand, String JavaDoc title,
568                              boolean requireJpty, boolean startSlime)
569     {
570         if (requireJpty && !Utilities.haveJpty()) {
571             MessageDialog.showMessageDialog(JPTY_NOT_FOUND, "Error");
572             return;
573         }
574         if (Platform.isPlatformWindows())
575             if (!Platform.isPlatformWindows5())
576                 return;
577         final Editor editor = Editor.currentEditor();
578         // Look for an existing LispShell buffer with the same shell command.
579
Buffer buf = findLisp(title);
580         if (buf == null) {
581             editor.setWaitCursor();
582             buf = createLispShell(shellCommand, title, startSlime);
583             if (buf != null)
584                 buf.setBusy(true);
585             editor.setDefaultCursor();
586         } else
587             startSlime = false; // Already started.
588
if (buf != null) {
589             editor.makeNext(buf);
590             Buffer b = editor.getBuffer();
591             if (b != null && b.isPaired())
592                 editor.switchToBuffer(buf);
593             else
594                 editor.activate(buf);
595             if (startSlime)
596                 startSlime(buf);
597         }
598     }
599
600     private static void startSlime(final Buffer buffer)
601     {
602         Runnable JavaDoc r = new Runnable JavaDoc() {
603             public void run()
604             {
605                 try {
606                     JLisp.runLispCommand("(sys:load-system-file \"slime-loader.lisp\")");
607                     JLisp.runLispCommand("(setq slime::*repl-buffer-name* \"" +
608                                          buffer.getTitle() + "\")");
609                     JLisp.runLispCommand("(slime:slime)");
610                 }
611                 catch (Throwable JavaDoc t) {
612                     Log.debug(t);
613                 }
614             }
615         };
616         new Thread JavaDoc(r).start();
617     }
618
619     private static String JavaDoc getDefaultLispShellCommand()
620     {
621         File java = null;
622         File javaHome = File.getInstance(System.getProperty("java.home"));
623         if (javaHome != null && javaHome.isDirectory()) {
624             java = File.getInstance(javaHome,
625                                     Platform.isPlatformWindows() ? "bin\\java.exe" : "bin/java");
626             if (java != null && !java.isFile())
627                 java = null;
628         }
629         // If j was invoked via "java -jar j.jar", use the canonical path
630
// of j.jar.
631
String JavaDoc classPath = System.getProperty("java.class.path");
632         if (classPath.equals("j.jar:.")) // IBM 1.4.0 on Linux
633
classPath = "j.jar";
634         if (classPath.indexOf(LocalFile.getPathSeparatorChar()) < 0) {
635             // Only one component in classpath.
636
String JavaDoc path = classPath;
637             if (Platform.isPlatformWindows())
638                 path = path.toLowerCase();
639             if (path.equals("j.jar") || path.endsWith("/j.jar") ||
640                 path.endsWith("\\j.jar"))
641             {
642                 File dir = File.getInstance(System.getProperty("user.dir"));
643                 File file = File.getInstance(dir, path);
644                 if (file != null && file.isFile())
645                     classPath = file.canonicalPath();
646             }
647         }
648         FastStringBuffer sb = new FastStringBuffer();
649         if (java != null) {
650             sb.append('"');
651             sb.append(java.canonicalPath());
652             sb.append('"');
653             String JavaDoc vendor = System.getProperty("java.vendor");
654             if (vendor != null) {
655                 if (vendor.indexOf("Sun") >= 0 ||
656                     vendor.indexOf("Blackdown") >= 0) {
657                     String JavaDoc vm = System.getProperty("java.vm.name");
658                     if (vm != null && vm.toLowerCase().indexOf("server") >= 0)
659                         sb.append(" -server");
660                     sb.append(" -Xmx128M");
661                     if (Platform.isPlatformUnix()) {
662                         String JavaDoc lispHome = org.armedbear.lisp.Site.getLispHome();
663                         if (lispHome != null) {
664                             sb.append(" -Xrs -Djava.library.path=");
665                             sb.append(lispHome);
666                             sb.append(":/usr/local/lib/abcl");
667                         }
668                     }
669                 } else if (vendor.indexOf("IBM") >= 0) {
670                     sb.append(" -Xss512K");
671                     sb.append(" -Xmx128M");
672                 }
673             }
674         } else
675             sb.append("java");
676         sb.append(" -cp ");
677         sb.append('"');
678         sb.append(classPath);
679         sb.append('"');
680         sb.append(" org.armedbear.lisp.Main");
681         return sb.toString();
682     }
683
684     public static CommandInterpreter findLisp(String JavaDoc title)
685     {
686         for (BufferIterator it = new BufferIterator(); it.hasNext();) {
687             Buffer b = it.nextBuffer();
688             if (b instanceof CommandInterpreter) {
689                 CommandInterpreter comint = (CommandInterpreter) b;
690                 if (comint.isLisp()) {
691                     if (title == null || title.equals(comint.getTitle()))
692                         return comint;
693                 }
694             }
695         }
696         return null;
697     }
698
699     private static final CommandInterpreter findSlime()
700     {
701         for (BufferIterator it = new BufferIterator(); it.hasNext();) {
702             Buffer b = it.nextBuffer();
703             if (b instanceof CommandInterpreter) {
704                 CommandInterpreter comint = (CommandInterpreter) b;
705                 if (comint.isLisp()) {
706                     String JavaDoc title = comint.getTitle();
707                     if (title != null && title.startsWith("slime"))
708                         return comint;
709                 }
710             }
711         }
712         return null;
713     }
714 }
715
Popular Tags