diff --git a/.travis.yml b/.travis.yml index 764e357bc..c90680c65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ matrix: - os: osx env: - VSCODE_VERSION="1.36.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" +before_install: +- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export MINIFY="true"; fi script: - scripts/ci.bash before_deploy: diff --git a/Dockerfile b/Dockerfile index 6d96dd85c..8a0ab698e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,14 +13,10 @@ RUN npm install -g yarn@1.13 WORKDIR /src COPY . . - -# In the future, we can use https://github.com/yarnpkg/rfcs/pull/53 to make -# yarn use the node_modules directly which should be fast as it is slow because -# it populates its own cache every time. RUN yarn \ - && yarn build "${codeServerVersion}" "${vscodeVersion}" linux x64 \ - && yarn binary "${codeServerVersion}" "${vscodeVersion}" linux x64 \ - && mv "/src/build/code-server${codeServerVersion}-vsc${vscodeVersion}-linux-x64" /src/build/code-server + && yarn build "${vscodeVersion}" "${codeServerVersion}" \ + && yarn binary "${vscodeVersion}" "${codeServerVersion}" \ + && mv "/src/build/code-server${codeServerVersion}-vsc${vscodeVersion}-linux-x86_64-built/code-server${codeServerVersion}-vsc${vscodeVersion}-linux-x86_64" /src/build/code-server # We deploy with ubuntu so that devs have a familiar environment. FROM ubuntu:18.04 diff --git a/README.md b/README.md index dc56a88b9..f1d91d25f 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ arguments when launching code-server with Docker. See it will build in this directory which will cause issues because `yarn watch` will try to compile the build directory as well. - For now `@coder/nbin` is a global dependency. -- Run `yarn build ${codeServerVersion} ${vscodeVersion} ${target} ${arch}` in - this directory (for example: `yarn build development 1.36.0 linux x64`). +- Run `yarn build ${vscodeVersion} ${codeServerVersion}` in this directory (for + example: `yarn build 1.36.0 development`). - If you target the same VS Code version our Travis builds do everything will work but if you target some other version it might not (we have to do some patching to VS Code so different versions aren't always compatible). @@ -93,6 +93,10 @@ yarn start # Visit http://localhost:8443 ``` +If you run into issues about a different version of Node being used, try running +`npm rebuild` in the VS Code directory and ignore the error at the end from +`vscode-ripgrep`. + ### Upgrading VS Code We have to patch VS Code to provide and fix some functionality. As the web portion of VS Code matures, we'll be able to shrink and maybe even entirely @@ -121,6 +125,7 @@ Our changes include: - Get telemetry working by adding a channel for it. - Change a regular expression used for mnemonics so it works on Firefox. - Make it possible for us to load code on the client. +- Modify the build process to include our code. ## License [MIT](LICENSE) diff --git a/package.json b/package.json index 074790709..9d2dcb14c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "watch": "yarn ensure-in-vscode && cd ../../../ && yarn watch", "build": "bash ./scripts/tasks.bash build", "package": "bash ./scripts/tasks.bash package", - "vstar": "bash ./scripts/tasks.bash vstar", + "package-prebuilt": "bash ./scripts/tasks.bash package-prebuilt", "binary": "bash ./scripts/tasks.bash binary", "patch:generate": "yarn ensure-in-vscode && cd ../../../ && git diff --staged > ./src/vs/server/scripts/vscode.patch", "patch:apply": "yarn ensure-in-vscode && cd ../../../ && git apply ./src/vs/server/scripts/vscode.patch" diff --git a/scripts/ci.bash b/scripts/ci.bash index 354647c06..5c94137ca 100755 --- a/scripts/ci.bash +++ b/scripts/ci.bash @@ -23,7 +23,7 @@ function docker-build() { function docker-exec() { local command="${1}" ; shift - local args="'${codeServerVersion}' '${vscodeVersion}' '${target}' '${arch}'" + local args="'${vscodeVersion}' '${codeServerVersion}'" docker exec "${containerId}" \ bash -c "cd /src && CI=true yarn ${command} ${args}" } @@ -41,8 +41,7 @@ function docker-build() { function local-build() { function local-exec() { local command="${1}" ; shift - CI=true yarn "${command}" \ - "${codeServerVersion}" "${vscodeVersion}" "${target}" "${arch}" + CI=true yarn "${command}" "${vscodeVersion}" "${codeServerVersion}" } local-exec build @@ -56,7 +55,6 @@ function main() { local vscodeVersion="${VSCODE_VERSION:-}" local ostype="${OSTYPE:-}" local target="${TARGET:-}" - local arch=x64 if [[ -z "${codeServerVersion}" ]] ; then >&2 echo "Must set VERSION environment variable"; exit 1 diff --git a/scripts/nbin.js b/scripts/nbin.js index 657e88936..119267294 100644 --- a/scripts/nbin.js +++ b/scripts/nbin.js @@ -1,11 +1,10 @@ -/* global require, __dirname, process */ const { Binary } = require("@coder/nbin"); const fs = require("fs"); const path = require("path"); -const target = process.argv[2]; -const arch = process.argv[3]; -const source = process.argv[4]; +const source = process.argv[2]; +const target = process.argv[3]; +const binaryName = process.argv[4]; const bin = new Binary({ mainFile: path.join(source, "out/vs/server/main.js"), @@ -15,7 +14,7 @@ const bin = new Binary({ bin.writeFiles(path.join(source, "**")); bin.build().then((binaryData) => { - const outputPath = path.join(source, "code-server"); + const outputPath = path.join(source, binaryName); fs.writeFileSync(outputPath, binaryData); fs.chmodSync(outputPath, "755"); }).catch((ex) => { diff --git a/scripts/tasks.bash b/scripts/tasks.bash index 7412140f4..79f09d2f5 100755 --- a/scripts/tasks.bash +++ b/scripts/tasks.bash @@ -1,5 +1,5 @@ #!/bin/bash -set -euo pipefail +set -euox pipefail function log() { local message="${1}" ; shift @@ -11,27 +11,12 @@ function log() { fi } -function exit-if-ci() { - if [[ -n "${ci}" ]] ; then - log "Pre-built VS Code ${vscodeVersion}-${target}-${arch} is incorrectly built" "error" - exit 1 - fi -} - # Copy code-server into VS Code along with its dependencies. function copy-server() { - log "Applying patch" - cd "${vscodeSourcePath}" - git reset --hard - git clean -fd - git apply "${rootPath}/scripts/vscode.patch" - - local serverPath="${vscodeSourcePath}/src/vs/server" + local serverPath="${sourcePath}/src/vs/server" rm -rf "${serverPath}" mkdir -p "${serverPath}" - log "Copying code-server code" - cp -r "${rootPath}/src" "${serverPath}" cp -r "${rootPath}/typings" "${serverPath}" cp "${rootPath}/main.js" "${serverPath}" @@ -39,14 +24,11 @@ function copy-server() { cp "${rootPath}/yarn.lock" "${serverPath}" if [[ -d "${rootPath}/node_modules" ]] ; then - log "Copying code-server build dependencies" cp -r "${rootPath}/node_modules" "${serverPath}" else - log "Installing code-server build dependencies" - cd "${serverPath}" # Ignore scripts to avoid also installing VS Code dependencies which has # already been done. - yarn --ignore-scripts + cd "${serverPath}" && yarn --ignore-scripts rm -r node_modules/@types/node # I keep getting type conflicts fi @@ -58,164 +40,113 @@ function copy-server() { # Prepend the nbin shim which enables finding files within the binary. function prepend-loader() { - local filePath="${codeServerBuildPath}/${1}" ; shift + local filePath="${buildPath}/${1}" ; shift cat "${rootPath}/scripts/nbin-shim.js" "${filePath}" > "${filePath}.temp" mv "${filePath}.temp" "${filePath}" # Using : as the delimiter so the escaping here is easier to read. # ${parameter/pattern/string}, so the pattern is /: (if the pattern starts # with / it matches all instances) and the string is \\: (results in \:). if [[ "${target}" == "darwin" ]] ; then - sed -i "" -e "s:{{ROOT_PATH}}:${codeServerBuildPath//:/\\:}:g" "${filePath}" + sed -i "" -e "s:{{ROOT_PATH}}:${buildPath//:/\\:}:g" "${filePath}" else - sed -i "s:{{ROOT_PATH}}:${codeServerBuildPath//:/\\:}:g" "${filePath}" + sed -i "s:{{ROOT_PATH}}:${buildPath//:/\\:}:g" "${filePath}" fi } # Copy code-server into VS Code then build it. function build-code-server() { copy-server + local min="" + if [[ -n "${minify}" ]] ; then + min="-min" + yarn gulp minify-vscode --max-old-space-size=32384 + else + yarn gulp optimize-vscode --max-old-space-size=32384 + fi - # TODO: look into making it do the full minified build for just our code - # (basically just want to skip extensions, target our server code, and get - # the same type of build you get with the vscode-linux-x64-min task). - # Something like: yarn gulp "vscode-server-${target}-${arch}-min" - log "Building code-server" - yarn gulp compile-client + rm -rf "${buildPath}" + mkdir -p "${buildPath}" - rm -rf "${codeServerBuildPath}" - mkdir -p "${codeServerBuildPath}" + # Rebuild to make sure native modules work on the target system. + cp "${sourcePath}/remote/"{package.json,yarn.lock,.yarnrc} "${buildPath}" + cd "${buildPath}" && yarn --production --force --build-from-source + rm "${buildPath}/"{package.json,yarn.lock,.yarnrc} local json="{\"codeServerVersion\": \"${codeServerVersion}\"}" + cp -r "${sourcePath}/.build/extensions" "${buildPath}" + node "${rootPath}/scripts/merge.js" "${sourcePath}/package.json" "${rootPath}/scripts/package.json" "${buildPath}/package.json" "${json}" + node "${rootPath}/scripts/merge.js" "${sourcePath}/product.json" "${rootPath}/scripts/product.json" "${buildPath}/product.json" + cp -r "${sourcePath}/out-vscode${min}" "${buildPath}/out" - cp -r "${vscodeBuildPath}/resources/app/extensions" "${codeServerBuildPath}" - node "${rootPath}/scripts/merge.js" "${vscodeBuildPath}/resources/app/package.json" "${rootPath}/scripts/package.json" "${codeServerBuildPath}/package.json" "${json}" - node "${rootPath}/scripts/merge.js" "${vscodeBuildPath}/resources/app/product.json" "${rootPath}/scripts/product.json" "${codeServerBuildPath}/product.json" - cp -r "${vscodeSourcePath}/out" "${codeServerBuildPath}" - rm -rf "${codeServerBuildPath}/out/vs/server/typings" - - # Rebuild to make sure the native modules work since at the moment all the - # pre-built packages are from one Linux system which compiles against the - # latest glibc. This means you must build on the target system. - log "Installing remote dependencies" - cd "${vscodeSourcePath}/remote" - yarn --production --force --build-from-source - mv "${vscodeSourcePath}/remote/node_modules" "${codeServerBuildPath}" - - # Only keep the production dependencies. - cd "${codeServerBuildPath}/out/vs/server" - yarn --production --ignore-scripts + # Only keep production dependencies for the server. + cp "${rootPath}/"{package.json,yarn.lock} "${buildPath}/out/vs/server" + cd "${buildPath}/out/vs/server" && yarn --production --ignore-scripts + rm "${buildPath}/out/vs/server/"{package.json,yarn.lock} prepend-loader "out/vs/server/main.js" prepend-loader "out/bootstrap-fork.js" - log "Final build: ${codeServerBuildPath}" + log "Final build: ${buildPath}" } -# Build VS Code if it hasn't already been built. If we're in the CI and it's -# not fully built, error and exit. -function build-vscode() { - if [[ ! -d "${vscodeSourcePath}" ]] ; then - exit-if-ci - log "${vscodeSourceName} does not exist, cloning" - git clone https://github.com/microsoft/vscode --quiet \ - --branch "${vscodeVersion}" --single-branch --depth=1 \ - "${vscodeSourcePath}" - else - log "${vscodeSourceName} already exists, skipping clone" - fi - - cd "${vscodeSourcePath}" - - if [[ ! -d "${vscodeSourcePath}/node_modules" ]] ; then - exit-if-ci - log "Installing VS Code dependencies" - yarn - - # Keep just what we need to keep the pre-built archive smaller. - rm -rf "${vscodeSourcePath}/test" - rm -rf "${vscodeSourcePath}/remote/node_modules" # Will rebuild. - else - log "${vscodeSourceName}/node_modules already exists, skipping install" - fi - - if [[ ! -d "${vscodeBuildPath}" ]] ; then - exit-if-ci - log "${vscodeBuildName} does not exist, building" - local builtPath="${buildPath}/VSCode-${target}-${arch}" - rm -rf "${builtPath}" - yarn gulp "vscode-${target}-${arch}-min" --max-old-space-size=32384 - mkdir -p "${vscodeBuildPath}/resources/app" - # Copy just what we need to keep the pre-built archive smaller. - mv "${builtPath}/resources/app/extensions" "${vscodeBuildPath}/resources/app" - mv "${builtPath}/resources/app/"*.json "${vscodeBuildPath}/resources/app" - rm -rf "${builtPath}" - else - log "${vscodeBuildName} already exists, skipping build" - fi -} - -# Download VS Code with either curl or wget depending on which is available. -function download-vscode() { - cd "${buildPath}" +# Download and extract a tar from a URL with either curl or wget depending on +# which is available. +function download-tar() { + local url="${1}" ; shift if command -v wget &> /dev/null ; then - log "Attempting to download ${tarName} with wget" - wget "${vsSourceUrl}" --quiet --output-document "${tarName}" + wget "${url}" --quiet -O - | tar -C "${stagingPath}" -xz else - log "Attempting to download ${tarName} with curl" - curl "${vsSourceUrl}" --silent --fail --output "${tarName}" + curl "${url}" --silent --fail | tar -C "${stagingPath}" -xz fi } -# Download pre-built VS Code if necessary. Build if there is no available -# download but not when in the CI. The pre-built package basically just -# provides us the dependencies and extensions so we don't have to install and -# build them respectively which takes a long time. -function prepare-vscode() { - if [[ ! -d "${vscodeBuildPath}" || ! -d "${vscodeSourcePath}" ]] ; then - mkdir -p "${buildPath}" - # TODO: for now everything uses the Linux build and we rebuild the modules. - # This means you must build on the target system. - local tarName="vstar-${vscodeVersion}-${target}-${arch}.tar.gz" - local linuxTarName="vstar-${vscodeVersion}-linux-${arch}.tar.gz" - local linuxVscodeBuildName="vscode-${vscodeVersion}-linux-${arch}-built" - local vsSourceUrl="https://codesrv-ci.cdr.sh/${linuxTarName}" - if download-vscode ; then - cd "${buildPath}" - rm -rf "${vscodeBuildPath}" - tar -xzf "${tarName}" - rm "${tarName}" - if [[ "${target}" != "linux" ]] ; then - mv "${linuxVscodeBuildName}" "${vscodeBuildName}" - fi - elif [[ -n "${ci}" ]] ; then - log "Pre-built VS Code ${vscodeVersion}-${target}-${arch} does not exist" "error" +# Download a pre-built package. If it doesn't exist and we are in the CI, exit. +# Otherwise the return will be whether it existed or not. The pre-built package +# is provided to reduce CI build time. +function download-pre-built() { + local archiveName="${1}" ; shift + local url="https://codesrv-ci.cdr.sh/${archiveName}" + if ! download-tar "${url}" ; then + if [[ -n "${ci}" ]] ; then + log "${url} does not exist" "error" exit 1 - else - log "${tarName} does not exist, building" - build-vscode - return fi - else - log "VS Code is already downloaded or built" + return 1 fi - - log "Ensuring VS Code is fully built" - build-vscode + return 0 } +# Fully build code-server. function build-task() { - prepare-vscode + mkdir -p "${stagingPath}" + if [[ ! -d "${sourcePath}" ]] ; then + if ! download-pre-built "vscode-${vscodeVersion}.tar.gz" ; then + git clone https://github.com/microsoft/vscode --quiet \ + --branch "${vscodeVersion}" --single-branch --depth=1 \ + "${sourcePath}" + fi + fi + cd "${sourcePath}" + git reset --hard && git clean -fd + git apply "${rootPath}/scripts/vscode.patch" + if [[ ! -d "${sourcePath}/node_modules" ]] ; then + if [[ -n "${ci}" ]] ; then + log "Pre-built VS Code ${vscodeVersion} has no node_modules" "error" + exit 1 + fi + yarn + fi + if [[ ! -d "${sourcePath}/.build/extensions" ]] ; then + if [[ -n "${ci}" ]] ; then + log "Pre-built VS Code ${vscodeVersion} has no built extensions" "error" + exit 1 + fi + yarn gulp extensions-build-package --max-old-space-size=32384 + fi build-code-server } -function vstar-task() { - local archivePath="${releasePath}/vstar-${vscodeVersion}-${target}-${arch}.tar.gz" - rm -f "${archivePath}" - mkdir -p "${releasePath}" - tar -C "${buildPath}" -czf "${archivePath}" "${vscodeSourceName}" "${vscodeBuildName}" - log "Archive: ${archivePath}" -} - +# Package the binary into a tar or zip for release. function package-task() { local archivePath="${releasePath}/${binaryName}" rm -rf "${archivePath}" @@ -223,8 +154,8 @@ function package-task() { cp "${buildPath}/${binaryName}" "${archivePath}/code-server" cp "${rootPath}/README.md" "${archivePath}" - cp "${vscodeSourcePath}/LICENSE.txt" "${archivePath}" - cp "${vscodeSourcePath}/ThirdPartyNotices.txt" "${archivePath}" + cp "${sourcePath}/LICENSE.txt" "${archivePath}" + cp "${sourcePath}/ThirdPartyNotices.txt" "${archivePath}" cd "${releasePath}" if [[ "${target}" == "darwin" ]] ; then @@ -236,50 +167,37 @@ function package-task() { fi } -# Package built code into a binary. +# Bundle built code into a binary. function binary-task() { # I had trouble getting VS Code to build with the @coder/nbin dependency due # to the types it installs (tons of conflicts), so for now it's a global # dependency. cd "${rootPath}" npm link @coder/nbin - node "${rootPath}/scripts/nbin.js" "${target}" "${arch}" "${codeServerBuildPath}" + node "${rootPath}/scripts/nbin.js" "${buildPath}" "${target}" "${binaryName}" rm node_modules/@coder/nbin - mv "${codeServerBuildPath}/code-server" "${buildPath}/${binaryName}" log "Binary: ${buildPath}/${binaryName}" } # Check if it looks like we are inside VS Code. function in-vscode () { - log "Checking if we are inside VS Code" local dir="${1}" ; shift - - local maybeVscode + local maybeVsCode local dirName - maybeVscode="$(realpath "${dir}/../../..")" - dirName="$(basename "${maybeVscode}")" - + maybeVsCode="$(realpath "${dir}/../../..")" + dirName="$(basename "${maybeVsCode}")" if [[ "${dirName}" != "vscode" ]] ; then return 1 fi - if [[ ! -f "${maybeVscode}/package.json" ]] ; then + if [[ ! -f "${maybeVsCode}/package.json" ]] ; then return 1 fi - if ! grep '"name": "code-oss-dev"' "${maybeVscode}/package.json" --quiet ; then + if ! grep '"name": "code-oss-dev"' "${maybeVsCode}/package.json" --quiet ; then return 1 fi - return 0 } -function ensure-in-vscode-task() { - if ! in-vscode "${rootPath}"; then - log "Not in vscode" "error" - exit 1 - fi - exit 0 -} - function main() { local relativeRootPath local rootPath @@ -288,52 +206,55 @@ function main() { local task="${1}" ; shift if [[ "${task}" == "ensure-in-vscode" ]] ; then - ensure-in-vscode-task + if ! in-vscode "${rootPath}"; then + log "Not in VS Code" "error" + exit 1 + fi + exit 0 fi - local codeServerVersion="${1}" ; shift - local vscodeVersion="${1}" ; shift - local target="${1}" ; shift - local arch="${1}" ; shift - local ci="${CI:-}" - # This lets you build in a separate directory since building within this # directory while developing makes it hard to keep developing since compiling # will compile everything in the build directory as well. local outPath="${OUT:-${rootPath}}" + local releasePath="${outPath}/release" + local stagingPath="${outPath}/build" - # If we're inside a vscode directory, assume we want to develop. In that case + # If we're inside a VS Code directory, assume we want to develop. In that case # we should set an OUT directory and not build in this directory. if in-vscode "${outPath}" ; then log "Set the OUT environment variable to something outside of VS Code" "error" exit 1 fi - local releasePath="${outPath}/release" - local buildPath="${outPath}/build" + local vscodeVersion="${1}" ; shift + local sourceName="vscode-${vscodeVersion}-source" + local sourcePath="${stagingPath}/${sourceName}" - local vscodeSourceName="vscode-${vscodeVersion}-source" - local vscodeBuildName="vscode-${vscodeVersion}-${target}-${arch}-built" - local vscodeSourcePath="${buildPath}/${vscodeSourceName}" - local vscodeBuildPath="${buildPath}/${vscodeBuildName}" - - local codeServerBuildName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}-built" - local codeServerBuildPath="${buildPath}/${codeServerBuildName}" - local binaryName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}" - - log "Running ${task} task" - log " rootPath: ${rootPath}" - log " outPath: ${outPath}" - log " codeServerVersion: ${codeServerVersion}" - log " vscodeVersion: ${vscodeVersion}" - log " target: ${target}" - log " arch: ${arch}" - if [[ -n "${ci}" ]] ; then - log " CI: yes" - else - log " CI: no" + if [[ "${task}" == "package-prebuilt" ]] ; then + local archiveName="vscode-${vscodeVersion}.tar.gz" + cd "${stagingPath}" + git reset --hard && git clean -xfd -e '.build/extensions' -e 'node_modules' + tar -czf "${archiveName}" "${sourceName}" + mkdir -p "${releasePath}" && mv -f "${archiveName}" "${releasePath}" + exit 0 fi + local ci="${CI:-}" + local minify="${MINIFY:-}" + local arch="x86_64" + local target="linux" + local ostype="${OSTYPE:-}" + if [[ "${ostype}" == "darwin"* ]] ; then + target="darwin" + else + arch=$(uname -m) + fi + + local codeServerVersion="${1}" ; shift + local binaryName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}" + local buildPath="${stagingPath}/${binaryName}-built" + "${task}-task" "$@" } diff --git a/scripts/vscode.patch b/scripts/vscode.patch index b890389a0..87586247d 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -1,3 +1,111 @@ +diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js +index 0dd2e5abf1..fc6875f3c2 100644 +--- a/build/gulpfile.compile.js ++++ b/build/gulpfile.compile.js +@@ -12,6 +12,7 @@ const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); + + // Full compile, including nls and inline sources in sourcemaps, for build + const compileClientBuildTask = task.define('compile-client-build', task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); ++exports.compileClientBuildTask = compileClientBuildTask; + + // All Build + const compileBuildTask = task.define('compile-build', task.parallel(compileClientBuildTask, compileExtensionsBuildTask)); +diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js +index 84a6be26e8..7fb43686cd 100644 +--- a/build/gulpfile.vscode.js ++++ b/build/gulpfile.vscode.js +@@ -34,7 +34,8 @@ const deps = require('./dependencies'); + const getElectronVersion = require('./lib/electron').getElectronVersion; + const createAsar = require('./lib/asar').createAsar; + const minimist = require('minimist'); +-const { compileBuildTask } = require('./gulpfile.compile'); ++const { /*compileBuildTask, */compileClientBuildTask } = require('./gulpfile.compile'); ++const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); + + const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); + // @ts-ignore +@@ -47,23 +48,28 @@ const nodeModules = ['electron', 'original-fs'] + + // Build + const vscodeEntryPoints = _.flatten([ +- buildfile.entrypoint('vs/workbench/workbench.main'), ++ buildfile.entrypoint('vs/workbench/workbench.web.api'), ++ buildfile.entrypoint('vs/server/src/cli'), ++ buildfile.entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux'), ++ buildfile.entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win'), ++ buildfile.entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin'), + buildfile.base, +- buildfile.workbench, +- buildfile.code ++ buildfile.workbenchWeb, ++ // buildfile.code + ]); + + const vscodeResources = [ +- 'out-build/main.js', +- 'out-build/cli.js', +- 'out-build/driver.js', ++ 'out-build/vs/server/main.js', ++ 'out-build/vs/server/src/uriTransformer.js', ++ // 'out-build/cli.js', ++ // 'out-build/driver.js', + 'out-build/bootstrap.js', + 'out-build/bootstrap-fork.js', + 'out-build/bootstrap-amd.js', + 'out-build/bootstrap-window.js', + 'out-build/paths.js', + 'out-build/vs/**/*.{svg,png,cur,html}', +- '!out-build/vs/code/browser/**/*.html', ++ // '!out-build/vs/code/browser/**/*.html', + 'out-build/vs/base/common/performance.js', + 'out-build/vs/base/node/languagePacks.js', + 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', +@@ -78,10 +84,11 @@ const vscodeResources = [ + 'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md', + 'out-build/vs/workbench/services/files/**/*.exe', + 'out-build/vs/workbench/services/files/**/*.md', +- 'out-build/vs/code/electron-browser/workbench/**', +- 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', +- 'out-build/vs/code/electron-browser/issue/issueReporter.js', +- 'out-build/vs/code/electron-browser/processExplorer/processExplorer.js', ++ 'out-build/vs/code/browser/workbench/**', ++ // 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', ++ // 'out-build/vs/code/electron-browser/issue/issueReporter.js', ++ // 'out-build/vs/code/electron-browser/processExplorer/processExplorer.js', ++ '!out-build/vs/server/doc/**', + '!**/test/**' + ]; + +@@ -94,7 +101,7 @@ const BUNDLED_FILE_HEADER = [ + const optimizeVSCodeTask = task.define('optimize-vscode', task.series( + task.parallel( + util.rimraf('out-vscode'), +- compileBuildTask ++ compileClientBuildTask // compileBuildTask + ), + common.optimizeTask({ + src: 'out-build', +@@ -127,6 +134,20 @@ const minifyVSCodeTask = task.define('minify-vscode', task.series( + common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) + )); + ++function packageExtensionsTask() { ++ return () => { ++ const destination = path.join(root, ".build"); ++ const sources = ext.packageExtensionsStream(); ++ return sources.pipe(vfs.dest(destination)); ++ }; ++} ++gulp.task(task.define('extensions-build-package', task.series( ++ compileExtensionsBuildTask, ++ packageExtensionsTask() ++))); ++gulp.task(optimizeVSCodeTask); ++gulp.task(minifyVSCodeTask); ++ + // Package + + // @ts-ignore JSON checking: darwinCredits is optional diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index 618861a5be..9d4fdea14e 100644 --- a/src/typings/require.d.ts @@ -74,8 +182,33 @@ index a7466e641a..6d91e66ad3 100644 + + export const codeServer: string = 'code-server'; } +diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts +index 4cba839fe5..b216c43bbc 100644 +--- a/src/vs/base/common/platform.ts ++++ b/src/vs/base/common/platform.ts +@@ -53,8 +53,18 @@ if (typeof navigator === 'object' && !isElectronRenderer) { + _isMacintosh = userAgent.indexOf('Macintosh') >= 0; + _isLinux = userAgent.indexOf('Linux') >= 0; + _isWeb = true; +- _locale = navigator.language; +- _language = _locale; ++ _locale = LANGUAGE_DEFAULT; ++ _language = LANGUAGE_DEFAULT; ++ const rawNlsConfig = typeof document !== 'undefined' ++ && document.getElementById('vscode-remote-nls-configuration')!.getAttribute('data-settings')!; ++ if (rawNlsConfig) { ++ try { ++ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); ++ _locale = nlsConfig.locale; ++ _translationsConfigFile = nlsConfig._translationsConfigFile; ++ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; ++ } catch (error) { /* Oh well. */ } ++ } + } else if (typeof process === 'object') { + _isWindows = (process.platform === 'win32'); + _isMacintosh = (process.platform === 'darwin'); diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html -index ff62e0a65a..21cd50eaf9 100644 +index ff62e0a65a..924b65fa7a 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -4,6 +4,8 @@ @@ -87,6 +220,14 @@ index ff62e0a65a..21cd50eaf9 100644 +@@ -20,6 +22,7 @@ + + + ++ + + + diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index 34f321f90d..b1bd6a4ac9 100644 --- a/src/vs/code/browser/workbench/workbench.js @@ -372,6 +513,35 @@ index e09049c5b9..7af2c20efd 100644 .then(extensions => { const toRemove: ILocalExtension[] = []; +diff --git a/src/vs/platform/localizations/electron-browser/localizationsService.ts b/src/vs/platform/localizations/electron-browser/localizationsService.ts +index 353161166e..7d64fe93c7 100644 +--- a/src/vs/platform/localizations/electron-browser/localizationsService.ts ++++ b/src/vs/platform/localizations/electron-browser/localizationsService.ts +@@ -6,8 +6,9 @@ + import { IChannel } from 'vs/base/parts/ipc/common/ipc'; + import { Event } from 'vs/base/common/event'; + import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; +-import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; ++// import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; + import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; ++import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + + export class LocalizationsService implements ILocalizationsService { + +@@ -15,8 +16,11 @@ export class LocalizationsService implements ILocalizationsService { + + private channel: IChannel; + +- constructor(@ISharedProcessService sharedProcessService: ISharedProcessService) { +- this.channel = sharedProcessService.getChannel('localizations'); ++ constructor( ++ // @ISharedProcessService sharedProcessService: ISharedProcessService ++ @IRemoteAgentService remoteAgentService: IRemoteAgentService, ++ ) { ++ this.channel = remoteAgentService.getConnection()!.getChannel('localizations'); + } + + get onDidLanguagesChange(): Event { return this.channel.listen('onDidLanguagesChange'); } diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 9f68b645b6..f0cae7111d 100644 --- a/src/vs/platform/log/common/logIpc.ts @@ -573,7 +743,7 @@ index 5a758eb786..7fcacb5ca7 100644 templateData.actionBar.context = ({ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }); templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts -index 1986fb6642..453d3e3e48 100644 +index 1986fb6642..70b0c789e3 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -35,6 +35,7 @@ import { SignService } from 'vs/platform/sign/browser/signService'; @@ -584,16 +754,15 @@ index 1986fb6642..453d3e3e48 100644 class CodeRendererMain extends Disposable { -@@ -71,6 +72,8 @@ class CodeRendererMain extends Disposable { +@@ -49,6 +50,7 @@ class CodeRendererMain extends Disposable { - // Startup - this.workbench.startup(); -+ -+ initialize(services.serviceCollection); - } + async open(): Promise { + const services = await this.initServices(); ++ await initialize(services.serviceCollection); - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService }> { -@@ -114,7 +117,8 @@ class CodeRendererMain extends Disposable { + await domContentLoaded(); + mark('willStartWorkbench'); +@@ -114,7 +116,8 @@ class CodeRendererMain extends Disposable { const channel = connection.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment())); @@ -604,7 +773,7 @@ index 1986fb6642..453d3e3e48 100644 const payload = await this.resolveWorkspaceInitializationPayload(); diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts -index b253e573ae..7a230fa3bd 100644 +index b253e573ae..94b2b7f287 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -53,6 +53,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur @@ -731,6 +900,14 @@ index b253e573ae..7a230fa3bd 100644 if (openFolderInNewWindow) { window.open(newAddress); } else { +@@ -1100,6 +1136,7 @@ export class SimpleWindowsService implements IWindowsService { + } + + relaunch(_options: { addArgs?: string[], removeArgs?: string[] }): Promise { ++ window.location.reload(); + return Promise.resolve(); + } + diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index f4ac3fe8dd..3a3616b39e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -1415,9 +1592,20 @@ index 306d58f915..58c603ad3d 100644 if (definition.fontCharacter || definition.fontColor) { let body = ''; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts -index c28adc0ad9..4517c308da 100644 +index c28adc0ad9..f76612a4d7 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts +@@ -72,8 +72,8 @@ import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycle + import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; + import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; + import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; +-// import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +-// import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; ++import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; ++import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; + // import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; + // import { IProductService } from 'vs/platform/product/common/product'; + // import { ProductService } from 'vs/platform/product/node/productService'; @@ -128,7 +128,7 @@ import 'vs/workbench/services/extensions/browser/extensionService'; // import 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; // import 'vs/workbench/services/extensions/node/multiExtensionManagement'; @@ -1427,6 +1615,24 @@ index c28adc0ad9..4517c308da 100644 // import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; // import 'vs/workbench/services/window/electron-browser/windowService'; +@@ -156,7 +156,7 @@ registerSingleton(IContextViewService, ContextViewService, true); + // registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); + // registerSingleton(IRequestService, RequestService, true); + registerSingleton(ILifecycleService, BrowserLifecycleService); +-// registerSingleton(ILocalizationsService, LocalizationsService); ++registerSingleton(ILocalizationsService, LocalizationsService); + // registerSingleton(ISharedProcessService, SharedProcessService, true); + // registerSingleton(IWindowsService, WindowsService); + // registerSingleton(IUpdateService, UpdateService); +@@ -194,7 +194,7 @@ import 'vs/workbench/services/files/common/workspaceWatcher'; + import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; + + // Localizations +-// import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; ++import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; + + // Preferences + import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; @@ -260,9 +260,9 @@ registerSingleton(IWebviewService, WebviewService, true); registerSingleton(IWebviewEditorService, WebviewEditorService, true); diff --git a/src/channel.ts b/src/channel.ts index 1b283f187..bfb2aeccc 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -19,6 +19,7 @@ import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"; import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints"; import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider"; +import { getTranslations } from "vs/server/src/nls"; import { getUriTransformer } from "vs/server/src/util"; /** @@ -214,7 +215,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel { } private async scanExtensions(locale: string): Promise { - const translations = {}; // TODO: translations + const translations = await getTranslations(locale, this.environment.userDataPath); const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { return Promise.all(paths.map((path) => { diff --git a/src/cli.ts b/src/cli.ts index dbd1e8e77..8f0bd528c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -10,6 +10,7 @@ import product from "vs/platform/product/node/product"; import { MainServer } from "vs/server/src/server"; import { enableExtensionTars } from "vs/server/src/tar"; import { AuthType, buildAllowedMessage, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util"; +import { main as vsCli } from "vs/code/node/cliProcessMain"; const { logger } = localRequire("@coder/logger/out/index"); @@ -65,10 +66,6 @@ options.push({ id: "socket", type: "string", cat: "o", description: "Listen on a options.push(last); -interface IMainCli { - main: (argv: ParsedArgs) => Promise; -} - const main = async (): Promise => { const args = validatePaths(parseMainProcessArgv(process.argv)) as Args; ["extra-extensions-dir", "extra-builtin-extensions-dir"].forEach((key) => { @@ -108,8 +105,7 @@ const main = async (): Promise => { }; if (shouldSpawnCliProcess()) { - const cli = await new Promise((c, e) => require(["vs/code/node/cliProcessMain"], c, e)); - await cli.main(args); + await vsCli(args); return process.exit(0); // There is a WriteStream instance keeping it open. } diff --git a/src/client.ts b/src/client.ts index 4ed40a309..0df27d8ac 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,12 +1,14 @@ -import { coderApi, vscodeApi } from "vs/server/src/api"; -import "vs/css!./media/firefox"; import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; +import { coderApi, vscodeApi } from "vs/server/src/api"; + +import "vs/css!./media/firefox"; + /** * This is called by vs/workbench/browser/web.main.ts after the workbench has * been initialized so we can initialize our own client-side code. */ -export const initialize = (services: ServiceCollection): void => { +export const initialize = async (services: ServiceCollection): Promise => { const target = window as any; target.ide = coderApi(services); target.vscode = vscodeApi(services); diff --git a/src/connection.ts b/src/connection.ts index 32d0cf892..c5d17e106 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -5,9 +5,11 @@ import { VSBuffer } from "vs/base/common/buffer"; import { Emitter } from "vs/base/common/event"; import { ISocket } from "vs/base/parts/ipc/common/ipc.net"; import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net"; +import { IEnvironmentService } from "vs/platform/environment/common/environment"; import { ILogService } from "vs/platform/log/common/log"; import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol"; +import { getNlsConfiguration } from "vs/server/src/nls"; import { Protocol } from "vs/server/src/protocol"; import { uriTransformerPath } from "vs/server/src/util"; @@ -57,19 +59,25 @@ export class ManagementConnection extends Connection { } export class ExtensionHostConnection extends Connection { - private process: cp.ChildProcess; + private process?: cp.ChildProcess; - public constructor(protocol: Protocol, buffer: VSBuffer, private readonly log: ILogService) { + public constructor( + locale:string, protocol: Protocol, buffer: VSBuffer, + private readonly log: ILogService, + private readonly environment: IEnvironmentService, + ) { super(protocol); this.protocol.dispose(); - this.process = this.spawn(buffer); + this.spawn(locale, buffer).then((p) => this.process = p); this.protocol.getUnderlyingSocket().pause(); } protected dispose(): void { if (!this.disposed) { this.disposed = true; - this.process.kill(); + if (this.process) { + this.process.kill(); + } this.protocol.getSocket().end(); this._onClose.fire(); } @@ -85,14 +93,15 @@ export class ExtensionHostConnection extends Connection { private sendInitMessage(buffer: VSBuffer): void { const socket = this.protocol.getUnderlyingSocket(); socket.pause(); - this.process.send({ + this.process!.send({ // Process must be set at this point. type: "VSCODE_EXTHOST_IPC_SOCKET", initialDataChunk: (buffer.buffer as Buffer).toString("base64"), skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket, }, socket); } - private spawn(buffer: VSBuffer): cp.ChildProcess { + private async spawn(locale: string, buffer: VSBuffer): Promise { + const config = await getNlsConfiguration(locale, this.environment.userDataPath); const proc = cp.fork( getPathFromAmdModule(require, "bootstrap-fork"), [ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath}` ], @@ -105,6 +114,7 @@ export class ExtensionHostConnection extends Connection { VSCODE_EXTHOST_WILL_SEND_SOCKET: "true", VSCODE_HANDLES_UNCAUGHT_ERRORS: "true", VSCODE_LOG_STACK: "false", + VSCODE_NLS_CONFIG: JSON.stringify(config), }, silent: true, }, diff --git a/src/nls.ts b/src/nls.ts new file mode 100644 index 000000000..e9ef4a8c5 --- /dev/null +++ b/src/nls.ts @@ -0,0 +1,81 @@ +import * as path from "path"; +import * as fs from "fs"; +import * as util from "util"; + +import { getPathFromAmdModule } from "vs/base/common/amd"; +import * as lp from "vs/base/node/languagePacks"; +import product from "vs/platform/product/node/product"; +import { Translations } from "vs/workbench/services/extensions/common/extensionPoints"; + +const configurations = new Map>(); +const metadataPath = path.join(getPathFromAmdModule(require, ""), "nls.metadata.json"); + +export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => { + return config && !!(config)._languagePackId; +}; + +const DefaultConfiguration = { + locale: "en", + availableLanguages: {}, +}; + +export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise => { + const id = `${locale}: ${userDataPath}`; + if (!configurations.has(id)) { + configurations.set(id, new Promise(async (resolve) => { + const config = product.commit && await util.promisify(fs.exists)(metadataPath) + ? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale) + : DefaultConfiguration; + if (isInternalConfiguration(config)) { + config._languagePackSupport = true; + } + resolve(config); + })); + } + return configurations.get(id)!; +}; + +export const getTranslations = async (locale: string, userDataPath: string): Promise => { + const config = await getNlsConfiguration(locale, userDataPath); + if (isInternalConfiguration(config)) { + try { + return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, "utf8")); + } catch (error) { /* Nothing yet. */} + } + return {}; +}; + +export const getLocaleFromConfig = async (userDataPath: string): Promise => { + let locale = "en"; + try { + const localeConfigUri = path.join(userDataPath, "User/locale.json"); + const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, "utf8")); + locale = JSON.parse(content).locale; + } catch (error) { /* Ignore. */ } + return locale; +}; + +// Taken from src/main.js in the main VS Code source. +const stripComments = (content: string): string => { + const regexp = /("(?:[^\\"]*(?:\\.)?)*")|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + + return content.replace(regexp, (match, _m1, _m2, m3, m4) => { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + const length_1 = m4.length; + if (length_1 > 2 && m4[length_1 - 1] === '\n') { + return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; + } + else { + return ''; + } + } else { + // We match a string + return match; + } + }); +}; diff --git a/src/server.ts b/src/server.ts index 9884713a4..9e0e0b9f0 100644 --- a/src/server.ts +++ b/src/server.ts @@ -14,8 +14,9 @@ import { sanitizeFilePath } from "vs/base/common/extpath"; import { UriComponents, URI } from "vs/base/common/uri"; import { generateUuid } from "vs/base/common/uuid"; import { getMachineId } from 'vs/base/node/id'; -import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc"; +import { NLSConfiguration } from "vs/base/node/languagePacks"; import { mkdirp, rimraf } from "vs/base/node/pfs"; +import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc"; import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"; import { IConfigurationService } from "vs/platform/configuration/common/configuration"; import { ConfigurationService } from "vs/platform/configuration/node/configurationService"; @@ -33,6 +34,7 @@ import { InstantiationService } from "vs/platform/instantiation/common/instantia import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; import { ILocalizationsService } from "vs/platform/localizations/common/localizations"; import { LocalizationsService } from "vs/platform/localizations/node/localizations"; +import { LocalizationsChannel } from "vs/platform/localizations/node/localizationsIpc"; import { getLogLevel, ILogService } from "vs/platform/log/common/log"; import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc"; import { SpdLogService } from "vs/platform/log/node/spdlogService"; @@ -56,6 +58,7 @@ import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api"; import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/server/src/connection"; import { ExtensionEnvironmentChannel, FileProviderChannel , } from "vs/server/src/channel"; import { TelemetryClient } from "vs/server/src/insights"; +import { getNlsConfiguration, getLocaleFromConfig } from "vs/server/src/nls"; import { Protocol } from "vs/server/src/protocol"; import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/util"; @@ -74,6 +77,7 @@ export interface Options { REMOTE_USER_DATA_URI: UriComponents | URI; PRODUCT_CONFIGURATION: IProductConfiguration | null; CONNECTION_AUTH_TOKEN: string; + NLS_CONFIGURATION: NLSConfiguration; } export interface Response { @@ -456,7 +460,8 @@ export class MainServer extends Server { util.promisify(fs.readFile)(filePath, "utf8"), this.servicesPromise, ]); - + const environment = this.services.get(IEnvironmentService) as IEnvironmentService; + const locale = environment.args.locale || await getLocaleFromConfig(environment.userDataPath); const webviewEndpoint = this.address(request) + "/webview/"; const cwd = process.env.VSCODE_CWD || process.cwd(); const workspacePath = parsedUrl.query.workspace as string | undefined; @@ -479,6 +484,7 @@ export class MainServer extends Server { ), PRODUCT_CONFIGURATION: product, CONNECTION_AUTH_TOKEN: "", + NLS_CONFIGURATION: await getNlsConfiguration(locale, environment.userDataPath), }; Object.keys(options).forEach((key) => { @@ -528,7 +534,10 @@ export class MainServer extends Server { } else { const buffer = protocol.readEntireBuffer(); connection = new ExtensionHostConnection( - protocol, buffer, this.services.get(ILogService) as ILogService, + message.args ? message.args.language : "en", + protocol, buffer, + this.services.get(ILogService) as ILogService, + this.services.get(IEnvironmentService) as IEnvironmentService, ); } connections.set(token, connection); @@ -576,7 +585,9 @@ export class MainServer extends Server { await new Promise((resolve) => { const instantiationService = new InstantiationService(this.services); - this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService)); + const localizationService = instantiationService.createInstance(LocalizationsService); + this.services.set(ILocalizationsService, localizationService); + this.ipc.registerChannel("localizations", new LocalizationsChannel(localizationService)); instantiationService.invokeFunction(() => { instantiationService.createInstance(LogsDataCleaner); this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));