1+ /// <reference path="..\services\services.ts" />
2+ /// <reference path="utilities.ts" />
3+ /// <reference path="scriptInfo.ts" />
4+
5+ namespace ts . server {
6+ export class LSHost implements ts . LanguageServiceHost , ModuleResolutionHost , ServerLanguageServiceHost {
7+ private compilationSettings : ts . CompilerOptions ;
8+ private readonly resolvedModuleNames : ts . FileMap < Map < ResolvedModuleWithFailedLookupLocations > > ;
9+ private readonly resolvedTypeReferenceDirectives : ts . FileMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ;
10+ private readonly getCanonicalFileName : ( fileName : string ) => string ;
11+
12+ constructor ( private readonly host : ServerHost , private readonly project : Project , private readonly cancellationToken : HostCancellationToken ) {
13+ this . getCanonicalFileName = ts . createGetCanonicalFileName ( this . host . useCaseSensitiveFileNames ) ;
14+ this . resolvedModuleNames = createFileMap < Map < ResolvedModuleWithFailedLookupLocations > > ( ) ;
15+ this . resolvedTypeReferenceDirectives = createFileMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
16+ }
17+
18+ private resolveNamesWithLocalCache < T extends { failedLookupLocations : string [ ] } , R > (
19+ names : string [ ] ,
20+ containingFile : string ,
21+ cache : ts . FileMap < Map < T > > ,
22+ loader : ( name : string , containingFile : string , options : CompilerOptions , host : ModuleResolutionHost ) => T ,
23+ getResult : ( s : T ) => R ) : R [ ] {
24+
25+ const path = toPath ( containingFile , this . host . getCurrentDirectory ( ) , this . getCanonicalFileName ) ;
26+ const currentResolutionsInFile = cache . get ( path ) ;
27+
28+ const newResolutions : Map < T > = { } ;
29+ const resolvedModules : R [ ] = [ ] ;
30+ const compilerOptions = this . getCompilationSettings ( ) ;
31+
32+ for ( const name of names ) {
33+ // check if this is a duplicate entry in the list
34+ let resolution = lookUp ( newResolutions , name ) ;
35+ if ( ! resolution ) {
36+ const existingResolution = currentResolutionsInFile && ts . lookUp ( currentResolutionsInFile , name ) ;
37+ if ( moduleResolutionIsValid ( existingResolution ) ) {
38+ // ok, it is safe to use existing name resolution results
39+ resolution = existingResolution ;
40+ }
41+ else {
42+ newResolutions [ name ] = resolution = loader ( name , containingFile , compilerOptions , this ) ;
43+ }
44+ }
45+
46+ ts . Debug . assert ( resolution !== undefined ) ;
47+
48+ resolvedModules . push ( getResult ( resolution ) ) ;
49+ }
50+
51+ // replace old results with a new one
52+ cache . set ( path , newResolutions ) ;
53+ return resolvedModules ;
54+
55+ function moduleResolutionIsValid ( resolution : T ) : boolean {
56+ if ( ! resolution ) {
57+ return false ;
58+ }
59+
60+ if ( getResult ( resolution ) ) {
61+ // TODO: consider checking failedLookupLocations
62+ return true ;
63+ }
64+
65+ // consider situation if we have no candidate locations as valid resolution.
66+ // after all there is no point to invalidate it if we have no idea where to look for the module.
67+ return resolution . failedLookupLocations . length === 0 ;
68+ }
69+ }
70+
71+ getProjectVersion ( ) {
72+ return this . project . getProjectVersion ( ) ;
73+ }
74+
75+ getCancellationToken ( ) {
76+ return this . cancellationToken ;
77+ }
78+
79+ resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string ) : ResolvedTypeReferenceDirective [ ] {
80+ return this . resolveNamesWithLocalCache ( typeDirectiveNames , containingFile , this . resolvedTypeReferenceDirectives , resolveTypeReferenceDirective , m => m . resolvedTypeReferenceDirective ) ;
81+ }
82+
83+ resolveModuleNames ( moduleNames : string [ ] , containingFile : string ) : ResolvedModule [ ] {
84+ return this . resolveNamesWithLocalCache ( moduleNames , containingFile , this . resolvedModuleNames , resolveModuleName , m => m . resolvedModule ) ;
85+ }
86+
87+ getDefaultLibFileName ( ) {
88+ const nodeModuleBinDir = ts . getDirectoryPath ( ts . normalizePath ( this . host . getExecutingFilePath ( ) ) ) ;
89+ return ts . combinePaths ( nodeModuleBinDir , ts . getDefaultLibFileName ( this . compilationSettings ) ) ;
90+ }
91+
92+ getScriptSnapshot ( filename : string ) : ts . IScriptSnapshot {
93+ const scriptInfo = this . project . getScriptInfo ( filename ) ;
94+ if ( scriptInfo ) {
95+ return scriptInfo . snap ( ) ;
96+ }
97+ }
98+
99+ setCompilationSettings ( opt : ts . CompilerOptions ) {
100+ this . compilationSettings = opt ;
101+ // conservatively assume that changing compiler options might affect module resolution strategy
102+ this . resolvedModuleNames . clear ( ) ;
103+ this . resolvedTypeReferenceDirectives . clear ( ) ;
104+ }
105+
106+ getCompilationSettings ( ) {
107+ // change this to return active project settings for file
108+ return this . compilationSettings ;
109+ }
110+
111+ getScriptFileNames ( ) {
112+ return this . project . getRootFiles ( ) ;
113+ }
114+
115+ getScriptKind ( fileName : string ) {
116+ const info = this . project . getScriptInfo ( fileName ) ;
117+ return info && info . scriptKind ;
118+ }
119+
120+ getScriptVersion ( filename : string ) {
121+ return this . project . getScriptInfo ( filename ) . getLatestVersion ( ) ;
122+ }
123+
124+ getCurrentDirectory ( ) : string {
125+ return "" ;
126+ }
127+
128+ removeReferencedFile ( info : ScriptInfo ) {
129+ if ( ! info . isOpen ) {
130+ this . resolvedModuleNames . remove ( info . path ) ;
131+ this . resolvedTypeReferenceDirectives . remove ( info . path ) ;
132+ }
133+ }
134+
135+ removeRoot ( info : ScriptInfo ) {
136+ this . resolvedModuleNames . remove ( info . path ) ;
137+ this . resolvedTypeReferenceDirectives . remove ( info . path ) ;
138+ }
139+
140+ resolvePath ( path : string ) : string {
141+ return this . host . resolvePath ( path ) ;
142+ }
143+
144+ fileExists ( path : string ) : boolean {
145+ return this . host . fileExists ( path ) ;
146+ }
147+
148+ directoryExists ( path : string ) : boolean {
149+ return this . host . directoryExists ( path ) ;
150+ }
151+
152+ readFile ( fileName : string ) : string {
153+ return this . host . readFile ( fileName ) ;
154+ }
155+
156+ getDirectories ( path : string ) : string [ ] {
157+ return this . host . getDirectories ( path ) ;
158+ }
159+ }
160+ }
0 commit comments