@@ -325,15 +325,28 @@ namespace ts.Completions {
325
325
return result ;
326
326
}
327
327
328
+ /**
329
+ * Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
330
+ */
328
331
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 = "" ;
332
334
}
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 ;
335
346
}
336
347
348
+ fragment = ensureTrailingDirectorySeparator ( fragment ) ;
349
+
337
350
const absolutePath = normalizeAndPreserveTrailingSlash ( isRootedDiskPath ( fragment ) ? fragment : combinePaths ( scriptPath , fragment ) ) ;
338
351
const baseDirectory = getDirectoryPath ( absolutePath ) ;
339
352
const ignoreCase = ! ( host . useCaseSensitiveFileNames && host . useCaseSensitiveFileNames ( ) ) ;
@@ -343,6 +356,12 @@ namespace ts.Completions {
343
356
const files = tryReadDirectory ( host , baseDirectory , extensions , /*exclude*/ undefined , /*include*/ [ "./*" ] ) ;
344
357
345
358
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
+ */
346
365
const foundFiles = createMap < boolean > ( ) ;
347
366
for ( let filePath of files ) {
348
367
filePath = normalizePath ( filePath ) ;
@@ -539,36 +558,44 @@ namespace ts.Completions {
539
558
return undefined ;
540
559
}
541
560
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
+
542
576
const text = sourceFile . text . substr ( range . pos , position - range . pos ) ;
543
577
544
578
const match = tripleSlashDirectiveFragmentRegex . exec ( text ) ;
579
+
545
580
if ( match ) {
546
581
const prefix = match [ 1 ] ;
547
582
const kind = match [ 2 ] ;
548
583
const toComplete = match [ 3 ] ;
549
584
550
585
const scriptPath = getDirectoryPath ( sourceFile . path ) ;
551
- let entries : CompletionEntry [ ] ;
552
586
if ( kind === "path" ) {
553
587
// Give completions for a relative path
554
588
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 ) ;
556
590
}
557
591
else {
558
592
// Give completions based on the typings available
559
593
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 ) ;
561
595
}
562
-
563
- return {
564
- isGlobalCompletion : false ,
565
- isMemberCompletion : false ,
566
- isNewIdentifierLocation : true ,
567
- entries
568
- } ;
569
596
}
570
597
571
- return undefined ;
598
+ return completionInfo ;
572
599
}
573
600
574
601
function getCompletionEntriesFromTypings ( host : LanguageServiceHost , options : CompilerOptions , scriptPath : string , span : TextSpan , result : CompletionEntry [ ] = [ ] ) : CompletionEntry [ ] {
@@ -1674,9 +1701,15 @@ namespace ts.Completions {
1674
1701
* Matches a triple slash reference directive with an incomplete string literal for its path. Used
1675
1702
* to determine if the caret is currently within the string literal and capture the literal fragment
1676
1703
* 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"
1678
1711
*/
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" ] * ) $ / ;
1680
1713
1681
1714
interface VisibleModuleInfo {
1682
1715
moduleName : string ;
0 commit comments