3
3
4
4
'use strict' ;
5
5
6
+ import * as assert from 'assert' ;
6
7
import { inject , injectable } from 'inversify' ;
7
- import * as semver from 'semver' ;
8
- import { IApplicationEnvironment , IWorkspaceService } from '../../common/application/types' ;
8
+ import * as path from 'path' ;
9
+ import { SemVer } from 'semver' ;
10
+ import { IWorkspaceService } from '../../common/application/types' ;
9
11
import { NugetPackage } from '../../common/nuget/types' ;
10
- import { IConfigurationService , Resource } from '../../common/types' ;
12
+ import { IConfigurationService , IExtensions , Resource } from '../../common/types' ;
11
13
import { IServiceContainer } from '../../ioc/types' ;
12
- import { traceWarning } from '../../logging' ;
13
14
import { LanguageServerFolderService } from '../common/languageServerFolderService' ;
14
- import {
15
- BundledLanguageServerFolder ,
16
- FolderVersionPair ,
17
- ILanguageServerFolderService ,
18
- NodeLanguageServerFolder
19
- } from '../types' ;
15
+ import { FolderVersionPair , ILanguageServerFolderService , NodeLanguageServerFolder } from '../types' ;
20
16
21
- // Must match languageServerVersion* keys in package.json
22
- export const NodeLanguageServerVersionKey = 'languageServerVersionV2' ;
17
+ export const PylanceExtensionName = 'ms-python.vscode-pylance' ;
23
18
24
19
class FallbackNodeLanguageServerFolderService extends LanguageServerFolderService {
25
20
constructor ( serviceContainer : IServiceContainer ) {
@@ -31,62 +26,90 @@ class FallbackNodeLanguageServerFolderService extends LanguageServerFolderServic
31
26
}
32
27
}
33
28
29
+ // Exported for testing.
30
+ export interface ILanguageServerFolder {
31
+ path : string ;
32
+ version : string ; // SemVer, in string form to avoid cross-extension type issues.
33
+ }
34
+
35
+ // Exported for testing.
36
+ export interface ILSExtensionApi {
37
+ languageServerFolder ?( ) : Promise < ILanguageServerFolder > ;
38
+ }
39
+
34
40
@injectable ( )
35
41
export class NodeLanguageServerFolderService implements ILanguageServerFolderService {
36
- private readonly _bundledVersion : semver . SemVer | undefined ;
37
42
private readonly fallback : FallbackNodeLanguageServerFolderService ;
38
43
39
44
constructor (
40
45
@inject ( IServiceContainer ) serviceContainer : IServiceContainer ,
41
- @inject ( IConfigurationService ) configService : IConfigurationService ,
42
- @inject ( IWorkspaceService ) workspaceService : IWorkspaceService ,
43
- @inject ( IApplicationEnvironment ) appEnv : IApplicationEnvironment
46
+ @inject ( IConfigurationService ) private configService : IConfigurationService ,
47
+ @inject ( IWorkspaceService ) private workspaceService : IWorkspaceService ,
48
+ @inject ( IExtensions ) readonly extensions : IExtensions
44
49
) {
45
50
this . fallback = new FallbackNodeLanguageServerFolderService ( serviceContainer ) ;
46
-
47
- // downloadLanguageServer is a bit of a misnomer; if false then this indicates that a local
48
- // development copy should be run instead of a "real" build, telemetry discarded, etc.
49
- // So, we require it to be true, even though in the bundled case no real download happens.
50
- if (
51
- configService . getSettings ( ) . downloadLanguageServer &&
52
- ! workspaceService . getConfiguration ( 'python' ) . get < string > ( 'packageName' )
53
- ) {
54
- const ver = appEnv . packageJson [ NodeLanguageServerVersionKey ] as string ;
55
- this . _bundledVersion = semver . parse ( ver ) || undefined ;
56
- if ( this . _bundledVersion === undefined ) {
57
- traceWarning (
58
- `invalid language server version ${ ver } in package.json (${ NodeLanguageServerVersionKey } )`
59
- ) ;
60
- }
61
- }
62
- }
63
-
64
- public get bundledVersion ( ) : semver . SemVer | undefined {
65
- return this . _bundledVersion ;
66
51
}
67
52
68
- public isBundled ( ) : boolean {
69
- return this . _bundledVersion !== undefined ;
53
+ public async skipDownload ( ) : Promise < boolean > {
54
+ return ( await this . lsExtensionApi ( ) ) !== undefined ;
70
55
}
71
56
72
57
public async getLanguageServerFolderName ( resource : Resource ) : Promise < string > {
73
- if ( this . _bundledVersion ) {
74
- return BundledLanguageServerFolder ;
58
+ const lsf = await this . languageServerFolder ( ) ;
59
+ if ( lsf ) {
60
+ assert . ok ( path . isAbsolute ( lsf . path ) ) ;
61
+ return lsf . path ;
75
62
}
76
63
return this . fallback . getLanguageServerFolderName ( resource ) ;
77
64
}
78
65
79
66
public async getLatestLanguageServerVersion ( resource : Resource ) : Promise < NugetPackage | undefined > {
80
- if ( this . _bundledVersion ) {
67
+ if ( await this . lsExtensionApi ( ) ) {
81
68
return undefined ;
82
69
}
83
70
return this . fallback . getLatestLanguageServerVersion ( resource ) ;
84
71
}
85
72
86
73
public async getCurrentLanguageServerDirectory ( ) : Promise < FolderVersionPair | undefined > {
87
- if ( this . _bundledVersion ) {
88
- return { path : BundledLanguageServerFolder , version : this . _bundledVersion } ;
74
+ const lsf = await this . languageServerFolder ( ) ;
75
+ if ( lsf ) {
76
+ assert . ok ( path . isAbsolute ( lsf . path ) ) ;
77
+ return {
78
+ path : lsf . path ,
79
+ version : new SemVer ( lsf . version )
80
+ } ;
89
81
}
90
82
return this . fallback . getCurrentLanguageServerDirectory ( ) ;
91
83
}
84
+
85
+ protected async languageServerFolder ( ) : Promise < ILanguageServerFolder | undefined > {
86
+ const extension = await this . lsExtensionApi ( ) ;
87
+ if ( ! extension ?. languageServerFolder ) {
88
+ return undefined ;
89
+ }
90
+ return extension . languageServerFolder ( ) ;
91
+ }
92
+
93
+ private async lsExtensionApi ( ) : Promise < ILSExtensionApi | undefined > {
94
+ // downloadLanguageServer is a bit of a misnomer; if false then this indicates that a local
95
+ // development copy should be run instead of a "real" build, telemetry discarded, etc.
96
+ // So, we require it to be true, even though in the pinned case no real download happens.
97
+ if (
98
+ ! this . configService . getSettings ( ) . downloadLanguageServer ||
99
+ this . workspaceService . getConfiguration ( 'python' ) . get < string > ( 'packageName' )
100
+ ) {
101
+ return undefined ;
102
+ }
103
+
104
+ const extension = this . extensions . getExtension < ILSExtensionApi > ( PylanceExtensionName ) ;
105
+ if ( ! extension ) {
106
+ return undefined ;
107
+ }
108
+
109
+ if ( ! extension . isActive ) {
110
+ return extension . activate ( ) ;
111
+ }
112
+
113
+ return extension . exports ;
114
+ }
92
115
}
0 commit comments