TiogaOps.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
written by Bill Paxton. June 1982
last written by Paxton. December 29, 1982 10:56 am
last written by Teitelman. January 19, 1983 12:02 pm
Michael Plass, March 15, 1985 10:56:12 am PST
Rick Beach, March 15, 1985 10:22:38 am PST
Doug Wyatt, April 14, 1985 5:03:43 pm PST
Other operations are available in TiogaExtraOps.mesa (they don't all fit in a single interface!)
DIRECTORY
Rope USING [ROPE],
TiogaOpsDefs USING [Location, Order, Ref, SelectionGrain, WhichNodes, WhichSelection],
ViewerClasses USING [Viewer];
TiogaOps: CEDAR DEFINITIONS
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
Viewer: TYPE ~ ViewerClasses.Viewer;
Ref: TYPE = TiogaOpsDefs.Ref; -- points to a Tioga node
Location: TYPE = TiogaOpsDefs.Location; -- RECORD [node: Ref, where: INT];
Order: TYPE = TiogaOpsDefs.Order; -- { before, same, after, disjoint }
WhichNodes: TYPE = TiogaOpsDefs.WhichNodes; -- { root, selection }
SelectionGrain: TYPE = TiogaOpsDefs.SelectionGrain; -- { point, char, word, node, branch }
WhichSelection: TYPE = TiogaOpsDefs.WhichSelection; -- { primary, secondary, feedback }
Operations on/using selections
Caret Operations (for primary selection)
GetCaret: PROC RETURNS [loc: Location];
CaretBefore: PROC;
CaretAfter: PROC;
CaretOnly: PROC;
GoToNextCharacter: PROC [n: INT ← 1];
GoToNextWord: PROC [n: INT ← 1];
GoToNextNode: PROC [n: INT ← 1];
GoToPreviousCharacter: PROC [n: INT ← 1];
GoToPreviousWord: PROC [n: INT ← 1];
GoToPreviousNode: PROC [n: INT ← 1];
Edits requiring secondary selections
ToPrimary: PROC;
ToSecondary: PROC;
Transpose: PROC;
Editing applying to primary selection
InsertRope: PROC [ROPE];
InsertChar: PROC [CHAR];
InsertLineBreak: PROC;
inserts a CR and then copies blanks from front of previous line
BackSpace: PROC [n: INT ← 1];
BackWord: PROC [n: INT ← 1];
DeleteNextCharacter: PROC [n: INT ← 1];
DeleteNextWord: PROC [n: INT ← 1];
InsertTime: PROC;
InsertBrackets: PROC [left, right: CHAR];
MakeControlCharacter: PROC;
UnMakeControlCharacter: PROC;
MakeOctalCharacter: PROC;
UnMakeOctalCharacter: PROC;
ExpandAbbreviation: PROC;
Delete: PROC;
Paste: PROC;
SaveForPaste: PROC;
SaveSpanForPaste: PROC [startLoc, endLoc: Location, grain: SelectionGrain ← char];
like SaveForPaste, but saves the given span rather than the selection
use grain = node or branch to simulate node or branch selections
span need not come from document currently in a viewer
AllLower: PROC;
AllCaps: PROC;
InitialCaps: PROC;
FirstCap: PROC;
MesaFormatting: PROC;
Repeat: PROC;
Undo: PROC;
Node Editing
Break: PROC;
Join: PROC;
Nest: PROC;
UnNest: PROC;
Looks
SetSelectionLooks: PROC [which: WhichSelection ← primary];
sets the selection looks from the looks of the character next to the caret
WhichLooks: TYPE = { caret, selection };
SetLooks: PROC [looks: ROPE, which: WhichLooks ← selection];
AddLooks: PROC [looks: ROPE, which: WhichLooks ← selection];
SubtractLooks: PROC [looks: ROPE, which: WhichLooks ← selection];
ClearLooks: PROC [which: WhichLooks ← selection];
CopyLooks: PROC;
copies looks from secondary selection to primary selection
see LooksReader.mesa if you need to read a lot of looks
Formats
SetFormat: PROC [format: ROPE, which: WhichNodes ← selection];
CaretNodeFormat: PROC;
will delete the word to the left of the caret and make it the format of the caret node
InsertFormat: PROC;
inserts the format name
CopyFormat: PROC;
copies format from secondary selection to primary selection
Styles
SetStyle: PROC [style: ROPE, which: WhichNodes ← selection];
Searching
To search for a specified pattern, see SelectionSearch in section on Search below.
SelectMatchingBrackets: PROC [before, after: CHAR] RETURNS [found: BOOL];
NextPlaceholder: PROC [dir: Dir ← forward, gotoend: BOOL,
startBoundaryNode, endBoundaryNode: Ref ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INTLAST[INT]]
RETURNS [found, wenttoend: BOOL];
If gotoend is true and doesn't find placeholder and not already at end of document,
will make point selection at end and return with found=false and wenttoend=true.
NextViewer: PROC [dir: Dir ← forward] RETURNS [found: BOOL];
Dir: TYPE = { forward, backwards };
FindText, FindWord, and FindDef correspond to Find, Word, Def in the Places menu.
FindText: PROC [viewer: Viewer, rope: ROPENIL, whichDir: SearchDir ← forwards,
which: WhichSelection ← primary,
case: BOOLTRUE -- case => case of characters is significant -- ]
RETURNS
[found: BOOL];
if rope is NIL, uses contents of primary selection instead
doesn't give user message if fails to find
which parameter determines which selection to make
FindWord: PROC [viewer: Viewer, rope: ROPENIL, whichDir: SearchDir ← forwards,
which: WhichSelection ← primary,
case: BOOLTRUE -- case => case of characters is significant -- ]
RETURNS [found: BOOL];
FindDef: PROC [viewer: Viewer, rope: ROPENIL, whichDir: SearchDir ← forwards,
which: WhichSelection ← primary,
case: BOOLTRUE -- case => case of characters is significant -- ]
RETURNS [found: BOOL];
Making/changing a selection
GetSelection: PROC [which: WhichSelection ← primary]
RETURNS [viewer: Viewer, start, end: Location, level: SelectionGrain,
caretBefore: BOOL, pendingDelete: BOOL];
SelectionRoot: PROC [which: WhichSelection ← primary] RETURNS [root: Ref];
Returns the document root for the selection.
SetSelection: PROC [viewer: Viewer, start, end: Location,
level: SelectionGrain ← char, caretBefore: BOOLTRUE,
pendingDelete: BOOLFALSE, which: WhichSelection ← primary];
SelectPoint: PROC [viewer: Viewer, caret: Location, which: WhichSelection ← primary];
SelectNodes: PROC [viewer: Viewer, start, end: Ref,
level: SelectionGrain ← node, caretBefore: BOOLTRUE,
pendingDelete: BOOLFALSE, which: WhichSelection ← primary];
SelectBranches: PROC [viewer: Viewer, start, end: Ref,
level: SelectionGrain ← node, caretBefore: BOOLTRUE,
pendingDelete: BOOLFALSE, which: WhichSelection ← primary];
SelectDocument: PROC [viewer: Viewer,
level: SelectionGrain ← node, caretBefore: BOOLTRUE,
pendingDelete: BOOLFALSE, which: WhichSelection ← primary];
SelectionError: ERROR [ec: SelectionErrorCode]; -- can be raised by the above procedures
SelectionErrorCode: TYPE = {
IllegalViewer, -- either NIL or destroyed or no document data
IllegalNode, -- either start or end node is NIL
WrongDoc, -- start node not part of the viewer document
WrongOrder, -- start location doesn't come before end location
BadStartOffset, -- not within size of start node
BadEndOffset }; -- not within size of end node
CancelSelection: PROC [which: WhichSelection ← primary];
SaveSelA: PROC;
RestoreSelA: PROC;
SaveSelB: PROC;
RestoreSelB: PROC;
GrowSelection: PROC;
GrowSelectionToBlanks: PROC;
GrowSelectionToSomething: PROC [left, right: PROC [CHAR] RETURNS [BOOL]];
grows until procs return true.
Locking a Selection
Note: CallWithLocks, described below, is the recommended way of locking selections and documents. If for some reason you cannot use CallWithLocks, here are more primitive locking procedures.
LockSel: PROC [which: WhichSelection ← primary];
ensures that no other process will change primary selection. same process can re-Lock several times.
UnlockSel: PROC [which: WhichSelection ← primary];
make sure you UnlockSel as many times as you LockSel.
Operations on nodes
Basic operations
GetRope: PROC [node: Ref] RETURNS [ROPE];
the contents of the node
Parent: PROC [node: Ref] RETURNS [Ref];
Root: PROC [node: Ref] RETURNS [Ref];
Next: PROC [node: Ref] RETURNS [Ref];
returns next sibling of n
returns NIL if n is last sibling
Previous: PROC [node: Ref, parent: Ref ← NIL] RETURNS [nx: Ref];
returns previous sibling of n
returns NIL if n is first sibling
runs faster if can supply parent
StepForward: PROC [node: Ref] RETURNS [nx: Ref];
returns next node in standard tree walk order
StepBackward: PROC [node: Ref, parent: Ref ← NIL] RETURNS [back: Ref];
returns preceding node in standard tree walk order
FirstSibling: PROC [node: Ref] RETURNS [Ref];
LastSibling: PROC [node: Ref] RETURNS [Ref];
LastWithin: PROC [node: Ref] RETURNS [Ref];
returns the last node within the branch
i.e., goes to last child of last child of last child ... until no child
FirstChild: PROC [node: Ref] RETURNS [Ref];
LastChild: PROC [node: Ref] RETURNS [Ref];
ViewerDoc: PROC [viewer: Viewer] RETURNS [Ref];
Returns root node for a text or typescript viewer.
Searching
See NodeSearch in section on Search below
Location procedures
LocRelative: PROC [location: Location, count: INT,
break: NAT ← 1, skipCommentNodes: BOOLFALSE]
RETURNS [Location];
count is interpreted as a character offset from given location
returns <node,offset> location corresponding to count
adds in break at the end of each text node
if skipCommentNodes is true, then ignores them in walking the tree
LocOffset: PROC [loc1, loc2: Location,
break: NAT ← 1, skipCommentNodes: BOOLFALSE]
RETURNS [count: INT];
returns character offset of location2 relative to location1
loc1 and loc2 can be in same node
but loc2 must not be in node before loc1 or get ERROR BadArgs
if skipCommentNodes is true, then ignores them in walking the tree
BadArgs: ERROR;
LastLocWithin: PROC [node: Ref] RETURNS [Location];
returns the last location within the branch
Jump: PROC [viewer: Viewer, loc: Location];
Normalizes the viewer with respect to the indicated location. loc must be in the document already being displayed in the viewer.
Property Lists
PutProp: PROC [n: Ref, name: ATOM, value: REF];
NIL is a valid value. Use RemProp to remove property. (Although GetProp will not distinguish between property value NIL and no property present, it is useful to be able to have NIL values to avoid creating garbage. Setting to NIL doesn't release the property record, so don't need to reallocate when next set to a non-NIL value.
GetProp: PROC [n: Ref, name: ATOM] RETURNS [REF];
Note: SetProp, RemProp, and MapProps are defined in TiogaExtraOps.
Text keys: persistent "addresses" for characters in text nodes
PutTextKey: PROC [node: Ref, where: INT, key: REF];
Associates the key with the given offset in the text node. Key moves with the text as edits take place in the node. Key can move to new node.
GetTextKey: PROC [node: Ref, key: REF] RETURNS [loc: Location];
Tells you where the key is in the node at the current time.
loc node may be different if addr has been moved
TextKeyNotFound: ERROR; -- raised by GetTextKey
Note: RemoveTextKey and MapTextKeys are defined in TiogaExtrasOps.
Looks, Formats, Styles
FetchLooks: PROC [node: Ref, index: INT] RETURNS [ROPE];
GetFormat: PROC [node: Ref] RETURNS [ROPE]; -- NIL for default
SetNodeFormat: PROC [format: ROPE, node: Ref];
will set format on a given node that need not be selected at the time
GetStyle: PROC [node: Ref] RETURNS [ROPE]; -- NIL for default
SetNodeStyle: PROC [style: ROPE, node: Ref];
will set style on a given node that need not be selected at the time
Comment Property
IsComment: PROC [node: Ref] RETURNS [BOOL];
SetComment: PROC;
applies to all selected nodes
SetNotComment: PROC;
applies to all selected nodes
Dirty and New bits on nodes
IsDirty: PROC [n: Ref] RETURNS [BOOL];
IsNew: PROC [n: Ref] RETURNS [BOOL];
ClearDirty: PROC [n: Ref];
turns off the dirty bit on the node
ClearNew: PROC [n: Ref];
turns off the new bit on the node
Comparing order of nodes or locations in document
CompareLocOrder: PROC [loc1, loc2: Location] RETURNS [order: Order];
CompareNodeOrder: PROC [node1, node2: Ref] RETURNS [order: Order];
Locking a node
CallWithLocks: PROC [proc: PROC [root: Ref], root: Ref ← NIL];
This is the recommended way to take care of locking selections and documents. This procedure locks the primary selection. If root is NIL, it gets SelectionRoot[]. Then it locks the document by calling Lock[root]. After calling proc with the root, it unlocks both the document and the selection. It also takes care of unlocking them if it sees an UNWIND signal. If root is NIL and there is no selection, it raises the error NoSelection.
NoSelection: ERROR;
raised by CallWithLocks
If for some good reason you cannot use CallWithLocks, here are the primitive locking procedures.
Lock: PROC [root: Ref];
ensures that no other process will edit document. same process can re-Lock several times.
Unlock: PROC [root: Ref];
make sure you Unlock as many times as you Lock. repaints are deferred until document is unlocked.
Searching
SelectionSearch searches the document that contains the current selection, and when finished, changes the current selection. NodeSearch is not tied to, nor does it change, the selection, but starts with a specified node, and traverses the tree, returning a location if the search is successful. Both take patterns which can be created as described below.
SelectionSearch: PROC [pattern: Pattern,
whichDir: SearchDir ← forwards, interrupt: REF BOOLNIL,
startBoundaryNode, endBoundaryNode: Ref ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INTLAST[INT]]
RETURNS [found: BOOL];
startBoundaryNode and startBoundaryOffset limit how far backwards the search will go. if startBoundaryNode=NIL, search will go to start of document
endBoundaryNode and endBoundaryOffset limit how far forwards the search will go. if endBoundaryNode=NIL, search will go to end of document.
NodeSearch: PROC [pattern: Pattern,
whichDir: SearchDir ← forwards, startLoc, endLoc: Location, interrupt: REF BOOLNIL,
startBoundaryNode, endBoundaryNode: Ref ← NIL,
startBoundaryOffset: INT ← 0, endBoundaryOffset: INTLAST[INT]]
RETURNS
[found: BOOL, start, end: Location];
Similar to SelectionSearch, but doesn't change the selection. Just returns the result.
startLoc and endLoc args serve to determine where to start the search just as primary selection serves in LookForPattern. This can be used to search in documents that are not currently being displayed in a viewer.
SearchDir: TYPE = { forwards, backwards, anywhere };
CreateSimplePattern: PROC [
target: ROPE, -- node from which to get the pattern
case: BOOLTRUE, -- if true, match case
literal: BOOLFALSE, -- if true, treat target literally rather than as a pattern
word: BOOLFALSE, -- if true, match words only
addBounds: BOOLFALSE] -- if true, add |'s to both ends of pattern
RETURNS
[pattern: Pattern];
CreateGeneralPattern: PROC [
target: Ref, -- node from which to get the pattern
text: BOOLTRUE, -- if true, match target text
looks: BOOLFALSE, -- if true, match target looks
format: BOOLFALSE, -- if true, match target format
style: BOOLFALSE, -- if true, match target style
comment: BOOLFALSE, -- if true, match target comment property
case: BOOLTRUE, -- if true, match case
literal: BOOLFALSE, -- if true, treat target literally rather than as a pattern
word: BOOLFALSE, -- if true, match words only
subset: BOOLTRUE, -- if true, use subset for looks test, else use equality
addBounds: BOOLFALSE] -- if true, add |'s to both ends of pattern
RETURNS
[pattern: Pattern];
CreateSimplePattern and CreateGeneralPattern can result in MalformedPattern being raised.
MalformedPattern: ERROR [ec: PatternErrorCode];
PatternErrorCode: TYPE = {
toobig, -- pattern too long
endquote, -- pattern ends with '
endtilda, -- pattern ends with ~
boundary, -- pattern has | inside rather than at beginning or end
missingNameEnd, -- pattern has < without matching >
unmatchedNameEnd, -- pattern has > without previous <
other -- other unspecified error in pattern
};
Pattern: TYPE = REF PatternRec;
PatternRec: TYPE = RECORD [
finder: Finder,
text: BOOL,
looks: BOOL,
looksExact: BOOL,
word: BOOL,
commentControl: CommentControl,
checkFormat: BOOL,
format: ROPE,
checkStyle: BOOL,
style: ROPE,
searchLooks: ROPE
];
Finder: TYPE = REF FinderRec;
FinderRec: TYPE;
CommentControl: TYPE = { includeComments, excludeComments, commentsOnly };
Registering Command procedures
These operations let you modify and extend the standard Tioga commands or add new commands of your own. The Tioga command interpreter keeps a table mapping each command atom to a list of CommandProc's. When it is given an atom to interpret, it gets the corresponding list of command procs and calls them one-by-one until someone returns with quit=TRUE. If any command proc returns with recordAtom=TRUE, the atom will be added to the repeat list for the current edit event (and will thus be re-interpreted if the user hits Repeat).
CommandProc: TYPE = PROC [viewer: Viewer ← NIL]
RETURNS
[recordAtom: BOOLTRUE, quit: BOOLFALSE];
The viewer argument to the command proc is the one passed to the interpreter which typically is the input focus viewer. For menu commands, the viewer is the one that contains the button.
RegisterCommand: PROC [name: ATOM, proc: CommandProc, before: BOOLTRUE];
If before is true, proc goes at front of list, else goes at end of list.
UnRegisterCommand: PROC [name: ATOM, proc: CommandProc];
Remove the proc from the list for the atom. Defined in TiogaExtraOps. Wouldn't fit here.
Interpret: PROC [viewer: Viewer, params: LIST OF REF ANY];
this calls the Tioga interpreter.
params can be atoms or ropes.
atoms are interpreted in manner described above, ropes are inserted at the caret.
Miscellaneous call-back procedures
RegisterAbbrevFailedProc: PROC [proc: PROC RETURNS [BOOL]];
If abbreviation expansion fails (i.e., cannot find the key), calls this proc. If it returns true, then it has done something useful. Otherwise, user gets normal error message. There can only be one of these registered at a time. Call RegisterAbbrevFailedProc with NIL to un-register.
RegisterFileNameProc: PROC [
proc: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE]
];
If try to load/open a file and filename is unknown, call this proc to get a corrected file name. There can only be one of these registered at a time. Call RegisterFileNameProc with NIL to un-register.
END.
January 9, 1983 5:16 pm moved SelectPoint, LastLocWithin here. Deleted Search, Find, LocWithin.