DIRECTORY Atom USING [DottedPairNode, GetPName, PropList], EditSpan USING [CannotDoEdit, Transpose], EditSpanSupport USING [CopySpan], FS USING [ErrorGroup, OpenFile], IO USING [PutChar, RopeFromROS, ROS, STREAM], LooksReader USING [Body, BumpIndex, FreeLooksReader, Get, GetLooksReader, Peek, Ref, SetIndex, SetPosition], MessageWindow USING [Append, Blink], NodeProps USING [CopyInfo, DoSpecs, GetProp, GetSpecs, MapProps, PutProp], NodeStyleOps USING [StyleNameForNode], Process USING [Detach], PutGet USING [FromFile, FromFileC, ToFile, ToFileC], Rope USING [Concat, FromRefText, Length, ROPE, Substr], RopeReader USING [Body, BumpIndex, FreeRopeReader, Get, GetIndex, GetRope, GetRopeReader, Peek, Ref, SetIndex, SetPosition], Rosary USING [Concat, FromItem, ROSARY, Size, Substr], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ], TEditDocument USING [Selection, TEditDocumentData], TEditInput USING [currentEvent, FreeTree], TEditInputOps USING [CallWithLocks, Delete], TEditSelection USING [GetSelectionGrain, MakeSelection], TextEdit USING [ChangeStyle, CharSet, Fetch, GetCharPropList], TextLooks USING [Concat, CreateRun, Looks, Runs, Substr], TextNode USING [FirstChild, Forward, LastLocWithin, LastWithin, Level, Location, LocOffset, LocWithin, MakeNodeLoc, MakeNodeSpan, NewTextNode, NodeItself, Parent, Ref, Root, Span], TiogaAccess USING [CharSet, TiogaChar], TiogaAccessPrivate USING [Reader, ReaderRep, ropePieces, Writer, WriterRep], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer]; TiogaAccessImpl: CEDAR PROGRAM IMPORTS Atom, EditSpan, EditSpanSupport, IO, LooksReader, MessageWindow, NodeProps, NodeStyleOps, Process, PutGet, Rope, RopeReader, Rosary, SafeStorage, TEditInput, TEditInputOps, TEditSelection, TextEdit, TextLooks, TextNode, ViewerOps EXPORTS TiogaAccess ~ BEGIN OPEN TiogaAccess; ROPE: TYPE ~ Rope.ROPE; ROSARY: TYPE ~ Rosary.ROSARY; Reader: TYPE ~ TiogaAccessPrivate.Reader; ReaderRep: PUBLIC TYPE ~ TiogaAccessPrivate.ReaderRep; ropePieces: NAT ~ TiogaAccessPrivate.ropePieces; textBufSize: NAT _ (574-SIZE[TEXT[0]])*2; Error: PUBLIC ERROR [group: FS.ErrorGroup, expl: ROPE] ~ CODE; GetExternalProp: PUBLIC PROC [key: ATOM, value: REF] RETURNS [ROPE] ~ { RETURN [NodeProps.GetSpecs[key, value]] }; GetInternalProp: PUBLIC PROC [key: ATOM, value: ROPE] RETURNS [REF] ~ { RETURN [NodeProps.DoSpecs[key, value]] }; MakePropList: PROC [textNode: TextNode.Ref] RETURNS [props: Atom.PropList _ NIL] ~ { Action: PROC [name: ATOM, value: REF] RETURNS [BOOLEAN _ FALSE] ~ { IF name = $CharSets OR name = $CharProps THEN RETURN; value _ NodeProps.CopyInfo[name: name, value: value]; IF value # NIL THEN { props _ CONS[NEW[Atom.DottedPairNode _ [key: name, val: value]], props]; }; }; [] _ NodeProps.MapProps[n: textNode, action: Action, formatFlag: FALSE, commentFlag: FALSE]; }; StartNewNode: PROC [reader: Reader, offset: INT _ 0] ~ { IF reader.textNode = NIL THEN { reader.nodeLength _ 0; reader.format _ NIL; reader.comment _ FALSE; reader.nodeProps _ NIL; RopeReader.SetPosition[reader.ropeReader, NIL, 0]; LooksReader.SetPosition[reader.looksReader, NIL, 0]; } ELSE { t: TextNode.Ref ~ reader.textNode; reader.format _ t.formatName; reader.nodeProps _ MakePropList[t]; reader.nodeLength _ Rope.Length[t.rope]; reader.comment _ t.comment; RopeReader.SetPosition[reader.ropeReader, t.rope, offset]; LooksReader.SetPosition[reader.looksReader, t.runs, offset]; }; }; InitIndex: PROC [reader: Reader] ~ { reader.textNodeIndex _ TextNode.LocOffset[loc1: [reader.root, 0], loc2: [reader.textNode, 0], skipCommentNodes: FALSE]; }; FromNode: PROC [textNode: TextNode.Ref, offset: INT _ INT.FIRST, style: ROPE _ NIL] RETURNS [Reader] ~ { new: Reader ~ NEW[ReaderRep]; new.root _ TextNode.Root[textNode]; IF style # NIL THEN TextEdit.ChangeStyle[new.root, style]; new.textNode _ textNode; new.level _ TextNode.Level[textNode]; new.ropeReader _ RopeReader.GetRopeReader[]; new.looksReader _ LooksReader.GetLooksReader[]; new.end _ (textNode = NIL); StartNewNode[new]; InitIndex[new]; new.startingOffsetFromOriginalRoot _ 0; IF offset # INT.FIRST THEN new.startingOffsetFromOriginalRoot _ offset-GetPosition[new]; SafeStorage.EnableFinalization[new]; RETURN [new]; }; FromNothing: PUBLIC PROC RETURNS [Reader] ~ { RETURN [FromNode[NIL]] }; FromFile: PUBLIC PROC [fileName: ROPE] RETURNS [Reader] ~ { RETURN [FromNode[PutGet.FromFile[fileName]]] }; FromOpenFile: PUBLIC PROC [openFile: FS.OpenFile] RETURNS [Reader] ~ { RETURN [FromNode[PutGet.FromFileC[openFile]]] }; NodeSpanFromSelection: PROC[sel: TEditDocument.Selection] RETURNS[span: TextNode.Span] = { RETURN[TextNode.MakeNodeSpan[first: sel.start.pos.node, last: sel.end.pos.node]] }; SelectionSpan: PROC [sel: TEditDocument.Selection] RETURNS[span: TextNode.Span] = { IF TEditSelection.GetSelectionGrain[sel]=node THEN RETURN[NodeSpanFromSelection[sel]] ELSE RETURN[[start: sel.start.pos, end: sel.end.pos]]; }; TakeNodeSubstr: PROC [t: TextNode.Ref, start: INT, length: INT] = { t.rope _ Rope.Substr[t.rope, start, length]; t.runs _ TextLooks.Substr[t.runs, start, length]; IF t.hascharsets THEN { WITH NodeProps.GetProp[t, $CharSets] SELECT FROM r: ROSARY => { NodeProps.PutProp[t, $CharSets, Rosary.Substr[r, start, length]]; }; ENDCASE => NULL; }; IF t.hascharprops THEN { WITH NodeProps.GetProp[t, $CharProps] SELECT FROM r: ROSARY => { NodeProps.PutProp[t, $CharProps, Rosary.Substr[r, start, length]]; }; ENDCASE => NULL; }; }; FromSelection: PUBLIC PROC RETURNS [Reader] ~ { textNode: TextNode.Ref _ NIL; myOffset: INT _ 0; style: Rope.ROPE _ NIL; CopySelection: PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ { style _ Atom.GetPName[NodeStyleOps.StyleNameForNode[root]]; IF tSel.granularity = point THEN NULL ELSE { span: TextNode.Span _ SelectionSpan[tSel]; copy: TextNode.Span _ EditSpanSupport.CopySpan[span]; myOffset _ TextNode.LocOffset[loc1: TextNode.MakeNodeLoc[root], loc2: span.start, skipCommentNodes: TRUE]; IF copy.end.where # TextNode.NodeItself AND copy.end.node # NIL THEN { TakeNodeSubstr[copy.end.node, 0, copy.end.where+1]; }; IF copy.start.where > 0 THEN { TakeNodeSubstr[copy.start.node, copy.start.where, INT.LAST]; copy.start.where _ 0; }; textNode _ copy.start.node; }; }; TEditInputOps.CallWithLocks[CopySelection, read]; RETURN [FromNode[textNode, myOffset, style]] }; FromViewer: PUBLIC PROC [viewer: ViewerClasses.Viewer] RETURNS [Reader] ~ { textNode: TextNode.Ref _ NIL; WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { r: TextNode.Ref _ tdd.text; r _ TextNode.Root[ EditSpanSupport.CopySpan[ -- adds an extra r node TextNode.MakeNodeSpan[r, TextNode.LastWithin[r]] ] .start.node ]; textNode _ TextNode.FirstChild[r]; -- get rid of the extra r node textNode.next _ NIL; -- no parent or siblings r.child _ NIL; r _ NIL;-- don't want r any more }; ENDCASE => NULL; RETURN [FromNode[textNode]] }; EndOf: PUBLIC PROC [reader: Reader] RETURNS [BOOLEAN] ~ { RETURN [reader.putback = NIL AND reader.end] }; Get: PUBLIC PROC [reader: Reader] RETURNS [TiogaChar] ~ { IF reader.putback # NIL THEN { t: LIST OF TiogaChar ~ reader.putback; reader.putback _ t.rest; t.rest _ NIL; RETURN [t.first]; } ELSE IF reader.end THEN {ERROR Error[client, "Attempt to read past end of document"]} ELSE IF RopeReader.GetIndex[reader.ropeReader] = reader.nodeLength THEN { t: TiogaChar _ [ charSet: 0, char: '\n, looks: ALL[FALSE], format: reader.format, comment: reader.comment, endOfNode: TRUE, deltaLevel: 0, propList: reader.nodeProps ]; t.deltaLevel _ SkipToNextNode[reader]; RETURN [t]; } ELSE { offset: INT ~ reader.ropeReader.GetIndex; t: TiogaChar ~ [ charSet: ( IF reader.textNode.hascharsets THEN TextEdit.Fetch[reader.textNode, offset].charSet ELSE 0), char: RopeReader.Get[reader.ropeReader], looks: LooksReader.Get[reader.looksReader], format: reader.format, comment: reader.comment, endOfNode: FALSE, deltaLevel: 0, propList: ( IF reader.textNode.hascharprops THEN TextEdit.GetCharPropList[reader.textNode, offset] ELSE NIL) ]; RETURN [t] }; }; GetIndex: PUBLIC PROC [reader: Reader] RETURNS [index: INT] ~ { index _ reader.textNodeIndex + reader.ropeReader.GetIndex; }; GetLength: PUBLIC PROC [reader: Reader] RETURNS [INT] ~ { RETURN [TextNode.LocOffset[ loc1: [reader.root, 0], loc2: TextNode.LastLocWithin[reader.root], skipCommentNodes: FALSE ]+1]; }; GetPosition: PUBLIC PROC [reader: Reader] RETURNS [position: INT] ~ { position _ TextNode.LocOffset[loc1: [reader.root, 0], loc2: [reader.textNode, reader.ropeReader.GetIndex], skipCommentNodes: TRUE] + reader.startingOffsetFromOriginalRoot; }; SetIndex: PUBLIC PROC [reader: Reader, index: INT] ~ { DiscardPutBack[reader]; IF index IN [reader.textNodeIndex..reader.textNodeIndex+NodeSize[reader.textNode]) THEN { offset: INT _ index - reader.textNodeIndex; RopeReader.SetIndex[reader.ropeReader, offset]; LooksReader.SetIndex[reader.looksReader, offset]; } ELSE SetLoc[reader, TextNode.LocWithin[n: reader.root, count: index, skipCommentNodes: FALSE]] }; SetPosition: PUBLIC PROC [reader: Reader, position: INT] ~ { DiscardPutBack[reader]; SetLoc[reader, TextNode.LocWithin[n: reader.root, count: position - reader.startingOffsetFromOriginalRoot, skipCommentNodes: TRUE]]; }; SetLoc: PROC [reader: Reader, location: TextNode.Location] ~ { reader.textNode _ location.node; StartNewNode[reader, IF location.where = TextNode.NodeItself THEN 0 ELSE location.where]; InitIndex[reader]; }; GetNodeProps: PUBLIC PROC [reader: Reader] RETURNS [Atom.PropList] ~ { RETURN [reader.nodeProps] }; Peek: PUBLIC PROC [reader: Reader] RETURNS [TiogaChar] ~ { IF reader.putback # NIL THEN { RETURN [reader.putback.first]; } ELSE IF RopeReader.GetIndex[reader.ropeReader] = reader.nodeLength THEN { t: TiogaChar _ [ charSet: 0, char: '\n, looks: ALL[FALSE], format: reader.format, comment: reader.comment, endOfNode: TRUE, deltaLevel: TextNode.Level[reader.textNode.next]-TextNode.Level[reader.textNode], propList: reader.nodeProps ]; RETURN [t]; } ELSE { offset: INT ~ reader.ropeReader.GetIndex; t: TiogaChar ~ [ charSet: ( IF reader.textNode.hascharsets THEN TextEdit.Fetch[reader.textNode, offset].charSet ELSE 0), char: RopeReader.Peek[reader.ropeReader], looks: LooksReader.Peek[reader.looksReader], format: reader.format, comment: reader.comment, endOfNode: FALSE, deltaLevel: 0, propList: ( IF reader.textNode.hascharprops THEN TextEdit.GetCharPropList[reader.textNode, offset] ELSE NIL) ]; RETURN [t]; }; }; PeekRope: PUBLIC PROC [reader: Reader] RETURNS [rope: ROPE _ NIL] ~ { IF reader.putback # NIL THEN { s: IO.STREAM _ IO.ROS[]; FOR t: LIST OF TiogaChar _ reader.putback, t.rest UNTIL t = NIL DO IF t.first.endOfNode THEN RETURN [IO.RopeFromROS[s]]; IO.PutChar[s, t.first.char]; ENDLOOP; rope _ IO.RopeFromROS[s]; }; rope _ Rope.Concat[rope, Rope.Substr[reader.ropeReader.GetRope, reader.ropeReader.GetIndex]]; }; PeekRuns: PROC [reader: Reader] RETURNS [runs: TextLooks.Runs _ NIL] ~ { t: TextNode.Ref ~ reader.textNode; start: INT ~ reader.ropeReader.GetIndex; end: INT ~ Rope.Length[t.rope]; IF t.runs # NIL THEN runs _ TextLooks.Substr[t.runs, start, end-start]; }; PeekCharSets: PROC [reader: Reader] RETURNS [charSets: ROSARY] ~ { t: TextNode.Ref ~ reader.textNode; start: INT ~ reader.ropeReader.GetIndex; end: INT ~ Rope.Length[t.rope]; IF t.hascharsets THEN charSets _ Rosary.Substr[NARROW[NodeProps.GetProp[t, $CharSets]], start, end-start] ELSE charSets _ Rosary.FromItem[NIL, end-start]; }; PeekCharProps: PROC [reader: Reader] RETURNS [charProps: ROSARY] ~ { t: TextNode.Ref ~ reader.textNode; start: INT ~ reader.ropeReader.GetIndex; end: INT ~ Rope.Length[t.rope]; IF t.hascharprops THEN charProps _ Rosary.Substr[NARROW[NodeProps.GetProp[t, $CharProps]], start, end-start] ELSE charProps _ Rosary.FromItem[NIL, end-start]; }; SkipToNextNode: PUBLIC PROC [reader: Reader] RETURNS [deltaLevel: INT] ~ { IF reader.end THEN Error[client, "Attempt to read past end of document"]; reader.textNodeIndex _ reader.textNodeIndex + NodeSize[reader.textNode]; [reader.textNode, deltaLevel] _ TextNode.Forward[reader.textNode]; reader.level _ reader.level + deltaLevel; reader.end _ (reader.level = 0); IF reader.end THEN NULL ELSE StartNewNode[reader]; }; PutBack: PUBLIC PROC [reader: Reader, tiogaChar: TiogaChar] ~ { reader.putback _ CONS[tiogaChar, reader.putback]; }; DiscardPutBack: PROC [reader: Reader] ~ { UNTIL reader.putback = NIL DO t: LIST OF TiogaChar _ reader.putback.rest; reader.putback.rest _ NIL; reader.putback _ t; ENDLOOP; }; DoneWith: PUBLIC PROC [reader: Reader] ~ { IF reader.root # NIL THEN { TEditInput.FreeTree[reader.root]; reader.root _ reader.textNode _ NIL; RopeReader.FreeRopeReader[reader.ropeReader]; reader.ropeReader _ NIL; LooksReader.FreeLooksReader[reader.looksReader]; reader.looksReader _ NIL; reader.format _ NIL; reader.nodeProps _ NIL; DiscardPutBack[reader]; }; }; Writer: TYPE ~ TiogaAccessPrivate.Writer; WriterRep: PUBLIC TYPE ~ TiogaAccessPrivate.WriterRep; Create: PUBLIC PROC RETURNS [Writer] ~ { new: Writer _ NEW[WriterRep]; new.textBuf _ NEW[TEXT[textBufSize]]; Reset[new]; SafeStorage.EnableFinalization[new]; RETURN [new]; }; InsertRoot: PROC [writer: Writer, x: TextNode.Ref] ~ { IF Rope.Length[x.rope] # 0 THEN Error[client, "Root may not contain text"]; x.last _ TRUE; writer.root _ x; writer.last _ x; writer.lastLevel _ 0; }; CreateOneMoreLevel: PROC [writer: Writer] ~ { IF writer.root = NIL THEN { InsertRoot[writer, TextNode.NewTextNode[]]; writer.level _ writer.level + 1; } ELSE { new: TextNode.Ref _ TextNode.NewTextNode[]; new^ _ writer.root^; writer.root.props _ NIL; writer.root.hasstyledef _ writer.root.hasprefix _ writer.root.haspostfix _ writer.root.hascharprops _ writer.root.hascharsets _ writer.root.hasartwork _ FALSE; new.last _ TRUE; new.child _ writer.root; new.next _ NIL; writer.root.next _ new; writer.root _ new; writer.lastLevel _ writer.lastLevel + 1; writer.level _ writer.level + 1; }; }; InsertSibling: PROC [writer: Writer, new: TextNode.Ref] ~ { x: TextNode.Ref ~ writer.last; new.next _ x.next; new.last _ x.last; x.next _ new; x.last _ FALSE; writer.last _ new; }; InsertChild: PROC [writer: Writer, new: TextNode.Ref] ~ { x: TextNode.Ref ~ writer.last; IF x.child # NIL THEN ERROR; new.next _ x; new.last _ TRUE; x.child _ new; writer.last _ new; writer.lastLevel _ writer.lastLevel + 1; }; FoldText: PROC [writer: Writer] ~ { FoldRope[writer, Rope.FromRefText[writer.textBuf]]; writer.textBuf.length _ 0; }; FoldRope: PROC [writer: Writer, rope: ROPE] ~ { i: NAT _ 0; writer.textBuf.length _ 0; DO IF writer.ropes[i] = NIL THEN {writer.ropes[i] _ rope; EXIT}; rope _ Rope.Concat[writer.ropes[i], rope]; writer.ropes[i] _ NIL; IF i < ropePieces-1 THEN i _ i + 1; ENDLOOP; }; GetRope: PROC [writer: Writer] RETURNS [rope: ROPE _ NIL] ~ { FoldText[writer]; FOR i: NAT IN [0..ropePieces) DO IF writer.ropes[i] # NIL THEN { rope _ Rope.Concat[writer.ropes[i], rope]; writer.ropes[i] _ NIL; }; ENDLOOP; }; FoldRuns: PROC [writer: Writer] ~ { IF writer.newLooksRepeat > 0 THEN writer.runs _ TextLooks.Concat[base: writer.runs, rest: TextLooks.CreateRun[writer.newLooksRepeat, writer.newLooks], baseLen: writer.nodeSize-writer.newLooksRepeat, restLen: writer.newLooksRepeat]; writer.newLooksRepeat _ 0; writer.newLooks _ ALL[FALSE]; }; GetRuns: PROC [writer: Writer] RETURNS [runs: TextLooks.Runs] ~ { FoldRuns[writer]; runs _ writer.runs; writer.runs _ NIL; }; FoldCharSets: PROC [writer: Writer] ~ { IF writer.newCharSetRepeat > 0 THEN { IF writer.charSets = NIL AND writer.newCharSet = NIL THEN NULL ELSE IF writer.charSets = NIL THEN { writer.charSets _ Rosary.Concat[Rosary.FromItem[NIL, writer.nodeSize-writer.newCharSetRepeat], Rosary.FromItem[writer.newCharSet, writer.newCharSetRepeat]]; } ELSE { writer.charSets _ Rosary.Concat[writer.charSets, Rosary.FromItem[writer.newCharSet, writer.newCharSetRepeat]]; }; }; writer.newCharSet _ NIL; writer.newCharSetRepeat _ 0; }; GetCharSets: PROC [writer: Writer] RETURNS [charSets: ROSARY] ~ { FoldCharSets[writer]; charSets _ writer.charSets; writer.charSets _ NIL; writer.newCharSet _ NIL; writer.newCharSetRepeat _ 0; IF Rosary.Size[charSets] = 0 THEN charSets _ NIL; }; FoldCharProps: PROC [writer: Writer] ~ { IF writer.newCharPropRepeat > 0 THEN { IF writer.charProps = NIL AND writer.newCharProp = NIL THEN NULL ELSE IF writer.charProps = NIL THEN { writer.charProps _ Rosary.Concat[Rosary.FromItem[NIL, writer.nodeSize-writer.newCharPropRepeat], Rosary.FromItem[writer.newCharProp, writer.newCharPropRepeat]]; } ELSE { writer.charProps _ Rosary.Concat[writer.charProps, Rosary.FromItem[writer.newCharProp, writer.newCharPropRepeat]]; }; }; writer.newCharProp _ NIL; writer.newCharPropRepeat _ 0; }; GetCharProps: PROC [writer: Writer] RETURNS [charProps: ROSARY] ~ { FoldCharProps[writer]; charProps _ writer.charProps; writer.charProps _ NIL; writer.newCharProp _ NIL; writer.newCharPropRepeat _ 0; IF Rosary.Size[charProps] = 0 THEN charProps _ NIL; }; Put: PUBLIC PROC [writer: Writer, tiogaChar: TiogaChar] ~ { IF tiogaChar.endOfNode THEN { new: TextNode.Ref _ TextNode.NewTextNode[]; new.formatName _ tiogaChar.format; new.comment _ tiogaChar.comment; new.rope _ GetRope[writer]; new.runs _ GetRuns[writer]; FOR l: Atom.PropList _ tiogaChar.propList, l.rest UNTIL l = NIL DO key: ATOM ~ NARROW[l.first.key]; NodeProps.PutProp[new, key, l.first.val]; ENDLOOP; NodeProps.PutProp[new, $CharProps, GetCharProps[writer]]; NodeProps.PutProp[new, $CharSets, GetCharSets[writer]]; IF writer.root = NIL AND writer.level=0 AND tiogaChar.deltaLevel=1 AND Rope.Length[new.rope] = 0 THEN InsertRoot[writer, new] ELSE { WHILE writer.level < 1 DO CreateOneMoreLevel[writer]; ENDLOOP; WHILE writer.level < writer.lastLevel DO writer.last _ TextNode.Parent[writer.last]; writer.lastLevel _ writer.lastLevel - 1; ENDLOOP; IF writer.level = writer.lastLevel THEN InsertSibling[writer, new] ELSE IF writer.level = writer.lastLevel + 1 THEN InsertChild[writer, new] ELSE Error[client, "Nesting level may not increase by more than one"]; }; IF writer.first = NIL THEN writer.first _ new; writer.nodeSize _ 0; IF writer.lastLevel # writer.level THEN ERROR; writer.level _ writer.lastLevel + tiogaChar.deltaLevel; } ELSE { text: REF TEXT ~ writer.textBuf; IF tiogaChar.looks # writer.newLooks THEN { FoldRuns[writer]; writer.newLooks _ tiogaChar.looks; }; writer.newLooksRepeat _ writer.newLooksRepeat + 1; IF NOT SameCharSet[tiogaChar.charSet, writer.newCharSet] THEN { FoldCharSets[writer]; IF tiogaChar.charSet # 0 THEN writer.newCharSet _ NEW[CharSet _ tiogaChar.charSet]; }; writer.newCharSetRepeat _ writer.newCharSetRepeat + 1; IF tiogaChar.propList # writer.newCharProp THEN { FoldCharProps[writer]; writer.newCharProp _ tiogaChar.propList; }; writer.newCharPropRepeat _ writer.newCharPropRepeat + 1; IF text.length = text.maxLength THEN FoldText[writer]; text[(text.length _ text.length+1)-1] _ tiogaChar.char; writer.nodeSize _ writer.nodeSize + 1; }; }; SameCharSet: PROC [a: CharSet, b: REF CharSet] RETURNS [BOOL] ~ INLINE { IF b = NIL THEN RETURN [a=0] ELSE RETURN [a=b^] }; GetNodeRefs: PUBLIC PROC [reader: Reader] RETURNS [root, current: REF, offset: INT] ~ { root _ reader.root; current _ reader.textNode; offset _ reader.ropeReader.GetIndex; }; CopyNode: PUBLIC PROC [writer: Writer, reader: Reader, maxLength: INT] RETURNS [nodeEnd: BOOLEAN] ~ { textRope: ROPE _ NIL; textRuns: TextLooks.Runs _ NIL; textCharSets: ROSARY _ NIL; textCharProps: ROSARY _ NIL; textSize: INT _ 0; WHILE maxLength > 0 AND reader.putback # NIL DO tchar: TiogaChar _ Get[reader]; Put[writer, tchar]; maxLength _ maxLength - 1; IF tchar.endOfNode THEN RETURN [TRUE]; ENDLOOP; FoldText[writer]; FoldRuns[writer]; FoldCharSets[writer]; FoldCharProps[writer]; textRope _ PeekRope[reader]; textSize _ Rope.Length[textRope]; textRuns _ PeekRuns[reader]; IF writer.charSets # NIL OR reader.textNode.hascharsets THEN textCharSets _ PeekCharSets[reader]; IF writer.charProps # NIL OR reader.textNode.hascharprops THEN textCharProps _ PeekCharProps[reader]; nodeEnd _ textSize < maxLength; IF NOT nodeEnd THEN { textRope _ Rope.Substr[textRope, 0, maxLength]; textSize _ Rope.Length[textRope]; IF textRuns # NIL THEN textRuns _ TextLooks.Substr[textRuns, 0, textSize]; IF textCharSets # NIL THEN textCharSets _ Rosary.Substr[textCharSets, 0, textSize]; IF textCharProps # NIL THEN textCharSets _ Rosary.Substr[textCharProps, 0, textSize]; }; FoldRope[writer, textRope]; writer.runs _ TextLooks.Concat[base: writer.runs, rest: textRuns, baseLen: writer.nodeSize, restLen: textSize]; IF textCharSets # NIL AND writer.charSets = NIL THEN { writer.charSets _ Rosary.FromItem[NIL, writer.nodeSize]; }; textCharSets _ Rosary.Concat[writer.charSets, textCharSets]; IF textCharProps # NIL AND writer.charProps = NIL THEN { writer.charProps _ Rosary.FromItem[NIL, writer.nodeSize]; }; textCharProps _ Rosary.Concat[writer.charProps, textCharProps]; RopeReader.BumpIndex[reader.ropeReader, textSize]; LooksReader.BumpIndex[reader.looksReader, textSize]; writer.nodeSize _ writer.nodeSize + textSize; IF nodeEnd THEN Put[writer, Get[reader]]; }; Nest: PUBLIC PROC [writer: Writer, delta: INT] ~ { level: INT ~ writer.level + delta; IF level > writer.lastLevel+1 THEN Error[client, "Nesting level may not increase by more than one"]; writer.level _ level; }; SetSelectionFromSpan: PROC [tSel: TEditDocument.Selection, span: TextNode.Span] ~ { IF span.start.node = span.end.node THEN { IF (span.start.where = TextNode.NodeItself) # (span.end.where = TextNode.NodeItself) THEN ERROR; -- someone made a bogus span. IF span.start.where = TextNode.NodeItself THEN { span.start.where _ 0; span.end.where _ MAX[NodeSize[span.end.node]-1, 0]; tSel.granularity _ node; } ELSE { tSel.granularity _ MIN[tSel.granularity, word]; IF span.start.where <= span.end.where THEN { tSel.granularity _ MAX[tSel.granularity, char]; } ELSE { tSel.granularity _ point; span.end.where _ span.start.where; }; }; } ELSE { IF span.start.where = TextNode.NodeItself AND span.end.where = TextNode.NodeItself THEN { tSel.granularity _ MAX[tSel.granularity, node]; } ELSE { tSel.granularity _ char; }; IF span.start.where = TextNode.NodeItself THEN span.start.where _ 0; IF span.end.where = TextNode.NodeItself THEN span.end.where _ MAX[NodeSize[span.end.node]-1, 0]; }; tSel.start.pos _ span.start; tSel.end.pos _ span.end; }; NodeSize: PROC [textNode: TextNode.Ref] RETURNS [INT] ~ { RETURN [Rope.Length[textNode.rope]]; }; FinishOff: PROC [writer: Writer] ~ { IF writer.ropes#ALL[NIL] OR writer.textBuf.length>0 THEN Put[writer, [ charSet: 0, char: '\n, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL ]]; }; WriteFile: PUBLIC PROC [writer: Writer, fileName: ROPE] ~ { FinishOff[writer]; [] _ PutGet.ToFile[fileName, writer.root]; Reset[writer]; }; WriteOpenFile: PUBLIC PROC [writer: Writer, openFile: FS.OpenFile] ~ { FinishOff[writer]; [] _ PutGet.ToFileC[openFile, writer.root]; Reset[writer]; }; WriteSelection: PUBLIC PROC [writer: Writer] ~ { FinishOff[writer]; IF writer.root = NIL THEN TEditInputOps.Delete[] ELSE { success: BOOL _ FALSE; lastSize: INT _ NodeSize[writer.last]; startOffset: INT _ IF lastSize=0 AND writer.first=writer.last THEN TextNode.NodeItself ELSE 0; endOffset: INT _ IF lastSize=0 THEN TextNode.NodeItself ELSE lastSize-1; alpha: TextNode.Span _ [start: [node: writer.first, where: startOffset], end: [node: writer.last, where: endOffset]]; DoTranspose: PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ { beta: TextNode.Span _ SelectionSpan[tSel]; documentRoot: TextNode.Ref _ TextNode.Root[beta.start.node]; IF tSel.granularity = node OR tSel.granularity = branch THEN { alpha.start.where _ alpha.end.where _ TextNode.NodeItself; }; success _ TRUE; [alpha, beta] _ EditSpan.Transpose[alphaRoot: writer.root, betaRoot: documentRoot, alpha: alpha, beta: beta, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => {success _ FALSE; CONTINUE}]; IF success THEN { tSel.pendingDelete _ FALSE; SetSelectionFromSpan[tSel, alpha]; TEditSelection.MakeSelection[new: tSel, selection: primary]; }; }; TEditInputOps.CallWithLocks[DoTranspose, write]; IF NOT success THEN { MessageWindow.Append["Can't do it.", TRUE]; MessageWindow.Blink[]; }; }; Reset[writer]; }; WriteViewer: PUBLIC PROC [writer: Writer, viewer: ViewerClasses.Viewer] ~ { FinishOff[writer]; IF viewer # NIL AND viewer.data # NIL AND viewer.class.set # NIL THEN { viewer.class.set[viewer, writer.root, FALSE, $TiogaDocument]; writer.root _ NIL; ViewerOps.PaintViewer[viewer, all]; }; Reset[writer]; }; WriteReader: PUBLIC PROC [writer: Writer] RETURNS [reader: Reader] ~ { FinishOff[writer]; reader _ FromNode[writer.first]; writer.root _ NIL; Reset[writer]; }; Reset: PUBLIC PROC [writer: Writer] ~ { IF writer.root # NIL THEN { TEditInput.FreeTree[writer.root]; writer.root _ NIL; }; writer.first _ writer.last _ NIL; writer.level _ 0; writer.lastLevel _ 0; writer.nodeSize _ 0; writer.ropes _ ALL[NIL]; writer.textBuf.length _ 0; writer.runs _ NIL; writer.newLooks _ ALL[FALSE]; writer.newLooksRepeat _ 0; writer.charSets _ NIL; writer.newCharSet _ NIL; writer.newCharSetRepeat _ 0; writer.charProps _ NIL; writer.newCharProp _ NIL; writer.newCharPropRepeat _ 0; }; finalizationQueue: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[]; FinalizationProcess: PROCEDURE ~ { DO WITH SafeStorage.FQNext[finalizationQueue] SELECT FROM reader: Reader => DoneWith[reader]; writer: Writer => Reset[writer]; ENDCASE => NULL; ENDLOOP; }; TRUSTED { SafeStorage.EstablishFinalization[ReaderRep.CODE, 0, finalizationQueue ! SafeStorage.CantEstablishFinalization => GOTO Exit]; SafeStorage.EstablishFinalization[WriterRep.CODE, 0, finalizationQueue ! SafeStorage.CantEstablishFinalization => GOTO Exit]; Process.Detach[FORK FinalizationProcess]; EXITS Exit => NULL; }; END. bTiogaAccessImpl.mesa Copyright (C) 1985, Xerox Corporation. All rights reserved. Michael Plass, May 16, 1985 8:47:48 am PDT Tim Diebert May 13, 1985 12:06:39 pm PDT Russ Atkinson (RRA) July 2, 1985 11:08:52 am PDT Largest size that will come from the small-grain allocator (see Allocator.maxSmallBlockSize) index _ TextNode.LocOffset[loc1: [reader.root, 0], loc2: [reader.textNode, reader.ropeReader.GetIndex], skipCommentNodes: FALSE]; Fast case if in the current node. Putback stuff is harder than all the rest! span is exactly one node. part of a node last before first means a point selection crosses multiple nodes includes all entire nodes. includes partial nodes. Now span has been sanitized of NodeItself flag, with the information (mostly) relayed to tSel.granularity. All that's left is to plug in the locations. Collects all the pieces together. Κ!E˜code™K™Kšœ œ*˜9Kšœ œ¦˜΄Kšœ œ˜'Kšœœ4˜LKšœœ ˜Kšœ œ˜K˜—šΠblœ ˜Kšœ"œΒ˜νKšœ ˜Kšœœœ˜K˜Kšœœœ˜šœœ œ˜K˜—Kšœœ˜)Kšœ œœ ˜6Kšœ œ!˜0šœ œœœ˜)Kšœ\™\K˜—š œœœ œœœ˜>K˜—šΟnœœœœ œœœ˜GKšœ!˜'Kšœ˜K˜—šŸœœœœ œœœ˜GKšœ ˜&Kšœ˜K˜—šŸ œœœœ˜TšŸœœœ œœœœ˜CKšœœœœ˜5Kšœ5˜5šœ œœ˜Kšœœœ8˜HKšœ˜—Kšœ˜—K•StartOfExpansionr[n: TextNode.Ref, action: NodeProps.MapPropsAction, typeFlag: BOOLEAN _ TRUE, commentFlag: BOOLEAN _ TRUE]šœAœœ˜\Kšœ˜K˜—šŸ œœœ ˜8šœœœ˜Kšœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ*œ˜2Kšœ,œ˜4Kšœ˜—šœ˜Kšœ"˜"Kšœ˜Kšœ#˜#Kšœ(˜(Kšœ˜Kšœ:˜:Kšœ<˜˜XKšœ$˜$Kšœ˜ Kšœ˜K˜—šŸ œœœœ ˜-Kšœ œ˜Kšœ˜K˜—š Ÿœœœ œœ ˜;Kšœ&˜,Kšœ˜K˜—š Ÿ œœœ œ œ ˜FKšœ'˜-Kšœ˜K˜—šŸœœœ˜ZKšœJ˜PK˜K˜—šŸ œœ œ˜SKšœ,œœ˜UKšœœ+˜6K˜K˜—šŸœœœ œ˜CKšœ,˜,Kšœ1˜1šœœ˜šœ!œ˜0šœœ˜KšœA˜AKšœ˜—Kšœœ˜—Kšœ˜—šœœ˜šœ"œ˜1šœœ˜KšœB˜BKšœ˜—Kšœœ˜—Kšœ˜—K˜K˜K˜—šŸ œœœœ ˜/Kšœœ˜Kšœ œ˜Kšœ œœ˜šŸ œœ8˜KKšœ;˜;Kšœœ˜%šœ˜Kšœ*˜*Kšœ5˜5Kšœdœ˜jšœ&œœœ˜FKšœ3˜3Kšœ˜—šœœ˜Kšœ2œœ˜Kšœ ˜ Kšœœ&œœ˜YKšœ˜Kšœ˜K˜—šŸ œœœœ˜FKšœ˜Kšœ˜K˜—šŸœœœœ˜:šœœœ˜Kšœ˜Kšœ˜—šœœ<œ˜Išœ˜Kšœ ˜ Kšœ ˜ Kšœœœ˜Kšœ˜Kšœ˜Kšœ œ˜K˜QKšœ˜Kšœ˜—Kšœ˜ Kšœ˜—šœ˜Kšœœ˜)šœ˜šœ ˜ Kšœ˜Kšœ0˜4Kšœ˜—Kšœ)˜)Kšœ,˜,Kšœ˜Kšœ˜Kšœ œ˜K˜šœ ˜ Kšœ˜Kšœ2˜6Kšœœ˜ —Kšœ˜—Kšœ˜ Kšœ˜—Kšœ˜K˜—š Ÿœœœœœœ˜Ešœœœ˜K™*Kš œœœœœ˜š œœœ$œœ˜BKšœœœœ˜5Kšœ˜Kšœ˜—Kšœœ˜Kšœ˜—Kšœ]˜]Kšœ˜K˜—šŸœœœœ˜HKšœ"˜"Kšœœ˜(Kšœœ˜Kšœ œœ3˜GKšœ˜K˜—šŸ œœœ œ˜BKšœ"˜"Kšœœ˜(Kšœœ˜Kšœ˜Kšœœ4˜XKšœœ ˜0Kšœ˜K˜—šŸ œœœ œ˜DKšœ"˜"Kšœœ˜(Kšœœ˜Kšœ˜Kšœœ5˜ZKšœœ ˜1Kšœ˜K˜—š Ÿœœœœœ˜JKšœ œ7˜IKšœH˜HKšœB˜BKšœ)˜)Kšœ ˜ Kšœ œ˜Kšœ˜Kšœ˜K˜—šŸœœœ+˜?Kšœœ˜1Kšœ˜K˜—šŸœœ˜)šœœ˜Kšœœœ!˜+Kšœœ˜Kšœ˜Kšœ˜—Kšœ˜K˜—šŸœœœ˜*šœœœ˜KšœBœ˜FKšœBœ˜FKšœFœ˜JKšœœ˜Kšœœ˜Kšœ˜Kšœ˜—Kšœ˜K˜—Kšœœ˜)šœ œœ ˜6K˜—šŸœœœœ ˜(Kšœœ ˜Kšœœœ˜%K˜ Kšœ$˜$Kšœ˜ Kšœ˜K˜—šŸ œœ&˜6Kšœœ,˜KKšœ œ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—šŸœœ˜-šœœœ˜Kšœ+˜+Kšœ ˜ Kšœ˜—šœ˜Kšœ+˜+Kšœ˜Kšœœ˜Kšœ™œ˜ŸKšœ œ˜Kšœ˜Kšœ œ˜Kšœ˜Kšœ˜Kšœ(˜(Kšœ ˜ Kšœ˜—Kšœ˜K˜—šŸ œœ(˜;Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ œ˜Kšœ˜Kšœ˜K˜—šŸ œœ(˜9Kšœ˜Kšœ œœœ˜Kšœ ˜ Kšœ œ˜K˜Kšœ˜Kšœ(˜(Kšœ˜K˜—šŸœœ˜#Kšœ3˜3Kšœ˜Kšœ˜K˜—šŸœœœ˜/Kšœœ˜ Kšœ˜š˜Kšœœœœ˜=Kšœ*˜*Kšœœ˜Kšœœ ˜#Kšœ˜—Kšœ˜K˜—š Ÿœœœœœ˜=Kšœ˜šœœœ˜ šœœœ˜Kšœ*˜*Kšœœ˜Kšœ˜—Kšœ˜—Kšœ˜K˜—šŸœœ˜#KšœœΖ˜ηKšœ˜Kšœœœ˜Kšœ˜K˜—šŸœœœ˜AKšœ˜Kšœ˜Kšœœ˜Kšœ˜K˜—šŸ œœ˜'šœœ˜%Kš œœœœœ˜>šœœœœ˜$Kšœ0œi˜œKšœ˜—šœ˜Kšœn˜nKšœ˜—Kšœ˜—Kšœœ˜Kšœ˜Kšœ˜K˜—šŸ œœœ œ˜AKšœ˜Kšœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœœ œ˜1Kšœ˜K˜—šŸ œœ˜(šœœ˜&Kš œœœœœ˜@šœœœœ˜%Kšœ1œl˜ Kšœ˜—šœ˜Kšœr˜rKšœ˜—Kšœ˜—Kšœœ˜Kšœ˜Kšœ˜K˜—šŸ œœœ œ˜CKšœ˜Kšœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœœ œ˜3Kšœ˜K˜—šŸœœœ+˜;šœœ˜Kšœ+˜+Kšœ"˜"Kšœ ˜ Kšœ˜Kšœ˜šœ/œœ˜BKšœœœ˜ Kšœ)˜)Kšœ˜—Kšœ9˜9Kšœ7˜7Kš œœœœœœ˜}šœ˜šœ˜Kšœ˜Kšœ˜—šœ!˜(Kšœ+˜+Kšœ(˜(Kšœ˜—Kšœ!œ˜BKšœœ%œ˜IKšœB˜FKšœ˜—Kšœœœ˜.Kšœ˜Kšœ!œœ˜.Kšœ7˜7Kšœ˜—šœ˜Kšœœœ˜ šœ#œ˜+Kšœ˜Kšœ"˜"K˜—Kšœ2˜2šœœ3œ˜?Kšœ˜Kšœœœ˜SKšœ˜—Kšœ6˜6šœ)œ˜1Kšœ˜Kšœ(˜(Kšœ˜—Kšœ8˜8Kšœœ˜6Kšœ7˜7K˜&Kšœ˜—Kšœ˜K˜—š Ÿ œœœ œœœ˜HKšœœœœ˜Kšœœ˜Kšœ˜K˜—š Ÿ œœœœœ œ˜WKšœ˜Kšœ˜Kšœ$˜$Kšœ˜K˜—š Ÿœœœ-œœ œ˜eKšœ œœ˜Kšœœ˜Kšœœœ˜Kšœœœ˜Kšœ œ˜šœœœ˜/Kšœ˜Kšœ˜Kšœ˜Kšœœœœ˜&Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ˜Kšœœœœ%˜aKšœœœœ'˜eKšœ˜šœœ œ˜Kšœ/˜/Kšœ!˜!Kšœ œœ4˜JKšœœœ9˜SKšœœœ:˜UKšœ˜—Kšœ˜Kšœo˜oš œœœœœ˜6Kšœ"œ˜8Kšœ˜—Kšœ<˜<š œœœœœ˜8Kšœ#œ˜9Kšœ˜—Kšœ?˜?Kšœ2˜2Kšœ4˜4Kšœ-˜-Kšœ œ˜)Kšœ˜K˜—šŸœœœœ˜2Kšœœ˜"KšœœB˜dKšœ˜Kšœ˜K˜—šŸœœ9˜Sšœ!œ˜)KšœSœœ ˜~šœ(œ˜0K™Kšœ˜Kšœœ˜3Kšœ˜Kšœ˜—šœ˜K™Kšœœ˜/šœ$œ˜,Kšœœ˜/Kšœ˜—šœ˜K™)Kšœ˜Kšœ"˜"Kšœ˜—Kšœ˜—Kšœ˜—šœ˜K™šœ(œ&œ˜YK™Kšœœ˜/K˜—šœ˜K™Kšœ˜Kšœ˜—šœ(˜.Kšœ˜—šœ&˜,Kšœœ˜3—Kšœ˜—K™˜K˜K˜Kšœ˜K˜—šŸœœœœ˜9Kšœ˜$Kšœ˜K˜—šŸ œœ˜$K™!š œœœœœ˜FKšœ ˜ Kšœ ˜ Kšœœœ˜Kšœœ˜ Kšœ œ˜Kšœ œ˜K˜Kšœ ˜ Kšœ˜—Kšœ˜K˜—šŸ œœœœ˜;Kšœ˜Kšœ*˜*Kšœ˜Kšœ˜K˜—šŸ œœœœ˜FKšœ˜Kšœ+˜+Kšœ˜Kšœ˜K˜—šŸœœœ˜0Kšœ˜Kšœœœ˜0šœ˜K–h -- [start: TextNode.Location _ [node: NIL, where: -1], end: TextNode.Location _ [node: NIL, where: -1]]šœ œœ˜Kšœ œ˜&Kš œ œœ œœœ˜^Kš œ œœ œœ ˜HKšœu˜ušŸ œœ8˜IKšœ*˜*Kšœ<˜<šœœœ˜>Kšœ:˜:Kšœ˜—Kšœ œ˜Kšœ²œœ˜Δšœ œ˜Kšœœ˜Kšœ"˜"Kšœ<˜