@@ -325,15 +325,28 @@ namespace ts.Completions {
325325 return result ;
326326 }
327327
328+ /**
329+ * Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
330+ */
328331 function getCompletionEntriesForDirectoryFragment ( fragment : string , scriptPath : string , extensions : string [ ] , includeExtensions : boolean , span : TextSpan , exclude ?: string , result : CompletionEntry [ ] = [ ] ) : CompletionEntry [ ] {
329- fragment = getDirectoryPath ( fragment ) ;
330- if ( ! fragment ) {
331- fragment = "./" ;
332+ if ( fragment === undefined ) {
333+ fragment = "" ;
332334 }
333- else {
334- fragment = ensureTrailingDirectorySeparator ( fragment ) ;
335+
336+ fragment = normalizeSlashes ( fragment ) ;
337+
338+ /**
339+ * Remove the basename from the path. Note that we don't use the basename to filter completions;
340+ * the client is responsible for refining completions.
341+ */
342+ fragment = getDirectoryPath ( fragment ) ;
343+
344+ if ( fragment === "" ) {
345+ fragment = "." + directorySeparator ;
335346 }
336347
348+ fragment = ensureTrailingDirectorySeparator ( fragment ) ;
349+
337350 const absolutePath = normalizeAndPreserveTrailingSlash ( isRootedDiskPath ( fragment ) ? fragment : combinePaths ( scriptPath , fragment ) ) ;
338351 const baseDirectory = getDirectoryPath ( absolutePath ) ;
339352 const ignoreCase = ! ( host . useCaseSensitiveFileNames && host . useCaseSensitiveFileNames ( ) ) ;
@@ -343,6 +356,12 @@ namespace ts.Completions {
343356 const files = tryReadDirectory ( host , baseDirectory , extensions , /*exclude*/ undefined , /*include*/ [ "./*" ] ) ;
344357
345358 if ( files ) {
359+ /**
360+ * Multiple file entries might map to the same truncated name once we remove extensions
361+ * (happens iff includeExtensions === false)so we use a set-like data structure. Eg:
362+ *
363+ * both foo.ts and foo.tsx become foo
364+ */
346365 const foundFiles = createMap < boolean > ( ) ;
347366 for ( let filePath of files ) {
348367 filePath = normalizePath ( filePath ) ;
@@ -539,36 +558,44 @@ namespace ts.Completions {
539558 return undefined ;
540559 }
541560
561+ const completionInfo : CompletionInfo = {
562+ /**
563+ * We don't want the editor to offer any other completions, such as snippets, inside a comment.
564+ */
565+ isGlobalCompletion : false ,
566+ isMemberCompletion : false ,
567+ /**
568+ * The user may type in a path that doesn't yet exist, creating a "new identifier"
569+ * with respect to the collection of identifiers the server is aware of.
570+ */
571+ isNewIdentifierLocation : true ,
572+
573+ entries : [ ]
574+ } ;
575+
542576 const text = sourceFile . text . substr ( range . pos , position - range . pos ) ;
543577
544578 const match = tripleSlashDirectiveFragmentRegex . exec ( text ) ;
579+
545580 if ( match ) {
546581 const prefix = match [ 1 ] ;
547582 const kind = match [ 2 ] ;
548583 const toComplete = match [ 3 ] ;
549584
550585 const scriptPath = getDirectoryPath ( sourceFile . path ) ;
551- let entries : CompletionEntry [ ] ;
552586 if ( kind === "path" ) {
553587 // Give completions for a relative path
554588 const span : TextSpan = getDirectoryFragmentTextSpan ( toComplete , range . pos + prefix . length ) ;
555- entries = getCompletionEntriesForDirectoryFragment ( toComplete , scriptPath , getSupportedExtensions ( compilerOptions ) , /*includeExtensions*/ true , span , sourceFile . path ) ;
589+ completionInfo . entries = getCompletionEntriesForDirectoryFragment ( toComplete , scriptPath , getSupportedExtensions ( compilerOptions ) , /*includeExtensions*/ true , span , sourceFile . path ) ;
556590 }
557591 else {
558592 // Give completions based on the typings available
559593 const span : TextSpan = { start : range . pos + prefix . length , length : match [ 0 ] . length - prefix . length } ;
560- entries = getCompletionEntriesFromTypings ( host , compilerOptions , scriptPath , span ) ;
594+ completionInfo . entries = getCompletionEntriesFromTypings ( host , compilerOptions , scriptPath , span ) ;
561595 }
562-
563- return {
564- isGlobalCompletion : false ,
565- isMemberCompletion : false ,
566- isNewIdentifierLocation : true ,
567- entries
568- } ;
569596 }
570597
571- return undefined ;
598+ return completionInfo ;
572599 }
573600
574601 function getCompletionEntriesFromTypings ( host : LanguageServiceHost , options : CompilerOptions , scriptPath : string , span : TextSpan , result : CompletionEntry [ ] = [ ] ) : CompletionEntry [ ] {
@@ -1674,9 +1701,15 @@ namespace ts.Completions {
16741701 * Matches a triple slash reference directive with an incomplete string literal for its path. Used
16751702 * to determine if the caret is currently within the string literal and capture the literal fragment
16761703 * for completions.
1677- * For example, this matches /// <reference path="fragment
1704+ * For example, this matches
1705+ *
1706+ * /// <reference path="fragment
1707+ *
1708+ * but not
1709+ *
1710+ * /// <reference path="fragment"
16781711 */
1679- const tripleSlashDirectiveFragmentRegex = / ^ ( \/ \/ \/ \s * < r e f e r e n c e \s + ( p a t h | t y p e s ) \s * = \s * (?: ' | " ) ) ( [ ^ \3] * ) $ / ;
1712+ const tripleSlashDirectiveFragmentRegex = / ^ ( \/ \/ \/ \s * < r e f e r e n c e \s + ( p a t h | t y p e s ) \s * = \s * (?: ' | " ) ) ( [ ^ \3" ] * ) $ / ;
16801713
16811714 interface VisibleModuleInfo {
16821715 moduleName : string ;
0 commit comments