DIRECTORY FS USING [Error, StreamOpen], InputFocus USING [GetInputFocus, SetInputFocus], IO USING [Flush, PutRope, STREAM], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], MessageWindow USING [Append, Blink], NodeProps USING [GetProp, PutProp], Process USING [CheckForAbort, Detach, EnableAborts, GetCurrent, MsecToTicks, SecondsToTicks, SetTimeout, Ticks], Rope USING [Concat, Fetch, FromProc, FromRefText, Length, ROPE, Size, Substr], RopeEdit USING [Concat, InsertChar], RopeFile USING [FromStream], RuntimeError USING [UNCAUGHT], SafeStorage USING [ReclaimCollectibleObjects], TEditCompile USING [minAvgLineLeading], TEditDocument USING [LineTable, LineTableRec, RecordViewerForRoot, Selection, SelectionRec, SpinAndLock, TEditDocumentData, TEditDocumentDataRec, TSInfoRec, ttyChars, Unlock], TEditImpl USING [ReloadTable, TEditNotifyProc], TEditInput USING [BadMouse, CommandProc, currentEvent, DontDoIt, FreeTree, InterpretAtom, Register, ResetInputStuff, SaveCoords], TEditInputOps USING [EditFailed], TEditLocks USING [Lock, LockRef, Unlock], TEditProfile USING [editTypeScripts], TEditRefresh USING [ScrollToEndOfDoc], TEditSelection USING [Alloc, Copy, Free, InsertionPoint, LockSel, MakePointSelection, MakeSelection, pSel, SelectionRoot, SetSelLooks, UnlockSel], TEditSplit USING [Split], TEditTouchup USING [LockAfterScroll, UnlockAfterRefresh], TextEdit USING [DeleteText, DocFromNode, FromRope, InsertRope, InsertString, MaxOffset, Size], TextLooks USING [Looks, LooksToRope, noLooks], TextNode USING [FirstChild, LastLocWithin, Location, Ref, RefTextNode, StepForward], TIPUser USING [RegisterTIPPredicate, TIPPredicate, TIPScreenCoords, TIPTable], TypeScript USING [], ViewerClasses USING [InitProc, Lock, NotifyProc, Viewer, ViewerClass, ViewerClassRec, ViewerRec], ViewerGroupLocks USING [CallRootAndLinksUnderWriteLock], ViewerOps USING [AddProp, CreateViewer, DestroyViewer, FetchProp, FetchViewerClass, RegisterViewerClass, SetMenu]; TEditTypeScriptImpl: CEDAR MONITOR IMPORTS InputFocus, IO, FS, Menus, MessageWindow, NodeProps, Process, Rope, RopeEdit, RopeFile, RuntimeError, SafeStorage, TextEdit, TextLooks, TextNode, TEditDocument, TEditImpl, TEditInput, TEditInputOps, TEditLocks, TEditProfile, TEditRefresh, TEditSelection, TEditSplit, TEditTouchup, TIPUser, ViewerGroupLocks, ViewerOps EXPORTS TypeScript, TEditImpl = BEGIN ROPE: TYPE = Rope.ROPE; TS: TYPE = ViewerClasses.Viewer; Destroyed: PUBLIC ERROR [ts: TS] = CODE; Create: PUBLIC PROC [info: ViewerClasses.ViewerRec, paint: BOOL _ TRUE] RETURNS [ts: TS] = { tdd: TEditDocument.TEditDocumentData _ NewData[]; info.data _ tdd; ts _ ViewerOps.CreateViewer[$Typescript, info, paint]; }; IsATypeScript: PUBLIC PROC [ts: TS] RETURNS [yes: BOOL] = { RETURN [ts # NIL AND ts.class.flavor = $Typescript]; }; Destroy: PUBLIC PROC [ts: TS] = { ViewerOps.DestroyViewer[ts]; }; Reset: PUBLIC PROC [ts: TS] = { ts.class.init[ts]; }; NewData: PROC [self: ViewerClasses.Viewer _ NIL] RETURNS [tdd: TEditDocument.TEditDocumentData] = { tdd _ NEW[TEditDocument.TEditDocumentDataRec]; [] _ TEditDocument.SpinAndLock[tdd, "TEditTypeScriptImplNewData"]; tdd.lineTable _ NEW[TEditDocument.LineTableRec[MAX[2, (IF self=NIL THEN 0 ELSE (self.ch/TEditCompile.minAvgLineLeading))+1]] _ [lastLine: 0, lastY: 0, lines: NULL]]; tdd.tsInfo _ NEW[TEditDocument.TSInfoRec]; tdd.text _ TextEdit.DocFromNode[TextEdit.FromRope[""]]; TEditDocument.Unlock[tdd]; TRUSTED { Process.EnableAborts[@tdd.tsInfo.iIncr]; Process.SetTimeout[@tdd.tsInfo.iIncr, Process.SecondsToTicks[10]]; }; }; InitTypeScriptDocument: ViewerClasses.InitProc = { tdd: TEditDocument.TEditDocumentData; InitTddText: PROC = { IF tdd.text # NIL THEN TEditInput.FreeTree[tdd.text]; tdd.text _ TextEdit.DocFromNode[TextEdit.FromRope[""]] }; InitLineTable: PROC = { tdd.lineTable.lastLine _ 0; tdd.lineTable[0].pos _ [TextNode.FirstChild[tdd.text], 0] }; IF InputFocus.GetInputFocus[].owner=self THEN InputFocus.SetInputFocus[]; IF self.link#NIL THEN { otherInit: ViewerClasses.Viewer _ NIL; otherTdd: TEditDocument.TEditDocumentData; tdd _ NARROW[self.data]; IF tdd = NIL THEN RETURN; [] _ TEditDocument.SpinAndLock[tdd, "InitTypeScriptDocument"]; FOR v: ViewerClasses.Viewer _ self.link, v.link UNTIL v= NIL OR v=self DO IF ~v.newVersion THEN {otherInit _ v; EXIT}; ENDLOOP; IF otherInit=NIL OR (otherTdd _ NARROW[otherInit.data]) = NIL THEN InitTddText[] ELSE tdd.text _ otherTdd.text; InitLineTable[]; } ELSE IF self.data=NIL THEN { -- first time we've seen this viewer self.data _ tdd _ (IF self.parent=NIL THEN NewData[] ELSE NewData[self]); IF tdd = NIL THEN RETURN; [] _ TEditDocument.SpinAndLock[tdd, "InitTypeScriptDocument"] } ELSE { tdd _ NARROW[self.data]; IF tdd = NIL THEN RETURN; [] _ TEditDocument.SpinAndLock[tdd, "InitTypeScriptDocument"]; InitTddText[]; InitLineTable[]; }; IF self.column#static AND self.parent=NIL THEN ViewerOps.SetMenu[self, typescriptMenu, FALSE]; TEditDocument.RecordViewerForRoot[self, tdd.text]; TEditDocument.Unlock[tdd]; }; aLittleWhile: Process.Ticks = Process.MsecToTicks[50]; untilTimesOutInALittleWhile: CONDITION; dontWait: BOOL _ FALSE; WaitForEditRepaintProcess: INTERNAL PROC[ts: TS] = { WHILE editRepaintProcess#NIL DO IF currentTS # ts THEN RETURN; -- no need to wait for editRepaintProcess dontWait _ TRUE; NOTIFY untilTimesOutInALittleWhile; -- no need to wait for a little while WAIT repaintDone; dontWait _ FALSE; ENDLOOP; }; PutChar: PUBLIC PROC [ts: TS, char: CHAR] = { IF char=1C THEN BackSpace[ts] ELSE { Process.CheckForAbort[]; PutCharEntry[ts, char ! ABORTED => GO TO abort]; EXITS abort => ERROR ABORTED; } }; PutCharEntry: ENTRY PROC [ts: TS, char: CHAR] = { ENABLE UNWIND => NULL; -- release lock count: INTEGER _ 0; IF (editRepaintProcess#NIL AND currentTS#ts) THEN { WHILE editRepaintProcess#NIL DO Process.CheckForAbort[]; WAIT repaintDone; ENDLOOP; }; currentTS _ ts; WHILE inputBuffer.length>=bufferMaxlen DO IF (count _ count+1) > 10 THEN { overflowRope _ Rope.Concat[overflowRope, Rope.FromRefText[inputBuffer]]; inputBuffer.length _ 0; EXIT }; BROADCAST untilTimesOutInALittleWhile; -- wake up the repaint process WAIT bufferClear; ENDLOOP; inputBuffer[inputBuffer.length] _ char; inputBuffer.length _ inputBuffer.length + 1; IF editRepaintProcess = NIL THEN TRUSTED { Process.Detach[editRepaintProcess _ FORK Repaint[ts]]; }; }; BackSpace: PUBLIC ENTRY PROC [ts: TS, count: INT _ 1] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; root: TextNode.Ref; selLock: BOOL; lockRef: TEditLocks.LockRef; Cleanup: PROC = { IF lockRef # NIL THEN { TEditLocks.Unlock[root]; lockRef _ NIL }; IF selLock THEN TEditSelection.UnlockSel[primary] }; IF tdd # NIL AND count > 0 AND tdd.tsInfo#NIL THEN { ENABLE UNWIND => Cleanup; flush: TextNode.Location; node: TextNode.RefTextNode; root _ tdd.text; WaitForEditRepaintProcess[ts]; TEditSelection.LockSel[primary, "TEditTypeScriptImplBackSpace"]; IF NOT (selLock _ TEditSelection.SelectionRoot[TEditSelection.pSel]=root) THEN TEditSelection.UnlockSel[primary]; lockRef _ TEditLocks.Lock[root, "TEditTypeScriptImplBackSpace"]; flush _ TextNode.LastLocWithin[root]; IF (node _ flush.node)=NIL THEN GOTO Bad; IF (count _ MIN[count,flush.where]) <= 0 THEN GOTO Bad; flush.where _ flush.where-count; TextEdit.DeleteText[root, node, flush.where, count, TEditInput.currentEvent]; IF selLock THEN FixPSel[ts, [flush.node, flush.where+count], flush]; Cleanup[]; EXITS Bad => { Cleanup[]; TEditInputOps.EditFailed[] }; } }; PutRope: PUBLIC PROC [ts: TS, rope: Rope.ROPE] = { FOR n: INT IN [0..Rope.Length[rope]) DO PutChar[ts, Rope.Fetch[rope, n]]; ENDLOOP; }; PutText: PUBLIC PROC [ts: TS, text: REF READONLY TEXT, start: INTEGER _ 0, stopPlusOne: INTEGER _ LAST[INTEGER]] = { IF text#NIL THEN FOR n: INTEGER IN [start..MIN[text.length, stopPlusOne]) DO PutChar[ts, text[n]]; ENDLOOP; }; ChangeLooks: PUBLIC ENTRY PROC [ts: TS, look: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript WaitForEditRepaintProcess[ts]; SELECT look FROM IN ['a..'z] => tdd.tsInfo.looks[look] _ TRUE; IN ['A..'Z] => tdd.tsInfo.looks[look+('a-'A)] _ FALSE; = ' => tdd.tsInfo.looks _ TextLooks.noLooks; -- blank means reset ENDCASE; }; AddLooks: PUBLIC ENTRY PROC [ts: TS, look: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript IF look NOT IN ['a..'z] THEN { IF look = ' THEN tdd.tsInfo.looks _ TextLooks.noLooks; -- blank means reset RETURN }; WaitForEditRepaintProcess[ts]; tdd.tsInfo.looks[look] _ TRUE }; RemoveLooks: PUBLIC ENTRY PROC [ts: TS, look: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript IF look NOT IN ['a..'z] THEN RETURN; WaitForEditRepaintProcess[ts]; tdd.tsInfo.looks[look] _ FALSE }; ClearLooks: PUBLIC ENTRY PROC [ts: TS] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN; WaitForEditRepaintProcess[ts]; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript tdd.tsInfo.looks _ TextLooks.noLooks }; GetLooks: PUBLIC ENTRY PROC [ts: TS] RETURNS [looks: Rope.ROPE] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript RETURN [TextLooks.LooksToRope[tdd.tsInfo.looks]] }; Flush: PUBLIC ENTRY PROC [ts: TS] = { ENABLE UNWIND => NULL; -- release lock IF ts # currentTS THEN RETURN; WaitForEditRepaintProcess[ts] }; editRepaintProcess: PROCESS _ NIL; repaintDone: CONDITION; bufferClear: CONDITION; bufferMaxlen: CARDINAL = 64; inputBuffer: REF TEXT _ NEW[TEXT[bufferMaxlen]]; overflowRope: Rope.ROPE; currentTS: TS _ NIL; RepaintDone: ENTRY PROC = { IF editRepaintProcess = Process.GetCurrent[] THEN editRepaintProcess _ NIL; BROADCAST repaintDone; }; InternalClearRepaint: INTERNAL PROC = { IF editRepaintProcess = Process.GetCurrent[] THEN editRepaintProcess _ NIL; BROADCAST repaintDone; }; Repaint: PROC [ts: TS] = { root: TextNode.Ref; pos: TextNode.Location; lockRef: TEditLocks.LockRef _ NIL; selLock, continue: BOOL _ FALSE; CheckForAutoScroll: PROC [ts: TS] = { prop: REF BOOL; tdd: TEditDocument.TEditDocumentData _ NARROW[ts.data]; PosIsVisible: PROC [] RETURNS [BOOL] = { lines: TEditDocument.LineTable = tdd.lineTable; IF lines[lines.lastLine].pos.where+lines[lines.lastLine].nChars >= pos.where THEN RETURN [TRUE]; RETURN [FALSE] }; WITH ts.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { IF TEditTouchup.LockAfterScroll[tdd, "TEditTypeScriptImplRepaint"] THEN {{ ENABLE UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]; IF (prop _ NARROW[ViewerOps.FetchProp[ts, $AutoScrollTypescript]]) = NIL THEN ViewerOps.AddProp[ts, $AutoScrollTypescript, prop _ NEW[BOOL]]; prop^ _ PosIsVisible[]}; TEditTouchup.UnlockAfterRefresh[tdd] }; }; ENDCASE; }; Cleanup: PROC = { IF lockRef # NIL THEN TEditLocks.Unlock[root]; lockRef _ NIL; IF selLock THEN TEditSelection.UnlockSel[primary]; selLock _ FALSE }; DoAutoScroll: PROC [ts: TS] = { prop: REF BOOL = NARROW[ViewerOps.FetchProp[ts, $AutoScrollTypescript]]; IF prop#NIL AND prop^ THEN TEditRefresh.ScrollToEndOfDoc[ts, TRUE] }; WaitForMoreInput: ENTRY PROC RETURNS [BOOL] = { ENABLE UNWIND => NULL; IF overflowRope=NIL AND inputBuffer.length { ENABLE { UNWIND => Cleanup[]; ABORTED => GOTO Quit; RuntimeError.UNCAUGHT => {Cleanup[]; RepaintDone[]; REJECT}; }; CheckAll: PROC = { lockRef _ TEditLocks.Lock[root, "TEditTypeScriptImplRepaint", read]; pos _ TextNode.LastLocWithin[root]; CheckForAutoScroll[ts]; FOR v: ViewerClasses.Viewer _ ts.link, v.link UNTIL v = NIL OR v=ts DO CheckForAutoScroll[v]; ENDLOOP; TEditLocks.Unlock[root]; lockRef _ NIL; }; ScrollAll: PROC = { DoAutoScroll[ts]; FOR v: ViewerClasses.Viewer _ ts.link, v.link UNTIL v = NIL OR v=ts DO DoAutoScroll[v]; ENDLOOP; }; IF (root _ tdd.text) = NIL THEN GO TO Quit; ViewerGroupLocks.CallRootAndLinksUnderWriteLock[CheckAll, ts]; DO TEditSelection.LockSel[primary, "TEditTypeScriptImplRepaint"]; IF NOT (selLock _ TEditSelection.SelectionRoot[TEditSelection.pSel]=root) THEN TEditSelection.UnlockSel[primary]; lockRef _ TEditLocks.Lock[root, "TEditTypeScriptImplRepaint"]; continue _ MakeEdits[ts, selLock]; Cleanup[]; ViewerGroupLocks.CallRootAndLinksUnderWriteLock[ScrollAll, ts]; IF ~continue OR ~WaitForMoreInput[] THEN EXIT; ENDLOOP; EXITS Quit => Cleanup[]; }; ENDCASE; RepaintDone[]; }; updateSel: TEditDocument.Selection _ NEW[TEditDocument.SelectionRec]; tsMaxSize: INT _ 100000; MakeEdits: ENTRY PROC [ts: TS, selLock: BOOL] RETURNS [continue: BOOL] = { ENABLE UNWIND => NULL; tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; node: TextNode.RefTextNode; pos: TextNode.Location; ropeFiledSizeRef: REF INT _ NIL; ropeFiledSize: INT _ 0; IF tdd=NIL THEN { overflowRope _ NIL; inputBuffer.length _ 0; InternalClearRepaint[]; RETURN [FALSE]; }; IF overflowRope=NIL AND inputBuffer.length=0 THEN { InternalClearRepaint[]; RETURN [FALSE]; }; pos _ TextNode.LastLocWithin[tdd.text]; node _ pos.node; IF overflowRope#NIL THEN { [] _ TextEdit.InsertRope[ root: tdd.text, dest: node, rope: overflowRope, destLoc: TextEdit.MaxOffset, inherit: FALSE, looks: tdd.tsInfo.looks, event: TEditInput.currentEvent]; overflowRope _ NIL; }; [] _ TextEdit.InsertString[root: tdd.text, dest: node, inherit: FALSE, looks: tdd.tsInfo.looks, string: inputBuffer, destLoc: TextEdit.MaxOffset]; inputBuffer.length _ 0; ropeFiledSizeRef _ NARROW[NodeProps.GetProp[n: tdd.text, name: $RopeFiledSize]]; ropeFiledSize _ (IF ropeFiledSizeRef # NIL THEN ropeFiledSizeRef^ ELSE 0); IF Rope.Size[node.rope]-ropeFiledSize > tsMaxSize THEN { ENABLE FS.Error => GOTO ForgetIt; ropeSize: INT ~ Rope.Size[node.rope]; stream: IO.STREAM ~ FS.StreamOpen[fileName: "[]<>Temp>TypescriptOverflow.txt", accessOptions: $create, createByteCount: ropeSize-ropeFiledSize, streamBufferParms: [1, 2]]; IF ropeFiledSizeRef = NIL THEN { ropeFiledSizeRef _ NEW[INT _ 0]; NodeProps.PutProp[n: tdd.text, name: $RopeFiledSize, value: ropeFiledSizeRef]; }; IF ropeFiledSize NOT IN [0..ropeSize] THEN ropeFiledSizeRef^ _ ropeFiledSize _ 0; IO.PutRope[stream, Rope.Substr[base: node.rope, start: ropeFiledSize, len: ropeSize-ropeFiledSize] ]; IO.Flush[stream]; node.rope _ Rope.Concat[ Rope.Substr[base: node.rope, start: 0, len: ropeFiledSize], RopeFile.FromStream[stream: stream, start: 0, len: ropeSize-ropeFiledSize, bufSize: 512, buffers: 3] ]; ropeFiledSizeRef^ _ ropeSize; SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; EXITS ForgetIt => NULL; }; IF selLock THEN FixPSel[ts, pos, TextNode.LastLocWithin[tdd.text]]; BROADCAST bufferClear; RETURN [TRUE]; }; FixPSel: PROC [ts: TS, pos, newPos: TextNode.Location] = { FixSel: PROC = { tSel: TEditDocument.Selection _ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; TEditSelection.MakePointSelection[tSel, newPos]; TEditSelection.SetSelLooks[TEditSelection.pSel]; TEditSelection.Free[tSel] }; IF TEditSelection.pSel.granularity=point AND TEditSelection.pSel.end.pos.where >= pos.where THEN IF ts=TEditSelection.pSel.viewer THEN FixSel[] ELSE FOR v: TS _ ts.link, v.link UNTIL v = NIL OR v=ts DO IF v=TEditSelection.pSel.viewer THEN { FixSel[]; EXIT }; ENDLOOP }; CaretAtEnd: PUBLIC PROC [sel: TEditDocument.Selection] RETURNS [yes: BOOL] = { root: TextNode.Ref = TEditSelection.SelectionRoot[sel]; IF root=NIL THEN RETURN [FALSE]; -- not a valid selection IF sel.granularity # point THEN RETURN [FALSE]; -- not a point selection [] _ TEditLocks.Lock[root, "TEditTypeScriptImplCaretAtEnd", read]; { caret: TextNode.Location = TEditSelection.InsertionPoint[sel]; node: TextNode.Ref = caret.node; IF node=NIL OR TextNode.StepForward[node] # NIL THEN yes _ FALSE ELSE IF caret.where # TextEdit.Size[node] THEN yes _ FALSE ELSE yes _ TRUE; TEditLocks.Unlock[root] } }; TypeScriptNotifyProc: ViewerClasses.NotifyProc = { IF TEditProfile.editTypeScripts THEN { tdd: TEditDocument.TEditDocumentData = NARROW[self.data]; IF tdd = NIL THEN RETURN; IF TEditSelection.SelectionRoot[TEditSelection.pSel]=tdd.text AND ~CaretAtEnd[TEditSelection.pSel] THEN { TEditImpl.TEditNotifyProc[self, input]; RETURN } }; TEditInput.ResetInputStuff; FOR params: LIST OF REF ANY _ input, params.rest UNTIL params=NIL DO z: REF ANY ~ params.first; WITH z SELECT FROM z: REF CHAR => {ForceInsertSel[self]; InputChar[self, z^]}; z: ATOM => { Flush[self]; InterpretAtom[self, z ! TEditInput.DontDoIt, TEditInput.BadMouse => EXIT] }; z: Rope.ROPE => {ForceInsertSel[self]; InputRope[self, z] }; z: REF INT => { tdd: TEditDocument.TEditDocumentData = NARROW[self.data]; IF tdd # NIL THEN tdd.tsInfo.intParam _ z^ }; z: REF TEXT => {ForceInsertSel[self]; FOR n: CARDINAL IN [0..z.length) DO InputChar[self, z[n]]; ENDLOOP }; z: TIPUser.TIPScreenCoords => TEditInput.SaveCoords[z.mouseX, self.ch-z.mouseY]; ENDCASE => IF z#NIL THEN { -- DKW: assume NIL was a ROPE MessageWindow.Append["Unknown input given to Tioga typescript interpreter.", TRUE]; MessageWindow.Blink[] }; ENDLOOP; }; ForceInsertSel: PROC [ts: TS] = { IF TEditSelection.pSel.viewer#ts OR (TEditSelection.pSel.granularity=point AND TEditSelection.pSel.insertion=before) THEN RETURN; TEditSelection.LockSel[primary, "TEditTypeScriptForceInsertSel"]; { ENABLE UNWIND => TEditSelection.UnlockSel[primary]; IF TEditSelection.pSel.viewer=ts AND (TEditSelection.pSel.granularity#point OR TEditSelection.pSel.insertion#before) THEN { tSel: TEditDocument.Selection _ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; tSel.insertion _ before; tSel.granularity _ point; tSel.start.pos _ tSel.end.pos _ TextNode.LastLocWithin[tSel.data.text]; TEditSelection.MakeSelection[new: tSel]; TEditSelection.Free[tSel]; }; TEditSelection.UnlockSel[primary]; } }; InterpretAtom: PROC [viewer: ViewerClasses.Viewer, atom: ATOM] = { SELECT atom FROM $Abort => SetUserAbort[viewer]; $ApplyCaretLook, $ApplyLook, $RemoveCaretLook, $RemoveLook => { tdd: TEditDocument.TEditDocumentData = NARROW[viewer.data]; IF tdd = NIL THEN RETURN; TEditImpl.TEditNotifyProc[viewer, LIST[NEW[INT _ tdd.tsInfo.intParam]]]; TEditInput.InterpretAtom[viewer,atom] }; $BackSpace => {ForceInsertSel[viewer]; InputChar[viewer, 1C]}; -- control A $BackWord => {ForceInsertSel[viewer]; InputChar[viewer, 27C]}; -- control W $ClearTypeScriptLooks => ClearLooks[viewer]; $Delete => {ForceInsertSel[viewer]; InputChar[viewer, 177C]}; -- DEL $Paste => {ForceInsertSel[viewer]; TEditInput.InterpretAtom[viewer,atom]}; ENDCASE => TEditInput.InterpretAtom[viewer,atom]; }; NumberToLook: PROC [viewer: ViewerClasses.Viewer] RETURNS [l: CHAR] = { tdd: TEditDocument.TEditDocumentData = NARROW[viewer.data]; IF tdd = NIL THEN RETURN['z]; RETURN['a+tdd.tsInfo.intParam] }; ApplyTypeScriptLook: TEditInput.CommandProc = { WaitUntilIdle[viewer]; AddLooks[viewer, NumberToLook[viewer]] }; RemoveTypeScriptLook: TEditInput.CommandProc = { WaitUntilIdle[viewer]; RemoveLooks[viewer, NumberToLook[viewer]] }; InsertRopeAtFrontOfBuffer: PUBLIC ENTRY PROC [ts: TS, rope: Rope.ROPE] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; inputRope _ RopeEdit.Concat[rope, Rope.Substr[inputRope, inputLoc]]; inputLoc _ 0; BROADCAST iIncr; }; }; InsertCharAtFrontOfBuffer: PUBLIC ENTRY PROC [ts: TS, char: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; inputRope _ RopeEdit.InsertChar[Rope.Substr[inputRope, inputLoc], char]; inputLoc _ 0; BROADCAST iIncr; }; }; FlushInputChars: INTERNAL PROC [tdd: TEditDocument.TEditDocumentData] = { OPEN tdd.tsInfo; num: INT = IF iQp > oQp THEN iQp-oQp ELSE TEditDocument.ttyChars-iQp+oQp; Char: PROC RETURNS [c: CHAR] = { c _ input[oQp]; oQp _ (oQp+1) MOD TEditDocument.ttyChars }; rope: Rope.ROPE; rope _ Rope.FromProc[num, Char]; inputRope _ Rope.Concat[inputRope, rope]; iQp _ oQp _ 0 }; InputRope: ENTRY PROC [ts: TS, rope: Rope.ROPE] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; IF iQp # oQp THEN FlushInputChars[tdd]; inputRope _ RopeEdit.Concat[inputRope, rope]; BROADCAST iIncr; }; }; InputChar: ENTRY PROC [ts: TS, char: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; newiQp: INTEGER = (iQp+1) MOD TEditDocument.ttyChars; IF newiQp = oQp THEN FlushInputChars[tdd]; input[iQp] _ char; iQp _ newiQp; BROADCAST iIncr; }; }; GetChar: PUBLIC ENTRY PROC [ts: TS] RETURNS [char: CHAR] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; size: INT; IF tdd = NIL THEN RETURN['a] -- Hmm. used to return garbage. PDR ELSE { OPEN tdd.tsInfo; WHILE inputLoc >= (size _ Rope.Size[inputRope]) AND iQp = oQp DO waitingInGetChar _ TRUE; WAIT iIncr; waitingInGetChar _ FALSE; IF ts.destroyed THEN RETURN WITH ERROR Destroyed[ts]; ENDLOOP; IF inputLoc < size THEN { -- get char from rope char _ Rope.Fetch[inputRope, inputLoc]; IF (inputLoc _ inputLoc+1)=size THEN { -- have finished this rope inputLoc _ 0; inputRope _ NIL } } ELSE { -- get from buffer char _ input[oQp]; oQp _ (oQp+1) MOD TEditDocument.ttyChars }; }; }; CharsAvailable: PUBLIC ENTRY PROC [ts: TS] RETURNS [BOOL] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd = NIL THEN RETURN [FALSE] ELSE { OPEN tdd.tsInfo; IF tdd.tsInfo=NIL THEN RETURN [FALSE]; -- not a typescript RETURN[inputLoc < Rope.Size[inputRope] OR iQp#oQp]; }; }; TypeIn: PUBLIC PROC [ts: TS, input: REF ANY] = {ts.class.notify[ts, LIST[input]]}; WaitUntilCharsAvail: PUBLIC ENTRY PROC [ts: TS] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript WHILE inputLoc >= Rope.Size[inputRope] AND iQp = oQp DO waitingInGetChar _ TRUE; WAIT iIncr; waitingInGetChar _ FALSE; IF ts.destroyed THEN RETURN WITH ERROR Destroyed[ts]; ENDLOOP; }; }; WaitUntilIdle: PUBLIC ENTRY PROC [ts: TS] = { ENABLE UNWIND => NULL; -- release lock tdd: TEditDocument.TEditDocumentData = NARROW[ts.data]; IF tdd # NIL THEN { OPEN tdd.tsInfo; IF tdd.tsInfo=NIL THEN RETURN; -- not a typescript WHILE inputLoc < Rope.Size[inputRope] OR iQp#oQp OR ~waitingInGetChar DO WAIT untilTimesOutInALittleWhile; ENDLOOP; }; }; typeScriptTIP: PUBLIC TIPUser.TIPTable; -- init below ReloadTypeScriptTipTable: PUBLIC PROC = { tipNames: ROPE ~ IF TEditProfile.editTypeScripts THEN "[]<>EditTypeScript.tip []<>Tioga.tip" ELSE "[]<>TypeScript.tip"; newTIP: TIPUser.TIPTable ~ TEditImpl.ReloadTable[oldTIP: typeScriptTIP, profileKey: "Tioga.TypescriptTip", default: tipNames]; IF newTIP # NIL THEN tsClass.tipTable _ typeScriptTIP _ newTIP; }; CaretAtEndOfTypescript: TIPUser.TIPPredicate --PROC RETURNS [BOOLEAN]-- = { tdd: TEditDocument.TEditDocumentData; ts: TS _ TEditSelection.pSel.viewer; IF ts=NIL THEN RETURN [FALSE]; -- no primary selection tdd _ NARROW[ts.data]; IF tdd = NIL THEN RETURN [FALSE]; IF tdd.tsInfo=NIL THEN RETURN [FALSE]; -- not a typescript RETURN [CaretAtEnd[TEditSelection.pSel]]; }; SetUserAbort: PROC [ts: TS] = { tdd: TEditDocument.TEditDocumentData _ NARROW[ts.data, TEditDocument.TEditDocumentData]; IF tdd # NIL THEN tdd.tsInfo.abort _ TRUE; }; typescriptMenu: Menus.Menu _ Menus.CreateMenu[]; splitDoc: ROPE ~ " Warning: Split typescript are known to misbehave. Keep your bags packed."; Split: Menus.MenuProc = {TEditSplit.Split[NARROW[parent]]}; Find: Menus.MenuProc = {viewer: ViewerClasses.Viewer ~ NARROW[parent]; viewer.class.notify[viewer, LIST[SELECT mouseButton FROM red => IF shift THEN $FindNextCaseless ELSE $FindNext, yellow => IF shift THEN $FindAnyCaseless ELSE $FindAny, blue => IF shift THEN $FindPrevCaseless ELSE $FindPrev, ENDCASE => ERROR]] }; tsClass: ViewerClasses.ViewerClass _ -- just like Tioga's NEW[ViewerClasses.ViewerClassRec _ ViewerOps.FetchViewerClass[$Text]^]; tsClass.init _ InitTypeScriptDocument; tsClass.notify _ TypeScriptNotifyProc; tsClass.icon _ typescript; ReloadTypeScriptTipTable[]; ViewerOps.RegisterViewerClass [$Typescript, tsClass]; TIPUser.RegisterTIPPredicate[$CaretAtEndOfTypescript, CaretAtEndOfTypescript]; TEditInput.Register[$ApplyTypeScriptLook, ApplyTypeScriptLook]; TEditInput.Register[$RemoveTypeScriptLook, RemoveTypeScriptLook]; Menus.AppendMenuEntry[typescriptMenu, Menus.CreateEntry["Find", Find]]; Menus.AppendMenuEntry[typescriptMenu, Menus.CreateEntry[name: "Split", proc: Split, documentation: splitDoc, guarded: TRUE]]; TRUSTED {Process.SetTimeout[@untilTimesOutInALittleWhile, aLittleWhile]}; TRUSTED {Process.SetTimeout[@bufferClear, aLittleWhile]; Process.EnableAborts[@bufferClear]}; END. FTEditTypeScriptImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Doug Wyatt, April 15, 1985 1:06:40 am PST Russ Atkinson (RRA) January 23, 1986 0:47:05 am PST Michael Plass, March 10, 1986 11:05:00 am PST Most things CaretAtEnd, typeScriptTIP, ReloadTypeScriptTipTable so can test if destroyed make sure another link didn't already init and if so, copy that data we're first somebody else already did the init old viewer getting re-initialised TTY PUT ROUTINES wait to flush buffer waited long enough see if pSel is in this typescript look char in 'a..'z means add that look; in 'A..'Z means remove; = blank means remove all looks BACKGROUND UPDATES Cannot make this an ENTRY procedure since must lock selection and document before enter the monitor. Else have deadlock with Copy to typescript which gets locks before calling InputRope. Make some attempt to give back the locks and restart things (also REJECT) see if pSel is in this typescript To avoid consuming a lot of VM, make the rope into a ropefile. Must have happened because somebody edited the typescript KEYBOARD INPUT Exported to TEditImpl need lock to synch with edits to typescript Caret in this typescript, but not at end. So treat like edit of normal Tioga doc. TTY GET ROUTINES raise error if viewer destroyed. Timeout for condition is set above Simulate user typein to a typescript can be REF CHAR, ROPE, REF TEXT, or ATOM (be careful). raise error if viewer destroyed. Timeout for condition is set above Tip Table Exported to TEditImpl User Abort TTY Class Defn except for a couple of differences Κ C˜codešœ™Kšœ Οmœ7™BKšœ)™)K™3K™-—K™šΟk ˜ Kšžœžœ˜Kšœ žœ ˜0Kšžœžœžœ˜"Kšœžœ<˜GKšœžœ˜$Kšœ žœ˜#Kšœžœc˜pKšœžœ0žœ˜NKšœ žœ˜$Kšœ žœ˜Kšœ žœžœ˜Kšœ žœ˜.Kšœ žœ˜'Kšœžœœ˜―Kšœ žœ ˜/Kšœ žœq˜Kšœžœ˜!Kšœ žœ˜)Kšœ žœ˜%Kšœ žœ˜&Kšœžœ~˜’Kšœ žœ ˜Kšœ žœ'˜9Kšœ žœP˜^Kšœ žœ˜.Kšœ žœF˜TKšœžœA˜NKšœ žœ˜KšœžœN˜aKšœžœ"˜8Kšœ žœc˜r—K˜šΠblœžœž˜"Kšžœ žœžœ«˜Εšž˜šœ ˜ K™ —šœ ˜ Kšœ3™3——Kšœž˜Kšžœžœžœ˜Kšžœžœ˜ š œ žœžœžœžœ˜(K˜—šΟnœžœžœ(žœžœžœžœ˜^K˜1K˜K˜6K˜—K˜š   œžœžœžœžœžœ˜;Kšžœžœžœ ˜4Kšœ˜—K˜š œžœžœžœ˜!Kšœ˜Kšœ˜—K˜š œžœžœžœ˜Kšœ˜Kšœ˜—K™š œžœžœžœ+˜cKšœžœ%˜.K˜Bšœžœžœ˜5Kš œžœžœžœžœ0˜HKšœžœ˜&—Kšœ žœ˜*K˜7K˜šžœ˜ šœ(˜(Kšœ™—KšœB˜BKšœ˜—K˜—K˜š œ˜4K˜%š  œžœ˜Kšžœ žœžœ˜5K˜7K˜—š  œžœ˜K˜K˜:K˜—Kšžœ'žœ˜Išžœ žœžœ˜Kšœ*™*Kšœ™Kšœ"žœ˜&K˜*Kšœžœ ˜Kšžœžœžœžœ˜K˜>š žœ-žœžœžœž˜IKšžœžœžœ˜,Kšžœ˜—š žœ žœžœ žœž˜=šžœ˜Kšœ ™ —šžœ˜Kšœ"™"——K˜K˜—š žœžœ žœžœΟc$˜AKš œžœ žœžœ žœ˜IKšžœžœžœžœ˜K˜>K˜—šžœ˜Kšœ!™!Kšœžœ ˜Kšžœžœžœžœ˜K˜>K˜K˜K˜—šžœžœ žœž˜.Kšœ(žœ˜/—K˜2K˜K˜—K˜—šžœžœž™K˜K˜6Kšœž œ˜'Kšœ žœžœ˜š œžœžœžœ˜4šžœžœž˜Kšžœžœžœ‘)˜HKšœ žœ˜Kšžœ‘%˜IKšžœ ˜Kšœ žœ˜Kšžœ˜—K˜—K˜š  œžœžœžœžœ˜-šžœ˜ Kšžœ˜šžœ˜Kšœ˜Kšœžœžœžœ˜0Kšžœ žœžœ˜Kšœ˜——K˜—K˜š   œžœžœžœžœ˜1Kšžœžœžœ‘˜&Kšœžœ˜šžœžœžœžœ˜3Kšœ™šžœžœž˜Kšœ˜Kšžœ ˜Kšžœ˜—K˜—K˜šžœ"ž˜)šžœžœ˜ Kšœ™KšœH˜HK˜Kšžœ˜K˜—Kšž œ‘˜EKšžœ ˜Kšžœ˜—K˜'K˜,šžœžœžœžœ˜*Kšœ$žœ˜6Kšœ˜—K˜—K˜š   œžœžœžœžœ žœ ˜;Kšžœžœžœ‘˜&Kšœ'žœ ˜7K˜Kšœ žœ˜K˜š œžœ˜Kšžœ žœžœ&žœ˜AKšžœ žœ#˜2K˜—š žœžœžœ žœ žœžœ˜4Kšžœžœ ˜K˜K˜K˜K˜K˜@Kšœ!™!KšžœžœDž˜NK˜"K˜@K˜%Kšžœžœžœžœ˜)Kšžœ žœžœžœ˜7K˜ K˜MKšžœ žœ5˜DK˜ Kšžœ2˜7K˜—K˜—K˜š  œžœžœžœ žœ˜2šžœžœžœž˜'K˜!Kšžœ˜—K˜—K˜š œžœžœžœžœžœžœ žœžœžœžœ˜tšžœžœžœžœžœžœ žœž˜LK˜Kšžœ˜—K˜—K˜š   œžœžœžœžœžœ˜7Kšœ_™_Kšžœžœžœ‘˜&Kšœ'žœ ˜7Kšžœžœžœžœ˜Kš žœ žœžœžœ‘˜2K˜šžœž˜Kšžœ&žœ˜-Kšžœ.žœ˜6Kšœ.‘˜BKšžœ˜—K˜—K˜š  œžœžœžœžœžœ˜4Kšžœžœžœ‘˜&Kšœ'žœ ˜7Kšžœžœžœžœ˜Kš žœ žœžœžœ‘˜2šžœžœžœ žœ˜Kšžœ žœ'‘˜LKšžœ˜K˜—K˜Kšœžœ˜K˜—K˜š   œžœžœžœžœžœ˜7Kšžœžœžœ‘˜&Kšœ'žœ ˜7Kšžœžœžœžœ˜Kš žœ žœžœžœ‘˜2Kš žœžœžœ žœžœ˜$K˜Kšœžœ˜K˜—K˜š   œžœžœžœžœ˜*Kšžœžœžœ‘˜&Kšœ'žœ ˜7Kšžœžœžœžœ˜K˜Kš žœ žœžœžœ‘˜2K˜%K˜—K˜š œžœžœžœžœžœžœ˜CKšžœžœžœ‘˜&Kšœ'žœ ˜7Kšžœžœžœžœ˜Kš žœ žœžœžœ‘˜2Kšžœ+˜1K˜—K˜š  œžœžœžœžœ˜%Kšžœžœžœ‘˜&Kšžœžœžœ˜K˜K˜—K˜—šž œž™K˜Kšœžœžœ˜"Kšœ ž œ˜Kšœ ž œ˜Kšœžœ˜Kš œ žœžœžœžœ˜0Kšœžœ˜Kšœ žœžœ˜š  œžœžœ˜Kšžœ+žœžœ˜KKšž œ ˜Kšœ˜—š œžœžœ˜'Kšžœ+žœžœ˜KKšž œ ˜K˜—š œžœžœ˜Kšœ»™»K˜K˜Kšœžœ˜"Kšœžœžœ˜ š œžœžœ˜%Kšœžœžœ˜Kšœ'žœ ˜7š  œžœžœžœ˜(K˜/šžœJž˜QKšžœžœ˜—Kšžœžœ˜K˜—šžœ žœž˜šœ)˜)šžœAžœ˜JKšžœžœ)˜6šžœ žœ4žœž˜MKšœ4žœžœ˜?—K˜K˜%K˜—K˜—Kšžœ˜—K˜—š œžœ˜Kšžœ žœžœ$žœ˜=Kšžœ žœ.žœ˜CK˜—š  œžœžœ˜Kšœžœžœžœ1˜HKš žœžœžœžœ#žœ˜CK˜—š  œžœžœžœžœ˜/Kšžœžœžœ˜Kšžœžœžœ!ž˜˜>šž˜K˜>Kšœ!™!šžœžœDž˜NK˜"—K˜>K˜"K˜ Kšœ?˜?Kšžœ žœžœžœ˜.Kšžœ˜—Kšžœ˜K˜—Kšžœ˜—Kšœ˜K˜—K˜Kšœ%žœ˜EKšœ žœ ˜š  œžœžœžœ žœžœ žœ˜LKšžœžœžœ˜Kšœ'žœ ˜7K˜K˜Kšœžœžœžœ˜ Kšœžœ˜šžœžœžœ˜Kšœžœ˜Kšœ˜Kšœ˜Kšžœžœ˜Kšœ˜—šžœžœžœžœ˜3Kšœ˜Kšžœžœ˜Kšœ˜—K˜'K˜šžœžœžœ˜˜K˜K˜0Kšœ žœ˜(K˜ —Kšœžœ˜Kšœ˜—šœ@žœ˜_K˜2—Kšœ˜Kšœžœ7˜PKš œžœžœžœžœ˜Jšžœ0žœ˜8K™>Kšžœžœ žœ ˜!Kšœ žœ˜%Kšœžœžœžœ•˜«šžœžœžœ˜ K•StartOfExpansion3[n: TextNode.Ref, name: ATOM, value: REF ANY]šœžœžœ˜ KšœN˜NKšœ˜—šžœžœžœžœ'˜QK™9—–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šžœ˜KšœO˜OKšœ˜—Kšžœ˜–g[stream: STREAM, start: INT _ 0, len: INT _ 2147483647, bufSize: INT _ 512, buffers: NAT _ 4]šœ˜Kšœ;˜;Kšœd˜dKšœ˜—Kšœ˜Kšœ1žœ˜8Kšžœ žœ˜K˜—Kšžœ žœ4˜CKšž œ ˜Kšžœžœ˜K˜—K˜š œžœžœ'˜<š œžœ˜K˜7K˜=K˜0K˜0K˜K˜—Kšžœ'žœ0ž˜`šžœ˜ Kšžœ ˜ š žœžœžœžœžœžœž˜9Kšžœžœ žœ˜8Kšžœ˜——K˜—K˜K˜—šžœž™K™š   œžœžœ žœžœ˜NK˜7Kš žœžœžœžœžœ‘˜9Kš žœžœžœžœ‘˜HK˜BKšœ+™+˜@K˜ š žœžœžœžœžœž˜@Kšžœžœ#žœž˜:Kšžœžœ˜—K˜K˜—K˜—˜2šžœžœ˜&Kšœ'žœ ˜9Kšžœžœžœžœ˜Kšžœ;˜=šžœ"žœ˜+KšœR™RKšœ(žœ˜/K˜—K˜—K˜šžœ žœžœžœžœžœžœž˜DKšœžœžœ˜šžœžœž˜Kšœžœžœ0˜;šœžœ˜ K˜ KšœDžœ˜JK˜—Kšœžœ0˜<šœžœžœ˜Kšœ'žœ ˜9Kšžœžœžœ˜+K˜—šœžœžœ˜%Kš žœžœžœžœž˜BK˜—K˜PKš žœžœžœžœ‘˜8KšœMžœ˜SK˜K˜—Kšžœ˜—K˜—K˜š œžœžœ˜#Kš žœžœ(žœ'žœžœ˜šœDžœžœ&˜wšžœžœ(žœ'žœ˜{K˜8K˜=K˜K˜K˜GK˜(K˜K˜—K˜"K˜—K˜—K˜š  œžœ&žœ˜Ešžœž˜K˜˜?Kšœ'žœ˜;Kšžœžœžœžœ˜Kšœ"žœžœžœ˜HK˜&K˜—Kšœ?‘ ˜KKšœ?‘ ˜KK˜,Kšœ>‘˜DK˜JKšžœ*˜1—K˜—K˜š  œžœ žœžœ˜GKšœ'žœ˜;Kšžœžœžœžœ˜Kšžœ˜K˜—˜/K˜>K˜—K˜˜0K˜AK˜—K˜š  œžœžœžœžœ žœ˜JKšžœžœžœ‘˜&Kšœ'žœ ˜7šžœžœžœžœ ˜$K˜DK˜ Kšž œ˜K˜—K˜—K˜š  œžœžœžœžœžœ˜EKšžœžœžœ‘˜&Kšœ'žœ ˜7šžœžœžœžœ ˜$K˜HK˜ Kšž œ˜K˜—K˜—K˜š œžœžœ-žœ ˜[Kš œžœžœ žœ žœ ˜Iš œžœžœžœ˜ Kšœžœ˜9K˜—Kšœ žœ˜K˜ K˜)K˜K˜—K˜š   œžœžœžœ žœ˜3Kšžœžœžœ‘˜&Kšœ'žœ ˜7šžœžœžœžœ ˜$Kšžœ žœ˜'K˜-Kšž œ˜K˜—K˜—K˜š   œžœžœžœžœ˜.Kšžœžœžœ‘˜&Kšœ'žœ ˜7šžœžœžœžœ ˜$Kšœžœ žœ˜5Kšžœžœ˜*K˜ Kšž œ˜K˜—K˜—K˜—šœ™š œžœžœžœžœžœžœ˜