TempTiogaOps.mesa
Copyright © 1985, 1986 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, September 12, 1986 6:33:55 pm PDT
DIRECTORY
Rope USING [ROPE],
ViewerClasses USING [Viewer];
TiogaOps: CEDAR DEFINITIONS
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
Viewer: TYPE ~ ViewerClasses.Viewer;
Ref: TYPE = REF NodeBody; -- points to a Tioga node
NodeBody: TYPE;
Location: TYPE = RECORD [node: Ref, where: INT];
Order: TYPE = { before, same, after, disjoint };
WhichNodes: TYPE = { root, selection };
SelectionGrain: TYPE = { point, char, word, node, branch };
WhichSelection: TYPE = { primary, secondary, feedback };
Feedback selection is provided as a mechanism for giving visible mark without changing edit selections. You can explicitly cancel it, but it will go away automatically as soon as a non-feedback selection is made in the same viewer.
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
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;
MesaFormatting: PROC;
Repeat: PROC;
Undo: PROC;
Looks
WhichLooks: TYPE = { caret, selection };
SetSelectionLooks: PROC [which: WhichSelection ← primary];
sets the selection looks from the looks of the character next to the caret
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
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
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
Viewer operations
ViewerDoc: PROC [viewer: Viewer] RETURNS [Ref];
Returns root node for a text or typescript viewer.
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.
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
RemoveTextKey: PROC [node: Ref, key: REF];
MapTextKeys: PROC [node: Ref,
proc: PROC [key: REF, where: INT] RETURNS [BOOL]
] RETURNS [BOOL];
Calls proc for all the keys for the given node.
Returns true if&when an action returns true
Looks, Formats, Styles
GetStyle: PROC [node: Ref] RETURNS [ROPE]; -- NIL for default
Dirty and New bits on nodes
IsDirty: PROC [node: Node] RETURNS [BOOL];
IsNew: PROC [node: Node] 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
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.
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.
File I/O
GetFile: PROC [name: ROPE] RETURNS [root: Ref];
PutFile: PROC [name: ROPE, root: Ref];
FreeTree: PROC [root: Ref];
If you have gotten access to the root by GetFile and know that there are no remaining references to it, call FreeTree to have all of the intra-tree pointers set to NIL. This causes circular reference chains to be broken so that the garbage collector can reclaim the tree. If you are not sure, don't call this!
END.