FileDWIMImpl.mesa
Copyright Ó 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on September 15, 1992 1:02 pm PDT
Willie-s, April 3, 1992 5:24 pm PST
DIRECTORY BasicTime, Commander, CommanderOps, Convert, DFCachingUtilities, DFUtilities, FileDWIM, IO, MorePfsNames, PFS, PFSNames, RefTab, RefText, Rope, RopeList, RopeParts, SimpleFeedback, SymTab, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults, ViewerClasses;
FileDWIMImpl:
CEDAR
PROGRAM
IMPORTS BasicTime, Commander, CommanderOps, Convert, DFCachingUtilities, IO, MorePfsNames, PFS, PFSNames, RefText, Rope, RopeList, RopeParts, SimpleFeedback, SymTab, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults
EXPORTS FileDWIM
=
BEGIN OPEN FileDWIM, MPfsN:MorePfsNames, RP:RopeParts;
RopePart: TYPE = RP.RopePart;
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:
ROPE ¬
NIL, searchHack:
BOOL ¬
FALSE, tryAll:
BOOL ¬
TRUE]
RETURNS [ans: Answer] = {
ans ¬ ProtectedResolveHint[hint, contextFileName, searchHack, tryAll !PFS.Error => GOTO Bombed];
EXITS Bombed => ans ¬ [NIL]};
ProtectedResolveHint:
PROC [hint, contextFileName:
ROPE, searchHack, tryAll:
BOOL]
RETURNS [ans: Answer] = {
hintPath: PFS.PATH ~ PFS.PathFromRope[hint];
contextPath: PFS.PATH ~ PFS.PathFromRope[contextFileName];
contextShortName: ROPE ~ IF contextPath.ComponentCount[] > 0 THEN contextPath.ShortName[].ComponentRope[--exclude version--] ELSE NIL;
isADfFileName: BOOL ~ Rope.Match["*.df", contextShortName, FALSE];
wDir: PFS.PATH ~ IF contextFileName#NIL THEN contextPath.Directory[] ELSE PFS.GetWDir[];
InWDir:
PROC ~ {
wDirName: ROPE ~ PFS.RopeFromPath[PFS.GetWDir[]];
fullHint: PFS.PATH ~ PFS.AbsoluteName[hintPath];
fullHintName: ROPE ~ PFS.RopeFromPath[fullHint];
IF OK[ans ¬ TryFull[fullHint]] THEN RETURN;
{hadDir: BOOL = hintPath.ComponentCount[] > 1;
hintShort: PFSNames.Component ~ fullHint.ShortName[];
hintExtParsed: PFS.PATH ~ ParseForExtension[hintShort];
hasExt: BOOL = hintExtParsed.ComponentCount[] > 1;
hasVersion: BOOL = hintShort.version.versionKind # none;
base: PFS.PATH = hintExtParsed.SubName[count: 1];
extComp: PFSNames.Component = FetchOrNil[hintExtParsed, 1];
ext: ROPE ~ extComp.ComponentRope[];
hintDir: PFS.PATH ~ fullHint.Directory[];
extless: PFS.PATH = hintDir.Cat[base];
extlessName: ROPE ~ PFS.RopeFromPath[extless];
dwimAns: PFS.PATH;
standardExtension: BOOL ¬ FALSE;
IF hasExt
OR hasVersion
OR
NOT tryAll
THEN
NULL
ELSE {
IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Concat["Trying source extensions to ", extlessName]];
IF OK[ans ¬ TryExtensions[fullHint, TEditProfile.sourceExtensions]] THEN RETURN;
};
[dwimAns, ans.searchFor, ans.line, ans.char] ¬ DWIMit[hintPath, NIL, contextPath, searchHack, isADfFileName];
IF dwimAns#NIL THEN {ans.fullFileName ¬ PFS.RopeFromPath[dwimAns]; RETURN};
IF hasVersion OR NOT tryAll THEN RETURN;
IF searchHack
AND hasExt
THEN {
standardExtension ¬ RopeList.Memb[TEditProfile.sourceExtensions, ext, FALSE];
IF
NOT standardExtension
THEN {
IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Cat["Looking for prog=", extlessName, ", search=", ext]];
IF OK[ans ¬ TryExtensions[extless, TEditProfile.implExtensions]] THEN {ans.searchFor ¬ ext; RETURN};
};
};
IF (
NOT hadDir)
AND TEditProfile.tryVersionMap
THEN {
baseName: ROPE ~ PFS.RopeFromPath[base];
IF verbose THEN SimpleFeedback.Append[$FileDWIM, begin, $FYI, Rope.Concat["Trying version maps for ", baseName]];
SELECT
TRUE
FROM
NOT hasExt => ans ¬ TryVersionMapExtensions[baseName, TEditProfile.sourceExtensions];
ENDCASE => {
asked: PFSNames.Component ~ StripComponentVersion[hintShort];
ans ¬ TryVersionMapExtensions[baseName, CONS[ext, IF NOT searchHack THEN NIL ELSE IF standardExtension THEN TEditProfile.sourceExtensions ELSE TEditProfile.implExtensions]];
IF OK[ans] AND searchHack AND NOT PFSNames.EqualComponents[StripComponentVersion[PFS.PathFromRope[ans.fullFileName].ShortName[]], asked, FALSE] THEN ans.searchFor ¬ ext};
IF verbose THEN SimpleFeedback.Append[$FileDWIM, end, $FYI, " ."];
};
}};
ans ¬ [NIL];
PFS.DoInWDir[wDir, InWDir];
RETURN};
OK: PROC [ans: Answer] RETURNS [ok: BOOL] = {ok ¬ ans.fullFileName.Length[] # 0};
TryFull:
PROC [fullFileName:
PFS.
PATH]
RETURNS [ans: Answer] = {
RETURN [IF Exists[fullFileName] THEN [PFS.RopeFromPath[fullFileName]] ELSE [NIL]]};
TryExtensions:
PROC [extless:
PFS.
PATH, extensions:
LIST
OF
ROPE]
RETURNS [ans: Answer] = {
ans ¬ [NIL];
FOR list: ROPEList ¬ extensions, list.rest
UNTIL list=
NIL
DO
extended: PFS.PATH ~ AddExtension[extless, list.first];
IF Exists[extended]
THEN {
ans.fullFileName ¬ PFS.RopeFromPath[extended];
RETURN};
ENDLOOP;
RETURN};
TryVersionMap:
PROC [shortName:
ROPE]
RETURNS [ans: Answer] = {
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source];
ranges: VersionMap.RangeList ~ VersionMap.ShortNameToRanges[mapList, shortName];
bestName: ROPE ¬ NIL;
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];
};
TryVersionMapExtensions:
PROC [name:
ROPE, extensions: ROPEList]
RETURNS [ans: Answer] = {
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source];
ans ¬ [VersionMap.LookupByExtension[mapList, name, extensions], NIL];
RETURN};
From old GetDWIM ...
processDF, assocExts, followAttachments: BOOLEAN ¬ FALSE;
BaseMatchTester: TYPE ~ PROC [dir: PFS.PATH, file: PFSNames.Component, ext: ROPE] RETURNS [stop: BOOL];
FullMatchTester: TYPE ~ PROC [dir: PFS.PATH, nonDir: PFSNames.Component] RETURNS [stop: BOOL];
DWIMit:
PROC [orgName, wDir, contextFileName:
PFS.
PATH, searchHack, isADfFileName:
BOOL]
RETURNS [fileName:
PFS.
PATH, search:
ROPE ¬
NIL, line, char:
INT ¬ -1] = {
Try:
PROC [dir, short:
PFS.
PATH,
BaseMatch: BaseMatchTester,
FullMatch: FullMatchTester]
RETURNS [stop:
BOOL] = {
testFull: PFS.PATH ~ PFS.AbsoluteName[short, dir];
testDir: PFS.PATH ~ testFull.Directory[];
testShort: PFSNames.Component ~ testFull.ShortName[];
stop ¬ FALSE;
IF goalVersioned
THEN {
IF testShort.EqualComponents[c2: goalNonDir, case: FALSE] THEN stop ¬ FullMatch[testDir, goalNonDir];
}
ELSE {
extParsed: PFS.PATH ~ ParseForExtension[testShort];
testBase: PFSNames.Component ~ extParsed.Fetch[0];
testExt: ROPE ~ FetchOrNil[extParsed, 1].ComponentRope[];
IF testBase.EqualComponents[goalBase,
FALSE]
THEN {
stop ¬ IF testExt.Equal[goalExt, FALSE] THEN FullMatch[testDir, goalNonDir] ELSE BaseMatch[testDir, testBase, testExt]
};
};
stop ¬ stop;
};
FullMatch:
PROC [dir:
PFS.
PATH, nonDir: PFSNames.Component]
RETURNS [stop:
BOOL] = {
IF Exists[fileName ¬ ExtendPath[dir, nonDir]]
THEN {
search ¬ NIL;
RETURN [found ¬ TRUE]};
stop ¬ FALSE;
};
BaseMatch:
PROC [dir:
PFS.
PATH, file: PFSNames.Component, ext:
ROPE]
RETURNS [stop:
BOOL] = {
allButExt: PFS.PATH ~ ExtendPath[dir, file];
stop ¬ FALSE;
IF goalExtended
THEN {
IF assocExts
AND Exists[fileName ¬ AddExtension[allButExt, goalExt]]
THEN {
search ¬ NIL;
RETURN [found ¬ TRUE]};
IF searchHack
AND RopeList.Memb[TEditProfile.implExtensions, ext,
FALSE]
AND Exists[fileName ¬ AddExtension[allButExt, 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 ¬ AddExtension[allButExt, ifel.first]]
THEN {
search ¬ goalExt;
RETURN [found ¬ TRUE]};
ENDLOOP;
};
};
IF
NOT goalExtended
THEN {
IF RopeList.Memb[TEditProfile.sourceExtensions, ext,
FALSE]
AND Exists[fileName ¬ AddExtension[allButExt, 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
NOT ifel.first.Equal[ext]
AND Exists[fileName ¬ AddExtension[allButExt, ifel.first]]
THEN {
search ¬ NIL;
RETURN [found ¬ TRUE]};
ENDLOOP;
};
};
stop ¬ stop;
};
SeekTags:
PROC [short, item:
ROPE]
RETURNS [
BOOL] ~ {
FOR tfs: Tagfiles ¬ tagfiles, tfs.rest
WHILE tfs#
NIL
DO
tf: Tagfile ~ tfs.first;
f: TaggedFile ¬ NARROW[tf.files.Fetch[short].val];
IF f#
NIL
THEN {
t: Tags ¬
IF f.items#
NIL
THEN
NARROW[f.items.Fetch[item].val]
ELSE NIL;
fileName ¬ f.name;
IF item=NIL THEN search ¬ NIL
ELSE IF t=NIL THEN LOOP
ELSE {search ¬ item; line ¬ t.line; char ¬ t.char};
RETURN [TRUE]};
ENDLOOP;
RETURN [FALSE]};
SeekExtTags:
PROC [short, item:
ROPE]
RETURNS [
BOOL] ~ {
FOR ifel: ROPEList ¬ TEditProfile.sourceExtensions, ifel.rest
WHILE ifel #
NIL
DO
IF SeekTags[short.Cat[".", ifel.first], item] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]};
found, resolved: BOOL ¬ FALSE;
goalExpanded, goalExtParsed: PFS.PATH;
goalShort, goalBase, goalNonDir: PFSNames.Component;
goalExt, goalBaseRope, goalShortRope: ROPE;
goalExtended, goalVersioned: BOOL;
lastSourceAssoced, lastImplAssoced: PFS.PATH ¬ NIL;
goalExpanded ¬ PFS.AbsoluteName[orgName, wDir];
goalShort ¬ goalExpanded.ShortName[];
goalExtParsed ¬ ParseForExtension[goalShort];
goalBase ¬ goalExtParsed.Fetch[0];
goalExt ¬ FetchOrNil[goalExtParsed, 1].ComponentRope[];
goalNonDir ¬ StripComponentVersion[goalShort];
goalExtended ¬ goalExtParsed.ComponentCount[] > 1;
goalVersioned ¬ goalShort.version.versionKind # none;
First, look around locally.
{
Consume:
PROC [fullFName, attachedTo:
PFS.
PATH, uniqueID:
PFS.UniqueID, bytes:
INT, mutability:
PFS.Mutability, fileType:
PFS.FileType]
RETURNS [continue:
BOOLEAN]
--FS.InfoProc-- = {
IF Try[NIL, fullFName, BaseMatch, FullMatch] THEN RETURN [FALSE];
IF followAttachments AND attachedTo#NIL AND attachedTo#fullFName AND Try[NIL, GuessView[attachedTo], BaseMatch, FullMatch] THEN RETURN [FALSE];
continue ¬ TRUE;
};
IF resolved THEN PFS.EnumerateForInfo[pattern: goalExpanded, proc: Consume !PFS.Error => CONTINUE];
IF found THEN RETURN;
IF assocExts
AND followAttachments
THEN {
goalOhneExt: PFS.PATH ~ goalExpanded.ReplaceShortName[goalBase];
IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Concat["Looking for attachments to follow from ", PFS.RopeFromPath[goalOhneExt]]];
PFS.EnumerateForInfo[pattern: goalOhneExt.SetVersionNumber[[highest]], proc: Consume !PFS.Error => CONTINUE];
IF found THEN RETURN;
PFS.EnumerateForInfo[pattern: goalExpanded.ReplaceShortName[[name: [goalBase.ComponentRope[--exclude version--].Concat[".*"], 0, NAT.LAST], version: [highest]]], proc: Consume !PFS.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.ComponentCount[] # 0
THEN {
IF processDF
AND isADfFileName
THEN {
Find[contextFileName, Try, BaseMatch, FullMatch];
IF found THEN RETURN;
};
};
Next, try tags files.
goalShortRope ¬ goalShort.ComponentRope[];
IF SeekTags[goalShortRope, NIL] THEN RETURN;
IF goalExtended
THEN {
goalBaseRope ¬ goalBase.ComponentRope[];
IF SeekTags[goalBaseRope, goalExt] THEN RETURN;
IF SeekExtTags[goalBaseRope, goalExt] THEN RETURN};
IF SeekExtTags[goalShortRope, NIL] THEN RETURN;
Lastly, give up.
fileName ¬ NIL;
search ¬ NIL;
line ¬ char ¬ -1;
RETURN};
uxC: PFSNames.Component ~ MPfsN.ConsComponent[RP.Make["-ux"]];
vuxC: PFSNames.Component ~ MPfsN.ConsComponent[RP.Make["-vux"]];
GuessView:
PROC [path:
PFS.
PATH]
RETURNS [
PFS.
PATH] ~ {
lastC: PFSNames.Component ~ path.ShortName[];
IF path.ComponentCount[]<2 OR lastC.version#[none] OR NOT uxC.EqualComponents[path.Fetch[0]] THEN RETURN [path];
{short: RopePart ~ MPfsN.ComponentName[lastC];
dotPos: INT ~ short.FindBackward[RP.Make["."]];
len: INT ~ short.Length[];
IF dotPos>0
AND dotPos+3<len
AND short.InlineFetch[dotPos+1]='~
AND short.InlineFetch[len-1]='~
AND short.SkipOver[dotPos+2,
RP.Make["1234567890"]]=len-1
THEN {
newC: PFSNames.Component ~ MPfsN.ConsComponent[short.Substr[len: dotPos], [numeric, Convert.IntFromRope[short.Substr[start: dotPos+2, len: len-dotPos-3].ToRope[]]] ];
RETURN path.ReplaceShortName[newC].ReplaceComponent[0, vuxC]};
RETURN [path]}};
Find:
PROC
[
dfFileName: PFS.PATH,
Try:
PROC
[
dir, short: PFS.PATH,
BaseMatch: BaseMatchTester,
FullMatch: FullMatchTester]
RETURNS [stop: BOOL],
BaseMatch: BaseMatchTester,
FullMatch: FullMatchTester]
=
BEGIN
stack: LIST OF ROPE ¬ LIST["Trying DF smarts"];
PerItem:
PROC [item:
REF
ANY]
RETURNS [stop, clip, dontCache:
BOOL ¬
FALSE]
--DFUtilities.ProcessItemProc-- =
BEGIN
WITH item
SELECT
FROM
di: REF DFUtilities.DirectoryItem => dir ¬ PFS.PathFromRope[di.path1];
fi: REF DFUtilities.FileItem => IF Try[dir, PFS.PathFromRope[fi.name], BaseMatch, FullMatch] THEN stop ¬ TRUE;
ni:
REF DFCachingUtilities.NestItem =>
SELECT ni.bracket
FROM
begin => {
dfPath: PFS.PATH ~ PFS.PathFromRope[ni.df.name];
IF pacifyDFEnumeration
THEN {
dfShort: PFSNames.Component ~ dfPath.ShortName[];
dfExtParsed: PFS.PATH ~ ParseForExtension[dfShort];
base: ROPE ~ dfExtParsed.Fetch[0].ComponentRope[];
this: ROPE ~ Rope.Concat[" ", base];
stack ¬ CONS[Rope.Concat[stack.first, this], stack];
SimpleFeedback.Append[$FileDWIM, middle, $FYI, this];
};
IF Try[NIL, dfPath, BaseMatch, FullMatch] THEN stop ¬ TRUE
ELSE {
tryThis: BOOL ¬ ni.filter.file.files=NIL;
RightOn:
PROC [dir:
PFS.
PATH, nonDir: PFSNames.Component]
RETURNS [stop:
BOOL] = {
tryThis ¬ stop ¬ TRUE;
};
Almost:
PROC [dir:
PFS.
PATH, file: PFSNames.Component, ext:
ROPE]
RETURNS [stop:
BOOL] = {
IF stop ¬ assocExts THEN tryThis ¬ TRUE;
};
TryPair:
PROC [key:
ROPE, val:
REF
ANY]
RETURNS [quit:
BOOL] ~ {
quit ¬ Try[NIL, PFS.PathFromRope[key], Almost, RightOn];
RETURN};
IF NOT tryThis THEN [] ¬ ni.filter.file.files.Pairs[TryPair];
clip ¬ NOT tryThis;
};
};
end => {
IF pacifyDFEnumeration
THEN {
stack ¬ stack.rest;
SimpleFeedback.Append[$FileDWIM, begin, $FYI, stack.first];
};
};
ENDCASE => ERROR;
ii: REF DFUtilities.ImportsItem => NULL;
ii: REF DFUtilities.IncludeItem => NULL;
c: REF DFUtilities.CommentItem => NULL;
w: REF DFUtilities.WhiteSpaceItem => NULL;
ENDCASE => ERROR;
RETURN;
END;
cache: DFCachingUtilities.EnumerationCache ~ DFCachingUtilities.CreateEnumerationCache[];
dir: PFS.PATH ¬ NIL;
IF verbose OR pacifyDFEnumeration THEN SimpleFeedback.Append[$FileDWIM, begin, $FYI, "Trying DF smarts"];
[] ¬ DFCachingUtilities.EnumerateDFContents[
df: [PFS.RopeFromPath[dfFileName]],
Consumer: PerItem,
cache: cache
! DFCachingUtilities.FSErrorOnDF => {
IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $Error, IO.PutFR["FS.Error[%g, %g]", [atom[error.code]], [rope[error.explanation]] ]];
RESUME};
DFCachingUtilities.Miss => RESUME;
DFCachingUtilities.SyntaxError => {
IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $Error, IO.PutFR["Syntax error (%g) at %g in %g", [rope[reason]], [integer[position]], [rope[dfFileName]] ]];
RESUME}
];
IF verbose OR pacifyDFEnumeration THEN SimpleFeedback.Append[$FileDWIM, end, $FYI, " ."];
END;
Tags Files
Tagfiles: TYPE ~ LIST OF Tagfile;
Tagfile: TYPE ~ REF TagfilePrivate;
TagfilePrivate:
TYPE ~
RECORD [
name: PFS.PATH,
uid: PFS.UniqueID,
files: SymTab.Ref--short name -> TaggedFile--
];
TaggedFile: TYPE ~ REF TaggedFilePrivate;
TaggedFilePrivate:
TYPE ~
RECORD [
name: PFS.PATH,
items: SymTab.Ref--name -> tags--
];
Tags: TYPE ~ REF TagsPrivate;
TagsPrivate: TYPE ~ RECORD [line, char: INT];
tagAnalyses: SymTab.Ref--tag file name -> Tagfile-- ~ SymTab.Create[];
tagfiles: Tagfiles ¬ NIL;
GetTagfiles:
PROC [filenames: ROPEList]
RETURNS [Tagfiles] ~ {
IF filenames=NIL THEN RETURN [NIL];
{tf1: Tagfile ~ GetTagfile[filenames.first];
RETURN [CONS[tf1, GetTagfiles[filenames.rest]]]}};
GetTagfile:
PROC [name:
ROPE]
RETURNS [tf: Tagfile] ~ {
path: PFS.PATH ¬ NIL;
in: IO.STREAM ¬ NIL;
uid: PFS.UniqueID ¬ PFS.nullUniqueID;
version: ROPE;
path ¬
PFS.PathFromRope[name !
PFS.Error => {
SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.PathFromRope[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]];
CONTINUE}];
IF path=NIL THEN RETURN [NIL];
tf ¬ NARROW[tagAnalyses.Fetch[name].val];
uid ¬
PFS.FileInfo[path !
PFS.Error => {
SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.FileInfo[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]];
CONTINUE}].uniqueID;
IF tf#NIL AND tf.uid=uid THEN RETURN;
tf ¬
NEW[TagfilePrivate ¬ [
name: path,
uid: uid,
files: SymTab.Create[] ]];
[] ¬ tagAnalyses.Store[name, tf];
in ¬
PFS.StreamOpen[path !
PFS.Error => {
SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.StreamOpen[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]];
CONTINUE}];
IF in=NIL THEN RETURN;
{ENABLE UNWIND => in.Close[abort: TRUE];
version ¬ in.GetTokenRope[FtagsToken].token;
SELECT
TRUE
FROM
version.Equal["<<FTAGS-1.0>>"] => ParseFtags1[PFSNames.Directory[path], name, tf, in];
ENDCASE => SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Unknown tags format %g in file %g", LIST[ [rope[version]], [rope[name]] ]];
};
in.Close[];
RETURN};
ParseFtags1:
PROC [wDir:
PFS.
PATH, name:
ROPE, tf: Tagfile, in:
IO.
STREAM] ~ {
taggedName: ROPE;
taggedPath: PFS.PATH;
taggedShort: ROPE;
db: INT;
f: TaggedFile;
buff: REF TEXT ¬ RefText.ObtainScratch[100];
NextNot:
PROC [e:
CHAR]
RETURNS [
BOOL] ~ {
c: CHAR ~ in.GetChar[];
IF c=e THEN RETURN [FALSE];
RefText.ReleaseScratch[buff];
SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Syntax error at %g in %g: '\\%03b expected, '\\%03b found", LIST[ [integer[in.GetIndex[]-1]], [rope[name]], [cardinal[e.ORD]], [cardinal[c.ORD]] ]];
RETURN [TRUE]};
IF NOT debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $FYI, "Reading %g.", [rope[name]] ];
{
WHILE
NOT in.EndOf[]
DO
IF in.PeekChar[]='<
THEN {
idx: INT ~ in.GetIndex[];
v2: ROPE ~ in.GetTokenRope[FtagsToken].token;
IF
NOT v2.Equal["<<FTAGS-1.0>>"]
THEN {
SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Syntax error at %g in %g: got unrecognized (%q) version, not <<FTAGS-1.0>>", LIST[ [integer[idx]], [rope[name]], [rope[v2]] ]];
RefText.ReleaseScratch[buff];
RETURN};
};
IF NextNot['\014] THEN RETURN;
taggedName ¬ in.GetTokenRope[IO.IDProc].token;
IF NextNot[',] THEN RETURN;
db ¬ in.GetInt[];
IF NextNot['\l] THEN RETURN;
taggedPath ¬ PFS.AbsoluteName[PFS.PathFromRope[taggedName], wDir];
taggedShort ¬ taggedPath.ShortNameRope[];
f ¬
NEW[TaggedFilePrivate ¬ [
name: taggedPath,
items: NIL ]];
[] ¬ tf.files.Store[taggedShort, f];
IF debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $Debug, "Noting %g", [rope[taggedShort]] ];
IF readFtagsItems
THEN {
f.items ¬ SymTab.Create[];
DO
item: ROPE;
tags: Tags;
IF in.EndOf[] THEN GOTO Done;
SELECT in.PeekChar[]
FROM
'\014, '< => EXIT;
ENDCASE => NULL;
item ¬ in.GetTokenRope[FtagsToken].token;
tags ¬ NEW [TagsPrivate];
IF NextNot['\177] THEN RETURN;
[] ¬ in.GetToken[FtagsToken, buff];
IF NextNot['\177] THEN RETURN;
tags.line ¬ in.GetInt[];
IF NextNot[',] THEN RETURN;
tags.char ¬ in.GetInt[];
IF NextNot['\012] THEN RETURN;
[] ¬ f.items.Store[item, tags];
ENDLOOP;
}
ELSE {
wuz: INT ~ in.GetIndex[];
in.SetIndex[wuz+db];
};
ENDLOOP;
EXITS Done => NULL};
RefText.ReleaseScratch[buff];
IF NOT debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $FYI, "Done reading %g.", [rope[name]] ];
RETURN};
debugParseFtags: BOOL ¬ FALSE;
readFtagsItems: BOOL ¬ FALSE;
FtagsToken:
PROC [char:
CHAR]
RETURNS [
IO.CharClass]
--IO.BreakProc--
~ {
SELECT char
FROM
'\177, '\012, '\014 => RETURN [break];
ENDCASE => RETURN [other]};
Tioga Helper
inPlace, doSearchHack: BOOL ¬ FALSE;
OldFNP: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE] ¬ NIL;
GetDWIM:
PROC [orgName:
ROPE, viewer: Viewer]
RETURNS [fileName:
ROPE ¬
NIL, search:
ROPE]
--TEditOps.FileNameProc-- = {
IF orgName.Length[]=0 THEN RETURN [NIL, NIL];
{orgPath: PFS.PATH ~ PFS.PathFromRope[orgName !PFS.Error => GOTO Bad];
IF orgPath.IsAbsolute[] THEN RETURN [NIL, NIL];
{contextFileName: ROPE ~ IF viewer # NIL THEN IF viewer.file = NIL THEN viewer.name ELSE viewer.file ELSE NIL;
contextPath: PFS.PATH ~ PFS.PathFromRope[contextFileName];
isADfFileName: BOOL ¬ FALSE;
Doit:
PROC ~ {
dwimAns: PFS.PATH;
[dwimAns, search,,] ¬ DWIMit[orgPath, NIL, contextPath, doSearchHack, isADfFileName];
IF dwimAns#NIL THEN fileName ¬ PFS.RopeFromPath[dwimAns];
RETURN};
IF contextFileName.Length[]#0
THEN {
wDir: PFS.PATH ~ contextPath.Directory[];
contextShort: ROPE ~ contextPath.ShortName[].ComponentRope[--exclude version--];
isADfFileName ¬ Rope.Match["*.df", contextShort, FALSE];
PFS.DoInWDir[wDir, Doit];
}
ELSE Doit[];
IF fileName=NIL AND OldFNP#NIL THEN [fileName, search] ¬ OldFNP[orgName, viewer];
};
EXITS Bad => search ¬ NIL};
RETURN};
Common Utoolities
wDirKey: ATOM ~ $WorkingDirectory;
verbose, pacifyDFEnumeration: BOOL ¬ TRUE;
Exists:
PROC [fileName:
PFS.
PATH]
RETURNS [exists:
BOOLEAN] = {
exists ¬ FALSE;
exists ¬
PFS.FileInfo[
name: fileName
! PFS.Error => CONTINUE].bytes # -1;
RETURN};
ParseForExtension:
PROC [c: PFSNames.Component]
RETURNS [
PFS.
PATH] ~ {
pos: INT ~ Rope.FindBackward[c.name.base, ".", c.name.start+c.name.len-1];
nameLen: INT ~ c.name.start+c.name.len;
len: INT ~ MIN[c.name.base.Length[], nameLen];
IF pos >= c.name.start
THEN
RETURN [PFSNames.ConstructName[
LIST[
[[c.name.base, c.name.start, pos-c.name.start]],
[[c.name.base, pos+1, len-pos-1]]
]]]
ELSE RETURN [PFSNames.ConstructName[LIST[c]]]};
ExtendPath:
PROC [path:
PFS.
PATH, component: PFSNames.Component]
RETURNS [
PFS.
PATH] ~ {
RETURN path.Cat[PFSNames.ConstructName[LIST[component]]]};
AddExtension:
PROC [path:
PFS.
PATH, ext:
ROPE]
RETURNS [
PFS.
PATH] ~ {
RETURN path.ReplaceShortName[[name: [path.ShortName[].ComponentRope[].Cat[".", ext], 0, NAT.LAST]]]};
FetchOrNil:
PROC [path:
PFS.
PATH, index:
INT]
RETURNS [PFSNames.Component] ~ {
IF index=path.ComponentCount[] THEN RETURN [[]];
RETURN path.Fetch[index]};
StripComponentVersion:
PROC [c: PFSNames.Component]
RETURNS [PFSNames.Component]
~ {RETURN [[c.name, [none]]]};
TestCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
ATOM ¬
NIL, msg:
ROPE ¬
NIL]
--Commander.CommandProc-- ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
hint, contextFileName: ROPE ¬ NIL;
searchHack, errd: BOOL ¬ FALSE;
tryAll: BOOL ¬ TRUE;
ans: Answer;
IF argv.argc<=1 THEN RETURN [$Failure, "Usage: FileDWIM (-s | +s | -a | +a)* || ([contextFileName] hint)"];
FOR i:
NAT
IN [1..argv.argc)
DO
SELECT
TRUE
FROM
argv[i].Equal["-s"] => searchHack ¬ FALSE;
argv[i].Equal["+s"] => searchHack ¬ TRUE;
argv[i].Equal["-a"] => tryAll ¬ FALSE;
argv[i].Equal["+a"] => tryAll ¬ TRUE;
hint=NIL => hint ¬ argv[i];
ENDCASE => contextFileName ¬ argv[i];
ENDLOOP;
ans ¬ ResolveHint[hint, contextFileName, searchHack, tryAll !
PFS.Error => {
errd ¬ TRUE;
msg ¬ IO.PutFR["PFS.Error[%g, %g]", [atom[error.code]], [rope[error.explanation]] ];
CONTINUE}];
IF errd THEN RETURN [$Failure, msg];
msg ¬ IO.PutFR["[fullFileName: %g, searchFor: %g]", [rope[ans.fullFileName]], [rope[ans.searchFor]] ];
RETURN};
NoticeProfileChanges:
PROC [reason: UserProfile.ProfileChangeReason]
--UserProfile.ProfileChangedProc-- = {
tagfilenames: ROPEList ¬ UserProfile.ListOfTokens["FileDWIM.Tagfiles", NIL];
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];
pacifyDFEnumeration ¬ UserProfile.Boolean["FileDWIM.PacifyDFEnumeration", FALSE];
tagfiles ¬ GetTagfiles[tagfilenames];
IF (processDF
OR assocExts)
AND
NOT inPlace
THEN
BEGIN
inPlace ¬ TRUE;
OldFNP ¬ TEditOps.ReplaceFileNameProc[GetDWIM];
END;
};
UserProfile.CallWhenProfileChanges[NoticeProfileChanges];
Commander.Register["FileDWIM", TestCmd, "FileDWIM (-s | +s | -a | +a)* || ([contextFileName] hint) tests FileDWIM"];
END.