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: BOOLEANFALSE;
OldFNP: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE] ← NIL;
log: IO.STREAMNIL;
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: BOOLFALSE;
goalFull, goalExpanded, goalBase, goalExt: ROPE;
goalCP: FS.ComponentPositions;
goalExtended, goalVersioned: BOOL;
wDir: ROPEIF viewer # NIL THEN TEditOps.WorkingDirectoryFromViewer[viewer] ELSE NIL;
lastSourceAssoced, lastImplAssoced: ROPENIL;
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: BOOLFALSE;
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: 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;
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: ROPENIL;
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.