<<>> <> <> <> <> <> DIRECTORY Atom, CharOps, NodeProps, Prop, RefText, Rope, RuntimeError, TEditDocument, TEditInput, TEditSelection, TEditSelectionOps, TextNode, Tioga, TiogaAccess, TiogaAccessViewers, TiogaOps, ViewerClasses, ViewerOps; EditPropertiesImpl: CEDAR PROGRAM IMPORTS Atom, CharOps, NodeProps, Prop, RefText, Rope, RuntimeError, TEditInput, TEditSelection, TEditSelectionOps, TextNode, TiogaAccess, TiogaAccessViewers, TiogaOps, ViewerOps ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Viewer: TYPE ~ ViewerClasses.Viewer; TranslateToOpenProperties: PROC [reader: TiogaAccess.Reader, writer: TiogaAccess.Writer, rootOnly: BOOL ¬ FALSE] ~ { t: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]; node: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 15C, looks: ALL[FALSE], format: $code, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL]; nest: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 15C, looks: ALL[FALSE], format: $code, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: NIL]; outc: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]; PutRope: PROC [rope: ROPE] ~ { FOR i: INT IN [0..Rope.Size[rope]) DO outc.char ¬ Rope.Fetch[rope, i]; TiogaAccess.Put[writer, outc]; ENDLOOP; }; PutOctal: PROC [b: [0..256)] ~ { outc.char ¬ '0+b/64; TiogaAccess.Put[writer, outc]; outc.char ¬ '0+(b/8) MOD 8; TiogaAccess.Put[writer, outc]; outc.char ¬ '0+b MOD 8; TiogaAccess.Put[writer, outc]; }; PutPropName: PROC [atom: ATOM] ~ { save: Tioga.Looks ¬ outc.looks; outc.looks['n] ¬ TRUE; PutRope[Atom.GetPName[atom]]; outc.looks ¬ save; PutRope[": "]; }; PutKey: PROC [rope: ROPE] ~ { save: Tioga.Looks ¬ outc.looks; outc.looks['k] ¬ TRUE; outc.looks['b] ¬ TRUE; PutRope[rope]; outc.looks ¬ save; }; PutProperties: PROC [propList: Prop.PropList] ~ { action: Prop.MapAction ~ { PutProperty[NARROW[key], val] }; [] ¬ Prop.Map[propList, action]; }; PutProperty: PROC [key: ATOM, value: REF] ~ { IF value # NIL THEN { rope: ROPE ~ TiogaAccess.GetExternalProp[key, value]; PutPropName[key]; IF rope = NIL OR NodeProps.Is[key, $ClientOnly] THEN { save: Tioga.Looks ¬ outc.looks; savedPropList: Prop.PropList ~ node.propList; outc.looks['c] ¬ TRUE; PutRope["(opaque)"]; outc.looks ¬ save; node.propList ¬ Prop.Put[node.propList, $OpaqueValue, value]; TiogaAccess.Put[writer, node]; node.propList ¬ savedPropList; } ELSE { PutRope[rope]; TiogaAccess.Put[writer, node]; }; }; }; TranslateNodeContents: PROC ~ { UNTIL t.endOfNode DO looks: Tioga.Looks ~ t.looks; propList: Prop.PropList ~ t.propList; IF t.charSet = 0 AND t.char IN [' ..'~] THEN { <> PutPropName[$Text]; WHILE NOT t.endOfNode AND t.charSet = 0 AND t.char IN [' ..'~] AND t.looks=looks AND t.propList = propList DO save: Tioga.Looks ¬ outc.looks; outc.char ¬ t.char; outc.looks['o] ¬ TRUE; TiogaAccess.Put[writer, outc]; outc.looks ¬ save; t ¬ TiogaAccess.Get[reader]; ENDLOOP; } ELSE { <> PutPropName[$Char]; PutRope["("]; PutOctal[t.charSet]; PutRope["|"]; PutOctal[ORD[t.char]]; PutRope[")"]; t ¬ TiogaAccess.Get[reader]; }; TiogaAccess.Put[writer, node]; IF looks # ALL[FALSE] THEN { <> TiogaAccess.Nest[writer, 1]; PutKey["LOOKS"]; PutRope["("]; FOR c: Tioga.Look IN Tioga.Look DO IF looks[c] THEN {outc.char ¬ c; TiogaAccess.Put[writer, outc]}; ENDLOOP; PutRope[")"]; TiogaAccess.Put[writer, node]; TiogaAccess.Nest[writer, -1]; }; IF propList # NIL THEN { <> TiogaAccess.Nest[writer, 1]; PutProperties[propList]; TiogaAccess.Nest[writer, -1]; }; ENDLOOP; }; UNTIL TiogaAccess.EndOf[reader] DO t ¬ TiogaAccess.Get[reader]; PutKey[IF rootOnly THEN "BEGIN ROOT NODE" ELSE "BEGIN NODE"]; TiogaAccess.Put[writer, nest]; IF NOT t.endOfNode THEN { PutKey["CONTENTS"]; TiogaAccess.Put[writer, nest]; TranslateNodeContents[]; TiogaAccess.Nest[writer, -1]; }; PutKey["PROPERTIES"]; TiogaAccess.Put[writer, nest]; PutProperty[$Comment, NodeProps.ValueFromBool[t.comment]]; PutProperty[$Format, NodeProps.ValueFromAtom[t.format]]; PutProperties[t.propList]; TiogaAccess.Nest[writer, -1]; PutKey["END NODE"]; TiogaAccess.Put[writer, node]; TiogaAccess.Nest[writer, t.deltaLevel-1]; IF rootOnly THEN EXIT; ENDLOOP; UNTIL TiogaAccess.EndOf[reader] DO [] ¬ TiogaAccess.CopyNode[writer: writer, reader: reader]; ENDLOOP; }; Cant: ERROR ~ CODE; TranslateFromOpenProperties: PROC [reader: TiogaAccess.Reader, writer: TiogaAccess.Writer, doRoot: BOOL] ~ { Must: PROC [assertion: BOOL] ~ {IF NOT assertion THEN Cant[]}; break: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]; Match: PROC [rope: ROPE] RETURNS [BOOL] ~ { targetLen: INT ~ Rope.Size[rope]; input: ROPE ~ TiogaAccess.PeekRope[reader]; matchLen: INT ~ Rope.Run[s1: rope, s2: input, case: TRUE]; IF matchLen=targetLen THEN { FOR i: INT IN [0..targetLen) DO [] ¬ TiogaAccess.Get[reader] ENDLOOP; RETURN [TRUE]; }; RETURN [FALSE]; }; level: INT ¬ 0; MatchBreak: PROC RETURNS [BOOL] ~ { IF TiogaAccess.Peek[reader].endOfNode THEN { break ¬ TiogaAccess.Get[reader]; level ¬ level + break.deltaLevel; RETURN [TRUE]; }; RETURN [FALSE]; }; MustNest: PROC ~ {Must[MatchBreak[] AND break.deltaLevel = 1]}; GetOctal3: PROC RETURNS [[0..256)] ~ { v: CARDINAL ¬ 0; FOR i: NAT IN [0..3) DO t: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader]; Must[t.char IN ['0..'8) AND t.charSet = 0]; v ¬ v*8 + (t.char-'0); ENDLOOP; Must[v IN [0..256)]; RETURN [v]; }; RopeToEnd: PROC RETURNS [ROPE] ~ { rope: ROPE ¬ TiogaAccess.PeekRope[reader]; FOR i: INT IN [0..Rope.Size[rope]) DO [] ¬ TiogaAccess.Get[reader] ENDLOOP; RETURN [rope]; }; ParseProp: PROC RETURNS [Prop.Pair] ~ { keyText: REF TEXT ~ RefText.ObtainScratch[100]; t: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader]; key: ATOM ¬ NIL; rope: ROPE ¬ NIL; value: REF; WHILE t.charSet = 0 AND CharOps.AlphaNumeric[t.char] DO Must[keyText.length < keyText.maxLength]; keyText[keyText.length] ¬ t.char; keyText.length ¬ keyText.length + 1; t ¬ TiogaAccess.Get[reader]; ENDLOOP; Must[keyText.length > 0]; Must[t.charSet = 0 AND t.char = ':]; Must[Match[" "]]; rope ¬ RopeToEnd[]; Must[MatchBreak[]]; key ¬ Atom.MakeAtomFromRefText[keyText]; value ¬ Prop.Get[break.propList, $OpaqueValue]; IF value = NIL THEN value ¬ TiogaAccess.GetInternalProp[key, rope]; RefText.ReleaseScratch[keyText]; RETURN [[key: key, val: value]]; }; first: BOOL ¬ TRUE; IF doRoot THEN { node: TextNode.Ref ~ TiogaAccess.GetLocation[reader].node; parent: TextNode.Ref ~ TextNode.Parent[node]; Must[TextNode.Parent[parent]=NIL AND TextNode.Next[node]=NIL]; -- no siblings allowed! }; UNTIL TiogaAccess.EndOf[reader] DO IF (NOT doRoot AND Match["BEGIN NODE"]) OR (doRoot AND Match["BEGIN ROOT NODE"]) THEN { node: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 15C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL]; nodeTail: Prop.PropList ¬ NIL; level ¬ 0; MustNest[]; IF Match["CONTENTS"] THEN { MustNest[]; WHILE level >= 2 DO outc: TiogaAccess.TiogaChar ¬ [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]; outcTail: Prop.PropList ¬ NIL; rope: ROPE ¬ NIL; Must[level = 2]; IF Match["Text: "] THEN rope ¬ RopeToEnd[] ELSE { Must[Match["Char: ("]]; outc.charSet ¬ GetOctal3[]; Must[Match["|"]]; rope ¬ Rope.FromChar[VAL[GetOctal3[]]]; Must[Match[")"]]; }; Must[MatchBreak[]]; WHILE level >= 3 DO Must[level = 3]; IF Match["LOOKS("] THEN { t: TiogaAccess.TiogaChar ¬ TiogaAccess.Peek[reader]; WHILE t.charSet = 0 AND t.char IN Tioga.Look DO t ¬ TiogaAccess.Get[reader]; outc.looks[t.char] ¬ TRUE; t ¬ TiogaAccess.Peek[reader]; ENDLOOP; Must[Match[")"]]; Must[MatchBreak[]]; } ELSE { pair: Prop.Pair ~ ParseProp[]; last: Prop.PropList ~ LIST[pair]; IF outcTail=NIL THEN outc.propList ¬ last ELSE outcTail.rest ¬ last; outcTail ¬ last; }; ENDLOOP; IF rope # NIL THEN { action: Rope.ActionType = { outc.char ¬ c; TiogaAccess.Put[writer, outc] }; [] ¬ Rope.Map[base: rope, action: action]; }; ENDLOOP; }; IF Match["PROPERTIES"] THEN { MustNest[]; WHILE level >= 2 DO pair: Prop.Pair; Must[level = 2]; pair ¬ ParseProp[]; SELECT pair.key FROM $Comment => node.comment ¬ NodeProps.BoolFromValue[pair.val]; $Format => node.format ¬ NodeProps.AtomFromValue[pair.val]; ENDCASE => { last: Prop.PropList ~ LIST[pair]; IF nodeTail=NIL THEN node.propList ¬ last ELSE nodeTail.rest ¬ last; nodeTail ¬ last; }; ENDLOOP; }; Must[level = 1]; Must[Match["END NODE"]]; Must[MatchBreak[]]; node.deltaLevel ¬ level; TiogaAccess.Put[writer, node]; } ELSE { Must[NOT first]; Must[TiogaAccess.CopyNode[writer, reader].nodeEnd]; }; first ¬ FALSE; ENDLOOP; }; MakeNodeSelection: PROC RETURNS [useRootViewer: Viewer ¬ NIL] ~ { tSel: TEditDocument.Selection; IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN; tSel ¬ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; IF tSel.start.pos.node = tSel.data.text.child AND tSel.start.pos.where = 0 AND tSel.insertion = before THEN { useRootViewer ¬ tSel.viewer; TEditSelection.Free[tSel]; RETURN; }; SELECT tSel.granularity FROM point, char, word => { tSel.start.pos.where ¬ 0; tSel.end.pos.where ¬ TextNode.EndPos[tSel.end.pos.node]; tSel.granularity ¬ node; }; ENDCASE => NULL; TEditSelection.MakeSelection[new: tSel]; TEditSelection.Free[tSel] }; OpenPropertiesOp: TEditInput.CommandProc ~ { OpenProperties: PROC [root: TiogaOps.Ref] ~ { reader: TiogaAccess.Reader ¬ NIL; writer: TiogaAccess.Writer ¬ NIL; useRootViewer: Viewer ~ MakeNodeSelection[]; reader ¬ IF useRootViewer#NIL THEN TiogaAccessViewers.FromViewer[useRootViewer] ELSE TiogaAccessViewers.FromSelection[]; writer ¬ TiogaAccess.Create[]; TranslateToOpenProperties[reader, writer, useRootViewer#NIL]; IF useRootViewer#NIL THEN { TiogaAccessViewers.WriteViewer[writer: writer, viewer: useRootViewer]; ViewerOps.SetNewVersion[viewer]; } ELSE TiogaAccessViewers.WriteSelection[writer]; }; TiogaOps.CallWithLocks[OpenProperties]; quit ¬ TRUE; }; SomethingBadHappenedProceedToDebug: SIGNAL ~ CODE; ClosePropertiesOp: TEditInput.CommandProc ~ { <> errorPosition: INT ¬ -1; CloseProperties: PROC [root: TiogaOps.Ref] ~ { reader: TiogaAccess.Reader ¬ NIL; writer: TiogaAccess.Writer ¬ NIL; useRootViewer: Viewer ~ MakeNodeSelection[]; IF useRootViewer#NIL THEN { reader ¬ TiogaAccessViewers.FromViewer[useRootViewer]; [] ¬ TiogaAccess.Get[reader]; -- discard actual root } ELSE { reader ¬ TiogaAccessViewers.FromSelection[] }; writer ¬ TiogaAccess.Create[]; TranslateFromOpenProperties[reader, writer, useRootViewer#NIL ! Cant => {errorPosition ¬ TiogaAccess.GetPosition[reader]} ]; IF useRootViewer#NIL THEN { TiogaAccessViewers.WriteViewer[writer: writer, viewer: useRootViewer]; ViewerOps.SetNewVersion[viewer]; } ELSE TiogaAccessViewers.WriteSelection[writer]; }; error: {none, cant, uncaught} ¬ none; TiogaOps.CallWithLocks[CloseProperties ! Cant => {error ¬ cant; CONTINUE}; RuntimeError.UNCAUGHT => {error ¬ uncaught; CONTINUE}; ]; SELECT error FROM none => IF errorPosition # -1 THEN ERROR; cant => { TEditSelectionOps.ShowGivenPosition[viewer: viewer, pos: errorPosition, skipCommentNodes: TRUE]; ViewerOps.BlinkViewer[viewer, 200]; }; uncaught => { SIGNAL SomethingBadHappenedProceedToDebug; CloseProperties[NIL]; -- do it again without lock for easy debug }; ENDCASE => ERROR; quit ¬ TRUE; }; TEditInput.Register[name: $OpenProperties, proc: OpenPropertiesOp, before: TRUE]; TEditInput.Register[name: $CloseProperties, proc: ClosePropertiesOp, before: TRUE]; END.