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, Size], 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; reader.end _ TRUE; 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; reader.end _ FALSE; 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[]; 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+TextEdit.Size[reader.textNode]) THEN { offset: INT _ index - reader.textNodeIndex; RopeReader.SetIndex[reader.ropeReader, offset]; LooksReader.SetIndex[reader.looksReader, offset]; } ELSE SetLoc[reader, IF index = 0 THEN [reader.root, 0] ELSE 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: 0, propList: reader.nodeProps ]; t.deltaLevel _ TextNode.Forward[reader.textNode].levelDelta; 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 + TextEdit.Size[reader.textNode] + 1; [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]; }; writer.charSets _ Rosary.Concat[writer.charSets, textCharSets]; IF textCharProps # NIL AND writer.charProps = NIL THEN { writer.charProps _ Rosary.FromItem[NIL, writer.nodeSize]; }; writer.charProps _ 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[TextEdit.Size[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[TextEdit.Size[span.end.node]-1, 0]; }; tSel.start.pos _ span.start; tSel.end.pos _ span.end; }; 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 _ TextEdit.Size[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. –TiogaAccessImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Michael Plass, October 28, 1986 12:02:38 pm PST Tim Diebert May 13, 1985 12:06:39 pm PDT Russ Atkinson (RRA) July 2, 1985 11:08:52 am PDT Doug Wyatt, July 18, 1986 2:17:49 pm 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. Κ!D˜code™Kšœ Οmœ7™BK™/K™(K™0K™(K™—šΟk ˜ Kšœžœ&˜0Kšœ žœ˜)Kšœžœ ˜!Kšžœžœ˜ Kšžœžœžœžœ˜-Kšœ žœ[˜lKšœžœ˜$Kšœ žœ;˜JKšœ žœ˜&Kšœžœ ˜Kšœžœ(˜4Kšœžœžœ ˜7Kšœ žœl˜|Kšœžœžœ˜6Kšœ žœj˜{Kšœžœ ˜3Kšœ žœ˜*Kšœžœ˜,Kšœžœ$˜8Kšœ žœ6˜DKšœ žœ*˜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šœ žœ˜Kšœ*žœ˜2Kšœ,žœ˜4Kšœ˜—šžœ˜Kšœ"˜"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˜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šœB˜BKšœ2˜2Kšœ4˜4Kšœ-˜-Kšžœ žœ˜)Kšœ˜K˜—š œžœžœžœ˜2Kšœžœ˜"KšžœžœB˜dKšœ˜Kšœ˜K˜—š œžœ9˜Sšžœ!žœ˜)KšžœSžœžœ‘˜~šžœ(žœ˜0K™Kšœ˜Kšœžœ$˜8Kšœ˜Kšœ˜—šžœ˜K™Kšœžœ˜/šžœ$žœ˜,Kšœžœ˜/Kšœ˜—šžœ˜K™)Kšœ˜Kšœ"˜"Kšœ˜—Kšœ˜—Kšœ˜—šžœ˜K™šžœ(žœ&žœ˜YK™Kšœžœ˜/K˜—šžœ˜K™Kšœ˜Kšœ˜—šžœ(ž˜.Kšœ˜—šžœ&ž˜,Kšœžœ$˜8—Kšœ˜—K™˜K˜K˜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šœ<˜