DIRECTORY Ascii USING [CR], Atom USING [DottedPairNode, PropList], EditSpan USING [CannotDoEdit, Transpose], EditSpanSupport USING [CopySpan], FS USING [ErrorGroup, OpenFile], IO USING [PutChar, RopeFromROS, ROS, STREAM], LooksReader USING [Body, BumpIndex, FreeLooksReader, Get, GetIndex, GetLooksReader, Peek, Ref, SetIndex, SetPosition], MessageWindow USING [Append, Blink], NameSymbolTable USING [MakeNameFromRope, RopeFromName], NodeProps USING [DoSpecs, GetSpecs, MapProps, PutProp], 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], RunReader USING [Get, GetIndex, GetRuns, Peek, SetIndex, SetPosition], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ], TEditDocument USING [Selection, TEditDocumentData], TEditInput USING [currentEvent, FreeTree], TEditInputOps USING [CallWithLocks, Delete], TEditSelection USING [GetSelectionGrain, MakeSelection], TextLooks USING [Concat, CreateRun, Looks, Runs, Size, Substr], TextLooksSupport USING [], TextNode USING [Body, FirstChild, Forward, LastLocWithin, LastWithin, Level, Location, LocOffset, LocWithin, MakeNodeLoc, MakeNodeSpan, NewTextNode, NodeItself, Offset, Parent, Ref, Root, Span, TextBody], TiogaAccess USING [Buffer, BufferRep, CharWithLooks, Looks, NodeInfo], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer]; TiogaAccessImpl: CEDAR PROGRAM IMPORTS EditSpan, EditSpanSupport, IO, LooksReader, MessageWindow, NameSymbolTable, NodeProps, Process, PutGet, Rope, RopeReader, SafeStorage, TEditInput, TEditInputOps, TEditSelection, TextLooks, TextNode, ViewerOps EXPORTS TiogaAccess ~ BEGIN OPEN TiogaAccess; ROPE: TYPE ~ Rope.ROPE; ropePieces: NAT ~ 15; textBufSize: NAT _ (574-SIZE[TEXT[0]])*2; PutBackBufferSize: NAT _ 100; maxPieceSize: INT ~ (INT[64]*1024-8-SIZE[BufferRep[0]])/SIZE[CharWithLooks]; Reader: TYPE ~ REF ReaderRep; ReaderRep: PUBLIC TYPE ~ RECORD [ root: TextNode.Ref, textNode: TextNode.Ref, textNodeIndex: INT, nodeInfo: NodeInfo, nodeLength: INT, ropeReader: RopeReader.Ref, looksReader: LooksReader.Ref, end: BOOLEAN, startingOffsetFromOriginalRoot: TextNode.Offset, putback: LIST OF Buffer ]; Error: PUBLIC ERROR [group: FS.ErrorGroup, expl: ROPE] ~ CODE; ObtainBuffer: PUBLIC PROC [pieceSize: NAT] RETURNS [Buffer] ~ { buffer: Buffer _ NEW[BufferRep[pieceSize]]; buffer.length _ 0; buffer.next _ NIL; RETURN [buffer] }; NextPieceSize: PROC [pieceSize: NAT] RETURNS [NAT] ~ { RETURN [MIN[maxPieceSize, 2*pieceSize]]; }; ReleaseBuffer: PUBLIC PROC [buffer: Buffer] ~ { IF buffer # NIL THEN { ReleaseBuffer[buffer.next]; buffer.next _ NIL; }; }; BufferSize: PUBLIC PROC [buffer: Buffer] RETURNS [INT] ~ { size: INT _ 0; WHILE buffer # NIL DO size _ size + buffer.length; buffer _ buffer.next; ENDLOOP; RETURN [size]; }; SetBufferSize: PUBLIC PROC [buffer: Buffer, newSize: INT] ~ { WHILE buffer.maxLength < newSize DO buffer.length _ buffer.maxLength; IF buffer.next = NIL THEN buffer.next _ ObtainBuffer[NextPieceSize[buffer.maxLength]]; newSize _ newSize - buffer.length; buffer _ buffer.next; ENDLOOP; buffer.length _ newSize; ReleaseBuffer[buffer.next]; buffer.next _ NIL; }; Append: PUBLIC PROC [buffer: Buffer, value: CharWithLooks] ~ { WHILE buffer.next # NIL DO buffer _ buffer.next; ENDLOOP; IF buffer.length = buffer.maxLength THEN { buffer.next _ ObtainBuffer[NextPieceSize[buffer.maxLength]]; buffer _ buffer.next; buffer.length _ 0; }; InlineAppend[buffer, value]; }; Store: PUBLIC PROC [buffer: Buffer, index: INT, value: CharWithLooks] ~ { UNTIL buffer = NIL OR index < buffer.length DO index _ index-buffer.length; buffer _ buffer.next; ENDLOOP; IF buffer = NIL THEN Error[client, "Index exceeds buffer bounds"]; buffer[index] _ value; }; Fetch: PUBLIC PROC [buffer: Buffer, index: INT] RETURNS [CharWithLooks] ~ { UNTIL buffer = NIL OR index < buffer.length DO index _ index-buffer.length; buffer _ buffer.next; ENDLOOP; IF buffer = NIL THEN Error[client, "Index exceeds buffer bounds"]; RETURN [buffer[index]]; }; GetExternalProp: PUBLIC PROC [key: REF, value: REF] RETURNS [ROPE] ~ { RETURN [NodeProps.GetSpecs[NARROW[key], value]] }; GetInternalProp: PUBLIC PROC [key: REF, value: ROPE] RETURNS [REF] ~ { RETURN [NodeProps.DoSpecs[NARROW[key], value]] }; MakePropList: PROC [textNode: TextNode.Ref] RETURNS [props: Atom.PropList _ NIL] ~ { Action: PROC [name: ATOM, value: REF] RETURNS [BOOLEAN _ FALSE] ~ { props _ CONS[NEW[Atom.DottedPairNode _ [key: name, val: NodeProps.GetSpecs[name, value]]], props]; }; [] _ NodeProps.MapProps[n: textNode, action: Action, typeFlag: FALSE, commentFlag: FALSE]; }; StartNewNode: PROC [reader: Reader, offset: INT _ 0] ~ { IF reader.textNode = NIL THEN { reader.nodeInfo.format _ NIL; reader.nodeInfo.comment _ FALSE; reader.nodeInfo.props _ NIL; } ELSE { reader.nodeInfo.format _ NameSymbolTable.RopeFromName[reader.textNode.typename]; reader.nodeInfo.props _ MakePropList[reader.textNode]; }; WITH reader.textNode SELECT FROM t: REF TextNode.Body.text => { reader.nodeLength _ Rope.Length[t.rope]; reader.nodeInfo.comment _ t.comment; RopeReader.SetPosition[reader.ropeReader, t.rope, offset]; LooksReader.SetPosition[reader.looksReader, t.runs, offset]; }; ENDCASE => { reader.nodeLength _ 0; reader.nodeInfo.comment _ TRUE; RopeReader.SetPosition[reader.ropeReader, NIL]; LooksReader.SetPosition[reader.looksReader, NIL]; }; }; InitIndex: PROC [reader: Reader] ~ { reader.textNodeIndex _ TextNode.LocOffset[loc1: [reader.root, 0], loc2: [reader.textNode, 0], skipCommentNodes: FALSE]; }; FromNode: PROC [textNode: TextNode.Ref, offset: TextNode.Offset _ INT.FIRST] RETURNS [Reader] ~ { new: Reader ~ NEW[ReaderRep]; new.root _ TextNode.Root[textNode]; new.textNode _ textNode; new.nodeInfo.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]]; }; CreateChain: PROC [levels: INT] RETURNS [root, last: TextNode.Ref] ~ { IF levels <= 0 THEN RETURN [NIL, NIL]; root _ TextNode.NewTextNode[]; root.last _ TRUE; last _ root; THROUGH [0..levels) DO n: REF TextNode.Body.text _ TextNode.NewTextNode[]; n.last _ TRUE; last.child _ n; last _ n; ENDLOOP; }; FromSelection: PUBLIC PROC RETURNS [Reader] ~ { textNode: TextNode.Ref _ NIL; myOffset: TextNode.Offset _ 0; CopySelection: PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ { 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 THEN { WITH copy.end.node SELECT FROM t: REF TextNode.Body.text => { t.rope _ Rope.Substr[t.rope, 0, copy.end.where+1]; t.runs _ TextLooks.Substr[t.runs, 0, copy.end.where+1]; }; ENDCASE => NULL; }; IF copy.start.where > 0 THEN { WITH copy.start.node SELECT FROM t: REF TextNode.Body.text => { t.rope _ Rope.Substr[t.rope, copy.start.where, INT.LAST]; t.runs _ TextLooks.Substr[t.runs, copy.start.where, INT.LAST]; copy.start.where _ 0; }; ENDCASE => NULL; }; textNode _ copy.start.node; }; }; TEditInputOps.CallWithLocks[CopySelection, read]; RETURN [FromNode[textNode, myOffset]] }; 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 [CharWithLooks] ~ { IF reader.putback # NIL THEN { buf: Buffer _ reader.putback.first; candl: CharWithLooks _ buf[buf.length-1]; IF (buf.length _ buf.length - 1) = 0 THEN { t: LIST OF Buffer _ reader.putback.rest; reader.putback.rest _ NIL; reader.putback _ t; ReleaseBuffer[buf]; }; RETURN [candl] } ELSE IF reader.end THEN {ERROR Error[client, "Attempt to read past end of document"]} ELSE IF RopeReader.GetIndex[reader.ropeReader] = reader.nodeLength THEN { SkipToNextNode[reader]; RETURN [[char: Ascii.CR, endOfNode: TRUE, looks: ALL[FALSE]]] } ELSE { RETURN [[char: RopeReader.Get[reader.ropeReader], endOfNode: FALSE, looks: LooksReader.Get[reader.looksReader]]] }; }; 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]; }; GetNodeInfo: PUBLIC PROC [reader: Reader] RETURNS [NodeInfo] ~ { RETURN [reader.nodeInfo] }; GetBufferBlock: PROC [reader: Reader, buffer: Buffer, maxLength: INT] RETURNS [endOfNode: BOOLEAN] ~ { n: NAT ~ MIN[maxLength, buffer.maxLength-buffer.length]; FOR i: NAT IN [buffer.length..buffer.length+n) DO IF (buffer[i] _ Get[reader]).endOfNode THEN { buffer.length _ i+1; RETURN [TRUE] }; ENDLOOP; buffer.length _ buffer.length+n; RETURN [FALSE]; }; GetBuffer: PUBLIC PROC [reader: Reader, buffer: Buffer, start: INT, maxLength: INT] RETURNS [endOfNode: BOOLEAN _ FALSE] ~ { WHILE buffer.maxLength <= start DO buffer.length _ buffer.maxLength; IF buffer.next = NIL THEN buffer.next _ ObtainBuffer[NextPieceSize[buffer.maxLength]]; start _ start - buffer.length; buffer _ buffer.next; ENDLOOP; buffer.length _ start; UNTIL maxLength = 0 DO endOfNode _ GetBufferBlock[reader, buffer, maxLength].endOfNode; IF endOfNode THEN EXIT; maxLength _ maxLength - buffer.length; IF buffer.next = NIL THEN buffer.next _ ObtainBuffer[NextPieceSize[buffer.maxLength]]; buffer _ buffer.next; buffer.length _ 0; ENDLOOP; ReleaseBuffer[buffer.next]; buffer.next _ NIL; }; Peek: PUBLIC PROC [reader: Reader] RETURNS [CharWithLooks] ~ { IF reader.putback # NIL THEN { buf: Buffer _ reader.putback.first; RETURN [buf[buf.length-1]]; } ELSE IF RopeReader.GetIndex[reader.ropeReader] = reader.nodeLength THEN { RETURN [[char: Ascii.CR, endOfNode: TRUE, looks: ALL[FALSE]]] } ELSE { RETURN [[char: RopeReader.Peek[reader.ropeReader], endOfNode: FALSE, looks: LooksReader.Peek[reader.looksReader]]] }; }; PeekRope: PUBLIC PROC [reader: Reader] RETURNS [rope: ROPE _ NIL] ~ { IF reader.putback # NIL THEN { s: IO.STREAM _ IO.ROS[]; FOR bl: LIST OF Buffer _ reader.putback, bl.rest UNTIL bl = NIL DO buf: Buffer _ bl.first; FOR i: NAT DECREASING IN [0..buf.length) DO candl: CharWithLooks _ buf[i]; IF candl.endOfNode THEN RETURN [IO.RopeFromROS[s]]; IO.PutChar[s, candl.char]; ENDLOOP; 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] ~ { WITH reader.textNode SELECT FROM t: REF TextNode.Body.text => { start: INT _ reader.ropeReader.GetIndex; end: INT _ Rope.Length[t.rope]; IF t.runs # NIL THEN runs _ TextLooks.Substr[t.runs, start, end-start]; }; ENDCASE => NULL; }; SkipToNextNode: PUBLIC PROC [reader: Reader] ~ { 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.nodeInfo.level _ reader.nodeInfo.level + deltaLevel; reader.nodeInfo.props _ NIL; reader.end _ (reader.nodeInfo.level = 0); IF reader.end THEN NULL ELSE StartNewNode[reader]; }; Full: PROC [buffer: Buffer] RETURNS [BOOLEAN] ~ INLINE {RETURN [buffer.length = buffer.maxLength]}; InlineAppend: PROC [buffer: Buffer, charWithLooks: CharWithLooks] ~ INLINE {buffer[(buffer.length _ buffer.length+1)-1] _ charWithLooks}; PutBack: PUBLIC PROC [reader: Reader, charWithLooks: CharWithLooks] ~ { IF reader.putback = NIL OR Full[reader.putback.first] THEN { buf: Buffer _ ObtainBuffer[100]; buf.length _ 0; reader.putback _ CONS[buf, reader.putback]; }; InlineAppend[reader.putback.first, charWithLooks]; }; PutBackBuffer: PUBLIC PROC [reader: Reader, buffer: Buffer, start: INT, maxLength: INT] ~ { end: NAT _ start + MIN[buffer.length-start, maxLength]; FOR i: INT IN [start..end) DO PutBack[reader, Fetch[buffer, i]]; ENDLOOP; }; DiscardPutBack: PROC [reader: Reader] ~ { UNTIL reader.putback = NIL DO t: LIST OF Buffer _ 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.nodeInfo _ [format: NIL, comment: FALSE, level: 0, props: NIL]; DiscardPutBack[reader]; }; }; Writer: TYPE ~ REF WriterRep; WriterRep: PUBLIC TYPE ~ RECORD [ root: REF TextNode.TextBody, first: TextNode.Ref, last: TextNode.Ref, lastLevel: INT, nodeInfo: NodeInfo, ropes: ARRAY [0..ropePieces) OF ROPE, textBuf: REF TEXT, runs: TextLooks.Runs, runsSize: INT, newRunLooks: TextLooks.Looks, newRunSize: INT ]; Create: PUBLIC PROC RETURNS [Writer] ~ { new: Writer _ NEW[WriterRep]; new.root _ NIL; new.first _ new.last _ NIL; new.lastLevel _ 0; new.nodeInfo _ [format: NIL, comment: FALSE, level: 0, props: NIL]; new.textBuf _ NEW[TEXT[textBufSize]]; new.textBuf.length _ 0; new.ropes _ ALL[NIL]; new.runs _ NIL; new.runsSize _ 0; new.newRunLooks _ ALL[FALSE]; new.newRunSize _ 0; SafeStorage.EnableFinalization[new]; RETURN [new]; }; InsertRoot: PROC [writer: Writer, x: REF TextNode.Body.text] ~ { IF Rope.Length[x.rope] # 0 THEN Error[client, "Root may not contain text"]; x.last _ TRUE; writer.root _ x; writer.last _ x; }; CreateChainFromRoot: PROC [writer: Writer] ~ { writer.root _ TextNode.NewTextNode[]; writer.root.last _ TRUE; writer.last _ writer.root; writer.lastLevel _ 0; UNTIL writer.nodeInfo.level = writer.lastLevel + 1 DO InsertChild[writer, TextNode.NewTextNode[]]; ENDLOOP; }; RunSize: PROC [runs: TextLooks.Runs] RETURNS [INT] ~ { RETURN [TextLooks.Size[runs]] }; 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.newRunSize > 0 THEN writer.runs _ TextLooks.Concat[base: writer.runs, rest: TextLooks.CreateRun[writer.newRunSize, writer.newRunLooks], baseLen: writer.runsSize, restLen: writer.newRunSize]; writer.runsSize _ writer.runsSize + writer.newRunSize; writer.newRunSize _ 0; }; GetRuns: PROC [writer: Writer] RETURNS [runs: TextLooks.Runs] ~ { FoldRuns[writer]; runs _ writer.runs; writer.runs _ NIL; writer.runsSize _ 0; }; Put: PUBLIC PROC [writer: Writer, charWithLooks: CharWithLooks] ~ { IF writer.nodeInfo.level < 0 THEN Error[client, "Level may not be negative"]; IF charWithLooks.endOfNode THEN { new: REF TextNode.Body.text _ TextNode.NewTextNode[]; new.typename _ NameSymbolTable.MakeNameFromRope[writer.nodeInfo.format]; new.comment _ writer.nodeInfo.comment; new.rope _ GetRope[writer]; new.runs _ GetRuns[writer]; FOR l: Atom.PropList _ writer.nodeInfo.props, l.rest UNTIL l = NIL DO key: ATOM ~ NARROW[l.first.key]; rope: ROPE ~ NARROW[l.first.val]; NodeProps.PutProp[new, key, GetInternalProp[key, rope]]; ENDLOOP; IF writer.root = NIL AND writer.nodeInfo.level = 0 THEN InsertRoot[writer, new] ELSE { IF writer.nodeInfo.level < 1 THEN Error[client, "Invalid nesting level"]; IF writer.root = NIL THEN CreateChainFromRoot[writer]; WHILE writer.nodeInfo.level < writer.lastLevel DO writer.last _ TextNode.Parent[writer.last]; writer.lastLevel _ writer.lastLevel - 1; ENDLOOP; IF writer.nodeInfo.level = writer.lastLevel THEN InsertSibling[writer, new] ELSE IF writer.nodeInfo.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.runsSize _ 0; } ELSE { text: REF TEXT ~ writer.textBuf; IF charWithLooks.looks # writer.newRunLooks THEN { FoldRuns[writer]; writer.newRunLooks _ charWithLooks.looks; }; writer.newRunSize _ writer.newRunSize + 1; IF text.length = text.maxLength THEN FoldText[writer]; text[(text.length _ text.length+1)-1] _ charWithLooks.char; }; }; 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, copyInfo: BOOLEAN] RETURNS [endOfNode: BOOLEAN] ~ { textRope: ROPE _ NIL; textRuns: TextLooks.Runs _ NIL; textSize: INT _ 0; IF copyInfo THEN PutNodeInfo[writer, GetNodeInfo[reader]]; WHILE maxLength > 0 AND reader.putback # NIL DO candl: CharWithLooks _ Get[reader]; Put[writer, candl]; maxLength _ maxLength - 1; IF candl.endOfNode THEN RETURN [TRUE]; ENDLOOP; FoldText[writer]; FoldRuns[writer]; textRope _ PeekRope[reader]; textSize _ Rope.Length[textRope]; textRuns _ PeekRuns[reader]; endOfNode _ textSize < maxLength; IF NOT endOfNode THEN { textRope _ Rope.Substr[textRope, 0, maxLength]; textSize _ Rope.Length[textRope]; IF textRuns # NIL THEN textRuns _ TextLooks.Substr[textRuns, 0, textSize]; }; FoldRope[writer, textRope]; writer.runs _ TextLooks.Concat[base: writer.runs, rest: textRuns, baseLen: writer.runsSize, restLen: textSize]; writer.runsSize _ writer.runsSize + textSize; RopeReader.BumpIndex[reader.ropeReader, textSize]; LooksReader.BumpIndex[reader.looksReader, textSize]; IF endOfNode THEN Put[writer, Get[reader]]; }; PutNodeInfo: PUBLIC PROC [writer: Writer, nodeInfo: NodeInfo] ~ { writer.nodeInfo _ nodeInfo; }; PutBuffer: PUBLIC PROC [writer: Writer, buffer: Buffer, start: INT, maxLength: INT] ~ { WHILE buffer#NIL AND start >= buffer.length DO start _ start - buffer.length; buffer _ buffer.next; ENDLOOP; WHILE buffer#NIL AND maxLength > 0 DO n: INT _ MIN[maxLength, buffer.length-start]; FOR i: NAT IN [NAT[start]..NAT[start+n]) DO Put[writer, buffer[i]]; ENDLOOP; maxLength _ maxLength - n; start _ 0; buffer _ buffer.next; ENDLOOP; }; 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 _ 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] ~ { WITH textNode SELECT FROM t: REF TextNode.Body.text => RETURN [Rope.Length[t.rope]]; ENDCASE => RETURN [0]; }; FinishOff: PROC [writer: Writer] ~ { IF writer.ropes#ALL[NIL] OR writer.textBuf.length>0 THEN Put[writer, [Ascii.CR, TRUE, ALL[FALSE]]]; }; 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, words: FALSE, 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.lastLevel _ 0; writer.nodeInfo _ [format: NIL, comment: FALSE, level: 0, props: NIL]; writer.newRunLooks _ ALL[FALSE]; writer.newRunSize _ 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, Xerox Corporation. All rights reserved. Michael Plass, March 5, 1985 10:19:49 am PST Last Edited by: Diebert, March 5, 1985 9:39:55 am PST Largest size that will come from the small-grain allocator (see Allocator.maxSmallBlockSize) Items are in the putback buffer in reverse order. Creates root and descendants with no siblings, just to get the levels right. 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! The contents of the current node is represented by the concatenation of ropes in order by decreasing index, together with the contents of textBuf. The size of ropes[i] is either 0 or approximately textBufSize*2**i; building the rope this way yields a balanced rope without a lot of flattening. Creates root and descendants with no siblings, just to get the levels right. For debugging using the golldurn INLINE 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. Ê"m˜™J™J˜—š Ïn œœœ œœ ˜?Jšœœ˜+Jšœ˜Jšœœ˜Jšœ ˜Jšœ˜J˜—š ž œœ œœœ˜6Jšœœ˜(Jšœ˜J˜—šž œœœ˜/šœ œœ˜Jšœ˜Jšœœ˜Jšœ˜—Jšœ˜J˜—š ž œœœœœ˜:Jšœœ˜šœ œ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ˜J˜—šž œœœœ˜=šœ˜#Jšœ!˜!Jšœœœ=˜VJšœ"˜"Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœœ˜Jšœ˜J˜—šžœœœ+˜>šœœ˜Jšœ˜Jšœ˜—šœ"œ˜*Jšœ<˜˜XJšœ$˜$Jšœ˜ Jšœ˜J˜—šž œœœœ ˜-Jšœ œ˜Jšœ˜J˜—š žœœœ œœ ˜;Jšœ&˜,Jšœ˜J˜—š ž œœœ œ œ ˜FJšœ'˜-Jšœ˜J˜—šžœœœ˜ZJšœJ˜PJ˜J˜—šž œœœ˜RJšœ,œœ˜UJšœœ+˜6J˜J˜—šž œœ œœ˜FJ™LJš œ œœœœ˜&Jšœ˜Jšœ œ˜Jšœ ˜ šœ ˜Jšœœ-˜3Jšœ œ˜Jšœ˜Jšœ ˜ Jšœ˜—Jšœ˜J˜—šž œœœœ ˜/Jšœœ˜Jšœ˜šž œœ8˜KJšœœ˜%šœ˜Jšœ*˜*Jšœ5˜5Jšœdœ˜jšœ&œ˜.šœœ˜šœœ˜Jšœ2˜2Jšœ7˜7Jšœ˜—Jšœœ˜—Jšœ˜—šœœ˜šœœ˜ šœœ˜Jšœ/œœ˜9Jšœ4œœ˜>Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜—Jšœ1˜1Jšœ˜%Jšœ˜J˜—šž œœœ œ ˜KJšœœ˜šœ œ˜šœ)˜)Jšœ˜˜šœÏcœŸ˜1J˜0J˜—J˜ J˜—Jšœ#ŸœŸ˜AJšœœŸ˜-Jšœ œœŸ˜/Jšœ˜—Jšœœ˜—Jšœ˜Jšœ˜J˜—š žœœœœœ˜9Jšœœœ ˜,Jšœ˜J˜—šžœœœœ˜=šœœœ˜Jšœ#˜#Jšœ)˜)šœ#œ˜+Jšœœœ˜(Jšœœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœœ œœ7˜Ušœœ<œ˜IJšœ˜Jš œœ œ œœ˜=Jšœ˜—šœ˜Jšœ7œ.˜pJšœ˜—Jšœ˜J˜—codeš žœœœœ œ˜?Jšœ:˜:Jšœzœ™Jšœ˜J˜—š ž œœœœœ˜9šœ˜Jšœ˜Jšœ*˜*Jšœ˜Jšœ˜—Jšœ˜J˜—š ž œœœœ œ˜EJšœ}œ*˜«Jšœ˜J˜—šžœœœœ˜6Jšœ˜šœœHœ˜YJ™!Jšœœ ˜+Jšœ/˜/Jšœ1˜1Jšœ˜—š˜JšœRœ˜Y—Jšœ˜J˜—šž œœœœ˜Jšœ ˜ Jšœœ&œœ˜YJšœ˜Jšœ˜J˜—šž œœœœ˜@Jšœ˜Jšœ˜J˜—š žœœ-œœ œ˜fJšœœœ,˜8šœœœ"˜1šœ%œ˜-Jšœ˜Jšœœ˜ Jšœ˜—Jšœ˜—Jšœ ˜ Jšœœ˜Jšœ˜J˜—šž œœœ)œ œœ œœ˜|šœ˜"Jšœ!˜!Jšœœœ=˜VJšœ˜Jšœ˜Jšœ˜—Jšœ˜šœ˜Jšœ@˜@Jšœ œœ˜Jšœ&˜&Jšœœœ=˜VJšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœœ˜Jšœ˜J˜—šžœœœœ˜>šœœœ˜Jšœ#˜#Jšœ˜Jšœ˜—šœœ<œ˜IJš œœ œ œœ˜=Jšœ˜—šœ˜Jšœ8œ/˜rJšœ˜—Jšœ˜J˜—š žœœœœœœ˜Ešœœœ˜J™*Jš œœœœœ˜š œœœ"œœ˜BJšœ˜š œœ œœ˜+Jšœ˜Jšœœœœ˜3Jšœ˜Jšœ˜—Jšœ˜—Jšœœ˜Jšœ˜—Jšœ]˜]Jšœ˜J˜—šžœœœœ˜Hšœœ˜ šœœ˜Jšœœ˜(Jšœœ˜Jšœ œœ3˜GJšœ˜—Jšœœ˜—Jšœ˜J˜—šžœœœ˜0Jšœ œ˜Jšœ œ7˜IJšœH˜HJšœB˜BJšœ;˜;Jšœœ˜Jšœ)˜)Jšœ œ˜Jšœ˜Jšœ˜J˜—šžœœœœ˜-Jšœœœ%˜5J˜—šž œœ/˜AJšœœ?˜GJ˜—šžœœœ3˜Gšœœœœ˜Jšœ:˜:Jšœ˜—Jšœ œ˜JšœtœGœœ˜Òšœ œ˜Jšœœ˜Jšœ"˜"Jšœ<˜