FileDWIMImpl.mesa
Mike Spreitzer August 7, 1986 5:59:21 pm PDT
DIRECTORY BasicTime, DFUtilities, FileDWIM, FS, IO, MessageWindow, Rope, RopeList, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults, ViewerClasses;
FileDWIMImpl: CEDAR PROGRAM
IMPORTS BasicTime, DFUtilities, FS, IO, MessageWindow, Rope, RopeList, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults
EXPORTS FileDWIM
=
BEGIN OPEN FileDWIM;
ROPEList: TYPE = LIST OF ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
The basic stuff
Modeled after TEditDocuments2Impl.LookupSource --- too bad that doesn't have quite the right interface.
ResolveHint: PUBLIC PROC [hint: ROPE, contextFileName: ROPENIL, searchHack: BOOLFALSE, tryAll: BOOLTRUE] RETURNS [ans: Answer] = {
fullContextName, fullShortName: ROPE;
contextCP, shortCP: FS.ComponentPositions;
wDir: ROPENIL;
isADfFileName, standardExtension: BOOLFALSE;
ans ← [NIL, NIL];
IF contextFileName.Length[] # 0 THEN {
[fullContextName, contextCP, ] ← FS.ExpandName[contextFileName];
wDir ← fullContextName.Substr[len: contextCP.base.start];
isADfFileName ← fullContextName.Substr[start: contextCP.ext.start, len: contextCP.ext.length].Equal["df", FALSE];
};
[fullShortName, shortCP, ] ← FS.ExpandName[hint, wDir];
IF OK[ans ← TryFull[fullShortName]] THEN RETURN;
{hadDir: BOOL = HasDir[hint];
hasExtension: BOOL = shortCP.ext.start # shortCP.base.start + shortCP.base.length;
hasVersion: BOOL = shortCP.ver.start # shortCP.ext.start + shortCP.ext.length;
base: ROPE = fullShortName.Substr[start: shortCP.base.start, len: shortCP.base.length];
ext: ROPE = fullShortName.Substr[start: shortCP.ext.start, len: shortCP.ext.length];
extless: ROPE = fullShortName.Substr[len: shortCP.base.start + shortCP.base.length];
shortPart: ROPE = fullShortName.Substr[start: shortCP.base.start];
IF hasExtension OR hasVersion OR NOT tryAll THEN NULL ELSE {
IF verbose THEN MessageWindow.Append[Rope.Cat["Trying source extensions to ", fullShortName], TRUE];
IF OK[ans ← TryExtensions[fullShortName, TEditProfile.sourceExtensions]] THEN RETURN;
};
[ans.fullFileName, ans.searchFor] ← DWIMit[hint, wDir, contextFileName, searchHack, isADfFileName];
IF OK[ans] OR hasVersion OR NOT tryAll THEN RETURN;
IF searchHack AND hasExtension THEN {
standardExtension ← RopeList.Memb[TEditProfile.sourceExtensions, ext, FALSE];
IF NOT standardExtension THEN {
IF verbose THEN MessageWindow.Append[Rope.Cat["Looking for prog=", extless, ", search=", ext], TRUE];
IF OK[ans ← TryExtensions[extless, TEditProfile.implExtensions]] THEN {ans.searchFor ← ext; RETURN};
};
};
IF (NOT hadDir) AND TEditProfile.tryVersionMap THEN {
IF verbose THEN MessageWindow.Append[Rope.Cat["Trying version maps for ", shortPart], TRUE];
IF OK[ans ← TryVersionMap[shortPart]] THEN NULL
ELSE IF NOT hasExtension THEN {
ans ← TryVersionMapExtensions[base, TEditProfile.sourceExtensions];
}
ELSE IF NOT standardExtension THEN {
IF OK[ans ← TryVersionMapExtensions[base, TEditProfile.implExtensions]] THEN ans.searchFor ← ext;
};
IF verbose THEN MessageWindow.Append[" .", FALSE];
};
}};
HasDir: PROC [rope: ROPE] RETURNS [has: BOOL] = {
FOR i: INT IN [0 .. rope.Length[]) DO
SELECT rope.Fetch[i] FROM
'[, '], '<, '>, '/ => RETURN [TRUE];
ENDCASE => NULL;
ENDLOOP;
has ← FALSE};
OK: PROC [ans: Answer] RETURNS [ok: BOOL] = {ok ← ans.fullFileName.Length[] # 0};
TryFull: PROC [fullFileName: ROPE] RETURNS [ans: Answer] = {
RETURN [IF Exists[fullFileName] THEN [fullFileName, NIL] ELSE [NIL, NIL]]};
TryExtensions: PROC [extless: ROPE, extensions: LIST OF ROPE] RETURNS [ans: Answer] = {
base: ROPE ~ extless.Concat["."];
ans ← [NIL, NIL];
FOR list: ROPEList ← extensions, list.rest UNTIL list=NIL DO
IF Exists[ans.fullFileName ← base.Concat[list.first]] THEN RETURN;
ENDLOOP;
ans.fullFileName ← NIL;
};
TryVersionMap: PROC [shortName: ROPE] RETURNS [ans: Answer] = {
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source];
ranges: VersionMap.RangeList ~ VersionMap.ShortNameToRanges[mapList, shortName];
bestName: ROPENIL;
bestDate: BasicTime.GMT ← BasicTime.nullGMT;
FOR list: VersionMap.RangeList ← ranges, list.rest UNTIL list=NIL DO
range: VersionMap.Range ← list.first;
WHILE range.len # 0 DO
fullName: ROPE;
created: BasicTime.GMT;
[name: fullName, created: created, next: range] ← VersionMap.RangeToEntry[range];
IF bestDate = BasicTime.nullGMT OR BasicTime.Period[from: bestDate, to: created] > 0 THEN {bestDate ← created; bestName ← fullName};
ENDLOOP;
ENDLOOP;
ans ← [bestName, NIL];
};
TryVersionMapExtensions: PROC [name: ROPE, extensions: ROPEList] RETURNS [ans: Answer] = {
base: ROPE ~ name.Concat["."];
FOR list: LIST OF ROPE ← extensions, list.rest UNTIL list=NIL DO
IF OK[ans ← TryVersionMap[base.Concat[list.first]]] THEN RETURN;
ENDLOOP;
ans ← [NIL, NIL];
};
From old GetDWIM ...
processDF, assocExts, followAttachments: BOOLEANFALSE;
DWIMit: PROC [orgName, wDir, contextFileName: ROPE, searchHack, isADfFileName: BOOL] RETURNS [fileName: ROPE, search: ROPE] =
BEGIN
Try: PROC [dir, short: ROPE, BaseMatch, FullMatch: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL]] RETURNS [stop: BOOL] = {
cp: FS.ComponentPositions;
expanded, dirPart: ROPE;
stop ← FALSE;
[expanded, cp, ] ← FS.ExpandName[dir.Concat[short], NIL];
dirPart ← expanded.Substr[start: 0, len: cp.base.start];
IF goalVersioned THEN {
IF expanded.Substr[start: cp.base.start, len: cp.ver.start+cp.ver.length - cp.base.start].Equal[s2: goalFull, case: FALSE] THEN stop ← FullMatch[dirPart, goalFull, NIL];
}
ELSE {
testBase: ROPE ← expanded.Substr[start: cp.base.start, len: cp.base.length];
testExt: ROPE ← expanded.Substr[start: cp.ext.start, len: cp.ext.length];
IF testBase.Equal[goalBase, FALSE] THEN {
stop ← IF testExt.Equal[goalExt, FALSE] THEN FullMatch[dirPart, goalFull, NIL] ELSE BaseMatch[dirPart, testBase, testExt]
};
};
stop ← stop;
};
FullMatch: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL] = {
IF (ext.Length[]=0 OR searchHack) AND Exists[fileName ← dir.Cat[file]] THEN {
search ← ext;
RETURN [found ← TRUE]};
stop ← stop;
};
BaseMatch: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL] = {
allButExt: ROPE ← dir.Cat[file];
stop ← FALSE;
IF goalExtended THEN {
IF assocExts AND Exists[fileName ← allButExt.Cat[".", goalExt]] THEN {
search ← NIL;
RETURN [found ← TRUE]};
IF searchHack AND RopeList.Memb[TEditProfile.implExtensions, ext, FALSE] AND Exists[fileName ← allButExt.Cat[".", ext]] THEN {
search ← goalExt;
RETURN [found ← TRUE]};
IF searchHack AND assocExts AND NOT allButExt.Equal[lastImplAssoced, FALSE] THEN {
lastImplAssoced ← allButExt;
FOR ifel: ROPEList ← TEditProfile.implExtensions, ifel.rest WHILE ifel # NIL DO
IF Exists[fileName ← allButExt.Cat[".", ifel.first]] THEN {
search ← goalExt;
RETURN [found ← TRUE]};
ENDLOOP;
};
};
IF NOT goalExtended THEN {
IF RopeList.Memb[TEditProfile.sourceExtensions, ext, FALSE] AND Exists[fileName ← allButExt.Cat[".", ext]] THEN {
search ← NIL;
RETURN [found ← TRUE]};
IF assocExts AND NOT allButExt.Equal[lastSourceAssoced, FALSE] THEN {
lastSourceAssoced ← allButExt;
FOR ifel: ROPEList ← TEditProfile.sourceExtensions, ifel.rest WHILE ifel # NIL DO
IF Exists[fileName ← allButExt.Cat[".", ifel.first]] THEN {
search ← NIL;
RETURN [found ← TRUE]};
ENDLOOP;
};
};
stop ← stop;
};
found: BOOLFALSE;
goalFull, goalExpanded, goalBase, goalExt: ROPE;
goalCP: FS.ComponentPositions;
goalExtended, goalVersioned: BOOL;
lastSourceAssoced, lastImplAssoced: ROPENIL;
goalFull ← orgName;
[goalExpanded, goalCP, ] ← FS.ExpandName[orgName];
goalBase ← goalExpanded.Substr[start: goalCP.base.start, len: goalCP.base.length];
goalExt ← goalExpanded.Substr[start: goalCP.ext.start, len: goalCP.ext.length];
goalExtended ← goalCP.ext.length # 0;
goalVersioned ← goalCP.ver.length # 0;
First, look around locally.
{
Consume: PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOLEAN] --FS.InfoProc-- = {
IF Try[NIL, fullFName, BaseMatch, FullMatch] THEN RETURN [FALSE];
IF followAttachments AND attachedTo # NIL AND Try[NIL, attachedTo, BaseMatch, FullMatch] THEN RETURN [FALSE];
};
IF assocExts AND followAttachments THEN {
IF verbose THEN MessageWindow.Append[Rope.Cat["Looking for attachments to follow from ", goalBase], TRUE];
FS.EnumerateForInfo[pattern: goalBase.Cat["!H"], proc: Consume, wDir: wDir !FS.Error => CONTINUE];
IF found THEN RETURN;
FS.EnumerateForInfo[pattern: goalBase.Cat[".*!H"], proc: Consume, wDir: wDir !FS.Error => CONTINUE];
IF found THEN RETURN;
}
ELSE {--redundant with first standard TryExtensions
EnumerateForInfo[base: goalBase, exts: TEditProfile.sourceExtensions, proc: Consume, wDir: wDir];
IF found THEN RETURN;
};
};
Next, try DF-file smarts.
IF contextFileName.Length[] # 0 THEN {
IF processDF AND isADfFileName THEN {
IF verbose THEN MessageWindow.Append["Trying DF smarts", TRUE];
[] ← Find[contextFileName, Try, BaseMatch, FullMatch];
IF verbose THEN MessageWindow.Append[" .", FALSE];
IF found THEN RETURN;
};
};
Lastly, give up.
fileName ← search ← NIL;
END;
EnumerateForInfo: PROC [base: ROPE, exts: ROPEList, proc: FS.InfoProc, wDir: ROPENIL] = {
FOR exts ← exts, exts.rest WHILE exts # NIL DO
fullFName, attachedTo: ROPE;
keep: CARDINAL;
bytes: INT;
created: BasicTime.GMT;
[fullFName:fullFName, attachedTo:attachedTo, keep:keep, bytes:bytes, created:created] ← FS.FileInfo[name: base.Cat[".", exts.first], remoteCheck: FALSE, wDir: wDir !FS.Error => LOOP];
IF NOT proc[fullFName:fullFName, attachedTo:attachedTo, keep:keep, bytes:bytes, created:created] THEN EXIT;
ENDLOOP;
wDir ← wDir;
};
Find: PROC
[
dfFileName: ROPE,
Try: PROC
[
dir, short: ROPE,
BaseMatch, FullMatch: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL]]
RETURNS [stop: BOOL],
BaseMatch, FullMatch: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL]]
RETURNS [stopped: BOOL] =
BEGIN
PerItem: PROC [item: REF ANY] RETURNS [stop: BOOLFALSE] --DFUtilities.ProcessItemProc-- =
BEGIN
WITH item SELECT FROM
di: REF DFUtilities.DirectoryItem => dir ← di.path1;
fi: REF DFUtilities.FileItem => IF Try[dir, fi.name, BaseMatch, FullMatch] THEN stop ← TRUE;
ii: REF DFUtilities.ImportsItem => {
IF Try[NIL, ii.path1, BaseMatch, FullMatch] THEN stop ← TRUE
ELSE {
tryThis: BOOL ← ii.list = NIL;
RightOn: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL] = {
tryThis ← stop ← TRUE;
};
Almost: PROC [dir, file, ext: ROPE] RETURNS [stop: BOOL] = {
IF stop ← assocExts THEN tryThis ← TRUE;
};
IF NOT tryThis THEN FOR i: NAT IN [0 .. ii.list.nEntries) WHILE NOT tryThis DO
[] ← Try["[]<>", ii.list[i].name, Almost, RightOn];
ENDLOOP;
IF tryThis THEN stop ← Find[ii.path1, Try, BaseMatch, FullMatch];
};
};
ii: REF DFUtilities.IncludeItem => stop ← Find[ii.path1, Try, BaseMatch, FullMatch];
c: REF DFUtilities.CommentItem => NULL;
w: REF DFUtilities.WhiteSpaceItem => NULL;
ENDCASE => ERROR;
stopped ← stop;
END;
from: IO.STREAMNIL;
dir: ROPENIL;
stopped ← FALSE;
from ← FS.StreamOpen[dfFileName !FS.Error => CONTINUE];
IF from = NIL THEN RETURN;
DFUtilities.ParseFromStream[in: from, proc: PerItem];
from.Close[];
END;
Tioga Helper
inPlace, doSearchHack: BOOLFALSE;
OldFNP: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE] ← NIL;
GetDWIM: PROC [orgName: ROPE, viewer: Viewer] RETURNS [fileName: ROPE, search: ROPE]--TEditOps.FileNameProc-- = {
contextFileName: ROPE = IF viewer # NIL THEN viewer.file ELSE NIL;
IF orgName.Length[] = 0 THEN RETURN [NIL, NIL];
SELECT orgName.Fetch[0] FROM
'[, '/, '<, '> => RETURN [NIL, NIL];
ENDCASE => NULL;
[fileName, search] ← ResolveHint[orgName, contextFileName, doSearchHack, FALSE].ans;
IF fileName = NIL AND OldFNP # NIL THEN [fileName, search] ← OldFNP[orgName, viewer];
};
Common Utoolities
verbose: BOOLTRUE;
Exists: PROC [fileName: ROPE] RETURNS [exists: BOOLEAN] =
BEGIN
exists ← TRUE;
[] ← FS.FileInfo[name: fileName, remoteCheck: FALSE ! FS.Error => {exists ← FALSE; CONTINUE}];
exists ← exists; --wouldn't it be nice if we could really break on exit?
END;
NoticeProfileChanges: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- =
BEGIN
doSearchHack ← UserProfile.Boolean["FileDWIM.DoSearchHack", FALSE];
processDF ← UserProfile.Boolean["FileDWIM.ProcessDF", TRUE];
assocExts ← UserProfile.Boolean["FileDWIM.AssociateExtensions", TRUE];
followAttachments ← UserProfile.Boolean["FileDWIM.FollowAttachments", TRUE];
verbose ← UserProfile.Boolean["FileDWIM.Verbose", FALSE];
IF (processDF OR assocExts) AND NOT inPlace THEN
BEGIN
inPlace ← TRUE;
OldFNP ← TEditOps.ReplaceFileNameProc[GetDWIM];
END;
END;
UserProfile.CallWhenProfileChanges[NoticeProfileChanges];
END.