1    	package acl2s.plugin.editors.lisp;
2    	
3    	import java.io.ByteArrayInputStream;
4    	import java.io.File;
5    	import java.io.InputStream;
6    	import java.util.ArrayList;
7    	import java.util.HashSet;
8    	import java.util.LinkedList;
9    	import java.util.TreeSet;
10   	
11   	import org.eclipse.core.runtime.CoreException;
12   	import org.eclipse.jface.text.BadLocationException;
13   	import org.eclipse.jface.text.DocumentEvent;
14   	import org.eclipse.jface.text.DocumentPartitioningChangedEvent;
15   	import org.eclipse.jface.text.IRegion;
16   	import org.eclipse.jface.text.Region;
17   	import org.eclipse.ui.IEditorInput;
18   	import org.eclipse.ui.IFileEditorInput;
19   	import org.peterd.util.io.MiscIO;
20   	
21   	import acl2s.lib.certify.Certifier;
22   	import acl2s.lib.parse.EmptyParse;
23   	import acl2s.lib.parse.IParseContext;
24   	import acl2s.lib.parse.ParseException;
25   	import acl2s.lib.parse.PositionedParseException;
26   	import acl2s.lib.parse.StringParser;
27   	import acl2s.lib.parse.SubstringParser;
28   	import acl2s.lib.parse.obj.LispObject;
29   	import acl2s.lib.session.IEnvInfo;
30   	import acl2s.lib.session.IStatusInfo;
31   	import acl2s.lib.session.SessionDeadException;
32   	import acl2s.lib.session.SessionException;
33   	import acl2s.lib.session.SessionMode;
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.parse.DocumentParser;
43   	import acl2s.plugin.parse.LispToken;
44   	import acl2s.plugin.parse.LispTokens;
45   	import acl2s.plugin.prefs.InteractionPrefs;
46   	import acl2s.uilib.cmd.BadCmdException;
47   	import acl2s.uilib.cmd.ResolvableUserInput;
48   	import acl2s.uilib.cmd.ResolvedInput;
49   	import acl2s.uilib.session.IInputInfo;
50   	import acl2s.uilib.session.ISessionModel;
51   	import acl2s.uilib.session.ISessionStatusListener;
52   	import acl2s.uilib.session.RedoInput;
53   	import acl2s.uilib.session.StartupInputAndEnv;
54   	import acl2s.uilib.session.UndoInput;
55   	
56   	public class LispDocument extends A2sDocument implements IPeerDocument {
57   		public static String ID = LispDocument.class.getCanonicalName();
58   		
59   		protected final Partitioner ldp = new Partitioner();
60   		protected ISessionDocument ses = null;
61   		
62   		// ====== DATA / ACCESS / STORAGE =======
63   		private class DoneInfo {
64   			int modelIdx;
65   			ResolvedInput input;
66   	
67   			protected DoneInfo(int modelIdx, ResolvedInput input) {
68   				this.modelIdx = modelIdx;
69   				this.input = input;
70   			}
71   			
72   			public int getInputType() {
73   				if (input == null) {
74   					return ResolvedInput.UNKNOWN;
75   				} else {
76   					return input.getType();
77   				}
78   			}
79   		}
80   		
81   		private class AboveInfo {
82   			Region region;
83   			ResolvableUserInput resolvable;
84   	
85   			protected AboveInfo(Region region, ResolvableUserInput resolvable) {
86   				this.region = region;
87   				this.resolvable = resolvable;
88   			}
89   		}
90   		
91   		protected final ArrayList<AboveInfo> aboveInfo = new ArrayList<AboveInfo>();
92   		protected final ArrayList<DoneInfo> doneInfo = new ArrayList<DoneInfo>();
93   		
94   	/*	protected BeginBookCmdInput beginBookInput = null;
95   		protected int beginBookIdx = -1;*/
96   		protected SessionMode acl2Mode = SessionMode.compatible;
97   		protected LineMode lineMode = LineMode.getDefault();
98   			
99   		// just a view
100  		protected transient int lineIdx = 0;
101  		protected transient int todoIdx = 0;
102  	
103  		protected void updateIdxs() {
104  			lineIdx = getPreviousEnd(doneInfo.size());
105  			todoIdx = getPreviousEnd(aboveInfo.size());
106  		}
107  	
108  		protected void checkIdxs() {
109  			if (lineIdx != getPreviousEnd(doneInfo.size())) {
110  				throw new IllegalStateException();
111  			}
112  			if (todoIdx != getPreviousEnd(aboveInfo.size())) {
113  				throw new IllegalStateException();
114  			}
115  		}
116  		
117  		private int getPreviousEnd(int i) {
118  			assert i <= aboveInfo.size();
119  			if (i > 0) {
120  				return regionEnd(aboveInfo.get(i-1).region);
121  			} else {
122  				return 0;
123  			}
124  		}
125  		
126  		public static int regionEnd(Region r) {
127  			return r.getOffset() + r.getLength();
128  		}
129  	
130  		public MyTypedRegion getTodoRegion() {
131  			return new MyTypedRegion(lineIdx,todoIdx-lineIdx,TODO);
132  		}
133  		
134  		public MyTypedRegion getBelowRegion() {
135  			return new MyTypedRegion(todoIdx,getLength()-todoIdx,DEFAULT);
136  		}
137  		
138  		public int readOnlyPoint() { return todoIdx; }
139  		
140  		@Override
141  		protected boolean allowReplace(final int pos, final int length, final String text) {
142  			if (pos < todoIdx) {
143  				String pref = InteractionPrefs.getAboveEditPref(Acl2sPlugin.inDisplayThread() ? Acl2sPlugin.getShell() : null, this);
144  				if (pref == InteractionPrefs.ABOVE_EDIT_UBT) {
145  					display.asyncExec(new Runnable() {
146  						public void run() {
147  							int oldTodoIdx;
148  							do {
149  								oldTodoIdx = todoIdx;
150  								retreatLine2(null);
151  							} while (oldTodoIdx > todoIdx && pos < todoIdx);
152  							LispDocument.this.replace2(pos, length, text);
153  						}
154  					});
155  				}
156  				return false;
157  			} else {
158  				return true;
159  			}
160  		}
161  	
162  		public void replace(int pos, int length, String text) throws BadLocationException {
163  			if (text != null && text.indexOf('\t') >= 0) {
164  				text = text.replace("\t","    ");
165  				// shouldn't happen ordinarily with sourceviewer autoedits
166  				// not a general solution because caret doesn't cooperate
167  			}
168  			super.replace(pos, length, text);
169  		}
170  	
171  		public SessionMode getAcl2Mode() { return acl2Mode; }
172  	
173  		public void setAcl2Mode(SessionMode mode) {
174  			if (ses != null && ses.getManager().getStatus() != ISessionStatusListener.NO_SESSION) {
175  				throw new IllegalStateException();
176  			}
177  			if (!mode.equals(acl2Mode)) {
178  				acl2Mode = mode;
179  				ensureDirty();
180  			}
181  			updateDescriptions();
182  		}
183  		
184  		public LineMode getLineMode() { return lineMode; }
185  	
186  		public void setLineMode(LineMode mode) {
187  			if (ses != null && ses.getManager().getStatus() != ISessionStatusListener.NO_SESSION) {
188  				throw new IllegalStateException();
189  			}
190  			if (!mode.equals(lineMode)) {
191  				lineMode = mode;
192  				ensureDirty();
193  				if (!lineMode.isAdvancable()) {
194  					resetLineAndTodo();
195  				}
196  			}
197  			updateDescriptions();
198  		}
199  		
200  		protected String doGetDescription() {
201  			String desc;
202  			if (ses != null) {
203  				desc = "" + acl2Mode.getName() + " Mode";
204  				if (!lineMode.equals(LineMode.getDefault())) {
205  					desc += " (" + lineMode.getName() + ")";
206  				}
207  				if (!acl2Mode.getUsable()) {
208  					desc += " (not installed!)";
209  				}
210  			} else {
211  				desc = "(no associated session)";
212  			}
213  			if (acl2Mode.getBookDevAllowed() && hasBookCode()) {
214  				desc += " + book code";
215  			}
216  			return desc;
217  		}
218  		
219  		public boolean hasBookCode() {
220  			return tokens.pastBeginBookForm() > 0;
221  		}
222  		
223  		public String getPreamble() {
224  			int pastIdx = tokens.pastBeginBookForm();
225  			if (pastIdx > 0) {
226  				return get2(0,pastIdx);
227  			} else {
228  				return null;
229  			}
230  		}
231  		
232  		public boolean hasCompletedTangent() {
233  			int i = 0;
234  			for (;;) {
235  				if (i >= doneInfo.size()) {
236  					return false;
237  				}
238  				if (doneInfo.get(i).getInputType() == ResolvedInput.BEGIN_BOOK) {
239  					break;
240  				}
241  				i++;
242  			}
243  			i++;
244  			if (i >= doneInfo.size()) {
245  				return false;
246  			}
247  			if (doneInfo.get(i).getInputType() != ResolvedInput.IN_PACKAGE) {
248  				return false;
249  			}
250  			for (;;) {
251  				i++;
252  				if (i >= doneInfo.size()) {
253  					return false;
254  				}
255  				if (doneInfo.get(i).input == null ||
256  					!doneInfo.get(i).input.isEmbeddedEvent()) {
257  					return true;
258  				}
259  			}
260  		}
261  		
262  		// CREATION/SETUP 
263  		
264  		public LispDocument(MyDocumentProvider dp) {
265  			super(dp);
266  			setDocumentPartitioner(ldp);
267  			ldp.connect(this);
268  			tokens = new LispTokens();
269  			tokens.connect(this);
270  		}
271  		
272  		public ISessionDocument getSession() { return ses; }
273  		
274  		public void dispose() {
275  			tokens.disconnect();
276  		}
277  	
278  		public void setA2s(ISessionDocument a) {
279  			if (ses != a) {
280  				ses = a;
281  				if (ses != null) {
282  					ses.killSession(null);
283  				}
284  				doneInfo.clear();
285  				updateIdxs();
286  			}
287  			/*
288  			int max = ses.getModel().countCompletedCmds();
289  			for (int i = 0; i < doneInfo.size(); i++) {
290  				int idx = doneInfo.get(i).modelIdx;
291  				if (idx < 0 || idx >= max) {
292  					Acl2sPlugin.logWarning("Session model index out of bounds.  Moving completed line appropriately.",null);
293  					removeBadModelIdxsFrom(i);
294  					ensureDirty();
295  					break;
296  				}
297  				IInputInfo in = ses.getModel().getInput(doneInfo.get(i).modelIdx);
298  				boolean formMatch = aboveInfo.get(i).resolvable.getInputString().equals(in.getInputString()); 
299  				if (formMatch && in instanceof ResolvedInput) {
300  					doneInfo.get(i).input = (ResolvedInput) in;
301  				} else if (formMatch && in instanceof RedoInput) {
302  					doneInfo.get(i).input = (ResolvedInput) (((RedoInput)in).getOriginalInput());
303  				} else {
304  					Acl2sPlugin.logWarning("Session history doesn't match indices stored for .lisp file.  Moving completed line appropriately.",null);
305  					removeBadModelIdxsFrom(i);
306  					ensureDirty();
307  					break;
308  				}
309  			}*/
310  			partitioningChangedAll();
311  			updateDescriptions();
312  		}
313  	
314  		/*
315  		private void removeBadModelIdxsFrom(int i) {
316  			for (int j = doneInfo.size() - 1; j >= i; j--) {
317  				doneInfo.remove(i);
318  			}
319  			int oldLineIdx = lineIdx;
320  			updateIdxs();
321  			partitioningChanged(lineIdx,oldLineIdx - lineIdx);
322  		}
323  		*/
324  		
325  		
326  		//  LOAD & SAVE
327  		
328  		static class Annotation implements Comparable<Annotation> {
329  			int idx;
330  			int prio;
331  			String str;
332  			
333  			public Annotation(int idx, String str, int prio) {
334  				this.idx = idx;
335  				this.str = str;
336  				this.prio = prio;
337  			}
338  			
339  			public int compareTo(Annotation that) {
340  				if (this.idx == that.idx) {
341  					return this.prio - that.prio;
342  				} else {
343  					return this.idx - that.idx;
344  				}
345  			}
346  		}
347  		
348  		public final static String TODO_STRING = "#|ACL2s-ToDo-Line|#\n";
349  		public final static String SMODE_PREFIX = ";$ACL2s-SMode$;";
350  		public final static String LMODE_PREFIX = ";$ACL2s-LMode$;";
351  	
352  		public final static String PREAMBLE_START = "#|$ACL2s-Preamble$;\n";
353  		public final static int PREAMBLE_START_LEN = PREAMBLE_START.length();
354  		public final static String PREAMBLE_END = ";$ACL2s-Preamble$|#\n";
355  		public final static int PREAMBLE_END_LEN = PREAMBLE_END.length();
356  	
357  		public final static String OLD_LINE_STRING = ";$ACL2-A2S-LINE$;";
358  		public final static String OLD_TODO_STRING = "#|THE-LINE-INPROGRESS|#\n";
359  		public final static String OLD_SMODE_PREFIX = ";$ACL2-A2S-MODE$;";
360  		public final static String OLD_LMODE_PREFIX = ";$ACL2-A2S-LMODE$;";
361  		
362  		public final static String OLD_PREAMBLE_START = "#|$ACL2-A2S-PREAMBLE$;\n";
363  		public final static int OLD_PREAMBLE_START_LEN = OLD_PREAMBLE_START.length();
364  		public final static String OLD_PREAMBLE_END = ";$ACL2-A2S-PREAMBLE$|#\n";
365  		public final static int OLD_PREAMBLE_END_LEN = OLD_PREAMBLE_END.length();
366  	
367  		public InputStream getDumpStream() {
368  			StringBuilder dump = new StringBuilder();
369  	
370  			int pastIdx = 0;
371  			if (acl2Mode.getBookDevAllowed()) {
372  				pastIdx = tokens.pastBeginBookForm();
373  			}
374  			if (pastIdx > 0) {
375  				dump.append(PREAMBLE_START);
376  			}
377  	
378  			// mode output
379  			if (acl2Mode != SessionMode.compatible) {
380  				String[] modeInit = acl2Mode.getInitStrings();
381  				if (modeInit != null) {
382  					for (int i = 0; i < modeInit.length; i++) {
383  						dump.append(modeInit[i]);
384  					}
385  				}
386  				dump.append(SMODE_PREFIX);
387  				dump.append(acl2Mode.getName());
388  				dump.append('\n');
389  			}
390  			
391  			if (lineMode != LineMode.enforced) {
392  				dump.append(LMODE_PREFIX);
393  				dump.append(lineMode.getName());
394  				dump.append('\n');
395  			}
396  			
397  			TreeSet<Annotation> annotations = new TreeSet<Annotation>();
398  	
399  			
400  			if (lineMode.savePositions() && todoIdx > 0) {
401  				annotations.add(new Annotation(todoIdx,TODO_STRING,1));
402  			}
403  	
404  			if (pastIdx > 0) {
405  				annotations.add(new Annotation(pastIdx,PREAMBLE_END,2));
406  			}
407  	
408  	
409  			int pos = 0;
410  			for (Annotation a : annotations) { // in order
411  				assert pos <= a.idx;
412  				dump.append(get2(pos,a.idx-pos));
413  				dump.append(a.str);
414  				pos = a.idx;
415  			}
416  			dump.append(get2(pos,getLength()-pos));
417  			
418  			return new ByteArrayInputStream(dump.toString().getBytes());
419  		}
420  	
421  		public void set(String init) {
422  			aboveInfo.clear();
423  			doneInfo.clear();
424  			setDocumentPartitioner(null);
425  			ldp.disconnect();
426  			acl2Mode = SessionMode.compatible;
427  	
428  			StringBuilder content = new StringBuilder(init);
429  			
430  			int idx = 0;  // translate all tabs to 8 spaces
431  			for (; idx < content.length();) {
432  				idx = content.indexOf("\t", idx+1);
433  				if (idx < 0) break;
434  				content.replace(idx,idx+1,"        ");
435  			}
436  			idx = 0;     // remove all carriage returns
437  			for (; idx < content.length();) {
438  				idx = content.indexOf("\r", idx+1);
439  				if (idx < 0) break;
440  				content.replace(idx,idx+1,"");
441  			}
442  			
443  			
444  			// get rid of preamble markings; they're really for .lisp compatibility
445  			idx = content.indexOf(PREAMBLE_START);
446  			if (idx >= 0) {
447  				content.replace(idx, idx + PREAMBLE_START_LEN, "");
448  			} else {
449  				idx = content.indexOf(OLD_PREAMBLE_START);
450  				if (idx >= 0) {
451  					content.replace(idx, idx + OLD_PREAMBLE_START_LEN, "");
452  				}
453  			}
454  			idx = content.indexOf(PREAMBLE_END);
455  			if (idx >= 0) {
456  				content.replace(idx, idx + PREAMBLE_END_LEN, "");
457  			} else {
458  				idx = content.indexOf(OLD_PREAMBLE_END);
459  				if (idx >= 0) {
460  					content.replace(idx, idx + OLD_PREAMBLE_END_LEN, "");
461  				}
462  			}
463  	
464  			// read and get rid of code before any session mode spec
465  			idx = content.indexOf(SMODE_PREFIX); 
466  			if (idx < 0) idx = content.indexOf(OLD_SMODE_PREFIX);
467  	
468  			if (idx >= 0) {
469  				int after = content.indexOf(";", idx+1) + 1;
470  				int eol = after;
471  				while (eol < content.length()) {
472  					char ch = content.charAt(eol);
473  					if (ch == '\r' || ch == '\n') break;
474  					eol++;
475  				}
476  				String modeString = content.substring(after,eol);
477  				acl2Mode = Acl2sPlugin.getBaseConfig().lookupMode(modeString);
478  				if (acl2Mode == null) {
479  					acl2Mode = SessionMode.getUninstalledMode(modeString, new String[] { content.substring(0, idx) });
480  					Acl2sPlugin.maybeWarnUninstalledMode(modeString);
481  					// old approach:
482  					//Acl2sPlugin.logWarning("Unrecognized session mode (using default instead): " + modeString, null);
483  					//acl2Mode = Acl2sPlugin.getBaseConfig().getDefaultMode();
484  					//ensureDirty();
485  				}
486  				int pastModeDef = eol+1;
487  				if (pastModeDef < content.length() && content.charAt(eol) == '\r' &&
488  						content.charAt(pastModeDef) == '\n') {
489  					pastModeDef++;
490  				}
491  				content.delete(0,pastModeDef);
492  			}
493  	
494  			// read and get rid of any line mode spec
495  			idx = content.indexOf(LMODE_PREFIX);
496  			if (idx < 0) idx = content.indexOf(OLD_LMODE_PREFIX);
497  			
498  			if (idx >= 0) {
499  				int after = content.indexOf(";", idx+1) + 1;
500  				int eol = after;
501  				while (eol < content.length()) {
502  					char ch = content.charAt(eol);
503  					if (ch == '\r' || ch == '\n') break;
504  					eol++;
505  				}
506  				String modeString = content.substring(after,eol);
507  				lineMode = LineMode.lookup(modeString);
508  				if (lineMode == null) {
509  					Acl2sPlugin.logWarning("Unrecognized line mode (using default instead): " + modeString, null);
510  					lineMode = LineMode.getDefault();
511  					ensureDirty();
512  				}
513  				int pastModeDef = eol+1;
514  				if (pastModeDef < content.length() && content.charAt(eol) == '\r' &&
515  						content.charAt(pastModeDef) == '\n') {
516  					pastModeDef++;
517  				}
518  				content.delete(0,pastModeDef);
519  			}
520  			
521  			// { Get rid of & ignore any old "line" spec
522  			idx = content.indexOf(OLD_LINE_STRING); 
523  			if (idx >= 0) {
524  				int eol = idx;
525  				while (eol < content.length()) {
526  					char ch = content.charAt(eol);
527  					if (ch == '\r' || ch == '\n') break;
528  					eol++;
529  				}
530  				int after = eol+1;
531  				if (after < content.length() && content.charAt(eol) == '\r' &&
532  						content.charAt(after) == '\n') {
533  					after++;
534  				}
535  				content.delete(idx,after);
536  			}
537  			// }
538  			
539  			lineIdx = 0;
540  			
541  			// now find actual todo position
542  			todoIdx = content.indexOf(TODO_STRING);
543  			if (todoIdx < 0) todoIdx = content.indexOf(OLD_TODO_STRING);
544  			
545  			if (todoIdx < 0) {
546  				todoIdx = 0;
547  			} else {
548  				int after = content.indexOf("\n", todoIdx) + 1;  // to accommodate either old or new
549  				content.delete(todoIdx, after);
550  				
551  				
552  				SubstringParser p = new SubstringParser(null,content,0,todoIdx);
553  				try {
554  					while (!p.eof()) {
555  						int start = p.getPos();
556  						ResolvableUserInput in = ResolvableUserInput.parse(p, ID, this.getSession()); 
557  						/*if (in instanceof BeginBookCmdInput && beginBookInput == null) {
558  								beginBookInput = (BeginBookCmdInput) in;
559  								beginBookIdx = inputs.size() - 1;
560  							}*/
561  						int end = p.getPos();
562  						aboveInfo.add(new AboveInfo(new Region(start,end-start),in));
563  					}
564  				} catch (EmptyParse ep) {
565  					// ok
566  				} catch (ParseException pe) {
567  					lineIdx = 0;
568  					todoIdx = 0;
569  					aboveInfo.clear();
570  					doneInfo.clear();
571  					Acl2sPlugin.logWarning("Error restoring line position.",pe);
572  					ensureDirty();
573  				} catch (BadCmdException bce) {
574  					lineIdx = 0;
575  					todoIdx = 0;
576  					aboveInfo.clear();
577  					doneInfo.clear();
578  					Acl2sPlugin.logWarning("Error restoring line position.",bce);
579  					ensureDirty();
580  				}
581  	
582  			}
583  			super.set(content.toString());
584  			setDocumentPartitioner(ldp);
585  			ldp.connect(this);
586  			partitioningChangedAll();
587  			updateDescriptions();
588  		}
589  	
590  		public LispDocumentProvider getProvider() {
591  			return Acl2sPlugin.getLispDocumentProvider();
592  		}
593  		
594  		
595  		// LINE MANIPULATION
596  		
597  		protected void doResetLine() {
598  			if (doneInfo.size() == 0) return;
599  			int oldLineIdx = lineIdx;
600  			doneInfo.clear();
601  			updateIdxs();
602  			partitioningChanged(lineIdx,oldLineIdx - lineIdx);
603  			//if (lineMode.savePositions()) ensureDirty(); // not anymore!
604  		}
605  	
606  		protected void doResetTodo() {
607  			if (todoIdx == lineIdx) return;
608  			int oldTodoIdx = todoIdx;
609  			while (aboveInfo.size() > doneInfo.size()) {
610  				aboveInfo.remove(aboveInfo.size()-1);
611  			}
612  			updateIdxs();
613  			partitioningChanged(todoIdx,oldTodoIdx - todoIdx);
614  			if (lineMode.savePositions()) ensureDirty();
615  		}
616  		
617  		protected void doResetLineAndTodo() {
618  			if (aboveInfo.size() == 0) return;
619  			
620  			int oldTodoIdx = todoIdx;
621  			doneInfo.clear();
622  			aboveInfo.clear();
623  			updateIdxs();
624  			partitioningChanged(0,oldTodoIdx);
625  			if (oldTodoIdx != todoIdx) 
626  				if (lineMode.savePositions())
627  					ensureDirty();
628  		}
629  	
630  		public boolean inOrAboveLine(int offset) {
631  			return offset < todoIdx;
632  		}
633  	
634  		protected void doAdvanceLine(int modelIdx, ResolvedInput in) {
635  			if (doneInfo.size() >= aboveInfo.size()) throw new IllegalStateException();
636  			int oldLineIdx = lineIdx;
637  			doneInfo.add(new DoneInfo(modelIdx,in));
638  			updateIdxs();
639  			partitioningChanged(oldLineIdx,lineIdx-oldLineIdx);
640  		}
641  	
642  		protected void doRetreatLine() {
643  			if (doneInfo.size() == 0 || aboveInfo.size() > doneInfo.size()) throw new IllegalStateException();
644  			int oldLineIdx = lineIdx;
645  			doneInfo.remove(doneInfo.size()-1);
646  			aboveInfo.remove(doneInfo.size());
647  			updateIdxs();
648  			partitioningChanged(lineIdx,oldLineIdx-lineIdx);
649  			if (lineMode.savePositions()) ensureDirty();
650  		}
651  	
652  		protected void doRetreatTodo() {
653  			if (todoIdx == lineIdx) throw new IllegalStateException();
654  			int oldTodoIdx = todoIdx;
655  			aboveInfo.remove(aboveInfo.size()-1);
656  			updateIdxs();
657  			partitioningChanged(todoIdx,oldTodoIdx - todoIdx);
658  			if (lineMode.savePositions()) ensureDirty();
659  		}
660  		
661  		protected void addNextForm() throws BadCmdException, PositionedParseException, EmptyParse {
662  			String below = getBelowRegion().getFromDocument(this);
663  			StringParser p = new StringParser(null, below);
664  			int start = todoIdx + p.getPos();
665  			ResolvableUserInput in;
666  			try {
667  				in = ResolvableUserInput.parse(p,ID,this.getSession());
668  			} catch (EmptyParse e) {
669  				throw e;
670  			} catch (ParseException pe) {
671  				throw new PositionedParseException(pe,todoIdx + p.getPos());
672  			}
673  			int end = todoIdx + p.getPos();
674  			aboveInfo.add(new AboveInfo(new Region(start,end-start),in));
675  		}
676  		
677  		protected void doAdvanceTodo() throws PositionedParseException, BadCmdException, EmptyParse {
678  			int oldTodoIdx = todoIdx;
679  			addNextForm();
680  			updateIdxs();
681  			partitioningChanged(oldTodoIdx,todoIdx-oldTodoIdx);
682  			if (lineMode.savePositions()) ensureDirty();
683  			notifyLineAdvancement();
684  			resetUndoStuff();
685  		}
686  		
687  		public boolean isEmptyBelow() {
688  			String below = getBelowRegion().getFromDocument(this);
689  			StringParser p = new StringParser(null, below);
690  			try {
691  				p.skipSpaceAndComments();
692  			} catch (ParseException e) {
693  				return false;
694  			}
695  			return p.eof();
696  		}
697  		
698  		// ADVANCEMENT (for clearing undo history stuff)
699  		
700  		protected final HashSet<LineAdvancementListener> ladvListeners = new HashSet<LineAdvancementListener>();
701  		
702  		static interface LineAdvancementListener {
703  			public void lineAdvanced();
704  		}
705  		
706  		public void addLineAdvancementListener(LineAdvancementListener l) {
707  			ladvListeners.add(l);
708  		}
709  		
710  		public void removeLineAdvancementListener(LineAdvancementListener l) {
711  			ladvListeners.remove(l);
712  		}
713  		
714  		protected void notifyLineAdvancement() {
715  			for (LineAdvancementListener l : ladvListeners) {
716  				l.lineAdvanced();
717  			}
718  		}
719  	
720  		// PARTITIONER
721  		
722  		public final static String PREAMBLE = "__a2s_preamble";
723  		public final static String BOOK = "__a2s_book";
724  		public final static String TANGENT = "__a2s_tangent";
725  		public final static String TODO = "__a2s_line_todo";
726  		public final static String DEFAULT = Partitioner.DEFAULT;
727  		
728  		protected void partitioningChangedAll() {
729  			partitioningChanged(0,getLength());
730  		}
731  		
732  		protected void partitioningChanged(int start, int len) {
733  			ldp.computePartitions();
734  			DocumentPartitioningChangedEvent dpce;
735  			dpce = new DocumentPartitioningChangedEvent(this);
736  			dpce.setPartitionChange(DEFAULT_PARTITIONING, start, len);
737  			fireDocumentPartitioningChanged(dpce);
738  		}
739  		
740  		public static String[] getContentTypes() {
741  			return new String[] { PREAMBLE, BOOK, TANGENT, TODO, DEFAULT };
742  		}
743  		
744  		public String[] getLegalContentTypes() {
745  			return getContentTypes();
746  		}
747  	
748  		protected class Partitioner extends AbstractDocumentPartitioner {
749  			private static final long serialVersionUID = 1L;
750  	
751  			public String[] getLegalContentTypes() {
752  				return getContentTypes();
753  			}
754  	
755  			public IRegion documentChanged2(DocumentEvent event) {
756  				//computePartitions();
757  				
758  				updateLast();
759  				
760  				//return null;
761  				//return new Region(todoIdx,doc.getLength()-todoIdx);
762  				
763  				//int docLen = doc.getLength();
764  				int added = event.fText.length() - event.fLength;
765  				if (added < 0) added = 0;
766  				return null; //new Region(docLen-added,added);
767  			}
768  			
769  			private void updateLast() {
770  				updateIdxs();
771  				int docLen = LispDocument.this.getLength();
772  				int lastIdx = regions.size() - 1;
773  				if (lastIdx >= 0 && regions.get(lastIdx).getType() == DEFAULT) {
774  					MyTypedRegion r = new MyTypedRegion(todoIdx,docLen-todoIdx,DEFAULT);
775  					replaceRegion(lastIdx,r,true);
776  				} else {
777  					addRegion(todoIdx,docLen,DEFAULT);
778  				}
779  			}
780  			
781  			private void computePartitions() {
782  				clearRegions();
783  				
784  				int docLen = LispDocument.this.getLength();
785  				int idx = 0;
786  	
787  				//updateIdxs();  PREREQUISITE!
788  				checkIdxs();
789  				
790  				if (lineIdx > idx) {  // <=> modelIdxs.size() > 0
791  					int i = 0;
792  					int toIdx;
793  					if (i < doneInfo.size() && doneInfo.get(i).getInputType() != ResolvedInput.BEGIN_BOOK) { 
794  						do {
795  							toIdx = regionEnd(aboveInfo.get(i).region);
796  							i++;
797  						} while (i < doneInfo.size() && doneInfo.get(i).getInputType() != ResolvedInput.BEGIN_BOOK);
798  						addRegion(idx, toIdx, PREAMBLE);
799  						idx = toIdx;
800  					}
801  					if (i < doneInfo.size()) { // <=> lineIdx > idx
802  						toIdx = regionEnd(aboveInfo.get(i).region);
803  						i++;
804  						if (i < doneInfo.size() && (doneInfo.get(i).getInputType() == ResolvedInput.IN_PACKAGE)) {
805  							do {
806  								toIdx = regionEnd(aboveInfo.get(i).region);
807  								i++;
808  							} while (i < doneInfo.size() &&
809  									doneInfo.get(i).input != null &&
810  									doneInfo.get(i).input.isEmbeddedEvent());
811  						}
812  						addRegion(idx, toIdx, BOOK);
813  						idx = toIdx;
814  						if (lineIdx > idx) {
815  							addRegion(idx, lineIdx, TANGENT);
816  							idx = lineIdx;
817  						}
818  					}
819  				}
820  				if (todoIdx > idx) {
821  					addRegion(idx, todoIdx, TODO);
822  					idx = todoIdx;
823  				}
824  				if (docLen > idx) {
825  					addRegion(idx, docLen, DEFAULT);
826  					idx = docLen;
827  				}
828  			}
829  		}
830  	
831  		
832  		/*===================== to be called by A2s =================*/	
833  		protected IInputInfo curInput = null;
834  		protected final LinkedList<UndoInput> undoInput = new LinkedList<UndoInput>();
835  		//protected boolean deleteOnError = false;
836  		
837  		private UndoInput getUndo(ResolvedInput in, int superIdx) {
838  			return new UndoInput(in,superIdx,
839  					ses.getManager().getSecret(),
840  					in.getUndoMsg());
841  		}
842  		
843  		public IInputInfo nextInputOption() {
844  			if (!lineMode.isAdvancable()) return null;
845  			if (!undoInput.isEmpty()) {
846  				curInput = undoInput.removeFirst();
847  				return curInput;
848  			}
849  			// else no pending undo
850  			if (doneInfo.size() == aboveInfo.size()) {
851  				curInput = null;
852  				return curInput;
853  			}
854  			// else pending 'todo' form(s)
855  			ResolvableUserInput foo = aboveInfo.get(doneInfo.size()).resolvable;
856  			ResolvedInput bar = null;
857  			if (ses.getManager().isAlive()) {
858  				try {
859  					bar = foo.resolve(ses.getManager());
860  				} catch (SessionException e) {
861  					// shouldn't happen
862  					Acl2sPlugin.logError("Acl2 session problem for system command.", e);
863  					return null;
864  				}
865  			}
866  			if (!lineMode.cancelOnError()) doAdvanceLine(0,bar);
867  			curInput = bar;
868  			if (ses.getManager().isAlive() && bar != null) { // bar != null probably redundant
869  				if (!bar.couldNeedUndo() ||
870  						bar.getUndoMsg() != null) {
871  					return curInput;
872  				}
873  			}
874  			if (!lineMode.cancelOnError()) { return curInput; } // no REDOs
875  			// else REDO might be possible.  keep checking...
876  			ISessionModel model = ses.getModel();
877  			IEnvInfo env = model.getEnv(model.countCompletedCmds());
878  			if (env == null) return curInput;  // no session yet!
879  			if (env.getSuperUndoneSize() == 0) return curInput;
880  			LispObject parsed = bar == null ? null : bar.getParsed();
881  			int preDone = env.getSuperDoneSize();
882  			int i = model.countCompletedCmds() - 1;
883  			int balance = 0;
884  			IEnvInfo oldPostEnv = env;
885  			IEnvInfo oldPreEnv;
886  			for (;i > 0;) { // the i > 0 shouldn't be needed
887  				IInputInfo old = model.getInput(i);
888  				oldPreEnv = model.getEnv(i);
889  				if (old instanceof StartupInputAndEnv) {
890  					return curInput;
891  				} else if (old instanceof RedoInput) {
892  					balance++;
893  				} else if (old instanceof UndoInput && balance > 0) {
894  					balance--;
895  				} else if (old instanceof UndoInput) {
896  					if (oldPostEnv.getSuperDoneSize() != preDone) return curInput;
897  					
898  					UndoInput oldUndo = (UndoInput) old;
899  					IInputInfo orig = oldUndo.getOriginalInput();
900  					if (!(orig instanceof ResolvedInput)) return curInput;
901  					ResolvedInput orig2 = (ResolvedInput) orig;
902  					
903  	                if (parsed != null) { // if post-resolved parsing is available, use it
904  	                    LispObject oldParsed = orig2.getParsed();
905  	                	if (!(parsed.equals(oldParsed))) return curInput;
906  	                } else {
907  	                	if (!(orig2.abstractlyEquals(foo,env.getParseContext()))) return curInput;
908  	                }
909  			
910  					int hist = oldPreEnv.getSuperDoneSize();
911  					if (hist > env.getSuperDoneSize() + env.getSuperUndoneSize()) return curInput;
912  					// everything checks out!  make it a REDO
913  					curInput = new RedoInput(orig,oldPreEnv.getSuperDoneSize(),ses.getManager().getSecret());
914  					return curInput;
915  				} else if (old instanceof ResolvedInput) {
916  					ResolvedInput old2 = (ResolvedInput) old;
917  					if (old2.couldNeedUndo()) {
918  						// return curInput;
919  						// break here if we want something that *potentially* affects
920  						// history (but doesn't) to interfere with allowability of REDO  
921  					}
922  					if (old2.getUndoMsg() != null) {
923  						return curInput;
924  						// could be something fishy
925  					}
926  				}
927  				if (preDone > oldPostEnv.getSuperDoneSize() + oldPostEnv.getSuperUndoneSize()) {
928  					return curInput;
929  					// optimization to clip search when we know we've past
930  					// where a matching undo would have been
931  				}
932  				
933  				// rewind one top-level form
934  				do {
935  					i--;
936  				} while (i > 0 && model.getEnv(i).inWormhole());
937  				oldPostEnv = oldPreEnv;
938  			}
939  			return curInput;
940  		}
941  	
942  		public void commandCompleted(IInputInfo in, IStatusInfo status, int modelIdx) {
943  			if (!lineMode.cancelOnError()) return;
944  			if (in == curInput) {
945  				if (in instanceof ResolvedInput) {
946  					if (!status.wasSuccess()) {
947  						doResetTodo();
948  					} else {
949  						doAdvanceLine(modelIdx,(ResolvedInput) in);
950  					}
951  				} else if (in instanceof RedoInput) {
952  					if (!status.wasSuccess()) {
953  						Acl2sPlugin.logError("Error while redoing in ACL2:" + ses.getModel().getOutput(modelIdx), null);
954  						ses.getManager().killSession();
955  					}
956  					IInputInfo orig = ((RedoInput)in).getOriginalInput();
957  					if (!(orig instanceof ResolvedInput)) {
958  						throw new IllegalStateException();
959  					}
960  					doAdvanceLine(modelIdx,(ResolvedInput) orig);
961  				} else if (in instanceof UndoInput) {
962  					if (!status.wasSuccess()) {
963  						Acl2sPlugin.logError("Error while undoing in ACL2:" + ses.getModel().getOutput(modelIdx), null);
964  						ses.getManager().killSession();
965  					}
966  					// nothing else to do
967  				} else {
968  					// impossible
969  					throw new IllegalStateException();
970  				}
971  				curInput = null;
972  			} else if (in.getSource().equals(ID)) {
973  				if (status.extendedLogicalState()) {
974  					if (!status.wasSuccess()) { // weird.  we'll just fix it.
975  						int superHistIdx = ses.getModel().getEnv(modelIdx).getSuperDoneSize();
976  						String cmd = UndoInput.buildUndoString(superHistIdx, ses.getManager().getSecret());
977  						try {
978  							/* (void) */ ses.getManager().systemCmd2(cmd);
979  							ses.getManager().updateModelEnv();
980  						} catch (SessionException e) {
981  							Acl2sPlugin.logError("Session died unexpectedly.", e);
982  						}
983  					} else { // it seems we canceled this in the mean time
984  						// Undo to last superhistory index 
985  						int superIdx;
986  						int doneCount = doneInfo.size();
987  						if (doneCount == 0) {
988  							superIdx = StartupInputAndEnv.instance.getSuperDoneSize();
989  						} else {
990  							superIdx = ses.getModel().getStatus(doneInfo.get(doneCount - 1).modelIdx).getSuperDoneSize();
991  						}
992  						undoInput.addFirst(getUndo((ResolvedInput) in, superIdx));
993  					}
994  				}
995  			} else if (in instanceof ResolvedInput &&
996  					status.extendedLogicalState()) {
997  				// didn't come from line action
998  				// & ResolvedInput means it came from user
999  				// & we only care if it modified the world
1000 				ResolvedInput c = (ResolvedInput) in;
1001 	
1002 				String pref = InteractionPrefs.getRelevantCmdPref(Acl2sPlugin.inDisplayThread() ? Acl2sPlugin.getShell() : null, ses);
1003 				if (pref.equals(InteractionPrefs.RELEVANT_CMD_INSERT)) {
1004 					int oldTodoIdx = todoIdx;
1005 					String formString = c.getInputString();
1006 					int formStart;
1007 					if (todoIdx == 0) {
1008 						formStart = todoIdx;
1009 						formString = formString + "\n\n";
1010 					} else {
1011 						formStart = todoIdx + 2;
1012 						formString = "\n\n" + formString;
1013 					}
1014 					replace2(todoIdx,0,formString);
1015 					aboveInfo.add(new AboveInfo(new Region(formStart,c.getInputString().length()),c));
1016 					doneInfo.add(new DoneInfo(modelIdx,c));
1017 					updateIdxs();
1018 					partitioningChanged(oldTodoIdx,todoIdx-oldTodoIdx);
1019 					notifyLineAdvancement();
1020 					resetUndoStuff();
1021 				} else {
1022 					// Undo to last superhistory index 
1023 					int superIdx;
1024 					int doneCount = doneInfo.size();
1025 					if (doneCount == 0) {
1026 						superIdx = StartupInputAndEnv.instance.getSuperDoneSize();
1027 					} else {
1028 						superIdx = ses.getModel().getStatus(doneInfo.get(doneCount - 1).modelIdx).getSuperDoneSize();
1029 					}
1030 					undoInput.addFirst(getUndo((ResolvedInput) in, superIdx));
1031 				}
1032 			}
1033 		}
1034 	
1035 		public void resetLineAndTodo() {  // called by a2s on clean session
1036 			curInput = null;
1037 			doResetLineAndTodo();
1038 		}
1039 	
1040 		public void resetLine() {  // called by a2s on restart/stop session
1041 			curInput = null;
1042 			doResetLine();
1043 			undoInput.clear();
1044 		}
1045 	
1046 	
1047 		/*=================== to be called from editors (by ADs) ================*/	
1048 		/*protected boolean checkA2s(A2sEditor ctx) {
1049 			if (a2s == null) {
1050 				ctx.infoMessageBox("Not supported", "Operation not supported because this lisp file has no associated session.  Go to menu `ACL2' | `Set session mode...' to change.");
1051 				return false;
1052 			} else {
1053 				return true;
1054 			}
1055 		}*/
1056 		
1057 		// allow null ctx!
1058 		public void cancelTodo(A2sEditor ctx) {
1059 			if (ses != null && curInput instanceof ResolvedInput) {
1060 				try {
1061 					ses.interrupt();
1062 				} catch (SessionException e) {
1063 				}
1064 				curInput = null;
1065 			}
1066 			int oldTodoIdx = todoIdx;
1067 			doResetTodo();
1068 			if (ctx instanceof LispEditor)
1069 				ctx.revealRange(todoIdx,oldTodoIdx-todoIdx);
1070 		}
1071 	
1072 		protected void advanceTodo2(A2sEditor ctx, boolean emptyOk) {
1073 			try {
1074 				doAdvanceTodo();
1075 				if (ses != null) {
1076 					ses.ping();
1077 				}
1078 			} catch (EmptyParse e) {
1079 				if (!emptyOk) {
1080 					ctx.infoMessageBox("Could not advance todo", "No more forms in Lisp file.");
1081 				}
1082 			} catch (PositionedParseException e) {
1083 				ctx.setHighlightRange(e.getPos(),0,true);
1084 				ctx.errorMessageBox("Parse Error", e.getMessage() + "\n\nCursor has been positioned at spot of error.");
1085 			} catch (BadCmdException e) {
1086 				String below = getBelowRegion().getFromDocument(this);
1087 				StringParser p = new StringParser(null, below);
1088 				try {
1089 					p.skipSpaceAndComments();
1090 				} catch (ParseException e1) {
1091 				}
1092 				ctx.setHighlightRange(todoIdx+p.getPos(),0,true);
1093 				ctx.errorMessageBox("Bad Command", e.getMessage() + "\n\nCursor has been positioned at violating command.");
1094 			}
1095 		}
1096 		
1097 		public void advanceTodo(A2sEditor ctx) {
1098 			if (!lineMode.isAdvancable()) {
1099 				ctx.infoMessageBox("Not supported", "Operation not supported in this line mode.  Go to menu `ACL2' | `Set line mode...' to enable line tracking.");
1100 				return;
1101 			}
1102 			int oldTodoIdx = todoIdx;
1103 			advanceTodo2(ctx,false);
1104 			if (ctx instanceof LispEditor)
1105 				ctx.revealRange(oldTodoIdx,todoIdx-oldTodoIdx);
1106 		}
1107 	
1108 		public void allTodo(A2sEditor ctx) {
1109 			if (!lineMode.isAdvancable()) {
1110 				ctx.infoMessageBox("Not supported", "Operation not supported in this line mode.  Go to menu `ACL2' | `Set line mode...' to enable line tracking.");
1111 				return;
1112 			}
1113 			int savedTodoIdx;
1114 			do {
1115 				savedTodoIdx = todoIdx;
1116 				advanceTodo2(ctx,true);
1117 			} while (savedTodoIdx < todoIdx);
1118 		}
1119 		
1120 		// allow null ctx!
1121 		protected void retreatLine2(A2sEditor ctx) {
1122 			if (doneInfo.size() < aboveInfo.size()) {
1123 				if (doneInfo.size() < aboveInfo.size() - 1) {
1124 					doRetreatTodo();
1125 				} else {
1126 					cancelTodo(ctx);
1127 				}
1128 			} else {
1129 				int inputIdx = doneInfo.size() - 1;
1130 				int modelIdx = doneInfo.get(inputIdx).modelIdx;
1131 				ResolvedInput input = doneInfo.get(inputIdx).input;
1132 				assert input != null;
1133 				/*if (input == null) {
1134 					ctx.errorMessageBox("Error moving line", "Could not retreat line because document is still initializing.");
1135 					return;
1136 				}*/
1137 				doRetreatLine();
1138 				if (ses != null && lineMode.cancelOnError() && input.couldNeedUndo()) {
1139 					IEnvInfo env = ses.getModel().getEnv(modelIdx);
1140 					UndoInput undo = getUndo(input,env.getSuperDoneSize());
1141 					
1142 					undoInput.addLast(undo);
1143 					ses.ping();
1144 				}
1145 			}
1146 		}
1147 		
1148 		public void retreatLine(A2sEditor ctx) {
1149 			if (aboveInfo.size() == 0) return;
1150 			
1151 			int oldTodoIdx = todoIdx;
1152 	
1153 			retreatLine2(ctx);
1154 			
1155 			if (ctx instanceof LispEditor)
1156 				ctx.revealRange(todoIdx,oldTodoIdx-todoIdx);
1157 		}
1158 		
1159 		public void retreatBackThrough(A2sEditor ctx, int modelIdx) {
1160 			int oldTodoIdx = todoIdx;
1161 	
1162 			for (;;) {
1163 				int lastDone = doneInfo.size() - 1;
1164 				if (lastDone < 0) break;
1165 				if (doneInfo.get(lastDone).modelIdx < modelIdx) break;
1166 				retreatLine2(ctx);
1167 			}
1168 			
1169 			if (ctx instanceof LispEditor)
1170 				ctx.revealRange(todoIdx,oldTodoIdx-todoIdx);
1171 		}
1172 	
1173 		public void resetLineAndTodo(A2sEditor ctx) {
1174 			if (aboveInfo.size() == 0) return;
1175 			int oldTodoIdx;
1176 			do {
1177 				oldTodoIdx = todoIdx;
1178 				retreatLine2(ctx);
1179 			} while (aboveInfo.size() > 0);
1180 			
1181 			if (ctx instanceof LispEditor)
1182 				ctx.revealRange(todoIdx,oldTodoIdx-todoIdx);
1183 		}
1184 	
1185 		public void moveTodoPastPoint(int index, A2sEditor ctx) {
1186 			if (index > todoIdx || (index == todoIdx && index == 0)) {
1187 				// skip to beginning of whitespace if in it
1188 				LispToken t = tokens.getToken(tokens.getTokenIdxContaining(index));
1189 				if (index < t.offset || t.type == LispToken.EOF_TOK) {
1190 					index = t.absoluteStart();
1191 				}
1192 				
1193 				int oldTodoIdx;
1194 				do {
1195 					oldTodoIdx = todoIdx;
1196 					advanceTodo2(ctx, false);
1197 				} while (oldTodoIdx < todoIdx && index > todoIdx);
1198 			} else {
1199 				int oldTodoIdx;
1200 				do {
1201 					if (aboveInfo.size() == 0) return;
1202 					oldTodoIdx = todoIdx;
1203 					retreatLine2(ctx);
1204 				} while (oldTodoIdx > todoIdx && index < todoIdx);
1205 			}
1206 		}
1207 		
1208 		public void moveTodoPastCursor(A2sEditor ctx) {
1209 			if (!(ctx instanceof LispEditor)) {
1210 				ctx.errorMessageBox("Not supported", "The invoked action is only supported in an ACL2/Lisp editor.");
1211 				return;
1212 			}
1213 			int cursor = ctx.getDocCaret();
1214 			if (lineMode.isAdvancable()) { // move line
1215 				moveTodoPastPoint(cursor,ctx);
1216 			} else { // submit form
1217 				if (ses == null) {
1218 					ctx.errorMessageBox("No associated session", "The invoked action is only supported with an " +
1219 						"associated session that is running and ready for command input.");
1220 				}
1221 				if (ses.getManager().getStatus() != ISessionStatusListener.READY_CMD_SESSION) {
1222 					ctx.errorMessageBox("Session not ready", "The invoked action is only supported with an " +
1223 						"associated session that is running and ready for command input.");
1224 					return;
1225 				}
1226 				int len = getLength();
1227 				int backSearchFrom = len;
1228 				if (cursor < backSearchFrom) {
1229 					int tIdx = tokens.getTokenIdxContaining(cursor);
1230 					backSearchFrom = tokens.getToken(tIdx).offset + 1;
1231 				}
1232 				
1233 				if (backSearchFrom >= len) {
1234 					ctx.errorMessageBox("No more input", "No more input forms after cursor.");
1235 					return;
1236 				}
1237 				
1238 				int start = tokens.formSearch(backSearchFrom, false);
1239 				
1240 				try {
1241 					IParseContext pc;
1242 					DocumentParser dp;
1243 					ResolvableUserInput preCmd;
1244 					ResolvedInput cmd;
1245 	
1246 					pc = ses.getManager().getCurrentEnv().getParseContext();
1247 					dp = new DocumentParser(pc,this,start);
1248 					try {
1249 						preCmd = ResolvableUserInput.parse(dp,ID, this.getSession());
1250 						cmd = preCmd.resolve(ses.getManager());
1251 						cmd.throwIfBad();
1252 					} catch (ParseException pe) {
1253 						ctx.setDocCaret(dp.getPos());
1254 						ctx.errorMessageBox("Parse Error", pe.getMessage() +
1255 						"\n\nCursor has been positioned at spot of error.");
1256 						return;
1257 					} catch (BadCmdException e) {
1258 						ctx.setDocCaret(start);
1259 						ctx.errorMessageBox("Bad Command", e.getMessage() +
1260 						"\n\nCursor has been positioned at violating command.");
1261 						return;
1262 					}
1263 					
1264 					ses.getManager().submitCmdInput(cmd);
1265 					try {
1266 						dp.skipSpaceAndComments();
1267 					} catch (ParseException e) {
1268 						// don't care anymore
1269 					}
1270 					ctx.setDocCaret(Math.min(dp.getPos(),getLength()));
1271 				} catch (SessionDeadException e) {
1272 					ctx.errorMessageBox("Session died",
1273 							"Session died before requested input could be submitted.");
1274 					return;
1275 				} catch (SessionException e) {
1276 					ctx.errorMessageBox("Unexpected session state",
1277 							"Unexpected session state before submitting input:\n\n" +
1278 							e.getMessage());
1279 					return;
1280 				}
1281 			}
1282 		}
1283 		
1284 		public String suckForm(A2sEditor ctx) {
1285 			if (!lineMode.isAdvancable()) {
1286 				ctx.infoMessageBox("Not supported", "Operation not supported in this line mode.  Go to menu `ACL2' | `Set line mode...' to enable line tracking.");
1287 				return null;
1288 			}
1289 			try {
1290 				String inputString;
1291 				if (lineMode.cancelOnError()) {
1292 					addNextForm();
1293 					AboveInfo a = aboveInfo.remove(aboveInfo.size() - 1);
1294 					replace2(a.region.getOffset(),a.region.getLength(),"");
1295 					inputString = a.resolvable.getInputString();
1296 				} else {
1297 					doAdvanceTodo();
1298 					doAdvanceLine(0,null);
1299 					inputString = aboveInfo.get(aboveInfo.size() - 1).resolvable.getInputString();
1300 				}
1301 				return inputString;
1302 			} catch (EmptyParse e) {
1303 				ctx.infoMessageBox("Cannot read next form", "No more forms in Lisp file.");
1304 				return null;
1305 			} catch (PositionedParseException e) {
1306 				ctx.errorMessageBox("Parse Error", e.getMessage());
1307 				return null;
1308 			} catch (BadCmdException e) {
1309 				ctx.errorMessageBox("Bad Command", e.getMessage());
1310 				return null;
1311 			}
1312 		}
1313 	
1314 		public void reparse() {
1315 			tokens.reparse();
1316 		}
1317 	
1318 		public void cleanOutputFiles(A2sEditor ctx) {
1319 			// GCL == .o  SBCL == .fasl  OpenMCL == .dfsl  + .acl2 + .cert
1320 			
Event model_returns_null: "acl2s.plugin.editors.A2sDocument.getOSFile(...)" might return null. [checked 2 of 3 uses] [details]
Also see events: [model_unchecked_deref]
1321 			File f = getOSFile();
Event model_unchecked_deref: Dereferencing "f" while f=null in "acl2s.lib.certify.Certifier.legalLispFile(...)". [details]
Also see events: [model_returns_null]
1322 			if (!Certifier.legalLispFile(f)) return;  // quietly do nothing
1323 			
1324 			String base = MiscIO.removeFileExtension(f.getPath());
1325 			new File(base + ".acl2").delete();
1326 			new File(base + ".cert").delete();
1327 			new File(base + ".o").delete();
1328 			new File(base + ".fasl").delete();
1329 			new File(base + ".dfsl").delete();
1330 			
1331 			IEditorInput input = getInput();
1332 			if (input instanceof IFileEditorInput) {
1333 				IFileEditorInput finput = (IFileEditorInput) input;
1334 				try {
1335 					finput.getFile().getParent().refreshLocal(1, null);
1336 				} catch (CoreException e) {
1337 					Acl2sPlugin.logError("Error refreshing directory after cleaning output files", e);
1338 				}
1339 			} // else, no need
1340 		}
1341 	}