From 369fdec6e560fe9a514e32cde68700fbc8b74963 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 8 Jun 2017 17:05:04 +0200 Subject: [PATCH 1/8] DLS: Fix passing classpath in Windows Classpath elements are separated by ';', not ':' on Windows --- .../src/dotty/tools/languageserver/DottyLanguageServer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 2863c4e35d0b..187a555ea4c6 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -64,7 +64,7 @@ class DottyLanguageServer extends LanguageServer myDrivers = new mutable.HashMap for (config <- configs) { - val classpathFlags = List("-classpath", (config.classDirectory +: config.dependencyClasspath).mkString(":")) + val classpathFlags = List("-classpath", (config.classDirectory +: config.dependencyClasspath).mkString(File.pathSeparator)) val settings = defaultFlags ++ config.compilerArguments.toList ++ classpathFlags myDrivers.put(config, new InteractiveDriver(settings)) } From 380be44a49ddeb0f78a8acfe8b389c2edc3a9875 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 9 Jun 2017 12:00:35 +0200 Subject: [PATCH 2/8] DLS: Be better at matching an open file URI to a compiler instance The DLS runs as many compiler instances as there are subprojects in the build . When a file is opened we need to decide which compiler instance should handle it, this is done by matching the file URI against the source directories configured for each subproject. This commit improves this matching in two way: - Use URI#getPath instead of URI#getRawPath to decode percent-encoded characters in the URI - Use File#getCanonicalPath instead of File#getAbsolutePath The first fix is necessary to get the DLS running in Windows, because URIs start with file:///C%3A/.../ and we need getPath to decode "C%3A" to "C:". --- .../src/dotty/tools/languageserver/DottyLanguageServer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 187a555ea4c6..1dcac81137ea 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -76,7 +76,7 @@ class DottyLanguageServer extends LanguageServer def driverFor(uri: URI): InteractiveDriver = { val matchingConfig = drivers.keys.find(config => config.sourceDirectories.exists(sourceDir => - uri.getRawPath.startsWith(sourceDir.getAbsolutePath.toString))) + new File(uri.getPath).getCanonicalPath.startsWith(sourceDir.getCanonicalPath))) matchingConfig match { case Some(config) => drivers(config) From 73a1cbea2522d6e2c44b84fb692529b98eb7206f Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 16 Jun 2017 17:38:15 +0200 Subject: [PATCH 3/8] Be better at starting Code On Windows, use "cmd.exe /C", this seems to be necessary. On every OS, pass "-n" to Code to start a new instance, this makes things more reproducible. --- project/Build.scala | 28 +++++++-------- .../tools/sbtplugin/DottyIDEPlugin.scala | 35 +++++++++++++------ 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 3af659252619..de0a193dbd8a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -15,6 +15,7 @@ import sbt.Package.ManifestAttributes import com.typesafe.sbteclipse.plugin.EclipsePlugin._ import dotty.tools.sbtplugin.DottyPlugin.autoImport._ +import dotty.tools.sbtplugin.DottyIDEPlugin.runProcess import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ import org.scalajs.sbtplugin.ScalaJSPlugin import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ @@ -766,8 +767,13 @@ object Build { val mainClass = "dotty.tools.languageserver.Main" val extensionPath = (baseDirectory in `vscode-dotty`).value.getAbsolutePath - val codeArgs = if (inputArgs.isEmpty) List((baseDirectory.value / "..").getAbsolutePath) else inputArgs - val allArgs = List("-client_command", "code", s"--extensionDevelopmentPath=$extensionPath") ++ codeArgs + val codeArgs = + s"--extensionDevelopmentPath=$extensionPath" +: + (if (inputArgs.isEmpty) List((baseDirectory.value / "..").getAbsolutePath) else inputArgs) + + val clientCommand = codeCommand.value ++ codeArgs + + val allArgs = "-client_command" +: clientCommand runTask(Runtime, mainClass, allArgs: _*) }.dependsOn(compile in (`vscode-dotty`, Compile)).evaluated @@ -893,7 +899,7 @@ object Build { sbtPlugin := true, - version := "0.1.2", + version := "0.1.3-SNAPSHOT", ScriptedPlugin.scriptedSettings, ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test", ScriptedPlugin.scriptedBufferLog := false, @@ -944,12 +950,7 @@ object Build { // Currently, vscode-dotty depends on daltonjorge.scala for syntax highlighting, // this is not automatically installed when starting the extension in development mode // (--extensionDevelopmentPath=...) - val exitCodeInstall = new java.lang.ProcessBuilder("code", "--install-extension", "daltonjorge.scala") - .inheritIO() - .start() - .waitFor() - if (exitCodeInstall != 0) - throw new MessageOnlyException("Installing dependency daltonjorge.scala failed") + runProcess(codeCommand.value ++ Seq("--install-extension", "daltonjorge.scala"), wait = true) sbt.inc.Analysis.Empty }, @@ -986,14 +987,9 @@ object Build { val inputArgs = spaceDelimited("").parsed val codeArgs = if (inputArgs.isEmpty) List((baseDirectory.value / "..").getAbsolutePath) else inputArgs val extensionPath = baseDirectory.value.getAbsolutePath - val processArgs = List("code", s"--extensionDevelopmentPath=${extensionPath}") ++ codeArgs + val processArgs = List(s"--extensionDevelopmentPath=${extensionPath}") ++ codeArgs - val exitCode = new java.lang.ProcessBuilder(processArgs: _*) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("Running Visual Studio Code failed") + runProcess(codeCommand.value ++ processArgs, wait = true) }.dependsOn(compile in Compile).evaluated ) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala index 1e4226d48aa3..1d17f25c684f 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala @@ -5,6 +5,7 @@ import sbt.Keys._ import java.io._ import java.lang.ProcessBuilder import scala.collection.mutable +import scala.util.Properties.{ isWin, isMac } import dotty.tools.languageserver.config.ProjectConfig @@ -123,9 +124,24 @@ object DottyIDEPlugin extends AutoPlugin { runTask(joinedTask, state) } + def runProcess(cmd: Seq[String], wait: Boolean = false, directory: File = null): Unit = { + val pb0 = new ProcessBuilder(cmd: _*).inheritIO() + val pb = if (directory != null) pb0.directory(directory) else pb0 + if (wait) { + val exitCode = pb.start().waitFor() + if (exitCode != 0) { + val cmdString = cmd.mkString(" ") + throw new MessageOnlyException("""Running command "${cmdString}" failed.""") + } + } + else + pb.start() + } + private val projectConfig = taskKey[Option[ProjectConfig]]("") object autoImport { + val codeCommand = taskKey[Seq[String]]("Command to start VSCode") val runCode = taskKey[Unit]("Start VSCode, usually called from launchIDE") val launchIDE = taskKey[Unit]("Configure and run VSCode on this project") } @@ -203,17 +219,16 @@ object DottyIDEPlugin extends AutoPlugin { override def buildSettings: Seq[Setting[_]] = Seq( commands ++= Seq(configureIDE, compileForIDE), + codeCommand := { + if (isWin) + Seq("cmd.exe", "/C", "code", "-n") + else + Seq("code", "-n") + }, + runCode := { - val exitCode = new ProcessBuilder("code", "--install-extension", "lampepfl.dotty") - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("Installing the Dotty support for VSCode failed") - - new ProcessBuilder("code", baseDirectory.value.getAbsolutePath) - .inheritIO() - .start() + runProcess(codeCommand.value ++ Seq("--install-extension", "lampepfl.dotty"), wait = true) + runProcess(codeCommand.value ++ Seq("."), directory = baseDirectory.value) } ) ++ addCommandAlias("launchIDE", ";configureIDE;runCode") From 21f8f75e1ffb0967a96ab8024f72cc584e4d308e Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 16 Jun 2017 17:48:55 +0200 Subject: [PATCH 4/8] Build.scala: Use newly-added runProcess where possible --- project/Build.scala | 42 +++---------------- .../tools/sbtplugin/DottyIDEPlugin.scala | 3 +- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index de0a193dbd8a..ac35fa1d0102 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -930,22 +930,10 @@ object Build { compile in Compile := { val coursier = baseDirectory.value / "out/coursier" val packageJson = baseDirectory.value / "package.json" - if (!coursier.exists || packageJson.lastModified > coursier.lastModified) { - val exitCode = new java.lang.ProcessBuilder("npm", "run", "update-all") - .directory(baseDirectory.value) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("'npm run update-all' in vscode-dotty failed") - } + if (!coursier.exists || packageJson.lastModified > coursier.lastModified) + runProcess(Seq("npm", "run", "update-all"), wait = true, directory = baseDirectory.value) val tsc = baseDirectory.value / "node_modules" / ".bin" / "tsc" - val exitCodeTsc = new java.lang.ProcessBuilder(tsc.getAbsolutePath, "--pretty", "--project", baseDirectory.value.getAbsolutePath) - .inheritIO() - .start() - .waitFor() - if (exitCodeTsc != 0) - throw new MessageOnlyException("tsc in vscode-dotty failed") + runProcess(Seq(tsc.getAbsolutePath, "--pretty", "--project", baseDirectory.value.getAbsolutePath), wait = true) // Currently, vscode-dotty depends on daltonjorge.scala for syntax highlighting, // this is not automatically installed when starting the extension in development mode @@ -955,33 +943,15 @@ object Build { sbt.inc.Analysis.Empty }, sbt.Keys.`package`:= { - val exitCode = new java.lang.ProcessBuilder("vsce", "package") - .directory(baseDirectory.value) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("vsce package failed") + runProcess(Seq("vsce", "package"), wait = true, directory = baseDirectory.value) baseDirectory.value / s"dotty-${version.value}.vsix" }, unpublish := { - val exitCode = new java.lang.ProcessBuilder("vsce", "unpublish") - .directory(baseDirectory.value) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("vsce unpublish failed") + runProcess(Seq("vsce", "unpublish"), wait = true, directory = baseDirectory.value) }, publish := { - val exitCode = new java.lang.ProcessBuilder("vsce", "publish") - .directory(baseDirectory.value) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("vsce publish failed") + runProcess(Seq("vsce", "publish"), wait = true, directory = baseDirectory.value) }, run := Def.inputTask { val inputArgs = spaceDelimited("").parsed diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala index 1d17f25c684f..ca0843bd3da0 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala @@ -131,7 +131,8 @@ object DottyIDEPlugin extends AutoPlugin { val exitCode = pb.start().waitFor() if (exitCode != 0) { val cmdString = cmd.mkString(" ") - throw new MessageOnlyException("""Running command "${cmdString}" failed.""") + val description = if (directory != null) s""" in directory "$directory"""" else "" + throw new MessageOnlyException(s"""Running command "${cmdString}"${description} failed.""") } } else From 00b817507792e4a36e22b257862c2328dcf72e2f Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 16 Jun 2017 18:42:15 +0200 Subject: [PATCH 5/8] Run all commands properly on Windows, not just code --- project/Build.scala | 4 ++-- .../dotty/tools/sbtplugin/DottyIDEPlugin.scala | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index ac35fa1d0102..2fedd5472b9e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -15,7 +15,7 @@ import sbt.Package.ManifestAttributes import com.typesafe.sbteclipse.plugin.EclipsePlugin._ import dotty.tools.sbtplugin.DottyPlugin.autoImport._ -import dotty.tools.sbtplugin.DottyIDEPlugin.runProcess +import dotty.tools.sbtplugin.DottyIDEPlugin.{ prepareCommand, runProcess } import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ import org.scalajs.sbtplugin.ScalaJSPlugin import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ @@ -771,7 +771,7 @@ object Build { s"--extensionDevelopmentPath=$extensionPath" +: (if (inputArgs.isEmpty) List((baseDirectory.value / "..").getAbsolutePath) else inputArgs) - val clientCommand = codeCommand.value ++ codeArgs + val clientCommand = prepareCommand(codeCommand.value ++ codeArgs) val allArgs = "-client_command" +: clientCommand diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala index ca0843bd3da0..8feea02dabe5 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala @@ -124,8 +124,17 @@ object DottyIDEPlugin extends AutoPlugin { runTask(joinedTask, state) } + /** Prepare command to be passed to ProcessBuilder */ + def prepareCommand(cmd: Seq[String]): Seq[String] = + if (isWin) Seq("cmd.exe", "/C") ++ cmd + else cmd + + /** Run `cmd`. + * @param wait If true, wait for `cmd` to return and throw an exception if the exit code is non-zero. + * @param directory If not null, run `cmd` in this directory. + */ def runProcess(cmd: Seq[String], wait: Boolean = false, directory: File = null): Unit = { - val pb0 = new ProcessBuilder(cmd: _*).inheritIO() + val pb0 = new ProcessBuilder(prepareCommand(cmd): _*).inheritIO() val pb = if (directory != null) pb0.directory(directory) else pb0 if (wait) { val exitCode = pb.start().waitFor() @@ -221,10 +230,7 @@ object DottyIDEPlugin extends AutoPlugin { commands ++= Seq(configureIDE, compileForIDE), codeCommand := { - if (isWin) - Seq("cmd.exe", "/C", "code", "-n") - else - Seq("code", "-n") + Seq("code", "-n") }, runCode := { From 2b1bb3ef7762ceb833ca675b12d4db11dcad544a Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 16 Jun 2017 18:57:56 +0200 Subject: [PATCH 6/8] Fix vscode-dotty/compile on Windows We cannot use "mkdir -p" because it doesn't work on Windows. Instead, add the vscode-dotty/out directory to git. --- vscode-dotty/.vscodeignore | 1 + vscode-dotty/out/.keep | 0 vscode-dotty/package.json | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 vscode-dotty/out/.keep diff --git a/vscode-dotty/.vscodeignore b/vscode-dotty/.vscodeignore index 560bfc0d5a8e..90a501fbe5a4 100644 --- a/vscode-dotty/.vscodeignore +++ b/vscode-dotty/.vscodeignore @@ -3,6 +3,7 @@ target/** .vscode/** .vscode-test/** out/test/** +out/.keep test/** src/** **/*.map diff --git a/vscode-dotty/out/.keep b/vscode-dotty/out/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/vscode-dotty/package.json b/vscode-dotty/package.json index ccd7d289b2c7..b0196eac500c 100644 --- a/vscode-dotty/package.json +++ b/vscode-dotty/package.json @@ -35,7 +35,7 @@ "tsc": "./node_modules/.bin/tsc", "vscode:prepublish": "npm run update-all && ./node_modules/.bin/tsc -p ./", "compile": "./node_modules/.bin/tsc -p ./", - "update-all": "npm install && node ./node_modules/vscode/bin/install && mkdir -p out && curl -L -o out/coursier https://github.com/coursier/coursier/raw/v1.0.0-RC3/coursier", + "update-all": "npm install && node ./node_modules/vscode/bin/install && curl -L -o out/coursier https://github.com/coursier/coursier/raw/v1.0.0-RC3/coursier", "test": "node ./node_modules/vscode/bin/test" }, "extensionDependencies": [ From ee7e327612efbcbc4505844ae6e0e4d1da807664 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 16 Jun 2017 19:19:44 +0200 Subject: [PATCH 7/8] Release sbt-dotty 0.1.3 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 2fedd5472b9e..807bed62ad09 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -899,7 +899,7 @@ object Build { sbtPlugin := true, - version := "0.1.3-SNAPSHOT", + version := "0.1.3", ScriptedPlugin.scriptedSettings, ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test", ScriptedPlugin.scriptedBufferLog := false, From d74757fa55add536f8020ad576cc41f0273f89d1 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 17 Jun 2017 18:26:22 +0200 Subject: [PATCH 8/8] Add instructions for setting up code on Mac --- docs/docs/usage/ide-support.md | 4 +++- vscode-dotty/README.md | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/docs/usage/ide-support.md b/docs/docs/usage/ide-support.md index 26818fea42cc..00183349ae0d 100644 --- a/docs/docs/usage/ide-support.md +++ b/docs/docs/usage/ide-support.md @@ -18,7 +18,9 @@ Usage ===== 1. Install [Visual Studio Code](https://code.visualstudio.com/). 2. Make sure `code`, the binary for Visual Studio Code, is on your `$PATH`, this - is the case if you can start the IDE by running `code` in a terminal. + is the case if you can start the IDE by running `code` in a terminal. This + is the default on all systems except Mac where you'll need to follow these + instructions: https://code.visualstudio.com/docs/setup/mac#_command-line 3. In your project, run: ```shell sbt launchIDE diff --git a/vscode-dotty/README.md b/vscode-dotty/README.md index 316a1e8ce82f..f16cc1c415b2 100644 --- a/vscode-dotty/README.md +++ b/vscode-dotty/README.md @@ -6,7 +6,10 @@ Dotty, please follow the instructions at https://github.com/lampepfl/dotty-examp ## Starting Visual Studio Code from sbt First, make sure `code`, the binary for Visual Studio Code, is on your `$PATH`, -this is the case if you can start the IDE by running `code` in a terminal. +this is the case if you can start the IDE by running `code` in a terminal. This +is the default on all systems except Mac where you'll need to follow these +instructions: https://code.visualstudio.com/docs/setup/mac#_command-line + If this is the case and your project succesfully compiles with dotty, you can simply use the `launchIDE` command provided by the sbt-dotty plugin: