@@ -16,73 +16,135 @@ export function activate(context: ExtensionContext) {
1616 extensionContext = context
1717 outputChannel = vscode . window . createOutputChannel ( 'Dotty Language Client' ) ;
1818
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 , "" , _ => { } )
3363 }
64+ } )
65+ }
66+ }
67+ }
3468
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 ) => {
3575 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" ]
3878 } )
3979 } )
40- } else {
41- fetchAndRun ( artifact )
4280 }
4381 } )
4482}
4583
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+ }
5389
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 = [
5696 "-jar" , coursierPath ,
5797 "fetch" ,
5898 "-p" ,
5999 artifact
60- ] )
61- const coursierProc = coursierPromise . childProcess
100+ ] . concat ( extra )
101+ const coursierPromise = cpp . spawn ( "java" , args )
102+ const coursierProc = coursierPromise . childProcess
62103
63- let classPath = ""
104+ let classPath = ""
64105
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 } )
71118 } )
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+ ] )
72137
73- coursierProc . on ( 'close' , ( code : number ) => {
138+ const sbtProc = sbtPromise . childProcess
139+ sbtProc . on ( 'close' , ( code : number ) => {
74140 if ( code != 0 ) {
75- let msg = "Fetching the language server failed."
141+ const msg = "Configuring the IDE failed."
76142 outputChannel . append ( msg )
77143 throw new Error ( msg )
78144 }
79-
80- run ( {
81- command : "java" ,
82- args : [ "-classpath" , classPath , "dotty.tools.languageserver.Main" , "-stdio" ]
83- } )
84145 } )
85- return coursierPromise
146+
147+ return sbtPromise
86148 } )
87149}
88150
0 commit comments