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/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 2863c4e35d0b..1dcac81137ea 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)) } @@ -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) diff --git a/project/Build.scala b/project/Build.scala index 3af659252619..807bed62ad09 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.{ prepareCommand, 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 = prepareCommand(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", ScriptedPlugin.scriptedSettings, ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test", ScriptedPlugin.scriptedBufferLog := false, @@ -924,76 +930,36 @@ 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 // (--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 }, 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 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 exitCode = new java.lang.ProcessBuilder(processArgs: _*) - .inheritIO() - .start() - .waitFor() - if (exitCode != 0) - throw new MessageOnlyException("Running Visual Studio Code failed") + val processArgs = List(s"--extensionDevelopmentPath=${extensionPath}") ++ codeArgs + + 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..8feea02dabe5 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,34 @@ 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(prepareCommand(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(" ") + val description = if (directory != null) s""" in directory "$directory"""" else "" + throw new MessageOnlyException(s"""Running command "${cmdString}"${description} 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 +229,13 @@ object DottyIDEPlugin extends AutoPlugin { override def buildSettings: Seq[Setting[_]] = Seq( commands ++= Seq(configureIDE, compileForIDE), + codeCommand := { + 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") 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/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: 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": [