Filename.st
changeset 440 017c88672c98
parent 422 63600ce8c7cc
child 441 41684f79f318
--- a/Filename.st	Tue Sep 19 02:55:04 1995 +0200
+++ b/Filename.st	Thu Sep 21 14:31:48 1995 +0200
@@ -20,7 +20,7 @@
 COPYRIGHT (c) 1992 by Claus Gittinger
 	     All Rights Reserved
 
-$Header: /cvs/stx/stx/libbasic/Filename.st,v 1.32 1995-09-07 11:58:28 claus Exp $
+$Header: /cvs/stx/stx/libbasic/Filename.st,v 1.33 1995-09-21 12:30:32 claus Exp $
 '!
 
 !Filename class methodsFor:'documentation'!
@@ -41,7 +41,7 @@
 
 version
 "
-$Header: /cvs/stx/stx/libbasic/Filename.st,v 1.32 1995-09-07 11:58:28 claus Exp $
+$Header: /cvs/stx/stx/libbasic/Filename.st,v 1.33 1995-09-21 12:30:32 claus Exp $
 "
 !
 
@@ -352,6 +352,94 @@
     ^ #('/')
 
     "Modified: 7.9.1995 / 10:45:25 / claus"
+!
+
+filenameCompletionFor:aString directory:inDirectory directoriesOnly:directoriesOnly filesOnly:filesOnly ifMultiple:aBlock
+    "perform filename completion on aString in some directory;
+     return the longest matching filename prefix as a string.
+     If directoriesOnly and filesOnly are true, only directories and files
+     are considered respectively. If multiple files match, the exception
+     block aBlock is evaluated with a filename representing the directory
+     (where the match was done) as argument.
+     (this may be different from the inDirectory argument, if aString is absolute
+      or starts with ../)"
+
+    |s f matchSet nMatch name words dir|
+
+    s := aString.
+    "
+     find the last word ...
+    "
+    words := s asCollectionOfWords.
+    words size == 0 ifTrue:[
+	^ aBlock value:'.' asFilename
+    ].
+
+    f := words last asFilename.
+
+    matchSet := f filenameCompletionIn:inDirectory.
+
+    dir := f directory.
+
+    directoriesOnly ifTrue:[
+	matchSet := matchSet select:[:aFilename |
+	    (dir construct:aFilename) isDirectory
+	].
+    ] ifFalse:[
+	filesOnly ifTrue:[
+	    matchSet := matchSet select:[:aFilename |
+		(dir construct:aFilename) isDirectory not
+	    ].
+	]
+    ].
+
+    (nMatch := matchSet size) ~~ 1 ifTrue:[
+	"
+	 more than one possible completion -
+	"
+	aBlock value:f
+    ].
+    "
+     even with more than one possible completion,
+     f's name is now the common prefix
+    "
+    name := f asString.
+    nMatch == 1 ifTrue:[
+	"
+	 exactly one possible completion -
+	"
+	f := dir construct:matchSet first.
+
+	directoriesOnly ifFalse:[
+	    f isDirectory ifTrue:[
+		(name endsWith:(Filename separator)) ifFalse:[
+		    name := name , '/'
+		].
+	    ].
+	]
+    ].
+
+    "
+     construct new contents, by taking
+     last words completion
+    "
+    s := ''.
+    1 to:(words size - 1) do:[:idx |
+	s := s , (words at:idx) , ' '
+    ].
+    s := s , name.
+
+    "/ special: if there was no change, and the string represented
+    "/ is a directories name, add a directory separator
+    s = aString ifTrue:[
+	(s endsWith:Filename separator) ifFalse:[
+	    s asFilename isDirectory ifTrue:[
+		^ s , Filename separator asString
+	    ]
+	]
+    ].
+
+    ^ s
 ! !
 
 !Filename methodsFor:'instance creation'!
@@ -817,114 +905,139 @@
 !
 
 filenameCompletion
-    "try to complete the filename. This method has both a side effect,
-     and a return value:
-	 it returns a collection of matching filename strings,
-	 and sets (as side effect) the receivers filename to the longest common
-	 match. 
-     (i.e. if the size of the returned collection is exactly 1,
-      the completion has succeeded and the receivers name has been changed to
-      that. 
-      If the returned collection is empty, nothing matched and the receivers
-      names is unchanged.
-      If the size of the returned collection is greater than one, the receivers
-      filename-string has been set to the longest common filename-prefix)"
+    "try to complete the recevier filename.
+     This method has both a return value and a side effect on the receiver:
+       it returns a collection of matching filename objects,
+       and leaves changes the receivers filename-string to the longest common
+       match.
+     If none matches, the returned collection is empty and the recevier is unchanged.
+     If there is only one match, the size of the returned collection is exactly 1,
+     containing the fully expanded filename and the receivers name is changed to it."
 
-    |dir name matching matchLen try allMatching sep parent prefix|
+    ^ self filenameCompletionIn:nil
 
-    sep := self class separator asString.
-    parent := self class parentDirectoryName.
+    " 
+     'mak' asFilename filenameCompletion  
+     'Make' asFilename filenameCompletion 
+     'Makef' asFilename filenameCompletion;yourself  
+     '/u' asFilename filenameCompletion             
+     '../../libpr' asFilename inspect filenameCompletion    
+    "
+!
 
