EditPropertiesImpl.mesa
Copyright Ó 1986, 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
Hal Murray, December 22, 1986 4:02:22 pm PST
Michael Plass, August 11, 1988 4:19:41 pm PDT
Doug Wyatt, February 25, 1992 4:37 pm PST
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 {
Emit a run of printable ascii characters, all with the same looks and properties
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 {
Emit a single XC character
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 {
Emit the looks that apply to the previous text or character
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 {
Emit the properties that apply to the previous text or character
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 ~ {
PROC [viewer: ViewerClasses.Viewer ← NIL] RETURNS [recordAtom: BOOLTRUE, quit: BOOLFALSE];
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.