1    	package acl2s.plugin.editors.session;
2    	
3    	import java.io.ByteArrayInputStream;
4    	import java.io.ByteArrayOutputStream;
5    	import java.io.EOFException;
6    	import java.io.File;
7    	import java.io.IOException;
8    	import java.io.InputStream;
9    	import java.io.ObjectInputStream;
10   	import java.io.ObjectOutputStream;
11   	import java.io.Serializable;
12   	import java.util.ArrayList;
13   	
14   	import org.eclipse.jface.text.BadLocationException;
15   	import org.eclipse.jface.text.DocumentEvent;
16   	import org.eclipse.jface.text.IDocument;
17   	import org.eclipse.jface.text.IRegion;
18   	import org.eclipse.jface.text.ITypedRegion;
19   	import org.eclipse.jface.text.Region;
20   	import org.eclipse.swt.SWT;
21   	
22   	import acl2s.lib.parse.ParseException;
23   	import acl2s.lib.parse.StringParser;
24   	import acl2s.lib.parse.obj.LispObject;
25   	import acl2s.lib.parse.obj.Sym;
26   	import acl2s.lib.session.IEnvInfo;
27   	import acl2s.lib.session.IResultInfo;
28   	import acl2s.lib.session.IStatusInfo;
29   	import acl2s.lib.session.MyInteractiveSessionConfig;
30   	import acl2s.lib.session.SessionDeadException;
31   	import acl2s.lib.session.SessionException;
32   	import acl2s.lib.session.SessionMode;
33   	import acl2s.lib.session.SessionStateException;
34   	import acl2s.plugin.Acl2sPlugin;
35   	import acl2s.plugin.IPeerDocument;
36   	import acl2s.plugin.ISessionDocument;
37   	import acl2s.plugin.editors.A2sDocument;
38   	import acl2s.plugin.editors.A2sEditor;
39   	import acl2s.plugin.editors.AbstractDocumentPartitioner;
40   	import acl2s.plugin.editors.MyDocumentProvider;
41   	import acl2s.plugin.editors.MyTypedRegion;
42   	import acl2s.plugin.editors.lisp.LineMode;
43   	import acl2s.plugin.prefs.Acl2Prefs;
44   	import acl2s.plugin.prefs.InteractionPrefs;
45   	import acl2s.plugin.prefs.SessionPrefs;
46   	import acl2s.uilib.cmd.BadCmdException;
47   	import acl2s.uilib.cmd.ResolvableUserInput;
48   	import acl2s.uilib.cmd.ResolvedInput;
49   	import acl2s.uilib.cmd.UnresolvedKeywordCmd;
50   	import acl2s.uilib.cmd.WormholeWrappedInput;
51   	import acl2s.uilib.session.IInputInfo;
52   	import acl2s.uilib.session.ISessionClient;
53   	import acl2s.uilib.session.ISessionModel;
54   	import acl2s.uilib.session.ISessionStatusListener;
55   	import acl2s.uilib.session.InteractiveSessionManager;
56   	import acl2s.uilib.session.StartupInputAndEnv;
57   	
58   	public class SessionDocument extends A2sDocument
59   	implements ISessionModel, ISessionDocument, ISessionClient, ISessionStatusListener {
60   		public static final String ID = SessionDocument.class.getCanonicalName();
61   		
62   		protected static final char versionId = 19;
63   		
64   		protected IPeerDocument peer = null;
65   		protected MyInteractiveSessionConfig config = null;
66   		protected InteractiveSessionManager session = null;
67   	
68   		protected Partitioner partitioner;
69   	
70   		protected CurElement cur;
71   		protected final ArrayList<DoneElement> done;
72   		protected int dumpLength;
73   		protected int newDumpLength;
74   	
75   		protected final ArrayList<Integer> inputIdxs;
76   		protected final ArrayList<Integer> checkpointIdxs;
77   		
78   		protected final ArrayList<String> cmdHistory;
79   		protected int cmdPos;
80   		protected final ArrayList<String> miscHistory;
81   		protected int miscPos;
82   		
83   		public SessionDocument(MyDocumentProvider dp) {
84   			super(dp);
85   			
86   			//setTextStore(new StringBuilderTextStore());
87   			
88   			session = new InteractiveSessionManager(Acl2sPlugin.getDefault(),this,this);
89   	
90   			done = new ArrayList<DoneElement>();
91   			inputIdxs = new ArrayList<Integer>();
92   			checkpointIdxs = new ArrayList<Integer>();
93   	
94   			cmdHistory = new ArrayList<String>();
95   			cmdHistory.add("");
96   			cmdPos = 0;
97   			miscHistory = new ArrayList<String>();
98   			miscHistory.add("");
99   			miscPos = 0;
100  			
101  			tokens = new SessionDocumentLispTokens();
102  			tokens.connect(this);
103  			
104  			session.addSessionStatusListener(this);
105  			
106  			reset();
107  		}
108  		
109  		/*public void setInput(IFileEditorInput input) {
110  			this.input = input;
111  		}*/
112  		
113  		public void setPeer(IPeerDocument p) {
114  			peer = p;
115  			config.setMode(getAcl2Mode());
116  		}
117  	
118  		public IPeerDocument getPeer() { return peer; }
119  		
120  		public InteractiveSessionManager getManager() { return session; }
121  	
122  		public ISessionModel getModel() { return this; }
123  		
124  		//public int getDumpLength() { return dumpLength; }
125  		
126  		public int getNewDumpLength() { return newDumpLength; }
127  		
128  		public boolean appendingDump() { return newDumpLength > dumpLength; }
129  		
130  		public String getImmediate() {
131  			return get2(dumpLength+1,getLength()-dumpLength-1);
132  		}
133  		
134  		public int readOnlyPoint() { return dumpLength + 1; }
135  		
136  		public boolean allowReplace(int pos, int length, String text) {
137  			// currently dumping I/O
138  			if (appendingDump()) return true;
139  			
140  			// otherwise, status must be ready for input
141  			int status = session.getStatus();
142  			if (status != READY_CMD_SESSION && status != READY_MISC_SESSION) return false;
143  			
144  			// allow if in immediate region
145  			return (pos > dumpLength) ||
146  			
147  			// allow if updating separator & immediate (see UpdateSeparatorStrategy)
148  			       (pos == dumpLength && text.length() > 0 && Character.isWhitespace(text.charAt(0)));
149  		}
150  	
151  		public SessionDocumentProvider getProvider() {
152  			return Acl2sPlugin.getA2sDocumentProvider();
153  		}
154  		
155  		/*
156  		public File getProjectWorkingDir() {
157  			IPath p = getOSPath();
158  			IFile f = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(p);
159  			if (f == null) { // not in a project; return containing dir
160  				return p.toFile().getParentFile();
161  			} else {         // in a project; return project root
162  				return f.getProject().getLocation().toFile();
163  			}
164  		}*/
165  	
166  		public File getDesiredCBD() {
167  			File me = getOSFile();
168  			if (me == null) {
169  				return new File(System.getProperty("user.dir")); // best we can do
170  			} else {
171  				return me.getParentFile();
172  			}
173  		}
174  		
175  		public IInputInfo nextInputOption() {
176  			if (peer != null) {
177  				return peer.nextInputOption();
178  			} else {
179  				return null;
180  			}
181  		}
182  	
183  		public void ping() {
184  			session.ping();
185  		}
186  	
187  		public void commandCompleted(IInputInfo in, IStatusInfo status, int modelIdx) {
188  			if (peer != null) {
189  				peer.commandCompleted(in,status,modelIdx);
190  			}
191  		}
192  	
193  		
194  	
195  		// description stuff
196  		transient int cachedStatus = 0;
197  		
198  		static final String[] descriptions = new String[] {
199  			"(not running)",
200  			"Starting up...",
201  			"Ready for command input",
202  			"Busy...",
203  			"**Error in session**",
204  			"Ready for non-command input"
205  		};
206  		
207  		protected String doGetDescription() {
208  			String desc = "";
209  			if (!peer.getLineMode().equals(LineMode.getDefault()))
210  			{
211  				desc = " (" + peer.getLineMode().getName() + ")";
212  			}
213  			return descriptions[cachedStatus] + "   " +  peer.getAcl2Mode().getName() + " Mode" + desc;
214  		}
215  		
216  		public void sessionStatusChanged(int status) {
At conditional (1): (status != this.cachedStatus): Taking true branch.
217  			if (status != cachedStatus) {
218  				cachedStatus = status;
219  				updateDescriptions();
Event branch_null: "this.peer" is null.
Also see events: [deref_while_null]
At conditional (2): (this.peer != null): Taking false branch.
At conditional (3): (status == 4): Taking true branch.
220  				if (peer != null && status == NO_SESSION || status == ERROR_SESSION) {
Event deref_while_null: Dereferencing "this.peer" while this.peer=null.
Also see events: [branch_null]
221  					peer.resetLine();
222  				}
223  			}
224  			resetUndoStuff();
225  		}
226  		
227  		// actions
228  		public void cleanSession(A2sEditor ctx) {
229  			session.killSession();
230  			if (peer != null) {
231  				peer.resetLine();
232  			}
233  			reset();
234  			if (ctx instanceof SessionEditor) {
235  				ctx.gotoEnd();
236  			}
237  		}
238  	
239  		public void restartSession(A2sEditor ctx) {
240  			SessionMode mode = getConfig().getMode();
241  			if (mode != null && !mode.getUsable()) {
242  				Acl2sPlugin.popupMessage("Session mode not installed",
243  						"The session cannot be started because its mode, \"" +
244  						mode.getName() + "\", is not installed under this Eclipse/ACL2s.  To " +
245  						"be able to start a session in this mode, you will need to install that mode.  "+
246  						"(Ask the person you got the file from.)",
247  						SWT.ICON_WARNING);
248  			} else {
249  				String pref;
250  				if (done.size() > 0) {
251  					pref = SessionPrefs.getRestartPref(ctx.getEditorSite().getShell(), this, session.isAlive());
252  				} else {
253  					pref = SessionPrefs.RESTART_EXTEND;
254  				}
255  				if (pref == SessionPrefs.RESTART_CANCEL) {
256  					return;
257  				}
258  				if (pref == SessionPrefs.RESTART_CLEAN) {
259  					cleanSession(ctx);
260  				} else {
261  					session.killSession();
262  					if (peer != null) {
263  						peer.resetLine();
264  					}
265  				}
266  				DoneElement d = cur.forceFinish();
267  				done.add(d);
268  				session.startNewSession(this,getConfig());
269  			}
270  		}
271  	
272  		public void interruptSession(A2sEditor ctx) {
273  			try {
274  				getManager().interruptSession();
275  			} catch (SessionDeadException e) {
276  				// ignore
277  			}
278  		}
279  	
280  		public void killSession(A2sEditor ctx) {
281  			getManager().killSession();
282  		}
283  	
284  		
285  		public void forgetRedo(A2sEditor ctx) {
286  			try {
287  				/*(void)*/ session.systemCmd("(acl2s::clear-reverted-super-history acl2::state)");
288  				cur.env.forgetRedo();
289  			} catch (SessionStateException sse) {
290  				ctx.errorMessageBox("Session busy", "Can't forget redo information while session is busy.");
291  			}
292  		}
293  		
294  	
295  		/*====================== Saving/Loading =======================*/
296  		public void reset() {
297  			done.clear();
298  			inputIdxs.clear();
299  			checkpointIdxs.clear();
300  			inputIdxs.add(Integer.valueOf(0));
301  			checkpointIdxs.add(Integer.valueOf(0));
302  			
303  			if (partitioner != null) {
304  				partitioner.disconnect();
305  				setDocumentPartitioner(null);
306  			}
307  			newDumpLength = 0;
308  			set(" ");
309  			dumpLength = 0;
310  			partitioner = new Partitioner();
311  			partitioner.connect(this);
312  			setDocumentPartitioner(partitioner);
313  	
314  			cmdPos = cmdHistory.size()-1;
315  			miscPos = miscHistory.size()-1;
316  			
317  			cur = new CurElement();
318  			cur.connect(this);
319  	
320  			if (peer != null) {
321  				peer.resetLine();
322  			}
323  			
324  			if (config == null) {
325  				config = new MyInteractiveSessionConfig(Acl2sPlugin.getBaseConfig(), Acl2sPlugin.getSessionSupportDir());
326  			}
327  			config.setMode(getAcl2Mode());
328  			
329  			for (IProofTreeListener l : listeners) {
330  				l.sessionCleaned();
331  			}
332  		}
333  		
334  		// returns true if initially dirty
335  		public void loadFrom(InputStream stream) {
336  			ObjectInputStream in;
337  			try {
338  				try {
339  					in = new ObjectInputStream(stream);
340  					int x = in.readInt();
341  					int m = x & 0xffff0000;
342  					int v = x & 0xffff;
343  					if (m != 0x0a250000) {
344  						throw new ClassCastException("Invalid magic number for a2s file.");
345  					}
346  					if (v != versionId) {
347  						throw new ClassCastException("Incompatible version number for a2s file.");
348  					}
349  				} catch (EOFException eofe) {
350  					reset();
351  					return; // just an empty file
352  				}
353  				if (partitioner != null) {
354  					partitioner.disconnect();
355  					setDocumentPartitioner(null);
356  				}
357  				set("");
358  	
359  				String data = (String)in.readObject();
360  				partitioner = (Partitioner) in.readObject();
361  				config = (MyInteractiveSessionConfig) in.readObject();
362  				config.setBaseConfig(Acl2sPlugin.getBaseConfig());
363  				cur = (CurElement) in.readObject();
364  				done.clear();
365  				for (Object o : (Iterable<?>) in.readObject()) {
366  					done.add((DoneElement) o);
367  				}
368  				dumpLength = in.readInt();
369  				newDumpLength = dumpLength;
370  				inputIdxs.clear();
371  				for (Object o : (Iterable<?>) in.readObject()) {
372  					inputIdxs.add((Integer) o);
373  				}
374  				checkpointIdxs.clear();
375  				for (Object o : (Iterable<?>) in.readObject()) {
376  					checkpointIdxs.add((Integer) o);
377  				}
378  				cmdHistory.clear();
379  				for (Object o : (Iterable<?>) in.readObject()) {
380  					cmdHistory.add((String) o);
381  				}
382  				cmdPos = in.readInt();
383  				miscHistory.clear();
384  				for (Object o : (Iterable<?>) in.readObject()) {
385  					miscHistory.add((String) o);
386  				}
387  				miscPos = in.readInt();
388  				in.close();
389  				
390  				set(data);
391  				partitioner.connect(this);
392  				setDocumentPartitioner(partitioner);
393  				cur.connect(this);
394  			} catch (ClassCastException cce) {
395  				Acl2sPlugin.logWarning("Data error while reading in session history.  Discarding previous history.", cce);
396  				reset();
397  				ensureDirty();
398  				return;
399  			} catch (ClassNotFoundException e) {
400  				Acl2sPlugin.logWarning("Data error while reading in session history.  Discarding previous history.", e);
401  				reset();
402  				ensureDirty();
403  				return;
404  			} catch (IOException ioe) {
405  				Acl2sPlugin.logWarning("I/O error while reading in session history.  Discarding previous history.", ioe);
406  				reset();
407  				ensureDirty();
408  				return;
409  			}
410  		}
411  	
412  		public InputStream getDumpStream() {
413  			try {
414  				ByteArrayOutputStream bytes = new ByteArrayOutputStream();
415  				ObjectOutputStream out = new ObjectOutputStream(bytes);
416  				out.writeInt(0x0a250000 + versionId);
417  				out.writeObject(get());
418  				out.writeObject(partitioner);
419  				out.writeObject(config);
420  				out.writeObject(cur);
421  				out.writeObject(done);
422  				out.writeInt(dumpLength);
423  				out.writeObject(inputIdxs);
424  				out.writeObject(checkpointIdxs);
425  				out.writeObject(cmdHistory);
426  				out.writeInt(cmdPos);
427  				out.writeObject(miscHistory);
428  				out.writeInt(miscPos);
429  				out.close();
430  				return new ByteArrayInputStream(bytes.toByteArray());
431  			} catch (IOException e) {
432  				Acl2sPlugin.logError("Error building session dump stream.", e);
433  				return null;
434  			}
435  		}
436  	
437  		
438  		
439  		/*===================== state building ======================*/
440  		private void doProcessOutputLine(String s) {
441  			if (s.length() == 0) return;
442  			if (cur.inProofTree) {
443  				int i = s.indexOf("#>\\>");
444  				if (i >= 0) {
445  					if (i > 0) {
446  						doProcessOutputLine(s.substring(0,i) + '\n');
447  					}
448  					cur.endProofTree();
449  					doProcessOutputLine(s.substring(i + 4));
450  				} else {
451  					doProcessOutputLine2(s);
452  				}
453  			} else {
454  				if (s.startsWith("#<\\<0")) {
455  					cur.startProofTree();
456  					doProcessOutputLine(s.substring(5));
457  				} else {
458  					doProcessOutputLine2(s);
459  				}
460  			}
461  		}
462  		
463  		private void doProcessOutputLine2(String s) {
464  			if (s.length() == 0) return;
465  			if (s.endsWith("$\n")) {
466  				/*if (s.endsWith("$ClAuSe{$\n")) {
467  					// TODO: clause stuff
468  				} else*/
469  				if (s.startsWith("$ChEcKpOiNt{$") &&
470  						s.endsWith("$ChEcKpOiNt}$\n")) {
471  					cur.layDownCheckpoint(s.substring(13,s.length() - 14));
472  					return;
473  				}
474  			}
475  			cur.appendOutput(s);
476  		}
477  	
478  		public void appendOutputLine(String s) {
479  			doProcessOutputLine(s);
480  			cur.flushOutput();
481  		}
482  		
483  		public void appendOutputLines(Iterable<String> ss) {
484  			for (String s: ss) {
485  				doProcessOutputLine(s);
486  			}
487  			cur.flushOutput();
488  		}
489  	
490  		protected transient String savedCmd = null;
491  		
492  		public int finishOutput(IStatusInfo statusInfo, IEnvInfo envInfo) {
493  			DoneElement d = cur.finish(statusInfo);
494  			done.add(d);
495  			cur.init(envInfo);
496  			replaceImmediate(cmdHistory.get(cmdPos));
497  			return done.size() - 1;
498  		}
499  	
500  		public void updateLastEnv(IEnvInfo envInfo) {
501  			cur.env = envInfo;
502  		}
503  	
504  		public void nextInput(IInputInfo in) {
505  			cur.nextInput(in);
506  			cmdHistory.set(cmdPos,getImmediate().trim());
507  			replaceImmediate(miscHistory.get(miscPos));
508  		}
509  	
510  		public void newSession() {
511  			cur.init(StartupInputAndEnv.instance);
512  			cur.nextInput(StartupInputAndEnv.instance);
513  		}
514  		
515  		
516  		/*========================== accessing stuff ========================*/
517  		public IEnvInfo getEnv(int idx) {
518  			if (idx == done.size()) {
519  				return cur.getEnv();
520  			} else { 
521  				return done.get(idx).getEnv();
522  			}
523  		}
524  	
525  		public IInputInfo getInput(int idx) {
526  			if (idx == done.size()) {
527  				return cur.getInput();
528  			} else {
529  				return done.get(idx).getInput();
530  			}
531  		}
532  	
533  		public String getOutput(int idx) {
534  			if (idx == done.size()) {
535  				return cur.getOutput();
536  			} else {
537  				return done.get(idx).getOutput();
538  			}
539  		}
540  	
541  		public String getProofTree(int idx) {
542  			if (idx == done.size()) {
543  				return cur.proofTree.toString();
544  			} else {
545  				return done.get(idx).getFinalProofTree();
546  			}
547  		}
548  	
549  		public IStatusInfo getStatus(int idx) {
550  			return done.get(idx).getStatus();
551  		}
552  	
553  		
554  		public int countCompletedCmds() {
555  			return done.size();
556  		}
557  	
558  		
559  		
560  		public int findCheckpointOffset(int idx, String withGoal) {
561  			int base = inputIdxs.get(idx).intValue();
562  			int len = getLength();
563  			try {
564  				for (; base < len;) {
565  					ITypedRegion r = getPartition(base);
566  					if (r.getType().equals(OUTPUT)) break;
567  					if (r.getType().equals(OUTPUTERR)) break;
568  					base = r.getOffset() + r.getLength();
569  				}
570  			} catch (BadLocationException e1) {
571  				// impossible
572  			}
573  			
574  			if (idx == done.size()) {
575  				for (int i = 0; i < cur.chkptNames.size(); i++) {
576  					if (withGoal.contains(cur.chkptNames.get(i)) &&
577  							withGoal.contains(cur.chkptNames.get(i) + " ")) {
578  						return base + cur.chkptOffsets.get(i).intValue();
579  					}
580  				}
581  				return -1;
582  			} else {
583  				DoneElement e = done.get(idx);
584  				for (int i = 0; i < e.chkptNames.length; i++) {
585  					if (withGoal.contains(e.chkptNames[i]) &&
586  							withGoal.contains(e.chkptNames[i] + " ")) {
587  						return base + e.chkptOffsets[i];
588  					}
589  				}
590  				return -1;
591  			}
592  		}
593  		
594  		
595  		/*========================= misc ==========================*/
596  		public SessionMode getAcl2Mode() {
597  			if (peer != null) {
598  				return peer.getAcl2Mode();
599  			} else {
600  				return config.getMode();
601  			}
602  		}
603  		
604  		public MyInteractiveSessionConfig getConfig() {
605  			config.setMode(getAcl2Mode());
606  			config.setBaseConfig(Acl2sPlugin.getBaseConfig());
607  			config.setAcl2CustomizationOverride(Acl2Prefs.getCustomizationOverride(null, this));
608  			config.setSuperHistoryEnabled(peer != null && peer.getLineMode().cancelOnError());
609  			config.setSuperHistoryTrackMonitorsOverride(InteractionPrefs.getTrackMonitorsOverride(null, this));
610  			config.setSuperHistoryTrackPresentationOverride(InteractionPrefs.getTrackPresentationOverride(null, this));
611  			return config;
612  		}
613  		
614  		public void setAcl2Mode(SessionMode mode) {
615  			if (peer != null) {
616  				peer.setAcl2Mode(mode);
617  			} else {
618  				config.setMode(mode);
619  			}
620  		}
621  		
622  		void append(String s, String typ) {
623  			if (s.length() == 0) return;
624  			partitioner.preAppend(typ);
625  			newDumpLength = dumpLength + s.length();
626  			replace2(dumpLength, 0, s);
627  			dumpLength = newDumpLength;
628  		}
629  		
630  		protected void doFireDocumentChanged2(DocumentEvent event) {
631  			super.doFireDocumentChanged2(event);
632  			/*if (event.fOffset > dumpLength &&
633  					(event.fText.equals("\r") || event.fText.equals("\n"))) {
634  				trySubmitImmediate();
635  			}*/
636  		}
637  		
638  		/*transient boolean replacingSep = false;
639  		public void updateSep() {
640  			String immediate = getImmediate();
641  			if (immediate.indexOf('\n') >= 0 || immediate.indexOf('\r') >= 0) {
642  				if (getChar2(dumpLength) == ' ') {
643  					replacingSep = true;
644  					replace2(dumpLength, 1, "\n");
645  					replacingSep = false;
646  				}
647  			} else {
648  				if (getChar2(dumpLength) != ' ') {
649  					replacingSep = true;
650  					replace2(dumpLength, 1, " ");
651  					replacingSep = false;
652  				}
653  			}
654  		}*/
655  		
656  		static final Sym U_KEYWORD = Sym.createKeyword("U");
657  		static final Sym UBT_KEYWORD = Sym.createKeyword("UBT");
658  		static final Sym UBTB_KEYWORD = Sym.createKeyword("UBT!");
659  		static final Sym UBU_KEYWORD = Sym.createKeyword("UBU");
660  		static final Sym UBUB_KEYWORD = Sym.createKeyword("UBU!");
661  		
662  		public boolean trySubmitImmediate(SessionEditor ctx) {
663  			String immediate = getImmediate() + "\n";
664  			StringParser p = new StringParser(null, immediate); // assign here to fix warning (!) 
665  			try {
666  				switch (session.getStatus()) {
667  					case ISessionStatusListener.READY_CMD_SESSION:
668  					{
669  						p = new StringParser(session.getCurrentEnv().getParseContext(), immediate);
670  						ResolvableUserInput preCmd = ResolvableUserInput.parse(p,ID,this);
671  						p.skipSpaceAndComments();
672  						String rest = immediate.substring(p.getPos());
673  						
674  						// special cases:
675  						boolean handledSpecially = false;
676  						if (preCmd instanceof UnresolvedKeywordCmd && getPeer() != null) {
677  							UnresolvedKeywordCmd preKeyCmd = (UnresolvedKeywordCmd) preCmd;
678  							Sym key = preKeyCmd.getKeyword();
679  							LispObject[] params = preKeyCmd.getParams();
680  							if (key == U_KEYWORD) {
681  								if (params.length == 0) {
682  									// EVERYTHING CHECKS OUT
683  									newImmediateCmd(preCmd.getInputString(),rest);
684  									IPeerDocument peer = getPeer();
685  									peer.retreatLine(ctx);
686  									handledSpecially = true;
687  								}
688  							} else if (key == UBT_KEYWORD || key == UBTB_KEYWORD ||
689  									   key == UBU_KEYWORD || key == UBUB_KEYWORD) {
690  								if (params.length == 1) {
691  									IResultInfo r;
692  									if (key == UBT_KEYWORD || key == UBTB_KEYWORD) {
693  										r = session.systemCmd2("(acl2s::print-ubt-superlen " + params[0].toString() + ")\n");
694  									} else {
695  										r = session.systemCmd2("(acl2s::print-ubu-superlen " + params[0].toString() + ")\n");
696  									}
697  									
698  									try {
699  										String output = r.getOutput();
700  										int lstart = output.indexOf("INDEX:");
701  										if (lstart < 0) throw new NumberFormatException();
702  										int numstart = lstart + "INDEX:".length();
703  										int numend = Math.max(output.indexOf('\n', numstart), numstart);
704  										output = output.substring(numstart, numend);
705  										int superLen = Integer.parseInt(output);
706  										
707  										int modlIdx = done.size() - 1;
708  										for (;;) {
709  											if (modlIdx < 0) break;
710  											DoneElement elt = done.get(modlIdx);
711  											if (elt == null) break;
712  											IEnvInfo env = elt.getEnv();
713  											if (env == null) break;
714  											if (env.getSuperDoneSize() <= superLen) {
715  												// EVERYTHING CHECKS OUT
716  												newImmediateCmd(preCmd.getInputString(),rest);
717  												IPeerDocument peer = getPeer();
718  												peer.retreatBackThrough(ctx, modlIdx);
719  												handledSpecially = true;
720  												break;
721  											}
722  											modlIdx--;
723  										}
724  									} catch (NumberFormatException e) {
725  										// do nothing
726  									}
727  								}
728  							}
729  						}
730  						
731  						if (!handledSpecially) {
732  							ResolvedInput cmd = preCmd.resolve(session);
733  							newImmediateCmd(preCmd.getInputString(),rest);
734  							if (getPeer() != null && cmd.couldNeedUndo() && InteractionPrefs.isRelevantCmdPrefWormhole(this)) {
735  								cmd = new WormholeWrappedInput(cmd);
736  							}
737  							session.submitCmdInput(cmd);
738  						}
739  					}
740  						break;
741  					case ISessionStatusListener.READY_MISC_SESSION:
742  					{
743  						//p = new StringParser(null, immediate);
744  						p.skipSpaceAndComments();
745  						final int start = p.getPos();
746  						LispObject.parse(p);
747  						final int end = p.getPos();
748  						String input = immediate.substring(start,end);
749  						newImmediateMisc(start,end);
750  						session.submitMiscInput(input);
751  					}
752  						break;
753  					default:
754  						// ignore
755  				}
756  				return true;  // just means don't insert the '\n'
757  			} catch (ParseException pe) {
758  				if (p.peek() == StringParser.EOF) return false; // just needs more input 
759  				ctx.setHighlightRange(dumpLength+1+p.getPos(),0,true);
760  				ctx.errorMessageBox("Parse Error", pe.getMessage() +
761  						"\n\nCursor has been positioned at spot of error.");
762  				return true;
763  			} catch (BadCmdException e) {
764  				ctx.setHighlightRange(dumpLength+1,0,true);
765  				ctx.errorMessageBox("Bad Command", e.getMessage() +
766  						"\n\nCursor has been positioned at violating command.");
767  				return true;
768  			} catch (SessionDeadException e) {
769  				// ignore
770  				return true;
771  			} catch (SessionException e) {
772  				// shouldn't happen
773  				Acl2sPlugin.logError("Unexpected session state.", e);
774  				return true;
775  			}
776  		}
777  		
778  		protected void newImmediateCmd(String input, String rest) {
779  			cmdPos = cmdHistory.size();
780  			cmdHistory.set(cmdPos-1,input);
781  			cmdHistory.add("");
782  			replaceImmediate(rest);
783  		}
784  		
785  		protected void newImmediateMisc(int start, int end) {
786  			String immediate = getImmediate();
787  			String input = immediate.substring(start,end);
788  			miscPos = miscHistory.size();
789  			miscHistory.set(miscPos-1,input);
790  			String rest = immediate.substring(end).trim();
791  			miscHistory.add(rest);
792  			replaceImmediate(miscHistory.get(miscPos));
793  		}
794  		
795  		/*protected void removeFromImmediate(int n) {
796  			replace2(dumpLength,Math.min(n,getLength()-dumpLength),"");
797  		}
798  		
799  		protected void stowImmediateCmd() {
800  			cmdHistory.set(cmdPos,getImmediate().trim());
801  		}
802  		
803  		protected void trimImmediateTail() {
804  			int newLen = getLength();
805  			while (newLen > 0 && Character.isSpaceChar(getChar2(newLen-1))) {
806  				newLen--;
807  			}
808  			replace2(newLen,getLength()-newLen,"");
809  		}*/
810  	
811  		
812  		public void replaceImmediate(String s) {
813  			String sep;
814  			if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0) {
815  				sep = "\n";
816  			} else {
817  				sep = " ";
818  			}
819  			replace2(dumpLength,getLength()-dumpLength,sep + s);
820  		}
821  	
822  	
823  		public void cmdPrevious() {
824  			switch (session.getStatus()) {
825  			case ISessionStatusListener.READY_CMD_SESSION:
826  				if (cmdPos == 0) return;
827  				cmdHistory.set(cmdPos,getImmediate());
828  				cmdPos--;
829  				replaceImmediate(cmdHistory.get(cmdPos));
830  				resetUndoStuff();
831  				break;
832  			case ISessionStatusListener.READY_MISC_SESSION:
833  				if (miscPos == 0) return;
834  				miscHistory.set(miscPos,getImmediate());
835  				miscPos--;
836  				replaceImmediate(miscHistory.get(miscPos));
837  				resetUndoStuff();
838  				break;
839  			}
840  		}
841  	
842  		public void cmdNext() {
843  			switch (session.getStatus()) {
844  			case ISessionStatusListener.READY_CMD_SESSION:
845  				if (cmdPos == cmdHistory.size()-1) return;
846  				cmdHistory.set(cmdPos,getImmediate());
847  				cmdPos++;
848  				replaceImmediate(cmdHistory.get(cmdPos));
849  				resetUndoStuff();
850  				break;
851  			case ISessionStatusListener.READY_MISC_SESSION:
852  				if (miscPos == miscHistory.size()-1) return;
853  				miscHistory.set(miscPos,getImmediate());
854  				miscPos++;
855  				replaceImmediate(miscHistory.get(miscPos));
856  				resetUndoStuff();
857  				break;
858  			}
859  		}
860  		
861  		public void cmdLast() {
862  			switch (session.getStatus()) {
863  			case ISessionStatusListener.READY_CMD_SESSION:
864  				if (cmdPos == cmdHistory.size()-1) return;
865  				cmdHistory.set(cmdPos,getImmediate());
866  				cmdPos = cmdHistory.size() - 1;
867  				replaceImmediate(cmdHistory.get(cmdPos));
868  				resetUndoStuff();
869  				break;
870  			case ISessionStatusListener.READY_MISC_SESSION:
871  				if (miscPos == miscHistory.size()-1) return;
872  				miscHistory.set(miscPos,getImmediate());
873  				miscPos = miscHistory.size() - 1;
874  				replaceImmediate(miscHistory.get(miscPos));
875  				resetUndoStuff();
876  				break;
877  			}
878  		}
879  		
880  		public int getNextInputIdx(int start) {
881  			return getAdjacentIdx(start,true,inputIdxs);
882  		}
883  		
884  		public int getPrevInputIdx(int start) {
885  			return getAdjacentIdx(start,false,inputIdxs);
886  		}
887  		
888  		public int getNextCheckpointIdx(int start) {
889  			return getAdjacentIdx(start,true,checkpointIdxs);
890  		}
891  		
892  		public int getPrevCheckpointIdx(int start) {
893  			return getAdjacentIdx(start,false,checkpointIdxs);
894  		}
895  		
896  		private int getPseudoIndexIdx(int i, ArrayList<Integer> src) {
897  			int sz = src.size();
898  			if (i < sz - 1) {
899  				return src.get(i).intValue();
900  			} else if (i == sz - 1) {
901  				return dumpLength + 1;
902  			} else {
903  				return getLength();
904  			}
905  		}
906  		
907  		private int getAdjacentIdx(int start, boolean direction, ArrayList<Integer> src) {
908  			int max = src.size();
909  			if (max == 0) return 0;
910  			int inc = 0x10000000;
911  			int i = 0;
912  			while (inc > 0) {  // binary search, undershoot
913  				if (i+inc <= max && getPseudoIndexIdx(i+inc,src) < start) {
914  					i += inc;
915  				}
916  				inc >>= 1;
917  			}
918  			if (direction && i < max) {
919  				i++;
920  				if (getPseudoIndexIdx(i,src) == start && i < max) {
921  					i++;
922  				}
923  			}
924  			return getPseudoIndexIdx(i,src);
925  		}
926  		
927  		public int getModelIdxOfOffset(int offset) {
928  			for (int i = inputIdxs.size() - 1; i > 0; i--) {
929  				if (offset >= inputIdxs.get(i).intValue()) return i;
930  			}
931  			return 0;
932  		}
933  	
934  		public int getOffsetOfModelIdx(int idx) {
935  			return inputIdxs.get(idx).intValue();
936  		}
937  	
938  		public static final String PROMPT = "__a2s_session_prompt";
939  		public static final String CMDTYPE = "__a2s_session_cmdtype";
940  		public static final String INPUT = "__a2s_session_input";
941  		public static final String OUTPUT = "__a2s_session_output";
942  		public static final String OUTPUTERR = "__a2s_session_outputerr";
943  		
944  		public static String[] getContentTypes() {
945  			return new String[] { PROMPT, CMDTYPE, INPUT, OUTPUT, OUTPUTERR, Partitioner.DEFAULT };
946  		}
947  		
948  		public String[] getLegalContentTypes() {
949  			return getContentTypes();
950  		}
951  	
952  		// must be static or else tries to serialize the document
953  		static class Partitioner extends AbstractDocumentPartitioner implements Serializable {
954  			private static final long serialVersionUID = 2L;
955  	
956  			transient String appendType = null;
957  			transient SessionDocument doc = null;
958  	
959  			public Partitioner() {
960  				super();
961  				addRegion(0,1,DEFAULT);
962  			}
963  			
964  			public String[] getLegalContentTypes() {
965  				return getContentTypes();
966  			}
967  	
968  			public void connect(IDocument document) {
969  				super.connect(document);
970  				doc = (SessionDocument) document;
971  			}
972  			
973  			public IRegion documentChanged2(DocumentEvent event) {
974  				int offset = event.getOffset();
975  				if (appendType != null) {
976  					int idx = getLastDumpRegionIdx();
977  					int len = event.getText().length();
978  					MyTypedRegion r = idx >= 0 ? regions.get(idx) : new MyTypedRegion(0,0,DEFAULT);
979  					if (r.getType().equals(appendType)) {
980  						//System.err.println("Replacing a " + appendType);
981  						replaceRegion(idx,new MyTypedRegion(r.getOffset(),r.getLength()+len,appendType),true);
982  						offset = r.getOffset();
983  					} else {
984  						//System.err.println("Inserting a " + appendType);
985  						insertRegion(idx+1,new MyTypedRegion(offset,len,appendType),true);
986  					}
987  					appendType = null;
988  				}
989  				MyTypedRegion r = new MyTypedRegion(doc.newDumpLength,doc.getLength()-doc.newDumpLength,DEFAULT);
990  				replaceRegion(getImmediateRegionIdx(),r,true);
991  				
992  				return new Region(offset,doc.getLength()-offset);
993  			}
994  			
995  			int getLastDumpRegionIdx() {
996  				return regions.size() - 2;
997  			}
998  			
999  			int getImmediateRegionIdx() {
1000 				return regions.size() - 1;
1001 			}
1002 			
1003 			void preAppend(String typ) {
1004 				if (appendType != null) {
1005 					throw new IllegalStateException();
1006 				}
1007 				appendType = typ;
1008 			}
1009 	
1010 			public void changeLastType(String fromTyp, String toTyp) {
1011 				int idx = getLastDumpRegionIdx();
1012 				if (idx < 0) return;
1013 				MyTypedRegion r = regions.get(idx);
1014 				if (r.getType().equals(fromTyp)) {
1015 					replaceRegion(idx,new MyTypedRegion(r.getOffset(),r.getLength(),toTyp),false);
1016 					doc.setDocumentPartitioner(Partitioner.this);
1017 				}
1018 			}
1019 		}
1020 	
1021 		
1022 		
1023 		static final class CurElement implements Serializable {
1024 			private static final long serialVersionUID = 5L;
1025 	
1026 			transient SessionDocument doc = null;
1027 			void connect(SessionDocument d) { doc = d; }
1028 			
1029 			IEnvInfo env;
1030 			IInputInfo input;
1031 			final StringBuilder output = new StringBuilder();
1032 			StringBuilder proofTree = new StringBuilder();
1033 			StringBuilder nextProofTree = new StringBuilder();
1034 			IStatusInfo status;
1035 			ArrayList<Integer> chkptOffsets = new ArrayList<Integer>();
1036 			ArrayList<String>  chkptNames   = new ArrayList<String>();
1037 			
1038 			boolean hideOutput;
1039 			int outputPos;
1040 			boolean inProofTree;
1041 			
1042 			public void init(IEnvInfo env) {
1043 				this.env = env;
1044 				input = null;
1045 				output.setLength(0);
1046 				outputPos = 0;
1047 				status = null;
1048 				if (env.getPrompt() != null)
1049 					doc.append(env.getPrompt(), PROMPT);
1050 				doc.inputIdxs.add(Integer.valueOf(doc.dumpLength));
1051 				doc.checkpointIdxs.add(Integer.valueOf(doc.dumpLength));
1052 				chkptOffsets.clear();
1053 				chkptNames.clear();
1054 				inProofTree = false;
1055 				proofTree.setLength(0);
1056 				nextProofTree.setLength(0);
1057 			}
1058 	
1059 			public void nextInput(IInputInfo i) {
1060 				if (input != null) throw new IllegalStateException();
1061 				input = i;
1062 				if (i.getInputTypeString() != null) 
1063 					doc.append(i.getInputTypeString() + " ", CMDTYPE);
1064 				if (i.getInputString() != null) {
1065 					if (i.getInputString().indexOf('\n') > 0) {
1066 						// prefix a newline so that indention looks right
1067 						doc.append("\n" + i.getInputString() + "\n", INPUT);
1068 					} else {
1069 						doc.append(i.getInputString() + "\n", INPUT);
1070 					}
1071 				}
1072 				if (i.getMetaOutput() != null) {
1073 					doc.append(i.getMetaOutput() + "\n", OUTPUT);
1074 				}
1075 				hideOutput = input.hideOutput();
1076 				
1077 				startProofTree();
1078 				endProofTree(); // notify listeners of clear output
1079 			}
1080 			
1081 			public void appendOutput(String s) {
1082 				if (input == null) throw new IllegalStateException();
1083 				if (inProofTree) {
1084 					nextProofTree.append(s);
1085 				} else {
1086 					output.append(s);
1087 				}
1088 			}
1089 	
1090 			//harshrc:: Heres where Peter sets the checkpoint offsets and 
1091 			//remembers the goal names.
1092 			public void layDownCheckpoint(String goalStr) {
1093 				if (input == null) throw new IllegalStateException();
1094 				chkptOffsets.add(Integer.valueOf(output.length()));
1095 				chkptNames.add(goalStr);
1096 				flushOutput();
1097 				doc.checkpointIdxs.add(Integer.valueOf(doc.dumpLength));
1098 				output.append("^^^ Checkpoint " + goalStr + " ^^^\n");
1099 			}
1100 	
1101 			public void flushOutput() {
1102 				if (!hideOutput && output.length() > outputPos) {
1103 					doc.append(output.substring(outputPos), OUTPUT);
1104 					outputPos = output.length();
1105 				}
1106 			}
1107 	
1108 			public void startProofTree() {
1109 				inProofTree = true;
1110 				nextProofTree.setLength(0);
1111 			}
1112 	
1113 			public void endProofTree() {
1114 				inProofTree = false;
1115 				
1116 				StringBuilder tmp = proofTree;
1117 				tmp.setLength(0); // recycle
1118 				proofTree = nextProofTree;
1119 				nextProofTree = tmp;
1120 				
1121 				int idx = doc.countCompletedCmds();
1122 				for (IProofTreeListener l : doc.listeners) {
1123 					l.newProofTree(doc,idx);
1124 				}
1125 			}
1126 			
1127 			
1128 			public DoneElement finish(IStatusInfo s) {
1129 				flushOutput();
1130 				/*if (proofTree.length() > 0) {
1131 					doc.append(">>> Final proof tree output:\n", OUTPUT);
1132 					doc.append(proofTree.toString(), OUTPUT);
1133 					doc.append("<<< (end of proof tree output)\n", OUTPUT);
1134 				}*/
1135 				if (!s.wasSuccess()) {
1136 					doc.partitioner.changeLastType(OUTPUT,OUTPUTERR);
1137 				}
1138 				status = s;
1139 				return new DoneElement(this);
1140 			}
1141 			
1142 			public DoneElement forceFinish() {
1143 				flushOutput();
1144 				return new DoneElement(this);
1145 			}
1146 			
1147 			public IEnvInfo getEnv() { return env; }
1148 			public IInputInfo getInput() { return input; }
1149 			public String getOutput() { return output.toString(); }
1150 			public IStatusInfo getStatus() { return status; }
1151 		}
1152 		
1153 		static final class DoneElement implements Serializable {
1154 			private static final long serialVersionUID = 5L;
1155 			
1156 			final IEnvInfo env;
1157 			final IInputInfo input;
1158 			final String output;
1159 			final IStatusInfo status;
1160 			final int[] chkptOffsets;
1161 			final String[] chkptNames;
1162 			final String finalProofTree;
1163 	
1164 			public DoneElement(CurElement that) {
1165 				this.env = that.env;
1166 				this.input = that.input;
1167 				this.output = that.output.toString();
1168 				this.status = that.status;
1169 				int sz = that.chkptOffsets.size();
1170 				assert sz == that.chkptNames.size();
1171 				this.chkptOffsets = new int[sz];
1172 				this.chkptNames = new String[sz];
1173 				for (int i = 0; i < sz; i++) {
1174 					this.chkptOffsets[i] = that.chkptOffsets.get(i).intValue();
1175 					this.chkptNames[i] = that.chkptNames.get(i);
1176 				}
1177 				finalProofTree = that.proofTree.toString();
1178 			}
1179 	
1180 			public IEnvInfo getEnv() { return env; }
1181 			public IInputInfo getInput() { return input; }
1182 			public String getOutput() { return output; }
1183 			public IStatusInfo getStatus() { return status; }
1184 			public String getFinalProofTree() { return finalProofTree; }
1185 		}
1186 	
1187 		public void dispose() {
1188 			session.killSession();
1189 			session.removeSessionStatusListener(this);
1190 			tokens.disconnect();
1191 		}
1192 	
1193 		public void interrupt() throws SessionException {
1194 			synchronized (session) {
1195 				switch (session.getStatus()) {
1196 				case READY_MISC_SESSION:
1197 				case BUSY_SESSION:
1198 					session.interruptSession();
1199 					// fall through
1200 				case READY_CMD_SESSION:
1201 					session.scheduleAbort();
1202 					break;
1203 				}
1204 			}
1205 		}
1206 	
1207 		
1208 		public void reparse() {
1209 			tokens.reparse();
1210 		}
1211 	
1212 		public void cleanCmdHist() {
1213 			cmdHistory.clear();
1214 			cmdHistory.add("");
1215 			cmdPos = 0;
1216 			miscHistory.clear();
1217 			miscHistory.add("");
1218 			miscPos = 0;
1219 		}
1220 	
1221 		
1222 		// PROOF TREE LISTENERS
1223 		
1224 		protected ArrayList<IProofTreeListener> listeners = new ArrayList<IProofTreeListener>();
1225 		
1226 		public void addProofTreeListener(IProofTreeListener l) {
1227 			listeners.add(l);
1228 		}
1229 	
1230 		public void removeProofTreeListener(IProofTreeListener l) {
1231 			listeners.remove(l);
1232 		}
1233 	
1234 	}