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;
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: INT ← MAX[0, MIN[loc.where, TextEdit.Size[node]-1]];
backStop: INT ← MAX[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:
BOOL ←
FALSE] = {
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:
BOOL ←
TRUE , wDir:
ROPE ←
NIL]
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:
ROPE ←
NIL]
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:
ROPE ←
NIL, flash:
BOOL ←
FALSE] = {
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:
ROPE ←
NIL] = {
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:
BOOL ←
FALSE]
RETURNS[name: ROPE ← NIL] = {
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:
BOOL ←
FALSE,
close: BOOL ← FALSE, 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: BOOL ← TRUE;
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:
ROPE ←
NIL]
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: ROPE ← NIL;
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: BOOL ← FALSE, search: ROPE ← NIL] = {
dot: INT ← 0;
hasExtension, standardExtension, simpleName: BOOL ← FALSE;
base, ext: ROPE ← NIL;
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: ROPE ← NIL] = {
cp: FS.ComponentPositions;
simpleName: BOOL ~ sel.SkipTo[skip: "[]<>/"]=sel.Size[];
xname, base, ext, impl: ROPE ← NIL;
[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: ROPE ← NIL; 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:
ROPE ←
NIL]
RETURNS[
ROPE] = {
fname: ROPE ← NIL; 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: BOOL ← FALSE;
search: ROPE ← NIL;
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:
ROPE ←
NIL] = {
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:
ROPE ←
NIL, close:
BOOL ←
FALSE,
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:
ROPE ←
NIL, 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:
ROPE ←
NIL]
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:
ROPE ←
NIL, close:
BOOL ←
FALSE]
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:
ROPE ←
NIL, 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:
ROPE ←
NIL]
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:
BOOL ←
FALSE] = {
[] ← FS.FileInfo[name ! FS.Error => { new ← TRUE; CONTINUE }];
};
PreStore:
PUBLIC Menus.MenuProc = {
-- called when unguarding Store
sel: ROPE ← ViewerTools.GetSelectionContents[];
fileName: ROPE ← NIL; new: BOOL ← FALSE;
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:
ROPE ←
NIL] = {
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.