@@ -1968,7 +1968,7 @@ namespace ts {
19681968 * for completions.
19691969 * For example, this matches /// <reference path="fragment
19701970 */
1971- const tripleSlashDirectiveFragmentRegex = / ^ \/ \/ \/ \s * < r e f e r e n c e \s + p a t h \s * = \s * (?: ' | " ) ( [ ^ ' " ] + ) $ / ;
1971+ const tripleSlashDirectiveFragmentRegex = / ^ \/ \/ \/ \s * < r e f e r e n c e \s + ( p a t h | t y p e s ) \s * = \s * (?: ' | " ) ( [ ^ ' " ] + ) $ / ;
19721972
19731973 let commandLineOptionsStringToEnum : CommandLineOptionOfCustomType [ ] ;
19741974
@@ -4501,7 +4501,7 @@ namespace ts {
45014501 result = getCompletionEntriesForDirectoryFragment ( fragment , normalizePath ( absolute ) , fileExtensions , /*includeExtensions*/ false ) ;
45024502
45034503 if ( paths ) {
4504- for ( var path in paths ) {
4504+ for ( const path in paths ) {
45054505 if ( paths . hasOwnProperty ( path ) ) {
45064506 if ( path === "*" ) {
45074507 if ( paths [ path ] ) {
@@ -4526,7 +4526,7 @@ namespace ts {
45264526 result = [ ] ;
45274527 }
45284528
4529-
4529+ getCompletionEntriesFromTypings ( host , options , scriptPath , result ) ;
45304530
45314531 forEach ( enumeratePotentialNonRelativeModules ( fragment , scriptPath ) , moduleName => {
45324532 result . push ( createCompletionEntryForModule ( moduleName , ScriptElementKind . externalModuleName ) ) ;
@@ -4558,7 +4558,7 @@ namespace ts {
45584558 // If we have a suffix, then we need to read the directory all the way down. We could create a glob
45594559 // that encodes the suffix, but we would have to escape the character "?" which readDirectory
45604560 // doesn't support. For now, this is safer but slower
4561- const includeGlob = normalizedSuffix ? "**/*" : "./*"
4561+ const includeGlob = normalizedSuffix ? "**/*" : "./*" ;
45624562
45634563 const matches = host . readDirectory ( baseDirectory , fileExtensions , undefined , [ includeGlob ] ) ;
45644564 const result : string [ ] = [ ] ;
@@ -4629,19 +4629,97 @@ namespace ts {
46294629 const text = sourceFile . text . substr ( node . pos , position ) ;
46304630 const match = tripleSlashDirectiveFragmentRegex . exec ( text ) ;
46314631 if ( match ) {
4632- const fragment = match [ 1 ] ;
4633- const scriptPath = getDirectoryPath ( sourceFile . path ) ;
4634- return {
4635- isMemberCompletion : false ,
4636- isNewIdentifierLocation : false ,
4637- entries : getCompletionEntriesForDirectoryFragment ( fragment , scriptPath , getSupportedExtensions ( program . getCompilerOptions ( ) ) , /*includeExtensions*/ true )
4638- } ;
4632+ const kind = match [ 1 ] ;
4633+ const fragment = match [ 2 ] ;
4634+ if ( kind === "path" ) {
4635+ // Give completions for a relative path
4636+ const scriptPath = getDirectoryPath ( sourceFile . path ) ;
4637+ return {
4638+ isMemberCompletion : false ,
4639+ isNewIdentifierLocation : false ,
4640+ entries : getCompletionEntriesForDirectoryFragment ( fragment , scriptPath , getSupportedExtensions ( program . getCompilerOptions ( ) ) , /*includeExtensions*/ true )
4641+ } ;
4642+ }
4643+ else {
4644+ // Give completions based on what is available in the types directory
4645+ }
46394646 }
46404647
46414648 return undefined ;
46424649 }
46434650 }
46444651
4652+ function getCompletionEntriesFromTypings ( host : LanguageServiceHost , options : CompilerOptions , scriptPath : string , result : CompletionEntry [ ] ) : CompletionEntry [ ] {
4653+ // Check for typings specified in compiler options
4654+ if ( options . types ) {
4655+ forEach ( options . types , moduleName => {
4656+ result . push ( createCompletionEntryForModule ( moduleName , ScriptElementKind . externalModuleName ) ) ;
4657+ } ) ;
4658+ }
4659+ else if ( options . typeRoots ) {
4660+ const absoluteRoots = map ( options . typeRoots , rootDirectory => getAbsoluteProjectPath ( rootDirectory , host , options . project ) ) ;
4661+ forEach ( absoluteRoots , absoluteRoot => getCompletionEntriesFromDirectory ( host , options , absoluteRoot , result ) ) ;
4662+ }
4663+
4664+ // Also get all @types typings installed in visible node_modules directories
4665+ forEach ( findPackageJsons ( scriptPath ) , package => {
4666+ const typesDir = combinePaths ( getDirectoryPath ( package ) , "node_modules/@types" ) ;
4667+ getCompletionEntriesFromDirectory ( host , options , typesDir , result ) ;
4668+ } ) ;
4669+
4670+ return result ;
4671+ }
4672+
4673+ function getAbsoluteProjectPath ( path : string , host : LanguageServiceHost , projectDir ?: string ) {
4674+ if ( isRootedDiskPath ( path ) ) {
4675+ return normalizePath ( path ) ;
4676+ }
4677+
4678+ if ( projectDir ) {
4679+ return normalizePath ( combinePaths ( projectDir , path ) ) ;
4680+ }
4681+
4682+ return normalizePath ( host . resolvePath ( path ) ) ;
4683+ }
4684+
4685+ function getCompletionEntriesFromDirectory ( host : LanguageServiceHost , options : CompilerOptions , directory : string , result : CompletionEntry [ ] ) {
4686+ if ( directoryProbablyExists ( directory , host ) ) {
4687+ const typeDirectories = host . readDirectory ( directory , getSupportedExtensions ( options ) , /*exclude*/ undefined , /*include*/ [ "./*/*" ] ) ;
4688+ const seen : { [ index : string ] : boolean } = { } ;
4689+ forEach ( typeDirectories , typeFile => {
4690+ const typeDirectory = getDirectoryPath ( typeFile ) ;
4691+ if ( ! hasProperty ( seen , typeDirectory ) ) {
4692+ seen [ typeDirectory ] = true ;
4693+ result . push ( createCompletionEntryForModule ( getBaseFileName ( typeDirectory ) , ScriptElementKind . externalModuleName ) ) ;
4694+ }
4695+ } ) ;
4696+ }
4697+ }
4698+
4699+ function findPackageJsons ( currentDir : string ) : string [ ] {
4700+ const paths : string [ ] = [ ] ;
4701+ let currentConfigPath : string ;
4702+ while ( true ) {
4703+ currentConfigPath = findConfigFile ( currentDir , ( f ) => host . fileExists ( f ) , "package.json" ) ;
4704+ if ( currentConfigPath ) {
4705+ paths . push ( currentConfigPath ) ;
4706+
4707+ currentDir = getDirectoryPath ( currentConfigPath ) ;
4708+ const parent = getDirectoryPath ( currentDir ) ;
4709+ if ( currentDir === parent ) {
4710+ break ;
4711+ }
4712+ currentDir = parent ;
4713+ }
4714+ else {
4715+ break ;
4716+ }
4717+ }
4718+
4719+ return paths ;
4720+ }
4721+
4722+
46454723 function enumerateNodeModulesVisibleToScript ( host : LanguageServiceHost , scriptPath : string , modulePrefix ?: string ) {
46464724 const result : VisibleModuleInfo [ ] = [ ] ;
46474725 findPackageJsons ( scriptPath ) . forEach ( ( packageJson ) => {
@@ -4672,29 +4750,6 @@ namespace ts {
46724750
46734751 return result ;
46744752
4675- function findPackageJsons ( currentDir : string ) : string [ ] {
4676- const paths : string [ ] = [ ] ;
4677- let currentConfigPath : string ;
4678- while ( true ) {
4679- currentConfigPath = findConfigFile ( currentDir , ( f ) => host . fileExists ( f ) , "package.json" ) ;
4680- if ( currentConfigPath ) {
4681- paths . push ( currentConfigPath ) ;
4682-
4683- currentDir = getDirectoryPath ( currentConfigPath ) ;
4684- const parent = getDirectoryPath ( currentDir ) ;
4685- if ( currentDir === parent ) {
4686- break ;
4687- }
4688- currentDir = parent ;
4689- }
4690- else {
4691- break ;
4692- }
4693- }
4694-
4695- return paths ;
4696- }
4697-
46984753 function tryReadingPackageJson ( filePath : string ) {
46994754 try {
47004755 const fileText = host . readFile ( filePath ) ;
@@ -4745,7 +4800,7 @@ namespace ts {
47454800 kind,
47464801 kindModifiers : ScriptElementKindModifier . none ,
47474802 sortText : name
4748- }
4803+ } ;
47494804 }
47504805
47514806 function getCompletionEntryDetails ( fileName : string , position : number , entryName : string ) : CompletionEntryDetails {
0 commit comments