<> <> <> <> <> <<>> 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.