IndexToolViewerImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by Rick Beach, July 12, 1983 4:34 pm
Rick Beach, April 6, 1985 9:12:14 pm PST
DIRECTORY
Atom USING [GetPName, MakeAtom],
Basics,
Buttons,
ChoiceButtons,
Commander USING [CommandProc, Register],
Containers,
Icons,
IndexNotify,
IndexProps,
IndexTree,
IndexToolViewer,
IO,
Labels,
List,
Menus,
MessageWindow,
NumberLabels USING [CreateNumber, NumberLabel, NumberLabelUpdate],
OrderedSymbolTableRef,
RedBlackTree,
Rope,
Rules,
RuntimeError USING [UNCAUGHT],
TEditScrolling,
TextEdit,
TextNode,
TiogaOps,
TiogaOpsDefs,
TIPUser,
ViewerClasses USING [InitProc, ViewerClass, ViewerClassRec, Viewer],
ViewerOps USING [AddProp, CreateViewer, FetchProp, FetchViewerClass, FindViewer, PaintViewer, RegisterViewerClass, SetMenu, SetNewVersion],
ViewerSpecs,
ViewerTools;
IndexToolViewerImpl: CEDAR PROGRAM
IMPORTS Atom, Buttons, ChoiceButtons, Commander, Containers, IndexProps, IndexTree, IO, Labels, List, Menus, MessageWindow, NumberLabels, RedBlackTree, Rope, Rules, RuntimeError, TEditScrolling, TextEdit, TextNode, TiogaOps, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS IndexToolViewer
= BEGIN OPEN IndexToolViewer;
ROPE: TYPE = Rope.ROPE;
IndexEntry: TYPE ~ IndexProps.IndexEntry;
Phrases: TYPE ~ IndexProps.Phrases;
Node: PROC [n: TiogaOps.Ref] RETURNS [TextNode.Ref] ~ TRUSTED {
RETURN [LOOPHOLE[n]];
};
Span: PROC [s, e: TiogaOps.Location] RETURNS [TextNode.Span] ~ TRUSTED {
RETURN [[LOOPHOLE[s], LOOPHOLE[e]]];
};
Creating new IndexTools
NewTool: PUBLIC PROCEDURE [documentName: ROPENIL, kindOfIndex: ROPENIL]
RETURNS [indexToolHandle: IndexToolHandle] = {
v: ViewerClasses.Viewer;
caption: ROPEIF documentName.IsEmpty
THEN "IndexTool"
ELSE Rope.Concat["Index for ", documentName];
IF NOT kindOfIndex.IsEmpty THEN caption ← Rope.Cat[kindOfIndex, " ", caption];
v ← ViewerOps.CreateViewer[
flavor: $IndexTool,
info: [scrollable: TRUE],
paint: FALSE];
indexToolHandle ← NARROW[ViewerOps.FetchProp[v, $IndexToolHandle]];
indexToolHandle.index ← IndexTree.CreateIndex[TiogaOps.ViewerDoc[v]];
indexToolHandle.kindOfIndex ← IF kindOfIndex.IsEmpty
THEN $Index
ELSE Atom.MakeAtom[Rope.Concat[kindOfIndex, "Index"]];
};
CreateIndexFromDocument: PUBLIC PROCEDURE [documentName: ROPENIL,
kindOfIndex: ROPENIL]
RETURNS [indexToolHandle: IndexToolHandle] = {
IF documentName.IsEmpty THEN RETURN[NIL]
ELSE {
documentViewer: ViewerClasses.Viewer ← ViewerOps.FindViewer[documentName];
IF documentViewer = NIL THEN
documentViewer ← ViewerOps.CreateViewer[flavor: $Text,
info: [name: documentName, file: documentName]
];
indexToolHandle ← CreateIndexFromViewer[documentViewer, kindOfIndex];
};
};
CreateIndexFromViewer: PUBLIC PROCEDURE [documentViewer: ViewerClasses.Viewer,
kindOfIndex: ROPENIL]
RETURNS [indexToolHandle: IndexToolHandle] = {
indexToolHandle ← NewTool[documentViewer.name, kindOfIndex];
indexToolHandle.documentViewer ← documentViewer;
ScanIndexProperties[documentViewer, indexToolHandle];
};
ScanIndexProperties: PUBLIC PROCEDURE [viewer: ViewerClasses.Viewer,
indexToolHandle: IndexToolHandle] = {
root: TiogaOps.Ref ← TiogaOps.ViewerDoc[viewer];
node: TiogaOps.Ref ← TiogaOps.FirstChild[root];
ScanIndexPropsOnNode: PROC [node: TextNode.Ref] ~ {
SetIndexProps: TextEdit.ModifyPropsAction ~ {
PROC [value: REF, index: INT, nChars: INT]
RETURNS [quit: BOOLFALSE, newValue: REF];
ixList: IndexProps.IndexEntryList ← NARROW[value];
SetIndexEntryProc: IndexProps.IndexEntryProc ~ {
PROC [ix: IndexEntry]
IndexTree.InsertNewIndexEntry[ix: ix,
range: [[node, index], [node, index+nChars-1]],
index: indexToolHandle.index];
};
IndexProps.MapIndexEntryList[ixList, SetIndexEntryProc];
};
[] ← TextEdit.ModifyCharProps[node, $IndexEntries, 0, INT.LAST, SetIndexProps, NIL, Node[root]];
};
ForEachNodeWithCharProps: PROC [root: TextNode.Ref,
nodeProc: PROC[node: TextNode.Ref]] ~ {
node: TextNode.Ref ← root;
WHILE (node ← TextNode.Next[node]) # NIL DO
IF node.hascharprops THEN nodeProc[node];
ENDLOOP;
};
disaster: BOOLFALSE;
TiogaOps.Lock[root];
ForEachNodeWithCharProps[Node[root], ScanIndexPropsOnNode ! RuntimeError.UNCAUGHT => {disaster ← TRUE; TiogaOps.Unlock[root]}];
IF NOT disaster THEN TiogaOps.Unlock[root];
TEditScrolling.ScrollToPosition[indexToolHandle.indexViewer, [indexToolHandle.rootIndexBranch, 0]];
};
IndexTool Viewer
IndexToolViewerInit: ViewerClasses.InitProc ~ {
indexToolHandle: IndexToolHandle ← NEW[IndexToolHandleRec];
firstColumn: INTEGER ~ 0;
secondColumn: INTEGER ~ ViewerSpecs.openRightWidth/2;
columnWidth: INTEGER ~ ViewerSpecs.openRightWidth/2;
numberOfPhrases: INTEGER ~ 10;
phraseSize: INTEGER ~ secondColumn - 100;
defaultPhraseRows: INTEGER ~ 3;
h: INTEGER ~ ViewerSpecs.captionHeight;
rowH: INTEGER ~ ViewerSpecs.messageWindowHeight;
thisY: INTEGER ← 0;
NextRow: PROCEDURE [rows: INTEGER ← 1] ~ { thisY ← thisY + rows*rowH; };
menu: Menus.Menu ← Menus.CreateMenu[2];
InsertMenuEntry: PROCEDURE [name: Rope.ROPE, proc: Menus.MenuProc,
line: Menus.MenuLine] ~ {
menuEntry: Menus.MenuEntry ← Menus.CreateEntry[
name: name,
proc: proc,
clientData: indexToolHandle,
documentation: NIL,
fork: FALSE,
guarded: FALSE];
Menus.InsertMenuEntry[menu, menuEntry, line];
};
CreatePhraseButtons: PROCEDURE [label: ROPE, x, y: INTEGER]
RETURNS [phraseContainer: ViewerClasses.Viewer] ~ {
Leave space for the header button above the phrase container
thisY ← y;
NextRow[];
phraseContainer ← Containers.Create[
info: [
scrollable: TRUE,
border: FALSE,
parent: self,
wx: x,
wy: thisY,
ww: columnWidth,
wh: defaultPhraseRows*rowH],
paint: FALSE];
Create the phrase viewers in reverse order since they are searched in list order by CollectPhrases
FOR i: INTEGER DECREASING IN [1..numberOfPhrases] DO
phraseData: PhraseButtonData ← NEW[PhraseButtonDataRec ← [indexToolHandle, i]];
button: Buttons.Button ← Buttons.Create[
info: [
name: " ",
wx: 0,
wy: (i-1)*rowH,
ww: 0,
wh: h,
parent: phraseContainer,
scrollable: FALSE,
border: TRUE],
clientData: phraseData,
proc: PhraseButtonProc,
paint: FALSE];
phraseData.viewer ← ViewerOps.CreateViewer[flavor: $Text,
info: [
wx: button.cw + 6*i,
wy: (i-1)*rowH,
ww: phraseContainer.cw - (button.cw + 6*i) - 5,
wh: rowH,
parent: phraseContainer,
scrollable: TRUE,
border: TRUE],
paint: FALSE];
ViewerOps.AddProp[phraseData.viewer, $IndexPhrase, $TRUE];
ENDLOOP;
[] ← Buttons.Create[
info: [
name: label,
wx: x,
wy: y,
wh: rowH,
parent: self,
scrollable: FALSE,
border: FALSE],
proc: ClearPhrasesButtonProc,
clientData: phraseContainer,
paint: FALSE];
thisY ← y;
};
FeedbackLabel: PROCEDURE [label: ROPE, x, y: INTEGER, charsInNumber: NAT ← 4]
RETURNS[v: NumberLabels.NumberLabel] ~ {
v ← Labels.Create[
info: [name: label, parent: self, wx: x, wy: y, wh: rowH, border: FALSE],
paint: FALSE
];
v ← NumberLabels.CreateNumber[
info: [
parent: self,
wx: x + v.ww,
wy: y,
ww: columnWidth - v.ww - 40,
wh: rowH,
border: FALSE],
chars: charsInNumber,
initialValue: 0,
paint: FALSE
];
};
Rule: PROCEDURE [h: CARDINAL] ~ {
rule: Rules.Rule ← Rules.Create[info: [parent: self, wx: 0, wy: thisY, ww: 0, wh: h]];
Containers.ChildXBound[self, rule];
};
Choices: PROCEDURE [choices: Phrases, x, y: INTEGER]
RETURNS [choiceRef: ChoiceButtons.EnumTypeRef] ~ {
choiceRef ← ChoiceButtons.BuildEnumTypeSelection[viewer: self, x: x, y: y, buttonNames: choices, clientdata: indexToolHandle, style: menuSelection];
};
initialize the container that this viewer really is
containerInitProc[self];
indexToolHandle.toolViewer ← self;
InsertMenuEntry[name: "CopyIndexTo", proc: CopyIndexToButtonProc, line: 0];
InsertMenuEntry[name: "NewIndexTool", proc: NewIndexToolButtonProc, line: 0];
InsertMenuEntry[name: "DeleteEntry", proc: DeleteEntryButtonProc, line: 0];
InsertMenuEntry[name: "InsertEntry!", proc: InsertEntryButtonProc, line: 0];
InsertMenuEntry[name: "AddNewKind", proc: AddNewKindButtonProc, line: 1];
InsertMenuEntry[name: "PermutePhrases", proc: PermutePhrasesButtonProc, line: 1];
ViewerOps.SetMenu[self, menu];
indexToolHandle.kindOfEntryChoices ← Choices[LIST["Ordinary", "See", "SeeAlso"], firstColumn, thisY];
NextRow[];
indexToolHandle.indexPhrasesContainer ←
CreatePhraseButtons["Index Entry Phrases:", firstColumn, thisY];
indexToolHandle.sortAsPhrasesContainer ←
CreatePhraseButtons["Sort As:", secondColumn, thisY];
NextRow[4];
indexToolHandle.seePhrasesContainer ←
CreatePhraseButtons["See phrases:", firstColumn, thisY];
NextRow[4];
Rule[1]; -- ==============================================================
indexToolHandle.entriesLabel ← FeedbackLabel["# Entries:", firstColumn, thisY];
indexToolHandle.seeCountLabel ← FeedbackLabel["# See references:", secondColumn, thisY];
NextRow[];
indexToolHandle.startPositionLabel ← FeedbackLabel["Start position:", firstColumn, thisY];
indexToolHandle.endPositionLabel ← FeedbackLabel["End position:", secondColumn, thisY];
NextRow[];
Rule[1]; -- ==============================================================
indexToolHandle.indexViewer ← ViewerOps.CreateViewer[
flavor: $TiogaButtons,
info: [
wx: firstColumn,
wy: thisY,
parent: self,
scrollable: TRUE],
paint: FALSE
];
Containers.ChildXBound[self, indexToolHandle.indexViewer];
Containers.ChildYBound[self, indexToolHandle.indexViewer];
ViewerOps.AddProp[self, $IndexToolHandle, indexToolHandle];
};
CopyIndexToButtonProc: Menus.ClickProc = {
indexToolHandle: IndexToolHandle ← NARROW[clientData, IndexToolHandle];
IF TiogaOps.GetSelection[primary].viewer = NIL THEN {
Sorry["Make a primary selection for the destination of the index."];
RETURN;
}
ELSE {
root: TiogaOps.Ref ~ TiogaOps.ViewerDoc[indexToolHandle.indexViewer];
TiogaOps.SelectNodes[viewer: indexToolHandle.indexViewer,
start: TiogaOps.FirstChild[root],
end: TiogaOps.LastLocWithin[root].node,
which: secondary];
TiogaOps.ToPrimary[];
};
};
NewIndexToolButtonProc: Menus.ClickProc = {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
documentName: ROPE ← ViewerTools.GetSelectionContents[];
IF selectedViewer = NIL THEN {
Sorry["Make a text selection of the document name (longer than 1 character)"];
documentName ← NIL;
}
ELSE IF documentName.IsEmpty THEN
documentName ← selectedViewer.name;
[] ← CreateIndexFromDocument[documentName];
};
DeleteEntryButtonProc: Menus.ClickProc = {
indexToolHandle: IndexToolHandle ← NARROW[clientData, IndexToolHandle];
Sorry["Not implemented"];
};
InsertEntryButtonProc: Menus.ClickProc = {
indexToolHandle: IndexToolHandle ← NARROW[clientData, IndexToolHandle];
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
check that the selection is in the expected document indexToolHandle.documentViewer
IF selectedViewer # NIL THEN {
Sorry["Make a text selection in the document for this index entry"];
RETURN;
}
ELSE IF ViewerTools.GetContents[indexToolHandle.indexPhrasesContainer.child].IsEmpty THEN {
Sorry["Supply an index phrase in the IndexTool before inserting it!"];
RETURN;
}
ELSE IF indexToolHandle.documentViewer = NIL THEN {
this index tool has no specific document yet
indexToolHandle.documentViewer ← selectedViewer;
ScanIndexProperties[selectedViewer, indexToolHandle];
change the name of this viewer by appending [" for ", document.name] to the caption
}
ELSE IF selectedViewer # indexToolHandle.documentViewer THEN {
Sorry["Can't index that document from this index tool"];
RETURN;
};
{
start, end: TiogaOps.Location;
[start~start, end~end] ← TiogaOps.GetSelection[primary];
IF start.node # end.node THEN {
Sorry["Index entries must be within a single node"];
RETURN;
}
ELSE {
ixList: IndexProps.IndexEntryList ← NARROW[TextEdit.GetCharProp[
node~Node[start.node], index~start.where, name~$IndexEntries]];
selectionRoot: TiogaOps.Ref ← TiogaOps.SelectionRoot[primary];
ix: IndexEntry ← IndexEntryFromTool[indexToolHandle];
IndexTree.InsertNewIndexEntry[ix, Span[start, end], indexToolHandle.index !
IndexTree.DuplicateKey => GOTO Duplicate];
TiogaOps.Lock[selectionRoot];
TextEdit.PutCharProp[node~Node[start.node], index~start.where,
name~$IndexEntries, value~IndexProps.AddEntryToList[ix, ixList],
nChars~end.where-start.where, root~Node[selectionRoot]];
TiogaOps.Unlock[selectionRoot];
ViewerOps.SetNewVersion[indexToolHandle.documentViewer];
set feedback selection and normalize indexViewer
NormalizeIndexViewer[indexToolHandle, ix];
UpdateFeedback[indexToolHandle.entriesLabel,
indexToolHandle.entries ← indexToolHandle.entries + 1];
IF indexToolHandle.kindOfEntry = $See OR
indexToolHandle.kindOfEntry = $SeeAlso THEN
UpdateFeedback[indexToolHandle.seeCountLabel,
indexToolHandle.seeCount ← indexToolHandle.seeCount + 1];
UpdateFeedback[indexToolHandle.nestingCountLabel, indexToolHandle.nestingCount ← MAX[NumberOfPhrases[ix.phrases], indexToolHandle.nestingCount]];
};
};
EXITS
Duplicate => Sorry["Index entry duplicates an entry already in table; NOT inserted."];
};
AddNewKindButtonProc: Menus.ClickProc = {
indexToolHandle: IndexToolHandle ← NARROW[clientData, IndexToolHandle];
Create an additional kind of index entry choice button
← ChoiceButtons.GetSelectedButton[indexToolHandle.kindOfEntryChoices];
Sorry["Not implemented"];
};
PermutePhrasesButtonProc: Menus.ClickProc = {
indexToolHandle: IndexToolHandle ← NARROW[clientData, IndexToolHandle];
Sorry["Not implemented"];
};
atTheBeginning: ViewerTools.SelPos = NEW[ViewerTools.SelPosRec ← [0, 0, FALSE, before]];
PhraseButtonData: TYPE = REF PhraseButtonDataRec;
PhraseButtonDataRec: TYPE = RECORD [
indexToolHandle: IndexToolHandle,
phraseNumber: CARDINAL ← 0,
viewer: ViewerClasses.Viewer ← NIL
];
PhraseButtonProc: Menus.ClickProc = {
phraseData: PhraseButtonData ← NARROW[clientData, PhraseButtonData];
indexToolHandle: IndexToolHandle ← phraseData.indexToolHandle;
SELECT mouseButton FROM
yellow =>
Sorry["Left-click to copy selection to phrase viewer, Right-click to clear phrase viewer"];
red => {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
IF selectedViewer = NIL THEN
Sorry["Please make a text selection of the phrase to index"]
ELSE {
start, end: TiogaOps.Location;
root: TiogaOps.Ref ← TiogaOps.SelectionRoot[primary];
[start~start, end~end] ← TiogaOps.GetSelection[primary];
check that the phrase is within a single node
IF start.node # end.node THEN
Sorry["Index phrases must be within a single node"]
ELSE {
ViewerTools.SetContents[phraseData.viewer, NIL];
TiogaOps.SelectDocument[viewer~phraseData.viewer, level~char, which~secondary];
TiogaOps.ToSecondary[]; -- copy the selected phrase
};
};
};
blue => {
ViewerTools.SetContents[phraseData.viewer, NIL];
ViewerTools.SetSelection[phraseData.viewer, atTheBeginning];
};
ENDCASE;
};
ClearPhrasesButtonProc: Buttons.ButtonProc = {
phraseContainer: ViewerClasses.Viewer ← NARROW[clientData, ViewerClasses.Viewer];
v: ViewerClasses.Viewer ← phraseContainer.child;
WHILE v # NIL DO
ViewerTools.SetContents[v, NIL];
v ← v.sibling;
ENDLOOP;
ViewerTools.SetSelection[phraseContainer.child, atTheBeginning];
};
Service Procs for the IndexTool
Sorry: PROCEDURE [rope: ROPE] = {
MessageWindow.Append[message: rope, clearFirst: TRUE];
MessageWindow.Blink[];
};
UpdatePhrases: PROC [phraseContainer: ViewerClasses.Viewer, phrases: Phrases] ~ {
v: ViewerClasses.Viewer ← phraseContainer.child;
WHILE v # NIL DO
IF ViewerOps.FetchProp[v, $IndexPhrase] # NIL THEN {
IF phrases # NIL AND phrases.rest # NIL THEN {
contents: ViewerTools.TiogaContents ← NEW[ViewerTools.TiogaContentsRec ← [NARROW[phrases.first, ROPE], NARROW[phrases.rest.first, ROPE]]];
ViewerTools.SetTiogaContents[v, contents];
phrases ← phrases.rest.rest;
}
ELSE ViewerTools.SetContents[v, NIL];
};
v ← v.sibling;
ENDLOOP;
};
CollectPhrases: PROCEDURE [phraseContainer: ViewerClasses.Viewer]
RETURNS [phrases: Phrases ← NIL] = {
v: ViewerClasses.Viewer ← phraseContainer.child;
phrase: ViewerTools.TiogaContents;
WHILE v # NIL DO
IF ViewerOps.FetchProp[v, $IndexPhrase] # NIL THEN {
phrase ← ViewerTools.GetTiogaContents[v];
IF phrase = NIL OR phrase.contents.IsEmpty THEN EXIT;
TRUSTED {phrases ← LOOPHOLE[List.Nconc1[LOOPHOLE[phrases], phrase.contents]]};
TRUSTED {phrases ← LOOPHOLE[List.Nconc1[LOOPHOLE[phrases], phrase.formatting]]};
};
v ← v.sibling;
ENDLOOP;
};
NumberOfPhrases: PROC [phrases: Phrases] RETURNS [NAT] ~ TRUSTED {
RETURN [List.Length[LOOPHOLE[phrases]]/2];
};
UpdateFeedback: PROC [numberLabel: NumberLabels.NumberLabel, value: INT] ~ {
IF numberLabel.destroyed THEN RETURN;
NumberLabels.NumberLabelUpdate[numberLabel, value];
};
IndexEntryToTool: PROC [ix: IndexEntry, indexToolHandle: IndexToolHandle] ~ {
IF ix.kindOfIndex # indexToolHandle.kindOfIndex THEN ERROR WrongIndexTool;
UpdatePhrases[indexToolHandle.indexPhrasesContainer, ix.phrases];
UpdatePhrases[indexToolHandle.sortAsPhrasesContainer, ix.sortAsPhrases];
UpdatePhrases[indexToolHandle.seePhrasesContainer, ix.seePhrases];
UpdateKindOfEntry[indexToolHandle, ix];
};
WrongIndexTool: ERROR = CODE; -- an internal logic error
IndexEntryFromTool: PROC [indexToolHandle: IndexToolHandle]
RETURNS [ix: IndexEntry ← NEW[IndexProps.IndexEntryRec]] ~ {
ix.kindOfIndex ← indexToolHandle.kindOfIndex;
ix.kindOfEntry ← ChoiceButtons.GetSelectedButton[indexToolHandle.kindOfEntryChoices];
ix.phrases ← CollectPhrases[indexToolHandle.indexPhrasesContainer];
ix.seePhrases ← CollectPhrases[indexToolHandle.seePhrasesContainer];
IF ix.seePhrases # NIL AND ix.kindOfEntry # $See AND ix.kindOfEntry # $SeeAlso
THEN { ix.kindOfEntry ← $See; UpdateKindOfEntry[indexToolHandle, ix]; };
ix.sortAsPhrases ← CollectPhrases[indexToolHandle.sortAsPhrasesContainer];
};
UpdateKindOfEntry: PROC [indexToolHandle: IndexToolHandle, ix: IndexEntry] ~ {
ChoiceButtons.UpdateChoiceButtons[indexToolHandle.toolViewer, indexToolHandle.kindOfEntryChoices, Atom.GetPName[ix.kindOfEntry] !
ChoiceButtons.ChoiceDoesntExist => GOTO AddKindToChoices];
EXITS
AddKindToChoices => {
NULL
someday figure out how to get ChoiceButtons to do this...
};
};
NormalizeIndexViewer: PROCEDURE [indexToolHandle: IndexToolHandle,
ix: IndexEntry] = {
nearestEntry: IndexEntry;
item, leftItem, equalItem, rightItem: REF;
[leftItem, equalItem, rightItem] ← RedBlackTree.Lookup3[indexToolHandle.index.table, ix];
item ← IF equalItem # NIL THEN equalItem ELSE IF leftItem # NIL THEN leftItem ELSE rightItem;
nearestEntry ← NARROW[item, IndexEntry];
TiogaOps.SelectNodes[viewer: indexToolHandle.indexViewer,
start: nearestEntry.branchLocationStart.node,
end: nearestEntry.branchLocationEnd.node,
level: word,
which: feedback];
TEditScrolling.AutoScroll[indexToolHandle.indexViewer, TRUE, FALSE, feedback];
ViewerOps.PaintViewer[indexToolHandle.indexViewer, client];
};
IndexTool Command
IndexToolCommand: Commander.CommandProc = {
documentName: ROPENIL;
documentName ← IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc !
IO.EndOfStream => CONTINUE].token;
we should accept a document kind and create an atom with that name
IF documentName.IsEmpty THEN [] ← NewTool[]
ELSE [] ← CreateIndexFromDocument[documentName];
};
Initialization
indexToolViewerClass: ViewerClasses.ViewerClass ~
NEW[ViewerClasses.ViewerClassRec ← ViewerOps.FetchViewerClass[$Container]^];
containerInitProc: ViewerClasses.InitProc ~ indexToolViewerClass.init;
indexToolViewerIcon: Icons.IconFlavor;
indexToolViewerClass.init ← IndexToolViewerInit;
indexToolViewerClass.icon ← tool;
indexToolViewerClass.icon ← private;
IconRegistry.RegisterIcon["IndexTool", "IndexTool.icons", 0];
indexToolViewerIcon ← IconRegistry.GetIcon["IndexTool", tool];
ViewerOps.RegisterViewerClass[$IndexTool, indexToolViewerClass];
Commander.Register[
key: "IndexTool",
proc: IndexToolCommand,
doc: "Create an index tool."
];
END.