@@ -16,73 +16,135 @@ export function activate(context: ExtensionContext) {
16
16
extensionContext = context
17
17
outputChannel = vscode . window . createOutputChannel ( 'Dotty Language Client' ) ;
18
18
19
- const artifactFile = `${ vscode . workspace . rootPath } /.dotty-ide-artifact`
20
- fs . readFile ( artifactFile , ( err , data ) => {
21
- if ( err ) {
22
- outputChannel . append ( `Unable to parse ${ artifactFile } ` )
23
- throw err
24
- }
25
- const artifact = data . toString ( ) . trim ( )
26
-
27
- if ( process . env [ 'DLS_DEV_MODE' ] ) {
28
- const portFile = `${ vscode . workspace . rootPath } /.dotty-ide-dev-port`
29
- fs . readFile ( portFile , ( err , port ) => {
30
- if ( err ) {
31
- outputChannel . append ( `Unable to parse ${ portFile } ` )
32
- throw err
19
+ const sbtArtifact = "org.scala-sbt:sbt-launch:1.2.3"
20
+ const buildSbtFile = `${ vscode . workspace . rootPath } /build.sbt`
21
+ const dottyPluginSbtFile = path . join ( extensionContext . extensionPath , './out/dotty-plugin.sbt' )
22
+ const disableDottyIDEFile = `${ vscode . workspace . rootPath } /.dotty-ide-disabled`
23
+ const languageServerArtifactFile = `${ vscode . workspace . rootPath } /.dotty-ide-artifact`
24
+ const languageServerDefaultConfigFile = path . join ( extensionContext . extensionPath , './out/default-dotty-ide-config' )
25
+ const coursierPath = path . join ( extensionContext . extensionPath , './out/coursier' ) ;
26
+
27
+ if ( process . env [ 'DLS_DEV_MODE' ] ) {
28
+ const portFile = `${ vscode . workspace . rootPath } /.dotty-ide-dev-port`
29
+ fs . readFile ( portFile , ( err , port ) => {
30
+ if ( err ) {
31
+ outputChannel . append ( `Unable to parse ${ portFile } ` )
32
+ throw err
33
+ }
34
+
35
+ run ( {
36
+ module : context . asAbsolutePath ( 'out/src/passthrough-server.js' ) ,
37
+ args : [ port . toString ( ) ]
38
+ } )
39
+ } )
40
+
41
+ } else {
42
+ // Check whether `.dotty-ide-artifact` exists. If it does, start the language server,
43
+ // otherwise, try propose to start it if there's no build.sbt
44
+ if ( fs . existsSync ( languageServerArtifactFile ) ) {
45
+ runLanguageServer ( coursierPath , languageServerArtifactFile )
46
+ } else if ( ! fs . existsSync ( disableDottyIDEFile ) && ! fs . existsSync ( buildSbtFile ) ) {
47
+ vscode . window . showInformationMessage (
48
+ "This looks like an unconfigured Scala project. Would you like to start the Dotty IDE?" ,
49
+ "Yes" , "No"
50
+ ) . then ( choice => {
51
+ if ( choice == "Yes" ) {
52
+ fs . readFile ( languageServerDefaultConfigFile , ( err , data ) => {
53
+ if ( err ) throw err
54
+ else {
55
+ const languageServerScalaVersion = data . toString ( ) . trim ( )
56
+ fetchAndConfigure ( coursierPath , sbtArtifact , languageServerScalaVersion , dottyPluginSbtFile ) . then ( ( ) => {
57
+ runLanguageServer ( coursierPath , languageServerArtifactFile )
58
+ } )
59
+ }
60
+ } )
61
+ } else {
62
+ fs . appendFile ( disableDottyIDEFile , "" , _ => { } )
33
63
}
64
+ } )
65
+ }
66
+ }
67
+ }
34
68
69
+ function runLanguageServer ( coursierPath : string , languageServerArtifactFile : string ) {
70
+ fs . readFile ( languageServerArtifactFile , ( err , data ) => {
71
+ if ( err ) throw err
72
+ else {
73
+ const languageServerArtifact = data . toString ( ) . trim ( )
74
+ fetchWithCoursier ( coursierPath , languageServerArtifact ) . then ( ( languageServerClasspath ) => {
35
75
run ( {
36
- module : context . asAbsolutePath ( 'out/src/passthrough-server.js' ) ,
37
- args : [ port . toString ( ) ]
76
+ command : "java" ,
77
+ args : [ "-classpath" , languageServerClasspath , "dotty.tools.languageserver.Main" , "-stdio" ]
38
78
} )
39
79
} )
40
- } else {
41
- fetchAndRun ( artifact )
42
80
}
43
81
} )
44
82
}
45
83
46
- function fetchAndRun ( artifact : string ) {
47
- const coursierPath = path . join ( extensionContext . extensionPath , './out/coursier' ) ;
48
-
49
- vscode . window . withProgress ( {
50
- location : vscode . ProgressLocation . Window ,
51
- title : 'Fetching the Dotty Language Server'
52
- } , ( progress ) => {
84
+ function fetchAndConfigure ( coursierPath : string , sbtArtifact : string , languageServerScalaVersion : string , dottyPluginSbtFile : string ) {
85
+ return fetchWithCoursier ( coursierPath , sbtArtifact ) . then ( ( sbtClasspath ) => {
86
+ return configureIDE ( sbtClasspath , languageServerScalaVersion , dottyPluginSbtFile )
87
+ } )
88
+ }
53
89
54
- const coursierPromise =
55
- cpp . spawn ( "java" , [
90
+ function fetchWithCoursier ( coursierPath : string , artifact : string , extra : string [ ] = [ ] ) {
91
+ return vscode . window . withProgress ( {
92
+ location : vscode . ProgressLocation . Window ,
93
+ title : `Fetching ${ artifact } `
94
+ } , ( progress ) => {
95
+ const args = [
56
96
"-jar" , coursierPath ,
57
97
"fetch" ,
58
98
"-p" ,
59
99
artifact
60
- ] )
61
- const coursierProc = coursierPromise . childProcess
100
+ ] . concat ( extra )
101
+ const coursierPromise = cpp . spawn ( "java" , args )
102
+ const coursierProc = coursierPromise . childProcess
62
103
63
- let classPath = ""
104
+ let classPath = ""
64
105
65
- coursierProc . stdout . on ( 'data' , ( data : Buffer ) => {
66
- classPath += data . toString ( ) . trim ( )
67
- } )
68
- coursierProc . stderr . on ( 'data' , ( data : Buffer ) => {
69
- let msg = data . toString ( )
70
- outputChannel . append ( msg )
106
+ coursierProc . stdout . on ( 'data' , ( data : Buffer ) => {
107
+ classPath += data . toString ( ) . trim ( )
108
+ } )
109
+
110
+ coursierProc . on ( 'close' , ( code : number ) => {
111
+ if ( code != 0 ) {
112
+ let msg = `Couldn't fetch '${ artifact } ' (exit code ${ code } ).`
113
+ outputChannel . append ( msg )
114
+ throw new Error ( msg )
115
+ }
116
+ } )
117
+ return coursierPromise . then ( ( ) => { return classPath } )
71
118
} )
119
+ }
120
+
121
+ function configureIDE ( sbtClasspath : string , languageServerScalaVersion : string , dottyPluginSbtFile : string ) {
122
+ return vscode . window . withProgress ( {
123
+ location : vscode . ProgressLocation . Window ,
124
+ title : 'Configuring the IDE for Dotty...'
125
+ } , ( progress ) => {
126
+
127
+ // Run sbt to configure the IDE. If the `DottyPlugin` is not present, dynamically load it and
128
+ // eventually run `configureIDE`.
129
+ const sbtPromise =
130
+ cpp . spawn ( "java" , [
131
+ "-classpath" , sbtClasspath ,
132
+ "xsbt.boot.Boot" ,
133
+ `--addPluginSbtFile=${ dottyPluginSbtFile } ` ,
134
+ `set every scalaVersion := "${ languageServerScalaVersion } "` ,
135
+ "configureIDE"
136
+ ] )
72
137
73
- coursierProc . on ( 'close' , ( code : number ) => {
138
+ const sbtProc = sbtPromise . childProcess
139
+ sbtProc . on ( 'close' , ( code : number ) => {
74
140
if ( code != 0 ) {
75
- let msg = "Fetching the language server failed."
141
+ const msg = "Configuring the IDE failed."
76
142
outputChannel . append ( msg )
77
143
throw new Error ( msg )
78
144
}
79
-
80
- run ( {
81
- command : "java" ,
82
- args : [ "-classpath" , classPath , "dotty.tools.languageserver.Main" , "-stdio" ]
83
- } )
84
145
} )
85
- return coursierPromise
146
+
147
+ return sbtPromise
86
148
} )
87
149
}
88
150
0 commit comments