TEditDocuments2Impl.mesa
McGregor on September 10, 1982 1:53 pm
Maxwell, January 6, 1983 11:06 am
Plass, April 15, 1983 1:10 pm
Paxton on June 7, 1983 9:38 am
Russ Atkinson, September 26, 1983 3:20 pm
L. Stewart, November 3, 1983 3:28 pm
Doug Wyatt, May 25, 1984 4:30:54 pm PDT
DIRECTORY
AMMiniModel USING [ImplementorName],
FS USING [ComponentPositions, Error, ExpandName, FileInfo, GetName, nullOpenFile, Open, OpenFile],
Menus USING [Menu, MenuEntry, MenuProc, SetLine],
MessageWindow USING [Append, Blink, Clear],
NodeProps USING [GetProp, NullCopy, NullRead, NullWrite, PutProp, Register],
Process USING [Detach],
Rope USING [Concat, Equal, Fetch, Flatten, Find, ROPE, Size, SkipTo, Substr],
TEditDisplay USING [EstablishLine],
TEditDocument USING [maxClip, SpinAndLock, TEditDocumentData, Unlock],
TEditDocumentPrivate USING [findMenu, FindUnsavedDocument, InitViewerDoc, levelMenu, PositionHistory, RecordUnsavedDocument],
TEditDocumentPrivateExtras USING [InitViewerDocInternal],
TEditInput USING [FreeTree],
TEditInputOps USING [WaitForInsertToFinish],
TEditOps USING [],
TEditOpsExtras USING [FileNameProc],
TEditProfile USING [DefaultMenuChoice, implExtensions, menu1, menu2, menu3, openFirstLevelOnly, sourceExtensions],
TEditProfileExtras USING [tryVersionMap],
TEditSelection USING [FindRope, fSel, LockSel, MakeSelection, pSel, sSel, UnlockSel],
TextEdit USING [Offset, Size],
TextNode USING [FirstChild, Location, LocNumber, LocWithin, NarrowToTextNode, nullLocation, Offset, pZone, Ref, RefTextNode, Root],
VersionMap USING [MapList, Range, RangeList, RangeToEntry, ShortNameToRanges],
VersionMapDefaults USING [GetMapList],
ViewerClasses USING [Viewer],
ViewerOps USING [AddProp, CloseViewer, ComputeColumn, CreateViewer, DestroyViewer, EnumerateViewers, EnumProc, EstablishViewerPosition, FetchProp, MoveBelowViewer, OpenIcon, PaintHint, PaintViewer, SaveViewer, SetMenu],
ViewerTools USING [GetContents, GetSelectionContents, SelPos, SelPosRec, SetSelection],
WorldVM USING [LocalWorld];
TEditDocuments2Impl: CEDAR MONITOR
IMPORTS AMMiniModel, FS, Menus, MessageWindow, NodeProps, Process, Rope, TEditDocument, TEditDocumentPrivate, TEditDocumentPrivateExtras, TextEdit, TextNode, TEditDisplay, TEditInput, TEditInputOps, TEditProfile, TEditProfileExtras, TEditSelection, VersionMap, VersionMapDefaults, ViewerOps, ViewerTools, WorldVM
EXPORTS TEditDocument, TEditDocumentPrivate, TEditOps, TEditOpsExtras
= BEGIN
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
File: TYPE = FS.OpenFile;
noFile: File = FS.nullOpenFile;
initialCaret: ViewerTools.SelPos ← NEW[ViewerTools.SelPosRec ← []];
ForceInitialCaret: PROC[viewer: Viewer] = {ViewerTools.SetSelection[viewer, initialCaret]};
lastRoot: TextNode.Ref;
lastViewer: Viewer;
GetViewerForRootI: INTERNAL PROC[root: TextNode.Ref] RETURNS[viewer: Viewer] = {
IF root = NIL THEN RETURN [NIL];
IF root = lastRoot THEN RETURN [lastViewer];
lastRoot ← root;
RETURN [lastViewer ← NARROW[NodeProps.GetProp[root,$Viewer]]];
};
RecordViewerForRootI: INTERNAL PROC[viewer: Viewer, root: TextNode.Ref] = {
NodeProps.PutProp[root,$Viewer,viewer];
IF root = lastRoot THEN lastViewer ← viewer; -- keep the cache up to date
};
GetViewerForRoot: PUBLIC ENTRY PROC[root: TextNode.Ref] RETURNS[viewer: Viewer] = {
ENABLE UNWIND => NULL;
RETURN [GetViewerForRootI[root]];
};
RecordViewerForRoot: PUBLIC ENTRY PROC[viewer: Viewer, root: TextNode.Ref] = {
ENABLE UNWIND => NULL;
RecordViewerForRootI[viewer, root];
};
ForgetViewer: PUBLIC ENTRY PROC[viewer: Viewer] = {
ENABLE UNWIND => NULL;
tdd: TEditDocument.TEditDocumentData = NARROW[viewer.data];
root: TextNode.Ref;
IF tdd=NIL THEN RETURN;
root ← tdd.text;
IF GetViewerForRootI[root] # viewer THEN RETURN;
IF viewer.link # NIL THEN { -- change to a linked viewer
RecordViewerForRootI[viewer.link,root];
RETURN };
RecordViewerForRootI[NIL,root];
};
LoadHistory: TYPE = RECORD[name: ROPE, place: TextNode.Offset];
CopyLoadHistory: PUBLIC PROC[from, to: Viewer] = {
old: REF LoadHistory ← NARROW[ViewerOps.FetchProp[from, $LoadHistory]];
new: REF LoadHistory ← NARROW[ViewerOps.FetchProp[to, $LoadHistory]];
IF old=NIL THEN RETURN;
IF new=NIL THEN {
new ← TextNode.pZone.NEW[LoadHistory];
ViewerOps.AddProp[to, $LoadHistory, new] };
new^ ← old^;
};
AllocLoadHistory: PROC[viewer: Viewer] RETURNS[prop: REF LoadHistory] = {
IF (prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]]) # NIL THEN RETURN;
prop ← TextNode.pZone.NEW[LoadHistory];
ViewerOps.AddProp[viewer, $LoadHistory, prop];
};
SetLoadHistoryInfo: PROC[viewer: Viewer, prop: REF LoadHistory] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
prop.name ← viewer.name;
prop.place ← TextNode.LocNumber[tdd.lineTable.lines[0].pos];
};
SetLoadHistory: PROC[parent, viewer: Viewer] = {
Make viewer's load history point to current contents of parent
prop: REF LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[parent, prop];
};
SaveLoadHistory: PROC[viewer: Viewer] = {
prop: REF LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[viewer, prop];
};
PositionHistory: TYPE = TEditDocumentPrivate.PositionHistory;
CopyPositionHistory: PUBLIC PROC[from, to: Viewer] = {
old: REF PositionHistory ← NARROW[ViewerOps.FetchProp[from, $PositionHistory]];
new: REF PositionHistory ← NARROW[ViewerOps.FetchProp[to, $PositionHistory]];
IF old=NIL THEN RETURN;
IF new=NIL THEN {
new ← TextNode.pZone.NEW[PositionHistory];
ViewerOps.AddProp[to, $PositionHistory, new] };
new^ ← old^;
};
ClearPositionHistory: PROC[viewer: Viewer] = {
prop: REF PositionHistory ← NARROW[ViewerOps.FetchProp[viewer, $PositionHistory]];
IF prop = NIL THEN RETURN;
prop.pos ← prop.prev ← TextNode.nullLocation;
};
RememberCurrentPosition: PUBLIC PROC[viewer: Viewer] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
prop: REF PositionHistory ← NARROW[ViewerOps.FetchProp[viewer, $PositionHistory]];
loc: TextNode.Location;
IF tdd=NIL THEN RETURN;
IF prop = NIL THEN {
prop ← TextNode.pZone.NEW[PositionHistory];
ViewerOps.AddProp[viewer, $PositionHistory, prop] };
[] ← TEditDocument.SpinAndLock[tdd, "RememberCurrentPosition"];
loc ← tdd.lineTable.lines[0].pos;
TEditDocument.Unlock[tdd];
IF loc = prop.pos THEN RETURN;
prop.prev ← prop.pos; prop.pos ← loc;
};
PositionViewer: PUBLIC PROC[viewer: Viewer, loc: TextNode.Location,
hint: ViewerOps.PaintHint ← client] RETURNS[ok: BOOLEAN] = {
CheckPosition: PROC[viewer: Viewer, loc: TextNode.Location]
RETURNS [good: BOOLEAN, goodloc: TextNode.Location] = {
root, node: TextNode.Ref;
t1: TextNode.RefTextNode;
tdd: TEditDocument.TEditDocumentData;
IF viewer=NIL OR viewer.destroyed OR (node ← loc.node)=NIL THEN GOTO Failed;
IF (tdd ← NARROW[viewer.data]) = NIL THEN GOTO Failed;
IF (root ← tdd.text)=NIL THEN GOTO Failed;
IF TextNode.Root[node] # root THEN GOTO Failed; -- make sure still in the tree
IF (t1 ← TextNode.NarrowToTextNode[node])=NIL OR
loc.where NOT IN [0..TextEdit.Size[t1]] THEN RETURN [TRUE, [node,0]];
RETURN [TRUE,loc];
EXITS Failed => RETURN [FALSE, TextNode.nullLocation];
};
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
node: TextNode.RefTextNode;
[ok, loc] ← CheckPosition[viewer, loc];
IF tdd = NIL OR ~ok THEN RETURN;
RememberCurrentPosition[viewer];
IF (node ← TextNode.NarrowToTextNode[loc.node]) # NIL THEN { -- backup to line start
where: INTMAX[0, MIN[loc.where, TextEdit.Size[node]-1]];
backStop: INTMAX[0, where-300];
UNTIL where<=backStop OR Rope.Fetch[node.rope, where-1]=15C DO
where ← where - 1;
ENDLOOP;
loc.where ← where };
TEditDisplay.EstablishLine[tdd, loc];
TRUSTED {Process.Detach[FORK ViewerOps.PaintViewer[viewer, hint ! ABORTED => CONTINUE]]};
};
KillSelections: PROC[parent: Viewer] = { OPEN TEditSelection;
IF pSel # NIL AND pSel.viewer = parent THEN MakeSelection[selection: primary];
IF sSel # NIL AND sSel.viewer = parent THEN MakeSelection[selection: secondary];
IF fSel # NIL AND fSel.viewer = parent THEN MakeSelection[selection: feedback];
};
CancelLinks: PUBLIC PROC[viewer: Viewer] = {
ForgetViewer[viewer];
IF viewer.link.link=viewer THEN {
viewer.link.link ← NIL;
ViewerOps.PaintViewer[viewer.link, caption];
}
ELSE FOR v: Viewer ← viewer.link.link, v.link UNTIL v.link=viewer DO
REPEAT FINISHED => v.link ← viewer.link;
ENDLOOP;
viewer.link ← NIL;
};
DefaultMenus: PUBLIC PROC[viewer: Viewer, paint: BOOLFALSE] = {
menu: Menus.Menu ← viewer.menu;
num: INTEGER ← 1;
DoLine: PROC [which: TEditProfile.DefaultMenuChoice] = {
entry: Menus.MenuEntry ← SELECT which FROM
places => TEditDocumentPrivate.findMenu,
levels => TEditDocumentPrivate.levelMenu,
ENDCASE => NIL;
IF entry = NIL THEN RETURN;
Menus.SetLine[menu, num, entry];
num ← num+1 };
DoLine[TEditProfile.menu1]; DoLine[TEditProfile.menu2]; DoLine[TEditProfile.menu3];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
IF paint THEN ViewerOps.PaintViewer[viewer, all];
};
MakeNewViewer: PROC[parent: Viewer, paint: BOOLTRUE , wDir: ROPENIL]
RETURNS[viewer: Viewer] = {
Cannot have document or tdd locked when call this or may deadlock.
IF wDir=NIL THEN wDir ← WorkingDirectoryFromViewer[parent];
viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: wDir,
column: IF parent=NIL THEN left ELSE parent.column, iconic: TRUE], paint: FALSE];
IF TEditProfile.openFirstLevelOnly THEN {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
IF tdd # NIL THEN tdd.clipLevel ← 1 };
ViewerOps.OpenIcon[icon: viewer, paint: FALSE];
Create it iconic and then open to minimize time that have a bad viewer tree.
IF parent # NIL THEN { -- move below parent and copy menus
ViewerOps.MoveBelowViewer[altered: viewer, static: parent, paint: FALSE];
ViewerOps.SetMenu[viewer, parent.menu, FALSE];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
}
ELSE DefaultMenus[viewer];
IF paint THEN ViewerOps.ComputeColumn[viewer.column]; -- paints the column
};
ReplaceByNewViewer: PROC[parent: Viewer, wDir: ROPENIL]
RETURNS[viewer: Viewer] = {
Cannot have document or tdd locked when call this or may deadlock.
KillSelections[parent]; -- need to deselect before making new viewer
viewer ← MakeNewViewer[parent, FALSE, wDir];
copy height and position info from old to new
viewer.openHeight ← parent.openHeight;
viewer.position ← parent.position;
SetLoadHistory[parent, viewer];
make parent iconic without doing paints in the column, just paint the icon
ViewerOps.CloseViewer[viewer: parent, paint: FALSE];
ViewerOps.PaintViewer[parent, all]; -- paint the icon
ViewerOps.ComputeColumn[viewer.column]; -- paint the column (to inform ViewerBLT)
};
FindOldViewer: PROC[name: ROPE, viewer: Viewer] RETURNS[old: Viewer ← NIL] = {
Match: ViewerOps.EnumProc -- PROC[v: Viewer] RETURNS[BOOL ← TRUE] -- = {
IF v#viewer AND Rope.Equal[name, v.name, FALSE] THEN { old ← v; RETURN[FALSE] };
};
ViewerOps.EnumerateViewers[Match];
};
PaintAndRemember: PROC[viewer: Viewer] = {
ViewerOps.PaintViewer[viewer,all]; RememberCurrentPosition[viewer];
};
Report: PROC[m1, m2, m3, m4: ROPENIL, flash: BOOLFALSE] = {
MessageWindow.Append[m1, TRUE];
IF m2#NIL THEN MessageWindow.Append[m2];
IF m3#NIL THEN MessageWindow.Append[m3];
IF m4#NIL THEN MessageWindow.Append[m4];
IF flash THEN MessageWindow.Blink[];
};
Flash: PROC[m1, m2, m3, m4: ROPENIL] = {
Report[m1: m1, m2: m2, m3: m3, m4: m4, flash: TRUE];
};
PleaseSelectFileName: PROC = {Flash["Please select file name."]};
IllegalFileName: PROC = {Flash["Illegal file name."]};
ReloadedMessage: PROC[name: ROPE] = {Flash[name," restored with previous unsaved edits."]};
WasLoadedMessage: PROC[name: ROPE] = {Report[name," was already loaded."]};
RemoveVersion: PROC[x: ROPE] RETURNS[ROPE] = { RETURN[x.Flatten[len: x.SkipTo[skip: "!"]]] };
GetFileName: PROC[file: FS.OpenFile, removeVersion: BOOLFALSE]
RETURNS[name: ROPENIL] = {
name ← FS.GetName[file ! FS.Error => CONTINUE].fullFName;
IF name#NIL AND removeVersion THEN name ← RemoveVersion[name];
};
LoadOp: TYPE ~ {load, open, replace};
LookupType: TYPE ~ {source, impl};
DoLoad: PROC[viewer: Viewer, file: FS.OpenFile, specificVersion: BOOLFALSE,
close: BOOLFALSE, place: TextNode.Offset ← 0] RETURNS[Viewer] = {
Load a file into a viewer
viewer is the viewer to be loaded with the contents of the file
name is the specified name for the file
file is the open file
tdd, tddOld: TEditDocument.TEditDocumentData;
oldViewer, destroyViewer: Viewer;
clearMessage: BOOLTRUE;
fileName: ROPE ~ FS.GetName[file].fullFName;
name: ROPE ← fileName;
IF NOT specificVersion THEN name ← name.Flatten[len: name.SkipTo[skip: "!"]];
IF Rope.Equal[viewer.file, fileName, FALSE] THEN {
Report[fileName, " is already loaded."]; RETURN[viewer];
};
IF close AND viewer.link=NIL THEN viewer ← ReplaceByNewViewer[viewer];
Report["Loading ", name];
KillSelections[viewer];
IF (tdd ← NARROW[viewer.data])=NIL THEN RETURN[viewer];
[] ← TEditDocument.SpinAndLock[tdd, "DoLoad"];
IF viewer.file#NIL THEN SaveLoadHistory[viewer];
IF viewer.link=NIL THEN {
ForgetViewer[viewer]; -- remove this from the root => viewer mapping
IF viewer.newVersion AND (NOT viewer.saveInProgress) AND viewer.file#NIL THEN
TEditDocumentPrivate.RecordUnsavedDocument[viewer.file, tdd.text]
ELSE TEditInput.FreeTree[tdd.text];
}
ELSE CancelLinks[viewer];
viewer.name ← name; viewer.file ← fileName;
viewer.newVersion ← viewer.newFile ← FALSE;
oldViewer ← FindOldViewer[name, viewer];
IF oldViewer#NIL AND NOT oldViewer.destroyed
AND (tddOld ← NARROW[oldViewer.data])#NIL THEN { -- link them
viewer.name ← oldViewer.name;
viewer.file ← oldViewer.file;
viewer.newVersion ← oldViewer.newVersion;
viewer.newFile ← oldViewer.newFile;
[] ← TEditDocument.SpinAndLock[tddOld, "DoLoad"];
tdd.text ← tddOld.text;
tdd.tsInfo ← tddOld.tsInfo;
TEditDocument.Unlock[tddOld];
tdd.lineTable.lines[0].pos ← [TextNode.FirstChild[tdd.text],0];
tdd.clipLevel ← TEditDocument.maxClip;
tdd.commentFilter ← includeComments;
viewer.link ← oldViewer;
IF oldViewer.link=NIL THEN {
oldViewer.link ← viewer;
ViewerOps.PaintViewer[oldViewer, caption];
}
ELSE {
FOR v: Viewer ← oldViewer.link, v.link UNTIL v.link=oldViewer DO
REPEAT FINISHED => v.link ← viewer;
ENDLOOP;
};
TRUSTED {Process.Detach[FORK WasLoadedMessage[name]]};
clearMessage ← FALSE;
FOR v: Viewer ← oldViewer, v.link UNTIL v.link=oldViewer DO
IF v.iconic THEN { destroyViewer ← v; EXIT }; -- destroy an iconic viewer
ENDLOOP
}
ELSE {
root: TextNode.Ref ~ TEditDocumentPrivate.FindUnsavedDocument[name];
tdd.text ← NIL; -- so InitViewerDoc won't worry about freeing it
IF root # NIL THEN {
TRUSTED {Process.Detach[FORK ReloadedMessage[name]]}; -- can deadlock if don't fork
clearMessage ← FALSE;
};
TEditDocumentPrivateExtras.InitViewerDocInternal[viewer, file, root];
viewer.newFile ← FALSE;
viewer.newVersion ← root#NIL;
};
ClearPositionHistory[viewer];
IF NOT PositionViewer[viewer, TextNode.LocWithin[tdd.text,place], all] THEN
TRUSTED {Process.Detach[FORK PaintAndRemember[viewer ! ABORTED => CONTINUE]]};
TEditDocument.Unlock[tdd];
IF destroyViewer#NIL THEN ViewerOps.DestroyViewer[destroyViewer];
Cannot do the destroy until after have released lock.
IF clearMessage THEN MessageWindow.Clear[];
RETURN[viewer];
};
DoGet: PROC[parent: Viewer, file: FS.OpenFile, specificVersion: BOOL,
op: LoadOp ← $load, place: TextNode.Offset ← 0] RETURNS[viewer: Viewer] = {
viewer ← DoLoad[viewer: IF op=$open THEN MakeNewViewer[parent, TRUE] ELSE parent,
file: file, specificVersion: specificVersion, close: op=$replace, place: place];
};
TryToOpen: PROC[name: ROPE, wDir: ROPENIL]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile] = {
file ← FS.Open[name: name, wDir: wDir
! FS.Error => IF error.code=$unknownFile THEN CONTINUE];
};
TryExtensions: PROC[name: ROPE, wDir: ROPE, extensions: LIST OF ROPE]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile] = {
base: ROPE ~ name.Concat["."];
FOR list: LIST OF ROPE ← extensions, list.rest UNTIL list=NIL DO
file ← TryToOpen[name: base.Concat[list.first], wDir: wDir];
IF file#FS.nullOpenFile THEN EXIT;
ENDLOOP;
};
IsAnExtension: PROC[ext: ROPE, extensions: LIST OF ROPE] RETURNS[BOOL] = {
FOR list: LIST OF ROPE ← extensions, list.rest UNTIL list=NIL DO
IF Rope.Equal[ext, list.first, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
FileNameProc: TYPE = TEditOpsExtras.FileNameProc;
fileNameProc: FileNameProc ← NIL;
RegisterFileNameProc: PUBLIC PROC [proc: FileNameProc] = { fileNameProc ← proc };
ReplaceFileNameProc: PUBLIC PROC[new: FileNameProc] RETURNS[old: FileNameProc] = {
old ← fileNameProc; fileNameProc ← new;
};
TryVersionMap: PROC[shortName: ROPE] RETURNS[file: FS.OpenFile ← FS.nullOpenFile] = {
Adapted from VersionMapCommandsImpl.FindSource.
Stops at the first name it finds, if any.
fullName: ROPENIL;
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source];
ranges: VersionMap.RangeList ~ VersionMap.ShortNameToRanges[mapList, shortName];
FOR list: VersionMap.RangeList ← ranges, list.rest UNTIL list=NIL DO
range: VersionMap.Range ~ list.first;
IF range.len#0 THEN { fullName ← VersionMap.RangeToEntry[range].name; EXIT };
ENDLOOP;
IF fullName#NIL THEN file ← FS.Open[fullName];
};
TryVersionMapExtensions: PROC[name: ROPE, extensions: LIST OF ROPE]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile] = {
base: ROPE ~ name.Concat["."];
FOR list: LIST OF ROPE ← extensions, list.rest UNTIL list=NIL DO
file ← TryVersionMap[base.Concat[list.first]];
IF file#FS.nullOpenFile THEN EXIT;
ENDLOOP;
};
FileNotFound: ERROR[fileName: ROPE] = CODE;
LookupSource: PROC[sel: ROPE, wDir: ROPE, fileNameProcViewer: Viewer ← NIL]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile,
specificVersion: BOOLFALSE, search: ROPENIL] = {
dot: INT ← 0;
hasExtension, standardExtension, simpleName: BOOLFALSE;
base, ext: ROPENIL;
proc: FileNameProc ~ fileNameProc;
Report["Directory lookup for ", sel];
FOR i: INT DECREASING IN[0..sel.Size[]) DO
SELECT sel.Fetch[i] FROM
'! => specificVersion ← TRUE;
'. => IF hasExtension THEN NULL ELSE { dot ← i; hasExtension ← TRUE };
'], '>, '/ => EXIT;
ENDCASE;
REPEAT FINISHED => simpleName ← TRUE;
ENDLOOP;
file ← TryToOpen[name: sel, wDir: wDir];
IF file#FS.nullOpenFile OR specificVersion THEN RETURN;
IF NOT hasExtension THEN {
file ← TryExtensions[name: sel, wDir: wDir, extensions: TEditProfile.sourceExtensions];
IF file#FS.nullOpenFile THEN RETURN;
};
IF proc#NIL THEN { pname, psearch: ROPE;
[pname, psearch] ← proc[sel, fileNameProcViewer];
IF pname.Size[]#0 THEN {
file ← TryToOpen[name: pname, wDir: wDir];
IF file#FS.nullOpenFile THEN { search ← psearch; RETURN }
ELSE ERROR FileNotFound[pname];
};
};
IF hasExtension THEN {
base ← sel.Substr[len: dot];
ext ← sel.Substr[start: dot+1];
IF IsAnExtension[ext, TEditProfile.sourceExtensions] THEN standardExtension ← TRUE;
IF NOT standardExtension THEN {
file ← TryExtensions[name: base, wDir: wDir, extensions: TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
};
IF simpleName AND TEditProfileExtras.tryVersionMap THEN {
Report["Get: trying version map for ", sel];
file ← TryVersionMap[sel]; -- try sel verbatim first
IF file#FS.nullOpenFile THEN RETURN;
IF NOT hasExtension THEN {
file ← TryVersionMapExtensions[sel, TEditProfile.sourceExtensions];
IF file#FS.nullOpenFile THEN RETURN;
}
ELSE IF NOT standardExtension THEN {
file ← TryVersionMapExtensions[base, TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
};
};
LookupImpl: PROC[sel: ROPE, wDir: ROPE]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile, search: ROPENIL] = {
cp: FS.ComponentPositions;
simpleName: BOOL ~ sel.SkipTo[skip: "[]<>/"]=sel.Size[];
xname, base, ext, impl: ROPENIL;
[fullFName: xname, cp: cp] ← FS.ExpandName[name: sel, wDir: wDir];
base ← xname.Substr[start: cp.base.start, len: cp.base.length]; -- base part is defs name
ext ← xname.Substr[start: cp.ext.start, len: cp.ext.length]; -- extension part is search item
IF ext.Size[]#0 THEN {
Report["Model lookup for implementor of ", base, ".", ext];
TRUSTED{ impl ← AMMiniModel.ImplementorName[
defsName: base, itemName: ext, world: WorldVM.LocalWorld[]] };
};
IF impl=NIL THEN impl ← base.Concat["Impl"];
Report["Directory lookup for ", impl];
file ← TryExtensions[name: impl, wDir: xname.Substr[len: cp.base.start],
extensions: TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
IF simpleName AND TEditProfileExtras.tryVersionMap THEN {
Report["GetImpl: trying version map for ", impl];
file ← TryVersionMapExtensions[impl, TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
ERROR FileNotFound[impl];
};
DoSearch: PROC[v: Viewer, search: ROPE] = {
search for definition
tdd: TEditDocument.TEditDocumentData ← NARROW[v.data];
loc: TextNode.Location;
IF tdd=NIL OR Rope.Size[search]=0 THEN RETURN; -- forget it
IF (loc ← TextNode.LocWithin[tdd.text,0]) # tdd.lineTable.lines[0].pos THEN {
TEditDisplay.EstablishLine[tdd, loc];
ViewerOps.PaintViewer[v, client] }; -- always search from start
TEditSelection.FindRope[
viewer: v, rope: search, case: FALSE, word: TRUE, def: TRUE, id: feedback];
};
WorkingDirectoryFromViewer: PUBLIC PROC[parent: Viewer] RETURNS[ROPE] = {
name, fname: ROPENIL; cp: FS.ComponentPositions;
IF parent#NIL THEN name ← parent.name;
IF name=NIL OR parent.file=NIL THEN
[fullFName: fname, cp: cp] ← FS.ExpandName[name: "*", wDir: name ! FS.Error => CONTINUE]
ELSE [fullFName: fname, cp: cp] ← FS.ExpandName[name: name ! FS.Error => CONTINUE];
IF fname=NIL THEN [fullFName: fname, cp: cp] ← FS.ExpandName[name: "*"];
RETURN[fname.Substr[len: cp.base.start]];
};
IsAWorkingDirectory: PROC[name: ROPE] RETURNS[BOOL] = {
size: INT ~ name.Size[];
IF size=0 THEN RETURN[FALSE]
ELSE RETURN[SELECT name.Fetch[size-1] FROM '>, '/ => TRUE, ENDCASE => FALSE];
};
CanonicalWorkingDirectory: PROC[txt: ROPE, wDir: ROPENIL] RETURNS[ROPE] = {
fname: ROPENIL; cp: FS.ComponentPositions;
[fullFName: fname, cp: cp] ← FS.ExpandName[name: txt.Concat["*"], wDir: wDir
! FS.Error => CONTINUE];
IF fname=NIL THEN RETURN[NIL]
ELSE RETURN[fname.Substr[len: cp.base.start]];
};
DoGetFile: PROC[txt: ROPE, parent: Viewer,
op: LoadOp ← $load, lookup: LookupType ← $source,
fileNameProcViewer: Viewer ← NIL]
RETURNS[viewer: Viewer ← NIL] = {
parentDir: ROPE ~ WorkingDirectoryFromViewer[parent];
file: FS.OpenFile ← FS.nullOpenFile;
specificVersion: BOOLFALSE;
search: ROPENIL;
IF txt=NIL THEN txt ← ViewerTools.GetSelectionContents[];
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
IF txt.Size[]=0 THEN { PleaseSelectFileName[]; GOTO Fail };
IF IsAWorkingDirectory[txt] THEN {
wDir: ROPE ~ CanonicalWorkingDirectory[txt, parentDir];
IF wDir=NIL THEN { Flash[txt, " is an illegal working directory."]; GOTO Fail };
SELECT op FROM
$load => DoEmptyViewer[parent: viewer ← parent, wDir: wDir];
$open => viewer ← MakeNewViewer[parent: parent, wDir: wDir];
$replace => viewer ← ReplaceByNewViewer[parent: parent, wDir: wDir];
ENDCASE => ERROR;
RETURN[viewer];
};
{
ENABLE {
FS.Error => { Flash[error.explanation]; GOTO Fail };
FileNotFound => { Flash[fileName, " not found."]; GOTO Fail };
};
SELECT lookup FROM
$source => [file: file, specificVersion: specificVersion, search: search] ←
LookupSource[sel: txt, wDir: parentDir, fileNameProcViewer: fileNameProcViewer];
$impl => [file: file, search: search] ← LookupImpl[sel: txt, wDir: parentDir];
ENDCASE => ERROR;
IF file=FS.nullOpenFile THEN { Flash[txt, " not found."]; GOTO Fail };
};
IF op=$open OR parent=NIL THEN parent ← MakeNewViewer[parent, TRUE]
ELSE {
tdd: TEditDocument.TEditDocumentData ~ NARROW[parent.data];
IF tdd=NIL THEN GOTO Fail;
[] ← TEditDocument.SpinAndLock[tdd, "DoGetFile"];
TEditDocument.Unlock[tdd]; -- let other op finish first
};
viewer ← DoLoad[viewer: parent, file: file, specificVersion: specificVersion, close: op=$replace];
IF viewer#NIL AND search#NIL THEN DoSearch[viewer, search];
EXITS Fail => RETURN[NIL];
};
The following are the top level procedures that implement the Tioga file operations: they are available as menu items, system buttons, or keystrokes (LF).
Clear:
EmptyViewer[viewer]
NewViewer[viewer]
CloseAndNewViewer[viewer]
Reset:
TEditDocuments3Impl.ResetOp[viewer]
Get:
[] ← DoLoadFile[viewer, selection, FALSE]
[] ← DoOpenFile[selection, viewer]
[] ← DoCloseAndOpenFile[viewer, selection]
GetImpl:
[] ← DoLoadImplFile[viewer, selection, FALSE]
[] ← DoOpenImplFile[selection, viewer]
[] ← DoCloseAndOpenImplFile[viewer, selection]
PrevFile:
LoadPreviousFile[viewer]
OpenPreviousFile[viewer]
CloseAndOpenPreviousFile[viewer]
Store:
DoStoreFile[viewer, selection]
Save:
TEditDocuments3Impl.SaveOp[viewer]
New button:
NewViewer[NIL]
Open button:
[] ← DoOpenFile[NIL, selection]
LF keyboard command:
AnonymousLoadFile[viewer]
AnonymousLoadImplFile[viewer]
DoEmptyViewer: PROC[parent: Viewer, wDir: ROPENIL] = {
IF parent#NIL AND parent.data#NIL THEN WITH parent.data SELECT FROM
tdd: TEditDocument.TEditDocumentData => {
IF wDir=NIL THEN wDir ← WorkingDirectoryFromViewer[parent];
TEditSelection.LockSel[primary, "EmptyViewer"];
{ ENABLE UNWIND => TEditSelection.UnlockSel[primary];
prop: REF LoadHistory;
[] ← TEditDocument.SpinAndLock[tdd, "EmptyViewer"];
KillSelections[parent];
SaveLoadHistory[parent];
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]]; -- hang onto it
IF parent.link#NIL THEN CancelLinks[parent]
ELSE IF parent.newVersion AND ~parent.saveInProgress AND parent.file # NIL THEN
TEditDocumentPrivate.RecordUnsavedDocument[parent.file, tdd.text]
ELSE TEditInput.FreeTree[tdd.text];
parent.name ← wDir; parent.file ← NIL;
parent.newVersion ← parent.newFile ← FALSE;
tdd.text ← NIL; -- so InitViewerDoc won't free it
TEditDocumentPrivate.InitViewerDoc[parent,NIL];
TEditDocument.Unlock[tdd];
ClearPositionHistory[parent];
ViewerOps.AddProp[parent, $LoadHistory, prop]; -- restore file history
ViewerOps.PaintViewer[parent, all];
ForceInitialCaret[parent];
};
TEditSelection.UnlockSel[primary];
};
ENDCASE;
};
EmptyViewer: PUBLIC PROC[parent: Viewer] = {
DoEmptyViewer[parent];
};
DoNewViewer: PUBLIC PROC[parent: Viewer ← NIL] RETURNS[new: Viewer] = {
TEditSelection.LockSel[primary, "DoNewViewer"];
{ ENABLE UNWIND => TEditSelection.UnlockSel[primary];
ForceInitialCaret[new ← MakeNewViewer[parent,TRUE]] };
TEditSelection.UnlockSel[primary];
};
NewViewer: PUBLIC PROC [parent: Viewer] = {
[] ← DoNewViewer[parent];
};
DoCloseAndNewViewer: PUBLIC PROC[parent: Viewer] RETURNS[new: Viewer] = {
TEditSelection.LockSel[primary, "DoCloseAndNewViewer"];
{ ENABLE UNWIND => TEditSelection.UnlockSel[primary];
new ← ReplaceByNewViewer[parent];
ViewerOps.PaintViewer[new, all];
ForceInitialCaret[new] };
TEditSelection.UnlockSel[primary];
};
CloseAndNewViewer: PUBLIC PROC[parent: Viewer] = {
[] ← DoCloseAndNewViewer[parent];
};
DoLoadFile: PUBLIC PROC[parent: Viewer, fileName: ROPENIL, close: BOOLFALSE,
fileNameProcViewer: Viewer ← NIL] RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent,
op: IF close THEN $replace ELSE $load, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
LoadFile: PUBLIC PROC[parent: Viewer] = {
[] ← DoLoadFile[parent: parent, fileName: NIL, close: FALSE];
};
DoOpenFile: PUBLIC PROC[fileName: ROPENIL, parent: Viewer,
fileNameProcViewer: Viewer ← NIL] RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent, op: $open, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
OpenFile: PUBLIC PROC[parent: Viewer] = {
[] ← DoOpenFile[fileName: NIL, parent: parent];
};
DoCloseAndOpenFile: PUBLIC PROC[parent: Viewer, fileName: ROPENIL]
RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent, op: $replace, lookup: $source];
};
CloseAndOpenFile: PUBLIC PROC[parent: Viewer,
fileNameProcViewer: Viewer ← NIL] = {
[] ← DoGetFile[txt: NIL, parent: parent, op: $replace, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
DoLoadImplFile: PUBLIC PROC[parent: Viewer, fileName: ROPENIL, close: BOOLFALSE]
RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent,
op: IF close THEN $replace ELSE $load, lookup: $impl];
};
LoadImplFile: PUBLIC PROC[parent: Viewer] = {
[] ← DoLoadImplFile[parent: parent, fileName: NIL];
};
DoOpenImplFile: PUBLIC PROC[fileName: ROPENIL, parent: Viewer ← NIL]
RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent, op: $open, lookup: $impl];
};
OpenImplFile: PUBLIC PROC[parent: Viewer] = {
[] ← DoOpenImplFile[fileName: NIL, parent: parent];
};
DoCloseAndOpenImplFile: PUBLIC PROC[parent: Viewer, fileName: ROPENIL]
RETURNS[viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, parent: parent, op: $replace, lookup: $impl];
};
CloseAndOpenImplFile: PUBLIC PROC[parent: Viewer] = {
[] ← DoCloseAndOpenImplFile[parent, NIL];
};
PreLoadPrevious: PUBLIC Menus.MenuProc = { -- called when unguarding PrevFile
viewer: Viewer = NARROW[parent];
tdd: TEditDocument.TEditDocumentData;
prop: REF LoadHistory;
propName: ROPE;
tdd ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
[] ← TEditDocument.SpinAndLock[tdd, "PreLoadPrevious"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]];
TEditDocument.Unlock[tdd];
IF prop=NIL OR Rope.Equal[prop.name, viewer.file, FALSE] THEN {
Flash["No record of previous file loaded in this viewer"];
RETURN };
propName ← prop.name;
Report[propName," ~ Click LEFT to load, MIDDLE for new, RIGHT for close & new"];
};
DoLoadPreviousFile: PROC[parent: Viewer, op: LoadOp] = {
tdd: TEditDocument.TEditDocumentData;
prop: REF LoadHistory;
propName: ROPE;
propPlace: TextNode.Offset;
file: FS.OpenFile;
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← TEditDocument.SpinAndLock[tdd, "DoLoadPreviousFile"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]];
TEditDocument.Unlock[tdd];
IF prop = NIL OR Rope.Equal[prop.name, parent.file, FALSE] THEN {
Flash["No record of previous file loaded in this viewer"];
RETURN };
propName ← prop.name; propPlace ← prop.place;
file ← FS.Open[propName ! FS.Error => { Flash[error.explanation]; GOTO Fail }];
[] ← DoGet[parent: parent, file: file, specificVersion: (propName.Find["!"]>=0),
op: op, place: propPlace];
EXITS Fail => NULL;
};
LoadPreviousFile: PUBLIC PROC[parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $load];
};
OpenPreviousFile: PUBLIC PROC[parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $open];
};
CloseAndOpenPreviousFile: PUBLIC PROC[parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $replace];
};
GetCreateName: PROC[parent: Viewer, name: ROPE] RETURNS[ROPE] = {
cp: FS.ComponentPositions;
wDir: ROPE ~ WorkingDirectoryFromViewer[parent];
[fullFName: name, cp: cp] ← FS.ExpandName[name, wDir ! FS.Error => GOTO Fail];
RETURN[name.Flatten[start: cp.subDirs.start, len: (cp.ext.start+cp.ext.length)-cp.subDirs.start]];
EXITS Fail => {
TRUSTED { Process.Detach[FORK IllegalFileName[]] };
RETURN[NIL];
};
};
IsNewFile: PROC[name: ROPE] RETURNS[new: BOOLFALSE] = {
[] ← FS.FileInfo[name ! FS.Error => { new ← TRUE; CONTINUE }];
};
PreStore: PUBLIC Menus.MenuProc = { -- called when unguarding Store
sel: ROPE ← ViewerTools.GetSelectionContents[];
fileName: ROPENIL; new: BOOLFALSE;
IF sel.Size[]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[NARROW[parent], sel];
IF fileName=NIL THEN RETURN;
new ← IsNewFile[fileName];
Report["Confirm Store to file: ", fileName, IF new THEN " [New File]" ELSE " [Old File]"];
};
DoStoreFile: PUBLIC PROC [parent: Viewer, fileName: ROPENIL] = {
linked: BOOL;
tdd: TEditDocument.TEditDocumentData;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF fileName.Size[]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[parent, fileName];
IF fileName=NIL THEN RETURN;
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← TEditDocument.SpinAndLock[tdd, "DoStoreFile"];
IF parent.file # NIL THEN SaveLoadHistory[parent];
IF linked ← (parent.link#NIL) THEN CancelLinks[parent]; -- remove viewer from link chain
NodeProps.PutProp[tdd.text, $FileCreateDate, NIL]; -- remove so Save won't test
TEditDocument.Unlock[tdd];
{ oldName: ROPE ~ parent.name; oldFile: ROPE ~ parent.file;
parent.name ← parent.file ← fileName;
ViewerOps.SaveViewer[parent
! UNWIND => { parent.name ← oldName; parent.file ← oldFile }];
};
IF linked THEN { -- copy tdd.text data structure
loc: TextNode.Offset;
KillSelections[parent];
[] ← TEditDocument.SpinAndLock[tdd, "DoStoreFile2"];
loc ← TextNode.LocNumber[tdd.lineTable.lines[0].pos];
tdd.text ← NIL; -- so InitViewerDoc won't free it
TEditDocumentPrivate.InitViewerDoc[parent, NIL];
IF ~PositionViewer[parent,TextNode.LocWithin[tdd.text,loc],all] THEN
TRUSTED {Process.Detach[FORK PaintAndRemember[parent]]};
TEditDocument.Unlock[tdd] };
};
StoreFile: PUBLIC PROC[parent: Viewer] = {
DoStoreFile[parent: parent, fileName: NIL];
};
GetViewerContents: PROC[viewer: Viewer] RETURNS[ROPE] = {
TEditInputOps.WaitForInsertToFinish[];
RETURN[ViewerTools.GetContents[viewer]];
};
DoAnonymousLoadFile: PROC[parent: Viewer, lookup: LookupType,
fileNameProcViewer: Viewer ← NIL] = {
IF parent.file#NIL THEN Flash["Viewer already contains a file."]
ELSE {
txt: ROPE ~ GetViewerContents[parent];
size: INT ~ txt.Size[];
IF size=0 THEN Flash["Enter a file name."]
ELSE [] ← DoGetFile[txt: txt, parent: parent, op: $load, lookup: lookup,
fileNameProcViewer: fileNameProcViewer];
};
};
AnonymousLoadFile: PUBLIC PROC[parent: Viewer, fileNameProcViewer: Viewer ← NIL] = {
DoAnonymousLoadFile[parent: parent, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
AnonymousLoadImplFile: PUBLIC PROC[parent: Viewer] = {
DoAnonymousLoadFile[parent: parent, lookup: $impl];
};
NodeProps.Register[$Viewer,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileCreateDate,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileExtension,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
END.