TiogaOps2Impl.mesa
Copyright Ó 1985, 1987, 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
written by Bill Paxton. June 1982
last written by Paxton. December 30, 1982 11:13 am
Last Edited by: Maxwell, January 6, 1983 11:48 am
Rick Beach, March 28, 1985 10:15:04 am PST
Russ Atkinson (RRA) January 23, 1986 0:02:15 am PST
Michael Plass, October 16, 1987 5:21:07 pm PDT
Willie-s, February 13, 1991 5:14 pm PST
Doug Wyatt, March 16, 1992 3:01 pm PST
DIRECTORY
Atom USING [GetPName, MakeAtom],
EditSpan USING [CompareNodeOrder, NodeOrder],
MessageWindow USING [Append],
NodeProps USING [CopyInfoProc, GetProp, MapProps, NullCopy, NullRead, NullWrite, PutProp, ReadSpecsProc, Register, RemProp, WriteSpecsProc],
NodeStyleOps USING [StyleNameForNode],
PFS USING [PathFromRope],
Rope USING [ROPE, IsEmpty],
TEditDocument USING [Selection, SpinAndLock, TEditDocumentData, Unlock],
TEditDocumentPrivate USING [CloseAndOpenPreviousFile, DefaultMenus, DoCloseAndNewViewer, DoCloseAndOpenImplFile, DoLoadFile, DoLoadImplFile, DoNewViewer, DoOpenFile, DoOpenImplFile, DoStoreFile, EmptyViewer, JumpToPrevious, LoadPreviousFile, OpenPreviousFile, Reselect, Reset, Save],
TEditInput USING [AllLevels, CloseEvent, CommandProc, FewerLevels, FirstLevelOnly, FreeTree, MoreLevels, Normalize, Register, UnRegister],
TEditInputOps USING [CallWithLocks, DoFindPlaceholders, DoNextViewer, DoSelectMatchingBrackets],
TEditOps USING [RememberCurrentPosition],
TEditProfile USING [selectionCaret],
TEditRefresh USING [ScrollToEndOfSel],
TEditScrolling USING [ScrollToPosition],
TEditSelection USING [MakeSelection, Position, pSel, SetSelLooks],
TextEdit USING [ChangeLooks, FromRope, GetFormat, Size, FetchLooks],
TextEditBogus USING [GetRope],
TextFind USING [Error, Target],
TextLooks USING [LooksToRope],
TextNode USING [FirstChild, FirstSibling, LastChild, LastLocWithin, LastSibling, LastWithin, Next, Parent, Previous, Root, StepBackward, StepForward],
Tioga USING [Node, Location, Looks, noLooks],
TiogaExtraOps USING [MapPropsAction],
TiogaFind USING [Match, Search, TargetFromNode],
TiogaIO USING [FromFile, ToFile],
TiogaMenuOps USING [],
TiogaOps USING [CommandProc, Dir, Pattern, PatternRec, PatternErrorCode, SearchDir, SetSelection],
TiogaOpsDefs USING [Order, WhichSelection],
ViewerClasses USING [Column, Viewer];
TiogaOps2Impl: CEDAR PROGRAM
IMPORTS Atom, EditSpan, MessageWindow, NodeProps, NodeStyleOps, PFS, Rope, TEditDocument, TEditDocumentPrivate, TEditInput, TEditInputOps, TEditOps, TEditProfile, TEditRefresh, TEditScrolling, TEditSelection, TextEdit, TextEditBogus, TextFind, TextLooks, TextNode, TiogaFind, TiogaIO, TiogaOps
EXPORTS TiogaOps, TiogaMenuOps, TiogaExtraOps
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Viewer: TYPE ~ ViewerClasses.Viewer;
Column: TYPE ~ ViewerClasses.Column;
Node: TYPE ~ Tioga.Node;
Location: TYPE ~ Tioga.Location;
Looks: TYPE ~ Tioga.Looks;
noLooks: Looks ~ Tioga.noLooks;
Search
Finder: TYPE = REF FinderRec;
FinderRec: PUBLIC TYPE = RECORD [target: TextFind.Target, case, addBounds: BOOL];
MakeName: PROC [r: ROPE] RETURNS [ATOM] ~ {
RETURN [IF r.IsEmpty THEN NIL ELSE Atom.MakeAtom[r]];
};
MalformedPattern: PUBLIC ERROR [ec: TiogaOps.PatternErrorCode] ~ CODE;
CreateGeneralPattern: PUBLIC PROC [
target: Node, -- node from which to get the pattern
text: BOOL ¬ TRUE, -- if true, match target text
looks: BOOL ¬ FALSE, -- if true, match target looks
format: BOOL ¬ FALSE, -- if true, match target format
style: BOOL ¬ FALSE, -- if true, match target style
comment: BOOL ¬ FALSE, -- if true, match target comment property
case: BOOL ¬ TRUE, -- if true, match case
literal: BOOL ¬ FALSE, -- if true, treat target literally rather than as a pattern
word: BOOL ¬ FALSE, -- if true, match words only
subset: BOOL ¬ TRUE, -- if true, use subset for looks test, else use equality
addBounds: BOOL ¬ FALSE] -- if true, add |'s to both ends of pattern
RETURNS [pattern: TiogaOps.Pattern] = {
txt: Node = target;
patternTxt: Node ¬ txt;
pattern ¬ NEW[TiogaOps.PatternRec];
IF looks AND ~text THEN { -- make a phony search pattern and get the looks
size: INT = TextEdit.Size[txt];
lks: Looks = IF size=0 THEN noLooks ELSE TextEdit.FetchLooks[txt,0];
pattern.searchLooks ¬ TextLooks.LooksToRope[lks];
FOR i: INT IN [1..size) DO
IF TextEdit.FetchLooks[txt,i]#lks THEN {
MessageWindow.Append["Search pattern does not have uniform looks.",TRUE];
MessageWindow.Append[" Using looks from first char."];
EXIT
};
ENDLOOP;
literal ¬ FALSE;
patternTxt ¬ TextEdit.FromRope["**#**"];
TextEdit.ChangeLooks[root: NIL, text: patternTxt, add: lks];
};
pattern.text ¬ text;
pattern.looks ¬ looks;
pattern.word ¬ word;
pattern.looksExact ¬ ~subset;
pattern.commentControl ¬ IF ~comment OR txt=NIL THEN includeComments
ELSE IF txt.comment THEN commentsOnly ELSE excludeComments;
pattern.checkFormat ¬ format;
pattern.format ¬ Atom.GetPName[TextEdit.GetFormat[target]];
pattern.checkStyle ¬ style;
pattern.style ¬ IF ~style THEN NIL ELSE Atom.GetPName[NodeStyleOps.StyleNameForNode[target]];
IF text OR looks THEN { -- create a description of the pattern
pattern.finder ¬ NEW[FinderRec ¬ [
target: TiogaFind.TargetFromNode[node: patternTxt, pattern: ~literal !
TextFind.Error => GOTO Malformed],
case: case,
addBounds: addBounds
]];
EXITS Malformed => ERROR MalformedPattern[other];
};
};
CreateSimplePattern: PUBLIC PROC [
target: ROPE, -- node from which to get the pattern
case: BOOL ¬ TRUE, -- if true, match case
literal: BOOL ¬ FALSE, -- if true, treat target literally rather than as a pattern
word: BOOL ¬ FALSE, -- if true, match words only
addBounds: BOOL ¬ FALSE] -- if true, add |'s to both ends of pattern
RETURNS [pattern: TiogaOps.Pattern] = {
RETURN CreateGeneralPattern[target: TextEdit.FromRope[target],
case: case, literal: literal, word: word, addBounds: addBounds];
};
SelectionSearch: PUBLIC PROC [
pattern: TiogaOps.Pattern, whichDir: TiogaOps.SearchDir ¬ forwards, interrupt: REF BOOL ¬ NIL,
startBoundaryNode, endBoundaryNode: Node ¬ NIL,
startBoundaryOffset: INT ¬ 0, endBoundaryOffset: INT ¬ LAST[INT]]
RETURNS [found: BOOL] = {
pSel: TEditDocument.Selection = TEditSelection.pSel;
Found: PROC [tSel: TEditDocument.Selection] = {
tSel.viewer ¬ pSel.viewer;
tSel.data ¬ pSel.data;
TEditOps.RememberCurrentPosition[pSel.viewer];
TEditSelection.SetSelLooks[tSel];
TEditSelection.MakeSelection[new: tSel];
TEditInput.CloseEvent[];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE]
};
Locations: PROC RETURNS [start, end: Location] = {
start ¬ pSel.start.pos; end ¬ pSel.end.pos
};
found ¬ DoSearch[pattern, whichDir, interrupt, Found, Locations,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset]
};
NodeSearch: PUBLIC PROC [
pattern: TiogaOps.Pattern, whichDir: TiogaOps.SearchDir ¬ forwards,
startLoc, endLoc: Location, interrupt: REF BOOL ¬ NIL,
startBoundaryNode, endBoundaryNode: Node ¬ NIL,
startBoundaryOffset: INT ¬ 0, endBoundaryOffset: INT ¬ LAST[INT]]
RETURNS [found: BOOL, start, end: Location] = {
Found: PROC [tSel: TEditDocument.Selection] = {
start ¬ tSel.start.pos; end ¬ tSel.end.pos;
};
Locations: PROC RETURNS [start, end: Location] = {
RETURN[start: startLoc, end: end];
};
found ¬ DoSearch[pattern, whichDir, interrupt, Found, Locations,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset]
};
DoSearch: PROC [pattern: TiogaOps.Pattern, whichDir: TiogaOps.SearchDir ¬ forwards, interrupt: REF BOOL ¬ NIL,
foundProc: PROC [tSel: TEditDocument.Selection],
locationProc: PROC RETURNS [start, end: Location],
startBoundaryNode, endBoundaryNode: Node ¬ NIL,
startBoundaryOffset: INT ¬ 0, endBoundaryOffset: INT ¬ LAST[INT]]
RETURNS [found: BOOL ¬ FALSE] = {
at, atEnd, offset: INT ¬ 0;
first: Node;
where: Node;
startLoc, endLoc: Location;
DoLookForPattern: PROC [root: Node, tSel: TEditDocument.Selection] = {
finder: Finder ~ pattern.finder;
target: TextFind.Target ~ IF finder#NIL THEN finder.target ELSE NIL;
case: BOOL ~ IF finder#NIL THEN finder.case ELSE TRUE;
match: TiogaFind.Match ~ IF finder#NIL AND finder.addBounds
THEN all ELSE IF pattern.word THEN word ELSE any;
Forwards: PROC = {
IF (offset ¬ endLoc.where+1) >= TextEdit.Size[first ¬ endLoc.node] THEN {
first ¬ TextNode.StepForward[first]; offset ¬ 0
};
[node: where, matchStart: at, matchEnd: atEnd] ¬ TiogaFind.Search[direction: forward,
loc1: [first, offset], loc2: [endBoundaryNode, endBoundaryOffset],
target: target, case: case, match: match,
checkLooks: pattern.looks, looksExact: pattern.looksExact,
checkComment: pattern.commentControl#includeComments,
comment: pattern.commentControl=commentsOnly,
checkFormat: pattern.checkFormat, format: MakeName[pattern.format],
checkStyle: pattern.checkStyle, style: MakeName[pattern.style],
styleProc: NodeStyleOps.StyleNameForNode, interrupt: interrupt];
};
Backwards: PROC = {
IF (offset ¬ startLoc.where)=0 THEN {
first ¬ TextNode.StepBackward[startLoc.node];
offset ¬ LAST[INT]
}
ELSE first ¬ startLoc.node;
[node: where, matchStart: at, matchEnd: atEnd] ¬ TiogaFind.Search[direction: backward,
loc1: [startBoundaryNode, startBoundaryOffset], loc2: [first, offset],
target: target, case: case, match: match,
checkLooks: pattern.looks, looksExact: pattern.looksExact,
checkComment: pattern.commentControl#includeComments,
comment: pattern.commentControl=commentsOnly,
checkFormat: pattern.checkFormat, format: MakeName[pattern.format],
checkStyle: pattern.checkStyle, style: MakeName[pattern.style],
styleProc: NodeStyleOps.StyleNameForNode, interrupt: interrupt];
};
[startLoc, endLoc] ¬ locationProc[];
IF interrupt#NIL THEN interrupt­ ¬ FALSE;
SELECT whichDir FROM
forwards => Forwards[];
backwards => Backwards[];
anywhere => {
Forwards[];
IF where#NIL THEN whichDir ¬ forwards ELSE {
whichDir ¬ backwards; Backwards[]
}
};
ENDCASE => ERROR;
IF where=NIL THEN RETURN ELSE found ¬ TRUE;
tSel.start.pos ¬ [where,at];
tSel.end.pos ¬ [where,MAX[0,atEnd-1]];
tSel.granularity ¬
IF ~pattern.looks AND ~pattern.text THEN node
ELSE IF match=word THEN word ELSE char;
tSel.insertion ¬ IF TEditProfile.selectionCaret=before THEN before ELSE after;
foundProc[tSel];
};
TEditInputOps.CallWithLocks[DoLookForPattern, read]
};
SelectMatchingBrackets: PUBLIC PROC [before, after: CHAR] RETURNS [found: BOOL] = {
found ¬ TEditInputOps.DoSelectMatchingBrackets[before, after]
};
NextPlaceholder: PUBLIC PROC [dir: TiogaOps.Dir ¬ forward, gotoend: BOOL,
startBoundaryNode, endBoundaryNode: Node ¬ NIL,
startBoundaryOffset: INT ¬ 0, endBoundaryOffset: INT ¬ LAST[INT]]
RETURNS [found, wenttoend: BOOL] = {
[found, wenttoend] ¬ TEditInputOps.DoFindPlaceholders[
dir=forward, gotoend,
startBoundaryNode, endBoundaryNode,
startBoundaryOffset, endBoundaryOffset]
};
NextViewer: PUBLIC PROC [dir: TiogaOps.Dir ¬ forward] RETURNS [found: BOOL] = {
found ¬ TEditInputOps.DoNextViewer[dir=forward]
};
Places menu commands
Position: PUBLIC PROC [viewer: Viewer] = {
TEditSelection.Position[viewer];
};
Normalize: PUBLIC PROC [viewer: Viewer] = {
[] ¬ TEditInput.Normalize[viewer];
};
PrevPlace: PUBLIC PROC [viewer: Viewer] = {
TEditDocumentPrivate.JumpToPrevious[viewer];
};
Reselect: PUBLIC PROC [viewer: Viewer] = {
TEditDocumentPrivate.Reselect[viewer];
};
Files and text viewers
Save: PUBLIC PROC [viewer: Viewer] = {
TEditDocumentPrivate.Save[viewer];
};
Load: PUBLIC PROC [viewer: Viewer, fileName: ROPE ¬ NIL,
fileNameProcViewer: Viewer ¬ NIL] = {
[] ¬ TEditDocumentPrivate.DoLoadFile[viewer, fileName, FALSE, fileNameProcViewer];
};
Open: PUBLIC PROC [fileName: ROPE ¬ NIL, fileNameProcViewer: Viewer ¬ NIL, column: Column ¬ left] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoOpenFile[fileName: fileName, column: column, fileNameProcViewer: fileNameProcViewer]];
};
CloseAndOpen: PUBLIC PROC [viewer: Viewer, fileName: ROPE ¬ NIL, fileNameProcViewer: Viewer ¬ NIL] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoLoadFile[viewer, fileName, TRUE, fileNameProcViewer]];
};
LoadImpl: PUBLIC PROC [viewer: Viewer, fileName: ROPE ¬ NIL] = {
[] ¬ TEditDocumentPrivate.DoLoadImplFile[viewer, fileName];
};
OpenImpl: PUBLIC PROC [fileName: ROPE ¬ NIL, column: Column ¬ left] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoOpenImplFile[fileName, column]];
};
CloseAndOpenImpl: PUBLIC PROC [viewer: Viewer, fileName: ROPE ¬ NIL] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoCloseAndOpenImplFile[viewer, fileName]];
};
LoadPreviousFile: PUBLIC PROC [parent: Viewer] = {
TEditDocumentPrivate.LoadPreviousFile[parent];
};
OpenPreviousFile: PUBLIC PROC [parent: Viewer] = {
TEditDocumentPrivate.OpenPreviousFile[parent];
};
CloseAndOpenPreviousFile: PUBLIC PROC [parent: Viewer] = {
TEditDocumentPrivate.CloseAndOpenPreviousFile[parent];
};
DefaultMenus: PUBLIC PROC [viewer: Viewer, paint: BOOL ¬ FALSE] = {
TEditDocumentPrivate.DefaultMenus[viewer, paint];
};
Store: PUBLIC PROC [viewer: Viewer, fileName: ROPE ¬ NIL] = {
TEditDocumentPrivate.DoStoreFile[viewer, fileName];
};
New: PUBLIC PROC [column: Column ¬ left] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoNewViewer[column]];
};
Empty: PUBLIC PROC [viewer: Viewer] = {
TEditDocumentPrivate.EmptyViewer[viewer];
};
CloseAndNewViewer: PUBLIC PROC [viewer: Viewer] RETURNS [Viewer] = {
RETURN [TEditDocumentPrivate.DoCloseAndNewViewer[viewer]];
};
Reset: PUBLIC PROC [viewer: Viewer] = {
TEditDocumentPrivate.Reset[viewer];
};
Jump: PUBLIC PROC [viewer: Viewer, loc: Location] = {
[] ¬ TEditScrolling.ScrollToPosition[viewer, loc];
};
Levels menu commands
FirstLevelOnly: PUBLIC PROC [viewer: Viewer] = { [] ¬ TEditInput.FirstLevelOnly[viewer] };
MoreLevels: PUBLIC PROC [viewer: Viewer] = { [] ¬ TEditInput.MoreLevels[viewer] };
FewerLevels: PUBLIC PROC [viewer: Viewer] = { [] ¬ TEditInput.FewerLevels[viewer] };
AllLevels: PUBLIC PROC [viewer: Viewer] = { [] ¬ TEditInput.AllLevels[viewer] };
TiogaExtraOps Implementation
Miscellaneous operations on nodes
GetRope: PUBLIC PROC [node: Node] RETURNS [ROPE] = {
RETURN [TextEditBogus.GetRope[node]]
};
Parent: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.Parent[node]] };
Root: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.Root[node]] };
Next: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.Next[node]] };
Previous: PUBLIC PROC [node: Node, parent: Node ¬ NIL] RETURNS [nx: Node] = { RETURN [TextNode.Previous[node]] };
StepForward: PUBLIC PROC [node: Node] RETURNS [nx: Node] = { RETURN [TextNode.StepForward[node]] };
StepBackward: PUBLIC PROC [node: Node, parent: Node ¬ NIL] RETURNS [back: Node] = { RETURN [TextNode.StepBackward[node, parent]] };
FirstSibling: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.FirstSibling[node]] };
LastSibling: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.LastSibling[node]] };
LastWithin: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.LastWithin[node]] };
LastLocWithin: PUBLIC PROC [node: Node] RETURNS [Location] = {
RETURN [TextNode.LastLocWithin[node]];
};
FirstChild: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.FirstChild[node]] };
LastChild: PUBLIC PROC [node: Node] RETURNS [Node] = { RETURN [TextNode.LastChild[node]] };
IsDirty: PUBLIC PROC [node: Node] RETURNS [BOOL] = { RETURN [node.dirty] };
IsNew: PUBLIC PROC [node: Node] RETURNS [BOOL] = { RETURN [node.new] };
ClearDirty: PUBLIC PROC [node: Node] = { node.dirty ¬ FALSE };
ClearNew: PUBLIC PROC [node: Node] = { node.new ¬ FALSE };
ViewerDoc: PUBLIC PROC [viewer: Viewer] RETURNS [root: Node] = {
tdd: TEditDocument.TEditDocumentData = NARROW[viewer.data];
IF tdd = NIL THEN RETURN [NIL];
[] ¬ TEditDocument.SpinAndLock[tdd, "ViewerDoc"]; -- make sure nothing else going on
root ¬ tdd.text;
TEditDocument.Unlock[tdd]
};
Node Property Lists
PutProp: PUBLIC PROC [n: Node, name: ATOM, value: REF] = {
NodeProps.PutProp[n, name, value]
};
GetProp: PUBLIC PROC [n: Node, name: ATOM] RETURNS [REF] = {
RETURN [NodeProps.GetProp[n, name]]
};
RemProp: PUBLIC PROC [n: Node, name: ATOM] = {
NodeProps.RemProp[n, name]
};
MapProps: PUBLIC PROC [n: Node, action: TiogaExtraOps.MapPropsAction, formatFlag, commentFlag: BOOL ¬ TRUE] RETURNS [BOOL] = {
RETURN [NodeProps.MapProps[n, action, formatFlag, commentFlag]]
};
read, write, copy props
RegisterPropProc: PUBLIC PROC [name: ATOM,
reader: NodeProps.ReadSpecsProc, writer: NodeProps.WriteSpecsProc, copier: NodeProps.CopyInfoProc] = {
NodeProps.Register[name, reader, writer, copier];
};
NullRead: PUBLIC NodeProps.ReadSpecsProc = { [] ¬ NodeProps.NullRead[name, specs] };
NullWrite: PUBLIC NodeProps.WriteSpecsProc = { [] ¬ NodeProps.NullWrite[name, value] };
NullCopy: PUBLIC NodeProps.CopyInfoProc = { [] ¬ NodeProps.NullCopy[name, value] };
Comparing order of nodes or locations in document
NodeOrder: PROC [order: EditSpan.NodeOrder] RETURNS [TiogaOpsDefs.Order] = {
RETURN [SELECT order FROM before => before, same => same, after => after, disjoint => disjoint, ENDCASE => ERROR] };
CompareLocOrder: PUBLIC PROC [loc1, loc2: Location] RETURNS [order: TiogaOpsDefs.Order] = {
order ¬ NodeOrder[EditSpan.CompareNodeOrder[loc1.node, loc2.node]];
IF order=same THEN order ¬ SELECT loc1.where FROM
< loc2.where => before,
= loc2.where => same,
ENDCASE => after };
CompareNodeOrder: PUBLIC PROC [node1, node2: Node] RETURNS [order: TiogaOpsDefs.Order] = {
RETURN [NodeOrder[EditSpan.CompareNodeOrder[node1, node2]]] };
File I/O
GetFile: PUBLIC PROC [name: ROPE] RETURNS [root: Node] = {
RETURN [TiogaIO.FromFile[PFS.PathFromRope[name]].root];
};
PutFile: PUBLIC PROC [name: ROPE, root: Node] = {
[] ¬ TiogaIO.ToFile[PFS.PathFromRope[name], root];
};
FreeTree: PUBLIC PROC [root: Node] = { TEditInput.FreeTree[root] };
Make point selection
SelectPoint: PUBLIC PROC [viewer: Viewer, caret: Location, which: TiogaOpsDefs.WhichSelection ¬ primary] = {
TiogaOps.SetSelection[viewer: viewer, start: caret, end: caret, level: point, which: which];
};
Command registration
RegisterCommand: PUBLIC PROC [name: ATOM, proc: TiogaOps.CommandProc, before: BOOL ¬ TRUE] = {
TEditInput.Register[name, proc, before]
};
UnRegisterCommand: PUBLIC PROC [name: ATOM, proc: TiogaOps.CommandProc] = {
TEditInput.UnRegister[name, proc]
};
END.