GetDWIM.Mesa
Last Edited by: Spreitzer, October 2, 1985 9:19:42 pm PDT
DIRECTORY Basics, BasicTime, DFOperations, DFUtilities, FS, IO, List, ProcessProps, RedBlackTree, Rope, TEditOps, UserProfile, ViewerClasses, ViewerTools;
GetDWIM:
CEDAR
PROGRAM
IMPORTS DFUtilities, FS, IO, RedBlackTree, Rope, TEditOps, UserProfile, ViewerTools =
BEGIN
ROPE: TYPE = Rope.ROPE;
ROPEList: TYPE = LIST OF ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
inPlace, processDF, assocExts, followAttachments, tryRenaming: BOOLEAN ← FALSE;
OldFNP: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE] ← NIL;
log: IO.STREAM ← NIL;
Debug: PROC = {log ← IO.ROS[]};
DontDebug: PROC = {log ← NIL};
GetLog: PROC RETURNS [r: ROPE] = {r ← log.RopeFromROS[]};
sourceFileExtensions, implFileExtensions: ROPEList ← NIL;
DWIMit:
PROC [orgName:
ROPE, viewer: Viewer]
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 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 Member[ext, implFileExtensions]
AND Exists[fileName ← allButExt.Cat[".", ext]]
THEN {
search ← goalExt;
RETURN [found ← TRUE]};
IF assocExts
AND
NOT allButExt.Equal[lastImplAssoced,
FALSE]
THEN {
lastImplAssoced ← allButExt;
FOR ifel: ROPEList ← implFileExtensions, 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 Member[ext, sourceFileExtensions]
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 ← sourceFileExtensions, ifel.rest
WHILE ifel #
NIL
DO
IF Exists[fileName ← allButExt.Cat[".", ifel.first]]
THEN {
search ← goalExt;
RETURN [found ← TRUE]};
ENDLOOP;
};
};
stop ← stop;
};
found: BOOL ← FALSE;
goalFull, goalExpanded, goalBase, goalExt: ROPE;
goalCP: FS.ComponentPositions;
goalExtended, goalVersioned: BOOL;
wDir: ROPE ← IF viewer # NIL THEN TEditOps.WorkingDirectoryFromViewer[viewer] ELSE NIL;
lastSourceAssoced, lastImplAssoced: ROPE ← NIL;
dotPos: INT;
mesaNote: MesaNote;
First, see if it's from a renaming OPEN.
IF tryRenaming
AND (dotPos ← orgName.Find["."]) >= 0
AND (mesaNote ← GetMesaNote[viewer]) #
NIL
THEN {
short: ROPE ← orgName.Substr[len: dotPos];
rest: ROPE ← orgName.Substr[start: dotPos];
found: BOOL ← FALSE;
FOR ml: MapList ← mesaNote.maps, ml.rest
WHILE ml #
NIL
AND
NOT found
DO
IF ml.first.short.Equal[short]
THEN {
found ← TRUE;
orgName ← ml.first.long.Concat[rest];
};
ENDLOOP;
};
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;
Next, 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 {
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 {
EnumerateForInfo[base: goalBase, exts: sourceFileExtensions, proc: Consume, wDir: wDir];
IF found THEN RETURN;
};
};
Next, try DF-file smarts.
IF viewer #
NIL
THEN
BEGIN
IF processDF
AND IsADFFileName[viewer.file]
THEN
BEGIN
[] ← Find[viewer.file, Try, BaseMatch, FullMatch];
IF found THEN RETURN;
END;
END;
Lastly, try whatever we replaced.
IF OldFNP # NIL THEN [fileName, search] ← OldFNP[orgName, viewer]
ELSE {fileName ← search ← NIL};
END;
EnumerateForInfo:
PROC [base:
ROPE, exts: ROPEList, proc:
FS.InfoProc, wDir:
ROPE ←
NIL] = {
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:
BOOL ←
FALSE]
--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.STREAM ← NIL;
dir: ROPE ← NIL;
stopped ← FALSE;
from ← FS.StreamOpen[dfFileName !FS.Error => CONTINUE];
IF from = NIL THEN RETURN;
DFUtilities.ParseFromStream[in: from, proc: PerItem];
from.Close[];
END;
Member:
PROC [r:
ROPE, in: ROPEList]
RETURNS [m:
BOOL] = {
FOR in ← in, in.rest
WHILE in #
NIL
DO
IF in.first.Equal[r, FALSE] THEN RETURN [TRUE];
ENDLOOP;
m ← FALSE};
IsADFFileName:
PROC [name:
ROPE]
RETURNS [isa:
BOOLEAN] =
{isa ← name.Find[s2: ".DF!", case: FALSE] >= 0};
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;
MesaNote: TYPE = REF MesaNoteRep;
MesaNoteRep:
TYPE =
RECORD [
name: ROPE,
maps: MapList
];
MapList: TYPE = LIST OF Map;
Map: TYPE = RECORD [short, long: ROPE];
mesaNotes: RedBlackTree.Table ← RedBlackTree.Create[GetMNKey, CompareMNs];
FlushMesaCache:
PROC = {
mesaNotes ← RedBlackTree.Create[GetMNKey, CompareMNs];
};
GetMesaNote:
PROC [v: Viewer]
RETURNS [mn: MesaNote] = {
vFull: ROPE ← NIL;
vcp: FS.ComponentPositions;
ext, name: ROPE;
IF v = NIL THEN RETURN [NIL];
[vFull, vcp] ← FS.ExpandName[v.name !FS.Error => CONTINUE];
IF vFull = NIL THEN RETURN [NIL];
ext ← vFull.Substr[start: vcp.ext.start, len: vcp.ext.length];
IF NOT (ext.Equal["mesa", FALSE] OR ext.Equal["cedar", FALSE]) THEN RETURN [NIL];
name ← vFull.Substr[len: vcp.ext.start + vcp.ext.length];
mn ← NARROW[mesaNotes.Lookup[name]];
IF mn # NIL THEN RETURN;
mn ←
NEW [MesaNoteRep ← [
name: name,
maps: AnalyzeMesa[ViewerTools.GetContents[v]]
]];
mesaNotes.Insert[mn, mn.name];
};
AnalyzeMesa:
PROC [module:
ROPE]
RETURNS [maps: MapList] = {
ENABLE IO.Error => CONTINUE;
Work:
PROC [intro:
ROPE] = {
start: INT = module.Find[intro];
in: IO.STREAM;
toke: ROPE;
IF start < 0 THEN RETURN;
in ← IO.RIS[module.Substr[start: start]];
toke ← in.GetCedarTokenRope[].token;
IF toke.Equal[intro]
THEN {
FOR toke ← in.GetCedarTokenRope[].token, in.GetCedarTokenRope[].token
DO
short, long: ROPE;
IF toke.Equal[";"] OR toke.Equal["}"] OR toke.Equal["END"] OR toke.Equal["="] OR toke.Equal["EXPORTS"] OR toke.Equal["SHARES"] THEN EXIT;
short ← toke;
toke ← in.GetCedarTokenRope[].token;
IF toke.Equal[","] THEN LOOP;
IF NOT toke.Equal[":"] THEN EXIT;
long ← in.GetCedarTokenRope[].token;
maps ← CONS[[short, long], maps];
toke ← in.GetCedarTokenRope[].token;
IF NOT toke.Equal[","] THEN EXIT;
ENDLOOP;
};
in.Close[];
};
maps ← NIL;
Work["IMPORTS"];
Work["OPEN"];
};
GetMNKey:
PROC [data:
REF
ANY]
RETURNS [key:
ROPE] = {
mn: MesaNote = NARROW[data];
RETURN [mn.name];
};
CompareMNs:
PROC [k, data:
REF
ANY]
RETURNS [Basics.Comparison] = {
k1: ROPE = NARROW[k];
k2: ROPE = GetMNKey[data];
RETURN [k1.Compare[k2]];
};
NoticeProfileChanges:
PROC [reason: UserProfile.ProfileChangeReason]
--UserProfile.ProfileChangedProc-- =
BEGIN
tryRenaming ← UserProfile.Boolean["GetDWIM.TryRenaming", TRUE];
processDF ← UserProfile.Boolean["GetDWIM.ProcessDF", TRUE];
assocExts ← UserProfile.Boolean["GetDWIM.AssociateExtensions", TRUE];
followAttachments ← UserProfile.Boolean["GetDWIM.FollowAttachments", TRUE];
sourceFileExtensions ← UserProfile.ListOfTokens["SourceFileExtensions", LIST["mesa", "tioga", "df", "cm", "config", "style"]];
implFileExtensions ← UserProfile.ListOfTokens["ImplFileExtensions", LIST["mesa", "cedar"]];
IF (processDF
OR assocExts)
AND
NOT inPlace
THEN
BEGIN
inPlace ← TRUE;
OldFNP ← TEditOps.ReplaceFileNameProc[DWIMit];
END;
END;
UserProfile.CallWhenProfileChanges[NoticeProfileChanges];
END.