-    dir := self directory.
-    prefix := parent , sep.
-    (nameString endsWith:sep) ifTrue:[
-	name := ''
-    ] ifFalse:[
-	name := self baseName.
-    ].
-    [name startsWith:prefix] whileTrue:[
-	self halt.
-	dir := dir directory.
-	name := name copyFrom:(prefix size + 1)
+filenameCompletionIn:aDirectory
+    "try to complete the recevier filename.
+     This method has both a return value and a side effect on the receiver:
+       it returns a collection of matching filename objects,
+       and leaves changes the receivers filename-string to the longest common
+       match.
+     If none matches, the returned collection is empty and the recevier is unchanged.
+     If there is only one match, the size of the returned collection is exactly 1,
+     containing the fully expanded filename and the receivers name is changed to it."
+
+    |dir baseName matching matchLen try allMatching 
+     sepString parentString prefix nMatch|
+
+    sepString := self class separator asString.
+    (nameString endsWith:sepString) ifTrue:[
+	^ #()
     ].
 
-    dir := dir asString.
-    name = parent ifTrue:[
-	^ dir asFilename filenameCompletion
+    parentString := self class parentDirectoryName.
+    baseName := self baseName.
+    baseName ~= nameString ifTrue:[
+	prefix := self directoryName.
+    ].
+
+    aDirectory isNil ifTrue:[
+	dir := self directory
+    ] ifFalse:[
+	dir := (aDirectory construct:nameString) directory
     ].
 
     matching := OrderedCollection new.
-    (FileDirectory directoryNamed:dir) do:[:fileName |
-	((fileName ~= '.') and:[fileName ~= parent]) ifTrue:[
-	    (fileName startsWith:name) ifTrue:[
+    dir directoryContents do:[:fileName |
+	((fileName ~= '.') and:[fileName ~= parentString]) ifTrue:[
+	    (fileName startsWith:baseName) ifTrue:[
 		matching add:fileName
 	    ]
 	]
     ].
-    "
-     if there is only one, change my name ...
+    (nMatch := matching size) > 1 ifTrue:[
+	"
+	 find the longest common prefix
+	"
+	matchLen := baseName size.
+	matchLen > matching first size ifTrue:[
+	    try := baseName.
+	    allMatching := false
+	] ifFalse:[
+	    try := matching first copyTo:matchLen.
+	    allMatching := true.
+	].
+
+	[allMatching] whileTrue:[
+	    matching do:[:aName |
+		(aName startsWith:try) ifFalse:[
+		    allMatching := false
+		]
+	    ].
+	    allMatching ifTrue:[
+		matchLen <  matching first size ifTrue:[
+		    matchLen := matchLen + 1.
+		    try := matching first copyTo:matchLen.
+		] ifFalse:[
+		    allMatching := false
+		]
+	    ] ifFalse:[
+		try := matching first copyTo:matchLen - 1.
+	    ]
+	].
+	"
+	 and set my name to the last full match
+	"
+	nameString := try
+    ].
+
     "
-    matching size == 1 ifTrue:[
-	dir = sep ifTrue:[
-	   dir := ''
+     if I had a directory-prefix, change names in collection ...
+    "
+    prefix notNil ifTrue:[
+	prefix = '/' ifTrue:[
+	    "/ avoid introducing double slashes
+	    prefix := ''
 	].
-	nameString := dir , sep , matching first.
-	matching first = name ifTrue:[
-	    self isDirectory ifTrue:[
-		nameString := nameString , self class separator asString
+	matching := matching collect:[:n | prefix , '/' , n].
+	nMatch == 1 ifTrue:[
+	    nameString := matching first
+	] ifFalse:[
+	    nMatch > 1 ifTrue:[
+		nameString := prefix , '/' , nameString
 	    ]
 	]
     ] ifFalse:[
-	matching size > 1 ifTrue:[
-	    "
-	     find the longest common prefix
-	    "
-	    matchLen := name size.
-	    matchLen > matching first size ifTrue:[
-		try := name.
-		allMatching := false
-	    ] ifFalse:[
-		try := matching first copyTo:matchLen.
-		allMatching := true.
-	    ].
-
-	    [allMatching] whileTrue:[
-		matching do:[:aName |
-		    (aName startsWith:try) ifFalse:[
-			allMatching := false
-		    ]
-		].
-		allMatching ifTrue:[
-		    matchLen <  matching first size ifTrue:[
-			matchLen := matchLen + 1.
-			try := matching first copyTo:matchLen.
-		    ] ifFalse:[
-			allMatching := false
-		    ]
-		] ifFalse:[
-		    try := matching first copyTo:matchLen - 1.
-		]
-	    ].
-	    "
-	     and set my name to the last full match
-	    "
-	    dir = sep ifTrue:[
-	       dir := ''
-	    ].
-	    nameString := dir , sep , try
+	nMatch == 1 ifTrue:[
+	    nameString := matching first
 	]
     ].
+
     "
      return the match-set, so caller can decide what to do
      (i.e. show the matches, output a warning etc ...)
     "
     ^ matching
 
-    "
-     'Make' asFilename filenameCompletion;yourself 
-     'Makef' asFilename filenameCompletion;yourself 
-     '/u' asFilename filenameCompletion 
-     '../../libpr' asFilename filenameCompletion 
+    " trivial cases:
+
+     '../' asFilename filenameCompletion    
+     '/' asFilename filenameCompletion      
+     '/usr/' asFilename filenameCompletion   
+
+     'mak' asFilename filenameCompletion   
+     'Make' asFilename filenameCompletion    
+     'Makef' asFilename filenameCompletion
+     '/u' asFilename filenameCompletion             
+     '../../libpr' asFilename filenameCompletion    
     "
 ! !