From a328204d8044b4d157e06500e7260b9b35dcbe3e Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 14 Jan 2019 14:58:34 -0600 Subject: [PATCH] Implement fs module (#3) * Implements the fs module * Add stats object * Add not implemented to createWriteStream * Update mkdtemp to use tmp dir * Unexport Stats * Add client web socket for commands and restructure --- package.json | 8 +- packages/electron-browser/package.json | 8 - packages/electron-browser/yarn.lock | 958 -- packages/ide/src/client.ts | 10 +- packages/ide/src/fill/child_process.ts | 4 + packages/ide/src/fill/client.ts | 157 + .../src => ide/src/fill}/dialog.scss | 15 + .../src => ide/src/fill}/dialog.ts | 19 +- .../src => ide/src/fill}/electron.ts | 0 .../src => ide/src/fill}/empty.ts | 0 packages/ide/src/fill/fs.ts | 4 + packages/ide/src/fill/net.ts | 3 + packages/ide/src/fill/util.ts | 5 + packages/ide/src/upload.ts | 2 +- packages/node-browser/package.json | 5 - packages/node-browser/src/child_process.ts | 184 - packages/node-browser/src/fs.ts | 719 -- packages/node-browser/src/index.ts | 2 - packages/node-browser/src/net.ts | 70 - packages/node-browser/src/util.ts | 138 - packages/node-browser/yarn.lock | 4 - packages/package.json | 11 +- packages/{server => protocol}/package.json | 3 +- .../scripts/generate_proto.sh | 0 .../src/browser/client.ts | 16 +- .../src/browser/command.ts | 0 .../src/browser/modules/child_process.ts | 52 + packages/protocol/src/browser/modules/fs.ts | 577 ++ packages/protocol/src/browser/modules/net.ts | 72 + packages/protocol/src/browser/modules/util.ts | 8 + .../src/common/connection.ts | 0 packages/protocol/src/common/util.ts | 14 + packages/protocol/src/index.ts | 6 + .../{server => protocol}/src/node/command.ts | 0 .../{server => protocol}/src/node/evaluate.ts | 2 +- .../{server => protocol}/src/node/server.ts | 0 .../src/proto/client.proto | 0 .../src/proto/client_pb.d.ts | 0 .../src/proto/client_pb.js | 0 .../src/proto/command.proto | 0 .../src/proto/command_pb.d.ts | 0 .../src/proto/command_pb.js | 0 .../{server => protocol}/src/proto/index.ts | 0 .../{server => protocol}/src/proto/node.proto | 0 .../src/proto/node_pb.d.ts | 0 .../{server => protocol}/src/proto/node_pb.js | 0 .../{server => protocol}/test/command.test.ts | 0 .../test/evaluate.test.ts | 0 packages/{server => protocol}/test/forker.js | 0 packages/{server => protocol}/test/helpers.ts | 0 packages/protocol/test/modules/fs.test.ts | 643 ++ packages/{server => protocol}/yarn.lock | 0 packages/rules/package.json | 7 - packages/rules/yarn.lock | 4 - packages/vscode/src/index.ts | 14 +- packages/{app => web}/src/index.html | 0 packages/{app => web}/src/index.scss | 0 packages/{app => web}/src/index.ts | 0 packages/yarn.lock | 120 +- .../src/curlyStatementNewlinesRule.ts | 0 {packages/rules => rules}/tsconfig.json | 0 {packages/scripts/src => scripts}/dummy.js | 0 .../src => scripts}/install-packages.ts | 4 +- .../scripts/src => scripts}/test-setup.js | 0 scripts/vscode.css.patch | 32 + scripts/vscode.patch | 8653 +++++++++++++++++ webpack.config.app.js | 30 +- yarn.lock | 158 +- 68 files changed, 10467 insertions(+), 2274 deletions(-) delete mode 100644 packages/electron-browser/package.json delete mode 100644 packages/electron-browser/yarn.lock create mode 100644 packages/ide/src/fill/child_process.ts create mode 100644 packages/ide/src/fill/client.ts rename packages/{electron-browser/src => ide/src/fill}/dialog.scss (82%) rename packages/{electron-browser/src => ide/src/fill}/dialog.ts (88%) rename packages/{electron-browser/src => ide/src/fill}/electron.ts (100%) rename packages/{node-browser/src => ide/src/fill}/empty.ts (100%) create mode 100644 packages/ide/src/fill/fs.ts create mode 100644 packages/ide/src/fill/net.ts create mode 100644 packages/ide/src/fill/util.ts delete mode 100644 packages/node-browser/package.json delete mode 100644 packages/node-browser/src/child_process.ts delete mode 100644 packages/node-browser/src/fs.ts delete mode 100644 packages/node-browser/src/index.ts delete mode 100644 packages/node-browser/src/net.ts delete mode 100644 packages/node-browser/src/util.ts delete mode 100644 packages/node-browser/yarn.lock rename packages/{server => protocol}/package.json (85%) rename packages/{server => protocol}/scripts/generate_proto.sh (100%) rename packages/{server => protocol}/src/browser/client.ts (89%) rename packages/{server => protocol}/src/browser/command.ts (100%) create mode 100644 packages/protocol/src/browser/modules/child_process.ts create mode 100644 packages/protocol/src/browser/modules/fs.ts create mode 100644 packages/protocol/src/browser/modules/net.ts create mode 100644 packages/protocol/src/browser/modules/util.ts rename packages/{server => protocol}/src/common/connection.ts (100%) create mode 100644 packages/protocol/src/common/util.ts create mode 100644 packages/protocol/src/index.ts rename packages/{server => protocol}/src/node/command.ts (100%) rename packages/{server => protocol}/src/node/evaluate.ts (96%) rename packages/{server => protocol}/src/node/server.ts (100%) rename packages/{server => protocol}/src/proto/client.proto (100%) rename packages/{server => protocol}/src/proto/client_pb.d.ts (100%) rename packages/{server => protocol}/src/proto/client_pb.js (100%) rename packages/{server => protocol}/src/proto/command.proto (100%) rename packages/{server => protocol}/src/proto/command_pb.d.ts (100%) rename packages/{server => protocol}/src/proto/command_pb.js (100%) rename packages/{server => protocol}/src/proto/index.ts (100%) rename packages/{server => protocol}/src/proto/node.proto (100%) rename packages/{server => protocol}/src/proto/node_pb.d.ts (100%) rename packages/{server => protocol}/src/proto/node_pb.js (100%) rename packages/{server => protocol}/test/command.test.ts (100%) rename packages/{server => protocol}/test/evaluate.test.ts (100%) rename packages/{server => protocol}/test/forker.js (100%) rename packages/{server => protocol}/test/helpers.ts (100%) create mode 100644 packages/protocol/test/modules/fs.test.ts rename packages/{server => protocol}/yarn.lock (100%) delete mode 100644 packages/rules/package.json delete mode 100644 packages/rules/yarn.lock rename packages/{app => web}/src/index.html (100%) rename packages/{app => web}/src/index.scss (100%) rename packages/{app => web}/src/index.ts (100%) rename {packages/rules => rules}/src/curlyStatementNewlinesRule.ts (100%) rename {packages/rules => rules}/tsconfig.json (100%) rename {packages/scripts/src => scripts}/dummy.js (100%) rename {packages/scripts/src => scripts}/install-packages.ts (89%) rename {packages/scripts/src => scripts}/test-setup.js (100%) create mode 100644 scripts/vscode.css.patch create mode 100644 scripts/vscode.patch diff --git a/package.json b/package.json index 48625314..b84ee8d7 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,12 @@ "license": "TBD", "description": "VS Code in the cloud.", "scripts": { + "build:rules": "cd ./rules && tsc -p .", "vscode:clone": "mkdir -p ./lib && test -d ./lib/vscode || git clone https://github.com/Microsoft/vscode/ ./lib/vscode", "vscode:install": "cd ./lib/vscode && git checkout tags/1.30.1 && yarn", "vscode": "npm-run-all vscode:*", - "packages:install": "cd ./packages && yarn", - "postinstall": "npm-run-all --parallel vscode packages:install", + "packages:install": "cd ./packages && yarn && ts-node ../scripts/install-packages.ts", + "postinstall": "npm-run-all --parallel vscode packages:install build:rules", "start": "webpack-dev-server --hot --config ./webpack.config.app.js", "test": "cd ./packages && yarn test" }, @@ -30,7 +31,10 @@ "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "ts-loader": "^5.3.3", + "ts-node": "^7.0.1", + "tslint": "^5.12.1", "typescript": "^3.2.2", + "typescript-tslint-plugin": "^0.2.1", "uglifyjs-webpack-plugin": "^2.1.1", "webpack": "^4.28.4", "webpack-bundle-analyzer": "^3.0.3", diff --git a/packages/electron-browser/package.json b/packages/electron-browser/package.json deleted file mode 100644 index 1ec31a66..00000000 --- a/packages/electron-browser/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@coder/electron-browser", - "description": "A browser implementation of Electron's API.", - "main": "src/index.ts", - "devDependencies": { - "electron": "^4.0.1" - } -} diff --git a/packages/electron-browser/yarn.lock b/packages/electron-browser/yarn.lock deleted file mode 100644 index 7e541478..00000000 --- a/packages/electron-browser/yarn.lock +++ /dev/null @@ -1,958 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/node@^10.12.18": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== - -ajv@^6.5.5: - version "6.6.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d" - integrity sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== - dependencies: - delayed-stream "~1.0.0" - -concat-stream@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -debug@2.6.9, debug@^2.1.3, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.0.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -electron-download@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" - integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== - dependencies: - debug "^3.0.0" - env-paths "^1.0.0" - fs-extra "^4.0.1" - minimist "^1.2.0" - nugget "^2.0.1" - path-exists "^3.0.0" - rc "^1.2.1" - semver "^5.4.1" - sumchecker "^2.0.2" - -electron@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-4.0.1.tgz#c41eaee9e081c2e5e4a4a4a761b7577a77d2eb18" - integrity sha512-kBWDLn1Vq8Tm6+/HpQc8gkjX7wJyQI8v/lf2kAirfi0Q4cXh6vBjozFvV1U/9gGCbyKnIDM+m8/wpyJIjg4w7g== - dependencies: - "@types/node" "^10.12.18" - electron-download "^4.1.0" - extract-zip "^1.0.3" - -env-paths@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" - integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@^1.0.3: - version "1.6.7" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" - integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= - dependencies: - concat-stream "1.6.2" - debug "2.6.9" - mkdirp "0.5.1" - yauzl "2.4.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= - dependencies: - pend "~1.2.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-extra@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= - dependencies: - builtin-modules "^1.0.0" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -meow@^3.1.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== - dependencies: - mime-db "~1.37.0" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -nugget@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0" - integrity sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA= - dependencies: - debug "^2.1.3" - minimist "^1.1.0" - pretty-bytes "^1.0.2" - progress-stream "^1.1.0" - request "^2.45.0" - single-line-log "^1.1.2" - throttleit "0.0.2" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-keys@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" - integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pretty-bytes@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" - integrity sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ= - dependencies: - get-stdin "^4.0.1" - meow "^3.1.0" - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -progress-stream@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77" - integrity sha1-LNPP6jO6OonJwSHsM0er6asSX3c= - dependencies: - speedometer "~0.1.2" - through2 "~0.2.3" - -psl@^1.1.24: - version "1.1.31" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" - integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -rc@^1.2.1: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -request@^2.45.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -"semver@2 || 3 || 4 || 5", semver@^5.4.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" - integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -single-line-log@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364" - integrity sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q= - dependencies: - string-width "^1.0.1" - -spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" - integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" - integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g== - -speedometer@~0.1.2: - version "0.1.4" - resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" - integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= - -sshpk@^1.7.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de" - integrity sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -sumchecker@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e" - integrity sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4= - dependencies: - debug "^2.2.0" - -throttleit@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" - integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= - -through2@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.2.3.tgz#eb3284da4ea311b6cc8ace3653748a52abf25a3f" - integrity sha1-6zKE2k6jEbbMis42U3SKUqvyWj8= - dependencies: - readable-stream "~1.1.9" - xtend "~2.1.1" - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -xtend@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" - integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= - dependencies: - object-keys "~0.4.0" - -yauzl@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" - integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= - dependencies: - fd-slicer "~1.0.1" diff --git a/packages/ide/src/client.ts b/packages/ide/src/client.ts index 78627353..1c31b4d1 100644 --- a/packages/ide/src/client.ts +++ b/packages/ide/src/client.ts @@ -1,14 +1,15 @@ import { exec } from "child_process"; import { promisify } from "util"; import { field, logger, time, Time } from "@coder/logger"; -import { escapePath } from "@coder/node-browser"; +import { escapePath } from "@coder/server"; +import { retry } from "./retry"; export interface IClientOptions { mkDirs?: string[]; } /** - * Client represents a general abstraction of an IDE client. + * A general abstraction of an IDE client. * * Everything the client provides is asynchronous so you can wait on what * you need from it without blocking anything else. @@ -36,6 +37,11 @@ export class Client { await promisify(exec)(`mkdir -p ${options.mkDirs.map(escapePath).join(" ")}`); } }); + + // Prevent Firefox from trying to reconnect when the page unloads. + window.addEventListener("unload", () => { + retry.block(); + }); } /** diff --git a/packages/ide/src/fill/child_process.ts b/packages/ide/src/fill/child_process.ts new file mode 100644 index 00000000..c5b6ce70 --- /dev/null +++ b/packages/ide/src/fill/child_process.ts @@ -0,0 +1,4 @@ +import { CP } from "@coder/server"; +import { client } from "./client"; + +export = new CP(client); diff --git a/packages/ide/src/fill/client.ts b/packages/ide/src/fill/client.ts new file mode 100644 index 00000000..23213de3 --- /dev/null +++ b/packages/ide/src/fill/client.ts @@ -0,0 +1,157 @@ +import { Emitter } from "@coder/events"; +import { logger, field } from "@coder/logger"; +import { Client, ReadWriteConnection } from "@coder/server"; +import { retry } from "../retry"; + +/** + * A connection based on a web socket. Automatically reconnects and buffers + * messages during connection. + */ +class Connection implements ReadWriteConnection { + + private activeSocket: WebSocket | undefined; + private readonly messageEmitter: Emitter; + private readonly closeEmitter: Emitter; + private readonly upEmitter: Emitter; + private readonly downEmitter: Emitter; + private readonly messageBuffer: Uint8Array[]; + private socketTimeoutDelay = 60 * 1000; + private retryName = "Web socket"; + private isUp: boolean | undefined; + private closed: boolean | undefined; + + public constructor() { + this.messageEmitter = new Emitter(); + this.closeEmitter = new Emitter(); + this.upEmitter = new Emitter(); + this.downEmitter = new Emitter(); + this.messageBuffer = []; + retry.register(this.retryName, () => this.connect()); + this.connect().catch(() => { + retry.block(this.retryName); + retry.run(this.retryName); + }); + } + + /** + * Register a function to be called when the connection goes up. + */ + public onUp(cb: () => void): void { + this.upEmitter.event(cb); + } + + /** + * Register a function to be called when the connection goes down. + */ + public onDown(cb: () => void): void { + this.downEmitter.event(cb); + } + + public send(data: Buffer | Uint8Array): void { + if (this.closed) { + throw new Error("web socket is closed"); + } + if (!this.activeSocket || this.activeSocket.readyState !== this.activeSocket.OPEN) { + this.messageBuffer.push(data); + } else { + this.activeSocket.send(data); + } + } + + public onMessage(cb: (data: Uint8Array | Buffer) => void): void { + this.messageEmitter.event(cb); + } + + public onClose(cb: () => void): void { + this.closeEmitter.event(cb); + } + + public close(): void { + this.closed = true; + this.dispose(); + this.closeEmitter.emit(); + } + +/** + * Connect to the server. + */ + private async connect(): Promise { + const socket = await this.openSocket(); + + socket.addEventListener("message", (event: MessageEvent) => { + this.messageEmitter.emit(event.data); + }); + + socket.addEventListener("close", (event) => { + if (this.isUp) { + this.isUp = false; + this.downEmitter.emit(undefined); + } + logger.warn( + "Web socket closed", + field("code", event.code), + field("reason", event.reason), + field("wasClean", event.wasClean), + ); + if (!this.closed) { + retry.block(this.retryName); + retry.run(this.retryName); + } + }); + + // Send any messages that were queued while we were waiting to connect. + while (this.messageBuffer.length > 0) { + socket.send(this.messageBuffer.shift()!); + } + + if (!this.isUp) { + this.isUp = true; + this.upEmitter.emit(undefined); + } + } + + /** + * Open a web socket, disposing the previous connection if any. + */ + private async openSocket(): Promise { + this.dispose(); + const socket = new WebSocket("websocket"); + socket.binaryType = "arraybuffer"; + this.activeSocket = socket; + + const socketWaitTimeout = window.setTimeout(() => { + socket.close(); + }, this.socketTimeoutDelay); + + await new Promise((resolve, reject): void => { + const onClose = (): void => { + clearTimeout(socketWaitTimeout); + socket.removeEventListener("close", onClose); + reject(); + }; + socket.addEventListener("close", onClose); + + socket.addEventListener("open", async () => { + clearTimeout(socketWaitTimeout); + resolve(); + }); + }); + + return socket; + } + + /** + * Dispose the current connection. + */ + private dispose(): void { + if (this.activeSocket) { + this.activeSocket.close(); + } + } + +} + +/** + * A client for proxying Node APIs based on web sockets. + */ +export const client = new Client(new Connection()); diff --git a/packages/electron-browser/src/dialog.scss b/packages/ide/src/fill/dialog.scss similarity index 82% rename from packages/electron-browser/src/dialog.scss rename to packages/ide/src/fill/dialog.scss index e804f5a1..7f54c583 100644 --- a/packages/electron-browser/src/dialog.scss +++ b/packages/ide/src/fill/dialog.scss @@ -66,3 +66,18 @@ .msgbox > .button-wrapper > button:not(:last-child) { margin-right: 8px; } + +.msgbox-overlay { + align-items: center; + background: rgba(0, 0, 0, 0.4); + bottom: 0; + display: flex; + justify-content: center; + left: 0; + opacity: 0; + position: absolute; + right: 0; + top: 0; + transition: 300ms opacity ease; + z-index: 15; +} diff --git a/packages/electron-browser/src/dialog.ts b/packages/ide/src/fill/dialog.ts similarity index 88% rename from packages/electron-browser/src/dialog.ts rename to packages/ide/src/fill/dialog.ts index 9563f05c..73b42879 100644 --- a/packages/electron-browser/src/dialog.ts +++ b/packages/ide/src/fill/dialog.ts @@ -1,5 +1,5 @@ import { IDisposable } from "@coder/disposable"; -import { Emitter } from "@coder/emitter"; +import { Emitter } from "@coder/events"; import "./dialog.scss"; @@ -36,11 +36,11 @@ export class Dialog { private options: IDialogOptions; private overlay: HTMLElement; - private cachedActiveElement: HTMLElement; - private input: HTMLInputElement; + private cachedActiveElement: HTMLElement | undefined; + private input: HTMLInputElement | undefined; private actionEmitter: Emitter; private errors: HTMLElement; - private buttons: HTMLElement[]; + private buttons: HTMLElement[] | undefined; public constructor(options: IDialogOptions) { this.options = options; @@ -105,9 +105,8 @@ export class Dialog { msgBox.appendChild(buttonWrapper); } - this.overlay = document.createElement("div"); - this.overlay.style.cssText = `display: flex; align-items: center; justify-content: center; top: 0; left: 0; right: 0; bottom: 0; z-index: 15; position: absolute; background: rgba(0, 0, 0, 0.4); opacity: 0; transition: 300ms opacity ease;`; + this.overlay.className = "msgbox-overlay"; this.overlay.appendChild(msgBox); setTimeout(() => { @@ -125,7 +124,7 @@ export class Dialog { /** * Input value if this dialog has an input. */ - public get inputValue(): string { + public get inputValue(): string | undefined { return this.input ? this.input.value : undefined; } @@ -153,10 +152,10 @@ export class Dialog { document.addEventListener("keydown", this.onKeydown); if (this.input) { this.input.focus(); - if (this.options.input.selection) { + if (this.options.input && this.options.input.selection) { this.input.setSelectionRange( this.options.input.selection.start, - this.options.input.selection.end + this.options.input.selection.end, ); } } else if (this.buttons) { @@ -173,7 +172,7 @@ export class Dialog { this.overlay.remove(); document.removeEventListener("keydown", this.onKeydown); this.cachedActiveElement.focus(); - this.cachedActiveElement = null; + this.cachedActiveElement = undefined; } } diff --git a/packages/electron-browser/src/electron.ts b/packages/ide/src/fill/electron.ts similarity index 100% rename from packages/electron-browser/src/electron.ts rename to packages/ide/src/fill/electron.ts diff --git a/packages/node-browser/src/empty.ts b/packages/ide/src/fill/empty.ts similarity index 100% rename from packages/node-browser/src/empty.ts rename to packages/ide/src/fill/empty.ts diff --git a/packages/ide/src/fill/fs.ts b/packages/ide/src/fill/fs.ts new file mode 100644 index 00000000..badf0214 --- /dev/null +++ b/packages/ide/src/fill/fs.ts @@ -0,0 +1,4 @@ +import { FS } from "@coder/server"; +import { client } from "./client"; + +export = new FS(client); diff --git a/packages/ide/src/fill/net.ts b/packages/ide/src/fill/net.ts new file mode 100644 index 00000000..ae484ce3 --- /dev/null +++ b/packages/ide/src/fill/net.ts @@ -0,0 +1,3 @@ +import { Net } from "@coder/server"; + +export = new Net(); diff --git a/packages/ide/src/fill/util.ts b/packages/ide/src/fill/util.ts new file mode 100644 index 00000000..909186d9 --- /dev/null +++ b/packages/ide/src/fill/util.ts @@ -0,0 +1,5 @@ +import { implementation as promisify } from "util.promisify"; + +export { + promisify, +} diff --git a/packages/ide/src/upload.ts b/packages/ide/src/upload.ts index f374abbe..56411bff 100644 --- a/packages/ide/src/upload.ts +++ b/packages/ide/src/upload.ts @@ -2,7 +2,7 @@ import { exec } from "child_process"; import { appendFile } from "fs"; import { promisify } from "util"; import { logger, Logger } from "@coder/logger"; -import { escapePath } from "@coder/node-browser"; +import { escapePath } from "@coder/server"; import { IURI } from "./uri"; /** diff --git a/packages/node-browser/package.json b/packages/node-browser/package.json deleted file mode 100644 index 3af12aca..00000000 --- a/packages/node-browser/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@coder/node-browser", - "description": "A browser implementation of async Node APIs.", - "main": "src/index.ts" -} diff --git a/packages/node-browser/src/child_process.ts b/packages/node-browser/src/child_process.ts deleted file mode 100644 index e87fed65..00000000 --- a/packages/node-browser/src/child_process.ts +++ /dev/null @@ -1,184 +0,0 @@ -import * as cp from "child_process"; -import * as stream from "stream"; -import * as events from "events"; -import * as net from "net"; -import { useBuffer, throwUnimplementedError, throwSyncError } from "./util"; - -/** - * Readable stream. - */ -class Readable extends stream.Readable { - - /** - * Read a chunk. - */ - public _read(_size: number): void { - // There is nothing to actually read. - } - -} - -/** - * Implementation of ChildProcess for the browser. - */ -class ChildProcess extends events.EventEmitter implements cp.ChildProcess { - - public connected: boolean = true; - public killed: boolean = false; - public pid = 0; - public stdin: stream.Writable; - public stdout: Readable; - public stderr: Readable; - public stdio: [stream.Writable, stream.Readable, stream.Readable]; - - private emitter = new events.EventEmitter(); - - public constructor(private session) { - super(); - - this.emitter = new events.EventEmitter(); - this.stdin = new stream.Writable(); - this.stdin._write = ( - chunk: any, // tslint:disable-line no-any so we can match the Node API. - _encoding: string, - callback: (error?: Error) => void, - ): void => { - session.sendStdin(chunk.toString()); - callback(); - }; - this.stdout = new Readable(); - this.stderr = new Readable(); - this.stdio = [this.stdin, this.stdout, this.stderr]; - - session.onDone((exitCode) => { - this.emitter.emit("exit", exitCode); - }); - - session.onDisconnect(() => { - this.emitter.emit("exit", -1); - }); - - session.onStdout((data) => { - this.stdout.emit("data", data); - }); - - session.onStderr((data) => { - this.stderr.emit("data", data); - }); - } - - /** - * Kill the session. - */ - public kill(): void { - this.session.close(); - } - - /** - * Not implemented. - */ - public disconnect(): void { - throwUnimplementedError(); - } - - /** - * Not implemented. - */ - public ref(): void { - throwUnimplementedError(); - } - - /** - * Not implemented. - */ - public unref(): void { - throwUnimplementedError(); - } - - /** - * Not implemented. - */ - public send( - _message: any, // tslint:disable-line no-any so we can match the Node API. - _sendHandle?: net.Socket | net.Server | ((error: Error) => void), - _options?: cp.MessageOptions | ((error: Error) => void), - _callback?: (error: Error) => void, - ): boolean { - throw throwUnimplementedError(); - } - - /** - * Add event listener. - */ - public on( - eventName: string, - callback: (...args: any[]) => void, // tslint:disable-line no-any so we can match the Node API. - ): this { - this.emitter.on(eventName, callback); - - return this; - } - -} - -// tslint:disable only-arrow-functions -function exec( - command: string, - options?: { encoding?: BufferEncoding | string | "buffer" | null } & cp.ExecOptions | null | ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void), - callback?: ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void), -): cp.ChildProcess { - const process = new ChildProcess(wush.execute({ command })); - - let stdout = ""; - process.stdout.on("data", (data) => { - stdout += data.toString(); - }); - - let stderr = ""; - process.stderr.on("data", (data) => { - stderr += data.toString(); - }); - - process.on("exit", (exitCode) => { - const error = exitCode !== 0 ? new Error(stderr) : null; - if (typeof options === "function") { - callback = options; - } - // @ts-ignore not sure how to make this work. - callback( - error, - useBuffer(options) ? Buffer.from(stdout) : stdout, - useBuffer(options) ? Buffer.from(stderr) : stderr, - ); - }); - - return process; -} - -function fork(modulePath: string): cp.ChildProcess { - return new ChildProcess(wush.execute({ - command: `node ${modulePath}`, - })); -} - -function spawn(_command: string, _args?: ReadonlyArray | cp.SpawnOptions, _options?: cp.SpawnOptions): cp.ChildProcess { - throw new Error("not implemented"); -} -// tslint:enable only-arrow-functions - -// To satisfy the types. -// tslint:disable no-any -exec.__promisify__ = undefined as any; -// tslint:enable no-any - -const exp: typeof cp = { - exec, - execFile: throwUnimplementedError, - execFileSync: throwSyncError, - execSync: throwSyncError, - fork, - spawn, - spawnSync: throwSyncError, -}; - -export = exp; diff --git a/packages/node-browser/src/fs.ts b/packages/node-browser/src/fs.ts deleted file mode 100644 index dce46dbe..00000000 --- a/packages/node-browser/src/fs.ts +++ /dev/null @@ -1,719 +0,0 @@ -import { ChildProcess } from "child_process"; -import * as fs from "fs"; -import { EventEmitter } from "events"; -import { promisify } from "util"; -import { Writable } from "stream"; -import { exec } from "./child_process"; -import { - bashCommand, throwUnimplementedError, throwSyncError, escapePath, - useBuffer, NewlineInputBuffer, Queue, -} from "./util"; - -/** - * An open file. - */ -interface IOpenFile { - readonly path: fs.PathLike; - position: number | undefined; -} - -type ReaddirCallback = (error?: NodeJS.ErrnoException, files?: string[]) => void; - -/** - * Queue for readdir. - */ -class ReaddirQueue extends Queue { - - public async run(items: Map): Promise { - const keys = Array.from(items.keys()); - try { - const stdio = await promisify(exec)(`bash -c '${keys.map((key) => `cd ${escapePath(key)} && ls -1a; echo;`).join(" ")}'`); - stdio.stdout.trim().split("\n\n").forEach((split, index) => { - const path = keys[index]; - const cbs = items.get(path)!; - if (split.indexOf("does not exist") !== -1) { - cbs.forEach((cb) => { - cb({ - code: "ENOENT", - message: "No such file or directory " + path, - name: "Not found", - }); - }); - } else { - const files = split.trim().split("\n"); - cbs.forEach((cb) => { - cb(undefined, files.filter((f) => f !== "." && f !== "..")); - }); - } - }); - } catch (error) { - items.forEach((cbs) => cbs.forEach((cb) => cb(new Error("failed to ls")))); - } - } - -} - -type StatCallback = (error?: NodeJS.ErrnoException, stats?: fs.Stats) => void; - -/** - * Queue for stat. - */ -class StatQueue extends Queue { - - public constructor() { - super(100); - } - - public async run(items: Map): Promise { - try { - const stats = await this.stat(Array.from(items.keys())); - items.forEach((callbacks, path) => { - if (stats.has(path)) { - callbacks.forEach((cb) => { - cb(undefined, stats.get(path)); - }); - } else { - callbacks.forEach((cb) => { - cb({ - code: "ENOENT", - message: "No such file or directory " + path, - name: "Not found", - }); - }); - } - }); - } catch (error) { - items.forEach((callbacks) => { - callbacks.forEach((cb) => { - cb({ - code: "ECMDFAIL", - message: "failed to stat", - name: "failed to stat", - }); - }); - }); - } - } - - /** - * Perform stat on multiple paths. Invalid files are ignored. - */ - private async stat(paths: string[]): Promise> { - const map = new Map(); - const pathsStr = paths.map(escapePath).join(" "); - const resp = await promisify(exec)( - `bash -c "stat ${pathsStr} -c \\\\'%n\\\\',%s,%F,%Y,%a,%g,%u,%X,%W,%d,%i,%b,%B,%Z,%h,%t"`, - ); - resp.stdout.split("\n").forEach((stat) => { - const matches = stat.trim().match(/(^'.*'|[^',\s]+)(?=\s*,|\s*$)/g); - if (!matches || matches.length < 16) { - return; - } - - const name = matches[0].substring(1, matches[0].length -1); - const size = parseInt(matches[1], 10); - const fileType = matches[2]; - const mtime = new Date(parseInt(matches[3], 10) * 1000); - const mode = parseInt(matches[4], 10); - const gid = parseInt(matches[5], 10); - const uid = parseInt(matches[6], 10); - const atime = new Date(parseInt(matches[7], 10) * 1000); - const birthtime = new Date(parseInt(matches[8], 10) * 1000); - const dev = parseInt(matches[9], 10); - const ino = parseInt(matches[10], 10); - const blocks = parseInt(matches[11], 10); - const blksize = parseInt(matches[12], 10); - const ctime = new Date(parseInt(matches[13], 10) * 1000); - const nlink = parseInt(matches[14], 10); - const rdev = parseInt(matches[15], 10); - - map.set(name, { - atime: atime, - atimeMs: atime.getTime(), - birthtime, - birthtimeMs: birthtime.getTime(), - blksize, - blocks, - ctime, - ctimeMs: ctime.getTime(), - dev, - gid, - ino, - isBlockDevice: (): boolean => fileType === "block special file", - isCharacterDevice: (): boolean => fileType === "character special file", - isDirectory: (): boolean => fileType === "directory", - isFIFO: (): boolean => fileType === "fifo", - isFile: (): boolean => fileType === "regular file", - isSocket: (): boolean => fileType === "socket", - isSymbolicLink: (): boolean => fileType === "symbolic link", - mode, - mtime, - mtimeMs: mtime.getTime(), - nlink, - rdev, - size, - uid, - }); - }); - - return map; - } - -} - -type ReadFileCallback = (err: NodeJS.ErrnoException, content: string) => void; - -/** - * Queue for readFile. - */ -class ReadFileQueue extends Queue { - - public constructor() { - super(100); - } - - public async run(items: Map): Promise { - try { - throwUnimplementedError(); - } catch (error) { - items.forEach((cbs) => cbs.forEach((cb) => cb(error, ""))); - } - } - -} - -class Watcher extends EventEmitter implements fs.FSWatcher { - - public constructor(private readonly process: ChildProcess) { - super(); - } - - public close(): void { - this.process.kill(); - } - -} - -class WriteStream extends Writable implements fs.WriteStream { - - public path: string; - - private process: ChildProcess; - - public constructor(path: fs.PathLike) { - super(); - - this.path = path.toString(); - this.process = exec(`cat > ${escapePath(this.path)}`); - setTimeout(() => { - // Set timeout so listeners have time to register. - this.emit("open"); - }, 0); - this.process.on("exit", () => { - this.emit("close"); - }); - } - - public get bytesWritten(): number { - throw throwUnimplementedError(); - } - - // tslint:disable-next-line no-any - public _write(chunk: any, _encoding: string, callback: () => void): void { - this.process.stdin.write(chunk); - callback(); - } - - public close(): void { - this.process.kill(); - } - -} - -// Used to identify files by descriptor. -let lastFileDescriptor = 0; -const readdirQueue = new ReaddirQueue(); -const readFileQueue = new ReadFileQueue(); -const statQueue = new StatQueue(); -const openFiles = new Map(); - -// tslint:disable only-arrow-functions -// A common pattern is to exec and call the callback with an error or null. -function execAndCallback(command: string, callback: (err: NodeJS.ErrnoException) => void): void { - promisify(exec)(command).then(() => { - callback(null as any); // tslint:disable-line no-any - }).catch((error) => { - callback(error); - }); -} - -function appendFile( - path: fs.PathLike | number, - data: any, // tslint:disable-line no-any - options?: { encoding?: string | null; mode?: string | number; flag?: string; } | string | undefined | null | ((err: NodeJS.ErrnoException) => void), - callback?: (err: NodeJS.ErrnoException) => void, -): void { - if (typeof options === "function") { - callback = options; - } - - if (typeof path === "number") { - if (!openFiles.has(path)) { - // @ts-ignore not sure how to make this work. - return callback(new Error("not open"), undefined as any); // tslint:disable-line no-any - } - path = openFiles.get(path)!.path; - } - - const process = exec(`${data ? "cat >>" : "touch"} ${escapePath(path.toString())}`, (error) => { - callback!(error as any); // tslint:disable-line no-any - }); - if (data) { - process.stdin.write(data); - } -} - -function close(fd: number, callback: (err: NodeJS.ErrnoException) => void): void { - if (!openFiles.has(fd)) { - return callback(new Error("file wasnt open")); - } - - openFiles.delete(fd); - callback(null as any); // tslint:disable-line no-any -} - -function createWriteStream(path: fs.PathLike, _options?: string | { - flags?: string; - encoding?: string; - fd?: number; - mode?: number; -}): fs.WriteStream { - return new WriteStream(path); -} - -function exists(path: fs.PathLike, callback: (exists: boolean) => void): void { - const pathStr = escapePath(path.toString()); - const command = bashCommand( - `if [ -d ${pathStr} ]; then echo true;` - + ` elif [ -f ${pathStr} ]; then echo true;` - + ` elif [ -s ${pathStr} ]; then echo true;` - + "fi", - ); - promisify(exec)(command).then((stdio) => { - callback(stdio.stdout.trim() === "true"); - }).catch(() => { - callback(false); - }); -} - -function fstat(fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { - if (!openFiles.has(fd)) { - return callback(new Error("not open"), null as any); // tslint:disable-line no-any - } - stat(openFiles.get(fd)!.path, callback); -} - -function futimes( - fd: number, - atime: string | number | Date, - mtime: string | number | Date, - callback: (err: NodeJS.ErrnoException) => void, -): void { - if (!openFiles.has(fd)) { - return callback(new Error("not opened")); - } - - const openFile = openFiles.get(fd)!; - const command = [ - { flag: "a", time: atime }, - { flag: "m", time: mtime }, - ] - .filter((item) => !!item.time) - .map((item) => `touch -${item.flag} --date="${item.time}" ${escapePath(openFile.path.toString())}`) - .join(";"); - - if (command.length === 0) { - return callback(new Error("atime or mtime required")); - } - - execAndCallback(command, callback); -} - -function lstat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { - stat(path, callback); -} - -function mkdir( - path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null | ((err: NodeJS.ErrnoException) => void), - callback?: (err: NodeJS.ErrnoException) => void, -): void { - execAndCallback( - `mkdir -p ${escapePath(path.toString())}`, - typeof mode === "function" ? mode : callback!, - ); -} - -function open( - path: fs.PathLike, - flags: string | number, mode: number | string | undefined | null | ((err: NodeJS.ErrnoException, fd: number) => void), - callback?: (err: NodeJS.ErrnoException, fd: number) => void, -): void { - if (typeof mode === "function") { - callback = mode; - } - - // Don't touch if read-only. - const promise = flags !== "r" - ? promisify(exec)(`touch ${escapePath(path.toString())}`) - .then(() => Promise.resolve()) - .catch((error) => { - if (error.message.indexOf("No such file or directory") !== -1) { - return Promise.reject({ - code: "ENOENT", - message: "No such file or directory " + path, - name: "Not found", - }); - } - - return Promise.reject(error); - }) - : Promise.resolve(); - - promise.then(() => { - const id = lastFileDescriptor++; - openFiles.set(id, { - path, - position: undefined, - }); - callback!(null as any, id); // tslint:disable-line no-any - }).catch((error) => { - callback!(error, undefined as any); // tslint:disable-line no-any - }); -} - -function read( - fd: number, - buffer: TBuffer, - offset: number, - length: number, - position: number | null, - callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void, -): void { - if (!openFiles.has(fd)) { - if (callback) { - // tslint:disable-next-line no-any - callback(new Error("not opened"), undefined as any, undefined as any); - } - - return; - } - - const hasPosition = typeof position === "number"; - const openFile = openFiles.get(fd)!; - - if (!hasPosition) { - position = openFile.position || 0; - } - - readFile(openFile.path, (error, data) => { - if (error) { - if (callback) { - // tslint:disable-next-line no-any - callback(error, undefined as any, undefined as any); - } - - return; - } - - const output = data.slice(position!, position! + length); - if (output.length !== 0) { - // TODO: seems to be no more set with v10, but need to decide if we'll be running v10. - buffer.set(output, offset); - } - - if (!hasPosition) { - if (typeof openFile.position !== "undefined") { - openFile.position += output.length; - } else { - openFile.position = output.length; - } - openFiles.set(fd, openFile); - } - - if (callback) { - callback(null as any, output.length, buffer); // tslint:disable-line no-any - } - }); -} - -function readFile( - path: fs.PathLike | number, - options: { encoding?: string | null; flag?: string; } | string | undefined | null | ((err: NodeJS.ErrnoException, data: Buffer) => void), - callback?: ((err: NodeJS.ErrnoException, data: Buffer | string) => void) | ((err: NodeJS.ErrnoException, data: Buffer) => void) | ((err: NodeJS.ErrnoException, data: string) => void), -): void { - if (typeof options === "function") { - callback = options; - } - - if (typeof path === "number") { - if (!openFiles.has(path)) { - // @ts-ignore not sure how to make this work. - return callback(new Error("not open"), undefined as any); // tslint:disable-line no-any - } - path = openFiles.get(path)!.path; - } - - readFileQueue.add(path.toString(), (error, result) => { - // @ts-ignore not sure how to make this work. - callback( - error, - result && useBuffer(options) ? Buffer.from(result) : result, - ); - }); -} - -function readdir( - path: fs.PathLike, - options: { encoding?: string | null, withFileTypes?: boolean } | string | undefined | null | ((err: NodeJS.ErrnoException, files: string[]) => void), - callback?: ((err: NodeJS.ErrnoException, files: string[]) => void) | ((err: NodeJS.ErrnoException, files: Buffer[]) => void) | ((err: NodeJS.ErrnoException, files: fs.Dirent[]) => void) -): void { - if (typeof options === "function") { - callback = options; - } - readdirQueue.add(path.toString(), (error, files) => { - if (typeof options === "function") { - callback = options; - } - // @ts-ignore not sure how to make this work. - callback( - error, - files && useBuffer(options) - ? files.map((f) => Buffer.from(f)) - : files, - ); - }); -} - -function realpath( - path: fs.PathLike, - options: { encoding?: string | null } | string | undefined | null | ((err: NodeJS.ErrnoException, resolvedPath: string) => void), - callback?: ((err: NodeJS.ErrnoException, resolvedPath: string) => void) | ((err: NodeJS.ErrnoException, resolvedPath: Buffer) => void) | ((err: NodeJS.ErrnoException, resolvedPath: string | Buffer) => void), -): void { - if (typeof options === "function") { - callback = options; - } - // @ts-ignore not sure how to make this work. - callback( - null, - useBuffer(options) ? Buffer.from(path.toString()) : path.toString(), - ); -} - -function rename(oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { - promisify(exec)(`mv ${escapePath(oldPath.toString())} ${escapePath(newPath.toString())}`).then(() => { - callback(null as any); // tslint:disable-line no-any - }).catch((error) => { - callback(error.message.indexOf("No such file or directory") !== -1 ? { - code: "ENOENT", - message: "No such file or directory " + oldPath, - name: "Not found", - } : error); - }); -} - -function rmdir(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { - execAndCallback(`rmdir ${escapePath(path.toString())}`, callback); -} - -function stat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { - statQueue.add(path.toString(), (error, stats) => { - callback(error as any, stats as any); // tslint:disable-line no-any - }); -} - -function unlink(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { - execAndCallback(`unlink ${escapePath(path.toString())}`, callback); -} - -function watch( - filename: fs.PathLike, - options: { encoding?: string | null, persistent?: boolean, recursive?: boolean } | string | undefined | null | ((event: string, filename: string) => void), - listener?: ((event: string, filename: string) => void) | ((event: string, filename: Buffer) => void), -): fs.FSWatcher { - const buffer = new NewlineInputBuffer((msg): void => { - msg = msg.trim(); - const index = msg.lastIndexOf(":"); - const events = msg.substring(index + 1).split(","); - const baseFilename = msg.substring(0, index).split("/").pop(); - events.forEach((event) => { - switch (event) { - // Rename is emitted when a file appears or disappears in the directory. - case "CREATE": - case "DELETE": - case "MOVED_FROM": - case "MOVED_TO": - watcher.emit("rename", baseFilename); - break; - case "CLOSE_WRITE": - watcher.emit("change", baseFilename); - break; - } - }); - }); - - const process = exec(`inotifywait ${escapePath(filename.toString())} -m --format "%w%f:%e"`); - process.on("exit", (exitCode) => { - watcher.emit("error", new Error(`process terminated unexpectedly with code ${exitCode}`)); - }); - process.stdout.on("data", (data) => { - buffer.push(data); - }); - - const watcher = new Watcher(process); - watcher.on("change", (filename) => { - // @ts-ignore not sure how to make this work. - listener("change", useBuffer(options) ? Buffer.from(filename) : filename); - }); - watcher.on("rename", (filename) => { - // @ts-ignore not sure how to make this work. - listener("rename", useBuffer(options) ? Buffer.from(filename) : filename); - }); - - return watcher; -} - -function writeFile( - path: fs.PathLike | number, - data: any, // tslint:disable-line no-any - options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null | ((err: NodeJS.ErrnoException) => void), - callback?: (err: NodeJS.ErrnoException) => void, -): void { - if (typeof options === "function") { - callback = options; - } - - if (typeof path === "number") { - if (!openFiles.has(path)) { - // @ts-ignore not sure how to make this work. - return callback(new Error("not open"), undefined as any); // tslint:disable-line no-any - } - path = openFiles.get(path)!.path; - } - - const process = exec(`${data ? "cat >" : "touch"} ${escapePath(path.toString())}`, (error) => { - callback!(error as any); // tslint:disable-line no-any - }); - if (data) { - process.stdin.write(data); - } -} -// tslint:enable only-arrow-functions - -// Just to satisfy the types. -// tslint:disable no-any -appendFile.__promisify__ = undefined as any; -close.__promisify__ = undefined as any; -exists.__promisify__ = undefined as any; -fstat.__promisify__ = undefined as any; -futimes.__promisify__ = undefined as any; -lstat.__promisify__ = undefined as any; -mkdir.__promisify__ = undefined as any; -open.__promisify__ = undefined as any; -read.__promisify__ = undefined as any; -readFile.__promisify__ = undefined as any; -readdir.__promisify__ = undefined as any; -realpath.__promisify__ = undefined as any; -rename.__promisify__ = undefined as any; -rmdir.__promisify__ = undefined as any; -stat.__promisify__ = undefined as any; -unlink.__promisify__ = undefined as any; -writeFile.__promisify__ = undefined as any; -realpath.native = undefined as any; -// tslint:enable no-any - -const exp: typeof fs = { - constants: fs.constants, - Stats: fs.Stats, - ReadStream: fs.ReadStream, - WriteStream: fs.WriteStream, - Dirent: fs.Dirent, - promises: fs.promises, - - access: throwUnimplementedError, - accessSync: throwSyncError, - appendFile, - appendFileSync: throwSyncError, - chmod: throwUnimplementedError, - chmodSync: throwSyncError, - chown: throwUnimplementedError, - chownSync: throwSyncError, - close, - copyFile: throwUnimplementedError, - copyFileSync: throwSyncError, - closeSync: throwSyncError, - createReadStream: throwUnimplementedError, - createWriteStream, - exists, - existsSync: throwSyncError, - fchmod: throwUnimplementedError, - fchmodSync: throwSyncError, - fchown: throwUnimplementedError, - fchownSync: throwSyncError, - fdatasync: throwUnimplementedError, - fdatasyncSync: throwSyncError, - fstat, - fstatSync: throwSyncError, - fsync: throwUnimplementedError, - fsyncSync: throwSyncError, - ftruncate: throwUnimplementedError, - ftruncateSync: throwSyncError, - futimes, - futimesSync: throwSyncError, - lchmod: throwUnimplementedError, - lchmodSync: throwSyncError, - lchown: throwUnimplementedError, - lchownSync: throwSyncError, - link: throwUnimplementedError, - linkSync: throwSyncError, - lstat, - lstatSync: throwSyncError, - mkdir, - mkdirSync: throwSyncError, - mkdtemp: throwUnimplementedError, - mkdtempSync: throwSyncError, - open, - openSync: throwSyncError, - read, - readFile, - readFileSync: throwSyncError, - readSync: throwSyncError, - readdir, - readdirSync: throwSyncError, - readlink: throwUnimplementedError, - readlinkSync: throwSyncError, - realpath, - realpathSync: throwSyncError, - rename, - renameSync: throwSyncError, - rmdir, - rmdirSync: throwSyncError, - stat, - statSync: throwSyncError, - symlink: throwUnimplementedError, - symlinkSync: throwSyncError, - truncate: throwUnimplementedError, - truncateSync: throwSyncError, - unlink, - unlinkSync: throwSyncError, - unwatchFile: throwUnimplementedError, - utimes: throwUnimplementedError, - utimesSync: throwSyncError, - watch, - watchFile: throwUnimplementedError, - write: throwUnimplementedError, - writeFile, - writeFileSync: throwSyncError, - writeSync: throwSyncError, -}; - -export = exp; diff --git a/packages/node-browser/src/index.ts b/packages/node-browser/src/index.ts deleted file mode 100644 index 1b55d1a6..00000000 --- a/packages/node-browser/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { bashCommand, escapePath, isBrowserEnvironment } from "./util"; -export { bashCommand, escapePath, isBrowserEnvironment }; diff --git a/packages/node-browser/src/net.ts b/packages/node-browser/src/net.ts deleted file mode 100644 index 9399588e..00000000 --- a/packages/node-browser/src/net.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as net from "net"; - -/** - * Implementation of Socket for the browser. - */ -class Socket extends net.Socket { - - public connect(): this { - throw new Error("not implemented"); - } - -} - -/** - * Implementation of Server for the browser. - */ -class Server extends net.Server { - - public listen( - _port?: number | any | net.ListenOptions, // tslint:disable-line no-any so we can match the Node API. - _hostname?: string | number | Function, - _backlog?: number | Function, - _listeningListener?: Function, - ): this { - throw new Error("not implemented"); - } - -} - -// tslint:disable only-arrow-functions -function connect(): net.Socket { - throw new Error("not implemented"); -} - -function createConnection(): net.Socket { - throw new Error("not implemented"); -} - -function isIP(_input: string): number { - throw new Error("not implemented"); -} - -function isIPv4(_input: string): boolean { - throw new Error("not implemented"); -} - -function isIPv6(_input: string): boolean { - throw new Error("not implemented"); -} - -function createServer( - _options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean } | ((socket: net.Socket) => void), - _connectionListener?: (socket: net.Socket) => void, -): Server { - return new Server(); -} -// tslint:enable only-arrow-functions - -const exp: typeof net = { - Socket, - Server, - connect, - createConnection, - isIP, - isIPv4, - isIPv6, - createServer, -}; - -export = exp; diff --git a/packages/node-browser/src/util.ts b/packages/node-browser/src/util.ts deleted file mode 100644 index a881e5c1..00000000 --- a/packages/node-browser/src/util.ts +++ /dev/null @@ -1,138 +0,0 @@ -// The type doesn't matter for these since we're just throwing. -// tslint:disable no-any -export const throwUnimplementedError = (): any => { - throw new Error("not implemented"); -}; -// In case the types except the promisify property. -throwUnimplementedError.__promisify__ = undefined as any; -// This one seems to be a mistake in the types for `link`. -throwUnimplementedError.link = undefined as any; -export const throwSyncError = (): any => { - throw new Error("sync is not supported"); -}; -// realpath & realpathSync. -throwSyncError.native = undefined as any; -// tslint:enable no-any - -/** - * Return true if the options specify to use a Buffer instead of string. - */ -export const useBuffer = (options: { encoding?: string | null } | string | undefined | null | Function): boolean => { - return options === "buffer" - || (!!options && typeof options !== "string" && typeof options !== "function" - && (options.encoding === "buffer" || options.encoding === null)); -}; - -/** - * Run a command with bash. - */ -export const bashCommand = (command: string): string => { - return `bash -c "${command.replace(/"/g, "\\\"")}"`; -}; - -/** - * Return true if we're in a browser environment (including web workers). - */ -export const isBrowserEnvironment = (): boolean => { - return typeof process === "undefined" || typeof process.stdout === "undefined"; -}; - -/** - * Escape a path. This prevents any issues with file names that have quotes, - * spaces, braces, etc. - */ -export const escapePath = (path: string): string => { - return `'${path.replace(/'/g, "'\\''")}'`; -}; - -/** - * This queues up items then runs on all the items at once after a timeout. Each - * item has a callback that expects the response for that item which is the - * extending class's responsibility to call. - * - * You can also specify a maximum number of items to keep in the queue. - */ -export abstract class Queue { - - private items: Map; - private timeout: number | NodeJS.Timer | undefined; - private max: number | undefined; - private timeoutDelay = 1; - - public constructor(max?: number) { - this.items = new Map(); - this.max = max; - } - - /** - * Add an item to the queue. - */ - public add(key: string, callback: T): void { - if (this.items.has(key)) { - this.items.get(key)!.push(callback); - } else { - this.items.set(key, [callback]); - } - - const run = (): void => { - // tslint:disable-next-line no-any because NodeJS.Timer is valid. - clearTimeout(this.timeout as any); - this.timeout = undefined; - const newMap = new Map(this.items); - this.items.clear(); - this.run(newMap); - }; - - if (typeof this.max !== "undefined" && this.items.size >= this.max) { - return run(); - } - - if (typeof this.timeout === "undefined") { - this.timeout = setTimeout(() => { - run(); - }, this.timeoutDelay); - } - } - - /** - * Run on the specified items then call their callbacks. - */ - protected abstract run(items: Map): void; - -} - -/** - * Class for safely taking input and turning it into separate messages. - * Assumes that messages are split by newlines. - */ -export class NewlineInputBuffer { - - private callback: (msg: string) => void; - private buffer: string | undefined; - - public constructor(callback: (msg: string) => void) { - this.callback = callback; - } - - /** - * Add data to be buffered. - */ - public push(data: string | Uint8Array): void { - let input = typeof data === "string" ? data : data.toString(); - if (this.buffer) { - input = this.buffer + input; - this.buffer = undefined; - } - const lines = input.split("\n"); - const length = lines.length - 1; - const lastLine = lines[length]; - if (lastLine.length > 0) { - this.buffer = lastLine; - } - lines.pop(); // This is either the line we buffered or an empty string. - for (let i = 0; i < length; ++i) { - this.callback(lines[i]); - } - } - -} diff --git a/packages/node-browser/yarn.lock b/packages/node-browser/yarn.lock deleted file mode 100644 index fb57ccd1..00000000 --- a/packages/node-browser/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/packages/package.json b/packages/package.json index c3959367..09d9adf3 100644 --- a/packages/package.json +++ b/packages/package.json @@ -1,16 +1,11 @@ { "scripts": { - "build:rules": "cd ./rules && yarn build", - "postinstall": "ts-node ./scripts/src/install-packages.ts && yarn build:rules", "test": "jest" }, "devDependencies": { "@types/jest": "^23.3.12", "jest": "^23.6.0", - "ts-jest": "^23.10.5", - "ts-node": "^7.0.1", - "tslint": "^5.12.1", - "typescript-tslint-plugin": "^0.2.1" + "ts-jest": "^23.10.5" }, "dependencies": { "xmlhttprequest": "1.8.0" @@ -23,10 +18,10 @@ "json" ], "setupFiles": [ - "/scripts/src/test-setup.js" + "/../scripts/test-setup.js" ], "moduleNameMapper": { - "^.+\\.(s?css|png|svg)$": "/scripts/src/dummy.js", + "^.+\\.(s?css|png|svg)$": "/../scripts/dummy.js", "@coder/(.*)/testing": "/$1/testing", "@coder/(.*)": "/$1/src" }, diff --git a/packages/server/package.json b/packages/protocol/package.json similarity index 85% rename from packages/server/package.json rename to packages/protocol/package.json index 840b6b11..73fbb53f 100644 --- a/packages/server/package.json +++ b/packages/protocol/package.json @@ -1,5 +1,6 @@ { - "name": "server", + "name": "@coder/server", + "main": "src/index.ts", "dependencies": { "express": "^4.16.4", "node-pty": "^0.8.0", diff --git a/packages/server/scripts/generate_proto.sh b/packages/protocol/scripts/generate_proto.sh similarity index 100% rename from packages/server/scripts/generate_proto.sh rename to packages/protocol/scripts/generate_proto.sh diff --git a/packages/server/src/browser/client.ts b/packages/protocol/src/browser/client.ts similarity index 89% rename from packages/server/src/browser/client.ts rename to packages/protocol/src/browser/client.ts index 3bebd90f..1a17d714 100644 --- a/packages/server/src/browser/client.ts +++ b/packages/protocol/src/browser/client.ts @@ -30,13 +30,13 @@ export class Client { }); } - public evaluate(func: () => R): Promise; - public evaluate(func: (a1: T1) => R, a1: T1): Promise; - public evaluate(func: (a1: T1, a2: T2) => R, a1: T1, a2: T2): Promise; - public evaluate(func: (a1: T1, a2: T2, a3: T3) => R, a1: T1, a2: T2, a3: T3): Promise; - public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4) => R, a1: T1, a2: T2, a3: T3, a4: T4): Promise; - public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5) => R, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5): Promise; - public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6) => R, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6): Promise; + public evaluate(func: () => R | Promise): Promise; + public evaluate(func: (a1: T1) => R | Promise, a1: T1): Promise; + public evaluate(func: (a1: T1, a2: T2) => R | Promise, a1: T1, a2: T2): Promise; + public evaluate(func: (a1: T1, a2: T2, a3: T3) => R | Promise, a1: T1, a2: T2, a3: T3): Promise; + public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4) => R | Promise, a1: T1, a2: T2, a3: T3, a4: T4): Promise; + public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5) => R | Promise, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5): Promise; + public evaluate(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6) => R | Promise, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6): Promise; /** * Evaluates a function on the server. * To pass variables, ensure they are serializable and passed through the included function. @@ -49,7 +49,7 @@ export class Client { * @param func Function to evaluate * @returns {Promise} Promise rejected or resolved from the evaluated function */ - public evaluate(func: (a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6) => R, a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6): Promise { + public evaluate(func: (a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6) => R | Promise, a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6): Promise { const newEval = new NewEvalMessage(); const id = this.evalId++; newEval.setId(id); diff --git a/packages/server/src/browser/command.ts b/packages/protocol/src/browser/command.ts similarity index 100% rename from packages/server/src/browser/command.ts rename to packages/protocol/src/browser/command.ts diff --git a/packages/protocol/src/browser/modules/child_process.ts b/packages/protocol/src/browser/modules/child_process.ts new file mode 100644 index 00000000..97c0f489 --- /dev/null +++ b/packages/protocol/src/browser/modules/child_process.ts @@ -0,0 +1,52 @@ +import * as cp from "child_process"; +import { Client } from "../client"; +import { useBuffer } from "./util"; + +export class CP { + + public constructor( + private readonly client: Client, + ) { } + + public exec( + command: string, + options?: { encoding?: BufferEncoding | string | "buffer" | null } & cp.ExecOptions | null | ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void), + callback?: ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void), + ): cp.ChildProcess { + const process = this.client.spawn(command); + + let stdout = ""; + process.stdout.on("data", (data) => { + stdout += data.toString(); + }); + + let stderr = ""; + process.stderr.on("data", (data) => { + stderr += data.toString(); + }); + + process.on("exit", (exitCode) => { + const error = exitCode !== 0 ? new Error(stderr) : null; + if (typeof options === "function") { + callback = options; + } + // @ts-ignore not sure how to make this work. + callback( + error, + useBuffer(options) ? Buffer.from(stdout) : stdout, + useBuffer(options) ? Buffer.from(stderr) : stderr, + ); + }); + + return process; + } + + public fork(modulePath: string): cp.ChildProcess { + return this.client.fork(modulePath); + } + + public spawn(command: string, args?: ReadonlyArray | cp.SpawnOptions, _options?: cp.SpawnOptions): cp.ChildProcess { + return this.client.spawn(command, args, options); + } + +} diff --git a/packages/protocol/src/browser/modules/fs.ts b/packages/protocol/src/browser/modules/fs.ts new file mode 100644 index 00000000..125e9be2 --- /dev/null +++ b/packages/protocol/src/browser/modules/fs.ts @@ -0,0 +1,577 @@ +import * as fs from "fs"; +import { Client } from "../client"; + +/** + * Implements the native fs module + * Doesn't use `implements typeof import("fs")` to remove need for __promisify__ impls + */ +export class FS { + + public constructor( + private readonly client: Client, + ) { } + + public access(path: fs.PathLike, mode: number | undefined, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.access)(path, mode); + }, path, mode).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public appendFile(file: fs.PathLike | number, data: any, options: { encoding?: string | null, mode?: string | number, flag?: string } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, data, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.appendFile)(path, data, options); + }, file, data, options).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public chmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.chmod)(path, mode); + }, path, mode).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public chown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, uid, gid) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.chown)(path, uid, gid); + }, path, uid, gid).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public close(fd: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.close)(fd); + }, fd).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public copyFile(src: fs.PathLike, dest: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((src, dest) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.copyFile)(src, dest); + }, src, dest).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public createWriteStream(): void { + throw new Error("not implemented"); + } + + public exists(path: fs.PathLike, callback: (exists: boolean) => void): void { + this.client.evaluate((path) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.exists)(path); + }, path).then((r) => { + callback(r); + }).catch(() => { + callback(false); + }); + } + + public fchmod(fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.fchmod)(fd, mode); + }, fd, mode).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public fchown(fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd, uid, gid) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.fchown)(fd, uid, gid); + }, fd, uid, gid).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public fdatasync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.fdatasync)(fd); + }, fd).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public fstat(fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { + this.client.evaluate(async (fd) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const stats = await util.promisify(fs.fstat)(fd); + return { + ...stats, + _isBlockDevice: stats.isBlockDevice(), + _isCharacterDevice: stats.isCharacterDevice(), + _isDirectory: stats.isDirectory(), + _isFIFO: stats.isFIFO(), + _isFile: stats.isFile(), + _isSocket: stats.isSocket(), + _isSymbolicLink: stats.isSymbolicLink(), + }; + }, fd).then((stats) => { + callback(undefined!, Stats.fromObject(stats)); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public fsync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.fsync)(fd); + }, fd).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public ftruncate(fd: number, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd, len) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.ftruncate)(fd, len); + }, fd, len).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public futimes(fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((fd, atime, mtime) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.futimes)(fd, atime, mtime); + }, fd, atime, mtime).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public lchmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.lchmod)(path, mode); + }, path, mode).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public lchown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, uid, gid) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.lchown)(path, uid, gid); + }, path, uid, gid).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public link(existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((existingPath, newPath) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.link)(existingPath, newPath); + }, existingPath, newPath).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public lstat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { + this.client.evaluate(async (path) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const stats = await util.promisify(fs.lstat)(path); + return { + ...stats, + _isBlockDevice: stats.isBlockDevice(), + _isCharacterDevice: stats.isCharacterDevice(), + _isDirectory: stats.isDirectory(), + _isFIFO: stats.isFIFO(), + _isFile: stats.isFile(), + _isSocket: stats.isSocket(), + _isSymbolicLink: stats.isSymbolicLink(), + }; + }, path).then((stats) => { + callback(undefined!, Stats.fromObject(stats)); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public mkdir(path: fs.PathLike, mode: number | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.mkdir)(path, mode); + }, path, mode).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public mkdtemp(prefix: string, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, folder: string) => void): void { + this.client.evaluate((prefix, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.mkdtemp)(prefix, options); + }, prefix, options).then((folder) => { + callback(undefined!, folder); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public open(path: fs.PathLike, flags: string | number, mode: string | number | undefined | null, callback: (err: NodeJS.ErrnoException, fd: number) => void): void { + this.client.evaluate((path, flags, mode) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.open)(path, flags, mode); + }, path, flags, mode).then((fd) => { + callback(undefined!, fd); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public read(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void { + this.client.evaluate(async (fd, bufferLength, length, position) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const buffer = new Buffer(length); + const resp = await util.promisify(fs.read)(fd, buffer, 0, length, position); + + return { + bytesRead: resp.bytesRead, + content: buffer.toString("utf8"), + }; + }, fd, buffer.byteLength, length, position).then((resp) => { + const newBuf = Buffer.from(resp.content, "utf8"); + buffer.set(newBuf, offset); + callback(undefined!, resp.bytesRead, newBuf as TBuffer); + }).catch((ex) => { + callback(ex, undefined!, undefined!); + }); + } + + public readFile(path: fs.PathLike | number, options: string | { encoding?: string | null | undefined; flag?: string | undefined; } | null | undefined, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void { + this.client.evaluate(async (path, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const value = await util.promisify(fs.readFile)(path, options); + + return value.toString(); + }, path, options).then((buffer) => { + callback(undefined!, buffer); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public readdir(path: fs.PathLike, options: { encoding: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void { + this.client.evaluate((path, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.readdir)(path, options); + }, path, options).then((files) => { + callback(undefined!, files); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public readlink(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, linkString: string) => void): void { + this.client.evaluate((path, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.readlink)(path, options); + }, path, options).then((linkString) => { + callback(undefined!, linkString); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public realpath(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => void): void { + this.client.evaluate((path, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.realpath)(path, options); + }, path, options).then((resolvedPath) => { + callback(undefined!, resolvedPath); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public rename(oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((oldPath, newPath) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.rename)(oldPath, newPath); + }, oldPath, newPath).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public rmdir(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.rmdir)(path); + }, path).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public stat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void { + this.client.evaluate(async (path) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const stats = await util.promisify(fs.stat)(path); + return { + ...stats, + _isBlockDevice: stats.isBlockDevice(), + _isCharacterDevice: stats.isCharacterDevice(), + _isDirectory: stats.isDirectory(), + _isFIFO: stats.isFIFO(), + _isFile: stats.isFile(), + _isSocket: stats.isSocket(), + _isSymbolicLink: stats.isSymbolicLink(), + }; + }, path).then((stats) => { + callback(undefined!, Stats.fromObject(stats)); + }).catch((ex) => { + callback(ex, undefined!); + }); + } + + public symlink(target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((target, path, type) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.symlink)(target, path, type); + }, target, path, type).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public truncate(path: fs.PathLike, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, len) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.truncate)(path, len); + }, path, len).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public unlink(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.unlink)(path); + }, path).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public utimes(path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, atime, mtime) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.utimes)(path, atime, mtime); + }, path, atime, mtime).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } + + public write(fd: number, buffer: TBuffer, offset: number | undefined, length: number | undefined, position: number | undefined, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void { + this.client.evaluate(async (fd, buffer, offset, length, position) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + const resp = await util.promisify(fs.write)(fd, Buffer.from(buffer, "utf8"), offset, length, position); + + return { + bytesWritten: resp.bytesWritten, + content: resp.buffer.toString("utf8"), + } + }, fd, buffer.toString(), offset, length, position).then((r) => { + callback(undefined!, r.bytesWritten, Buffer.from(r.content, "utf8") as TBuffer); + }).catch((ex) => { + callback(ex, undefined!, undefined!); + }); + } + + public writeFile(path: fs.PathLike | number, data: any, options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void { + this.client.evaluate((path, data, options) => { + const fs = require("fs") as typeof import("fs"); + const util = require("util") as typeof import("util"); + return util.promisify(fs.writeFile)(path, data, options); + }, path, data, options).then(() => { + callback(undefined!); + }).catch((ex) => { + callback(ex); + }); + } +} + +class Stats implements fs.Stats { + + public static fromObject(object: any): Stats { + return new Stats(object); + } + + // @ts-ignore + public readonly dev: number; + // @ts-ignore + public readonly ino: number; + // @ts-ignore + public readonly mode: number; + // @ts-ignore + public readonly nlink: number; + // @ts-ignore + public readonly uid: number; + // @ts-ignore + public readonly gid: number; + // @ts-ignore + public readonly rdev: number; + // @ts-ignore + public readonly size: number; + // @ts-ignore + public readonly blksize: number; + // @ts-ignore + public readonly blocks: number; + // @ts-ignore + public readonly atimeMs: number; + // @ts-ignore + public readonly mtimeMs: number; + // @ts-ignore + public readonly ctimeMs: number; + // @ts-ignore + public readonly birthtimeMs: number; + // @ts-ignore + public readonly atime: Date; + // @ts-ignore + public readonly mtime: Date; + // @ts-ignore + public readonly ctime: Date; + // @ts-ignore + public readonly birthtime: Date; + // @ts-ignore + private readonly _isFile: boolean; + // @ts-ignore + private readonly _isDirectory: boolean; + // @ts-ignore + private readonly _isBlockDevice: boolean; + // @ts-ignore + private readonly _isCharacterDevice: boolean; + // @ts-ignore + private readonly _isSymbolicLink: boolean; + // @ts-ignore + private readonly _isFIFO: boolean; + // @ts-ignore + private readonly _isSocket: boolean; + + private constructor(stats: object) { + Object.assign(this, stats); + } + + public isFile(): boolean { + return this._isFile; + } + + public isDirectory(): boolean { + return this._isDirectory; + } + + public isBlockDevice(): boolean { + return this._isBlockDevice; + } + + public isCharacterDevice(): boolean { + return this._isCharacterDevice; + } + + public isSymbolicLink(): boolean { + return this._isSymbolicLink; + } + + public isFIFO(): boolean { + return this._isFIFO; + } + + public isSocket(): boolean { + return this._isSocket; + } + + public toObject(): object { + return JSON.parse(JSON.stringify(this)); + } + +} diff --git a/packages/protocol/src/browser/modules/net.ts b/packages/protocol/src/browser/modules/net.ts new file mode 100644 index 00000000..a545283c --- /dev/null +++ b/packages/protocol/src/browser/modules/net.ts @@ -0,0 +1,72 @@ +import * as net from "net"; + +/** + * Implementation of Socket for the browser. + */ +class Socket extends net.Socket { + + public connect(): this { + throw new Error("not implemented"); + } + +} + +/** + * Implementation of Server for the browser. + */ +class Server extends net.Server { + + public listen( + _port?: number | any | net.ListenOptions, // tslint:disable-line no-any so we can match the Node API. + _hostname?: string | number | Function, + _backlog?: number | Function, + _listeningListener?: Function, + ): this { + throw new Error("not implemented"); + } + +} + +type NodeNet = typeof net; + +/** + * Implementation of net for the browser. + */ +export class Net implements NodeNet { + + public get Socket(): typeof net.Socket { + return Socket; + } + + public get Server(): typeof net.Server { + return Server; + } + + public connect(): net.Socket { + throw new Error("not implemented"); + } + + public createConnection(): net.Socket { + throw new Error("not implemented"); + } + + public isIP(_input: string): number { + throw new Error("not implemented"); + } + + public isIPv4(_input: string): boolean { + throw new Error("not implemented"); + } + + public isIPv6(_input: string): boolean { + throw new Error("not implemented"); + } + + public createServer( + _options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean } | ((socket: net.Socket) => void), + _connectionListener?: (socket: net.Socket) => void, + ): Server { + return new Server(); + } + +} diff --git a/packages/protocol/src/browser/modules/util.ts b/packages/protocol/src/browser/modules/util.ts new file mode 100644 index 00000000..1690ec46 --- /dev/null +++ b/packages/protocol/src/browser/modules/util.ts @@ -0,0 +1,8 @@ +/** + * Return true if the options specify to use a Buffer instead of string. + */ +export const useBuffer = (options: { encoding?: string | null } | string | undefined | null | Function): boolean => { + return options === "buffer" + || (!!options && typeof options !== "string" && typeof options !== "function" + && (options.encoding === "buffer" || options.encoding === null)); +}; diff --git a/packages/server/src/common/connection.ts b/packages/protocol/src/common/connection.ts similarity index 100% rename from packages/server/src/common/connection.ts rename to packages/protocol/src/common/connection.ts diff --git a/packages/protocol/src/common/util.ts b/packages/protocol/src/common/util.ts new file mode 100644 index 00000000..99f4cf8d --- /dev/null +++ b/packages/protocol/src/common/util.ts @@ -0,0 +1,14 @@ +/** + * Return true if we're in a browser environment (including web workers). + */ +export const isBrowserEnvironment = (): boolean => { + return typeof process === "undefined" || typeof process.stdout === "undefined"; +}; + +/** + * Escape a path. This prevents any issues with file names that have quotes, + * spaces, braces, etc. + */ +export const escapePath = (path: string): string => { + return `'${path.replace(/'/g, "'\\''")}'`; +}; diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts new file mode 100644 index 00000000..af625b47 --- /dev/null +++ b/packages/protocol/src/index.ts @@ -0,0 +1,6 @@ +export * from "./browser/client"; +export * from "./browser/modules/child_process"; +export * from "./browser/modules/fs"; +export * from "./browser/modules/net"; +export * from "./common/connection"; +export * from "./common/util"; diff --git a/packages/server/src/node/command.ts b/packages/protocol/src/node/command.ts similarity index 100% rename from packages/server/src/node/command.ts rename to packages/protocol/src/node/command.ts diff --git a/packages/server/src/node/evaluate.ts b/packages/protocol/src/node/evaluate.ts similarity index 96% rename from packages/server/src/node/evaluate.ts rename to packages/protocol/src/node/evaluate.ts index f0ad80b3..659511dd 100644 --- a/packages/server/src/node/evaluate.ts +++ b/packages/protocol/src/node/evaluate.ts @@ -51,7 +51,7 @@ export const evaluate = async (connection: SendableConnection, message: NewEvalM connection.send(serverMsg.serializeBinary()); }; try { - const value = vm.runInNewContext(`(${message.getFunction()})(${argStr.join(",")})`, { require, setTimeout }, { + const value = vm.runInNewContext(`(${message.getFunction()})(${argStr.join(",")})`, { Buffer, require, setTimeout }, { timeout: message.getTimeout() || 30000, }); sendResp(await value); diff --git a/packages/server/src/node/server.ts b/packages/protocol/src/node/server.ts similarity index 100% rename from packages/server/src/node/server.ts rename to packages/protocol/src/node/server.ts diff --git a/packages/server/src/proto/client.proto b/packages/protocol/src/proto/client.proto similarity index 100% rename from packages/server/src/proto/client.proto rename to packages/protocol/src/proto/client.proto diff --git a/packages/server/src/proto/client_pb.d.ts b/packages/protocol/src/proto/client_pb.d.ts similarity index 100% rename from packages/server/src/proto/client_pb.d.ts rename to packages/protocol/src/proto/client_pb.d.ts diff --git a/packages/server/src/proto/client_pb.js b/packages/protocol/src/proto/client_pb.js similarity index 100% rename from packages/server/src/proto/client_pb.js rename to packages/protocol/src/proto/client_pb.js diff --git a/packages/server/src/proto/command.proto b/packages/protocol/src/proto/command.proto similarity index 100% rename from packages/server/src/proto/command.proto rename to packages/protocol/src/proto/command.proto diff --git a/packages/server/src/proto/command_pb.d.ts b/packages/protocol/src/proto/command_pb.d.ts similarity index 100% rename from packages/server/src/proto/command_pb.d.ts rename to packages/protocol/src/proto/command_pb.d.ts diff --git a/packages/server/src/proto/command_pb.js b/packages/protocol/src/proto/command_pb.js similarity index 100% rename from packages/server/src/proto/command_pb.js rename to packages/protocol/src/proto/command_pb.js diff --git a/packages/server/src/proto/index.ts b/packages/protocol/src/proto/index.ts similarity index 100% rename from packages/server/src/proto/index.ts rename to packages/protocol/src/proto/index.ts diff --git a/packages/server/src/proto/node.proto b/packages/protocol/src/proto/node.proto similarity index 100% rename from packages/server/src/proto/node.proto rename to packages/protocol/src/proto/node.proto diff --git a/packages/server/src/proto/node_pb.d.ts b/packages/protocol/src/proto/node_pb.d.ts similarity index 100% rename from packages/server/src/proto/node_pb.d.ts rename to packages/protocol/src/proto/node_pb.d.ts diff --git a/packages/server/src/proto/node_pb.js b/packages/protocol/src/proto/node_pb.js similarity index 100% rename from packages/server/src/proto/node_pb.js rename to packages/protocol/src/proto/node_pb.js diff --git a/packages/server/test/command.test.ts b/packages/protocol/test/command.test.ts similarity index 100% rename from packages/server/test/command.test.ts rename to packages/protocol/test/command.test.ts diff --git a/packages/server/test/evaluate.test.ts b/packages/protocol/test/evaluate.test.ts similarity index 100% rename from packages/server/test/evaluate.test.ts rename to packages/protocol/test/evaluate.test.ts diff --git a/packages/server/test/forker.js b/packages/protocol/test/forker.js similarity index 100% rename from packages/server/test/forker.js rename to packages/protocol/test/forker.js diff --git a/packages/server/test/helpers.ts b/packages/protocol/test/helpers.ts similarity index 100% rename from packages/server/test/helpers.ts rename to packages/protocol/test/helpers.ts diff --git a/packages/protocol/test/modules/fs.test.ts b/packages/protocol/test/modules/fs.test.ts new file mode 100644 index 00000000..a2879e11 --- /dev/null +++ b/packages/protocol/test/modules/fs.test.ts @@ -0,0 +1,643 @@ +import * as nativeFs from "fs"; +import * as os from "os"; +import * as path from "path"; +import { createClient } from "../helpers"; +import { FS } from "../../src/browser/modules/fs"; + +describe("fs", () => { + const client = createClient(); + const fs = new FS(client); + const testFile = path.join(__dirname, "fs.test.ts"); + const tmpFile = () => path.join(os.tmpdir(), `tmpfile-${Math.random()}`); + const createTmpFile = (): string => { + const tf = tmpFile(); + nativeFs.writeFileSync(tf, ""); + return tf; + }; + + describe("access", () => { + it("should access file", (done) => { + fs.access(testFile, undefined, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to access file", (done) => { + fs.access(tmpFile(), undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("append", () => { + it("should append to file", (done) => { + const file = createTmpFile(); + fs.appendFile(file, "howdy", undefined, (err) => { + expect(err).toBeUndefined(); + const content = nativeFs.readFileSync(file).toString(); + expect(content).toEqual("howdy"); + done(); + }); + }); + + it("should fail to append to file", (done) => { + fs.appendFile(tmpFile(), "howdy", undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("chmod", () => { + it("should chmod file", (done) => { + fs.chmod(createTmpFile(), "755", (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to chmod file", (done) => { + fs.chmod(tmpFile(), "755", (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("chown", () => { + it("should chown file", (done) => { + fs.chown(createTmpFile(), 1, 1, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to chown file", (done) => { + fs.chown(tmpFile(), 1, 1, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("close", () => { + it("should close file", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "r"); + fs.close(id, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to close file", (done) => { + fs.close(99999999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("copyFile", () => { + it("should copy file", (done) => { + const file = createTmpFile(); + fs.copyFile(file, tmpFile(), (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to copy file", (done) => { + fs.copyFile(tmpFile(), tmpFile(), (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("exists", () => { + it("should output file exists", (done) => { + fs.exists(testFile, (exists) => { + expect(exists).toBeTruthy(); + done(); + }); + }); + + it("should output file does not exist", (done) => { + fs.exists(tmpFile(), (exists) => { + expect(exists).toBeFalsy(); + done(); + }); + }); + }); + + describe("fchmod", () => { + it("should fchmod", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "r"); + fs.fchmod(id, "755", (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to fchmod", (done) => { + fs.fchmod(2242342, "755", (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("fchown", () => { + it("should fchown", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "r"); + fs.fchown(id, 1, 1, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to fchown", (done) => { + fs.fchown(99999, 1, 1, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("fdatasync", () => { + it("should fdatasync", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "r"); + fs.fdatasync(id, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to fdatasync", (done) => { + fs.fdatasync(99999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("fstat", () => { + it("should fstat", (done) => { + const id = nativeFs.openSync(testFile, "r"); + fs.fstat(id, (err, stats) => { + expect(err).toBeUndefined(); + expect(stats.size).toBeGreaterThan(0); + done(); + }); + }); + + it("should fail to fstat", (done) => { + fs.fstat(9999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("fsync", () => { + it("should fsync", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "r"); + fs.fsync(id, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to fsync", (done) => { + fs.fsync(99999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("ftruncate", () => { + it("should ftruncate", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "w"); + fs.ftruncate(id, 1, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to ftruncate", (done) => { + fs.ftruncate(99999, 9999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("futimes", () => { + it("should futimes", (done) => { + const file = createTmpFile(); + const id = nativeFs.openSync(file, "w"); + fs.futimes(id, 1, 1, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to futimes", (done) => { + fs.futimes(99999, 9999, 9999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("lchmod", () => { + it("should lchmod file", (done) => { + fs.lchmod(createTmpFile(), "755", (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to lchmod file", (done) => { + fs.lchmod(tmpFile(), "755", (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("lchown", () => { + it("should lchown file", (done) => { + fs.lchown(createTmpFile(), 1, 1, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to lchown file", (done) => { + fs.lchown(tmpFile(), 1, 1, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("link", () => { + it("should link file", (done) => { + const newFile = createTmpFile(); + const targetFile = tmpFile(); + fs.link(newFile, targetFile, (err) => { + expect(err).toBeUndefined(); + expect(nativeFs.existsSync(targetFile)).toBeTruthy(); + done(); + }); + }); + + it("should fail to link file", (done) => { + fs.link(tmpFile(), tmpFile(), (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("lstat", () => { + it("should lstat", (done) => { + fs.lstat(testFile, (err, stats) => { + expect(err).toBeUndefined(); + expect(stats.size).toBeGreaterThan(0); + done(); + }); + }); + + it("should fail to lstat", (done) => { + fs.lstat(path.join(__dirname, "no-exist"), (err, stats) => { + expect(err).toBeDefined(); + expect(stats).toBeUndefined(); + done(); + }); + }); + }); + + describe("mkdir", () => { + const target = tmpFile(); + it("should create directory", (done) => { + fs.mkdir(target, undefined, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to create directory", (done) => { + fs.mkdir(target, undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("mkdtemp", () => { + it("should create temp dir", (done) => { + fs.mkdtemp(path.join(os.tmpdir(), "example"), undefined, (err, folder) => { + expect(err).toBeUndefined(); + done(); + }); + }); + }); + + describe("open", () => { + it("should open file", (done) => { + fs.open(testFile, "r", undefined, (err, fd) => { + expect(err).toBeUndefined(); + expect(fd).toBeDefined(); + done(); + }); + }); + + it("should fail to open file", (done) => { + fs.open("asdfoksfg", "r", undefined, (err, fd) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("read", () => { + it("should read file", async () => { + const fd = nativeFs.openSync(testFile, "r"); + const stat = nativeFs.fstatSync(fd); + const buffer = new Buffer(stat.size); + let bytesRead = 0; + let chunkSize = 2048; + while (bytesRead < stat.size) { + if ((bytesRead + chunkSize) > stat.size) { + chunkSize = stat.size - bytesRead; + } + + await new Promise((res, rej) => { + fs.read(fd, buffer, bytesRead, chunkSize, bytesRead, (err) => { + if (err) { + rej(err); + } else { + res(); + } + }); + }); + bytesRead += chunkSize; + } + + expect(buffer.toString()).toEqual(nativeFs.readFileSync(testFile).toString()); + }); + + it("should fail to read file", (done) => { + fs.read(99999, new Buffer(10), 9999, 999, 999, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("readFile", () => { + it("should read file", (done) => { + fs.readFile(testFile, undefined, (err, data) => { + expect(err).toBeUndefined(); + expect(data.toString()).toEqual(nativeFs.readFileSync(testFile).toString()); + done(); + }); + }); + + it("should fail to read file", (done) => { + fs.readFile("donkey", undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("readdir", () => { + it("should read directory", (done) => { + fs.readdir(__dirname, undefined, (err, paths) => { + expect(err).toBeUndefined(); + expect(paths.length).toBeGreaterThan(0); + done(); + }); + }); + + it("should fail to read directory", (done) => { + fs.readdir("moocow", undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("readlink", () => { + it("should read link", (done) => { + const srcFile = createTmpFile(); + const linkedFile = tmpFile(); + nativeFs.symlinkSync(srcFile, linkedFile); + fs.readlink(linkedFile, undefined, (err, link) => { + expect(err).toBeUndefined(); + expect(link).toEqual(srcFile); + done(); + }); + }); + + it("should fail to read link", (done) => { + fs.readlink(tmpFile(), undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("realpath", () => { + it("should read real path", (done) => { + const srcFile = createTmpFile(); + const linkedFile = tmpFile(); + nativeFs.symlinkSync(srcFile, linkedFile); + fs.realpath(linkedFile, undefined, (err, link) => { + expect(err).toBeUndefined(); + expect(link).toEqual(srcFile); + done(); + }); + }); + + it("should fail to read real path", (done) => { + fs.realpath(tmpFile(), undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("rename", () => { + it("should rename file", (done) => { + const srcFile = createTmpFile(); + const targetFile = tmpFile(); + fs.rename(srcFile, targetFile, (err) => { + expect(err).toBeUndefined(); + expect(nativeFs.existsSync(targetFile)).toBeTruthy(); + done(); + }); + }); + + it("should fail to rename file", (done) => { + fs.rename(tmpFile(), tmpFile(), (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("rmdir", () => { + it("should rmdir", (done) => { + const srcFile = tmpFile(); + nativeFs.mkdirSync(srcFile); + fs.rmdir(srcFile, (err) => { + expect(err).toBeUndefined(); + expect(nativeFs.existsSync(srcFile)).toBeFalsy(); + done(); + }); + }); + + it("should fail to rmdir", (done) => { + fs.rmdir(tmpFile(), (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("stat", () => { + it("should stat", (done) => { + fs.stat(testFile, (err, stats) => { + expect(err).toBeUndefined(); + expect(stats.size).toBeGreaterThan(0); + expect(stats.isFile()).toBeTruthy(); + expect(stats.isFIFO()).toBeFalsy(); + done(); + }); + }); + + it("should fail to stat", (done) => { + fs.stat(path.join(__dirname, "no-exist"), (err, stats) => { + expect(err).toBeDefined(); + expect(stats).toBeUndefined(); + done(); + }); + }); + }); + + describe("symlink", () => { + it("should symlink file", (done) => { + const newFile = createTmpFile(); + const targetFile = tmpFile(); + fs.symlink(newFile, targetFile, "file", (err) => { + expect(err).toBeUndefined(); + expect(nativeFs.existsSync(targetFile)).toBeTruthy(); + done(); + }); + }); + + it("should fail to symlink file", (done) => { + fs.symlink(tmpFile(), tmpFile(), "file", (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("truncate", () => { + it("should truncate file", (done) => { + const newFile = tmpFile(); + nativeFs.writeFileSync(newFile, "hiiiiii"); + fs.truncate(newFile, 2, (err) => { + expect(err).toBeUndefined(); + expect(nativeFs.statSync(newFile).size).toEqual(2); + done(); + }); + }); + + it("should fail to truncate file", (done) => { + fs.truncate(tmpFile(), 0, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("unlink", () => { + it("should unlink file", (done) => { + const newFile = createTmpFile(); + const targetFile = tmpFile(); + nativeFs.symlinkSync(newFile, targetFile, "file"); + fs.unlink(targetFile, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to unlink file", (done) => { + fs.unlink(tmpFile(), (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("utimes", () => { + it("should update times on file", (done) => { + fs.utimes(createTmpFile(), 100, 100, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + + it("should fail to update times", (done) => { + fs.utimes(tmpFile(), 100, 100, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("write", () => { + it("should write to file", (done) => { + const file = createTmpFile(); + const fd = nativeFs.openSync(file, "w"); + fs.write(fd, Buffer.from("hi"), undefined, undefined, undefined, (err, written) => { + expect(err).toBeUndefined(); + expect(written).toEqual(2); + nativeFs.closeSync(fd); + expect(nativeFs.readFileSync(file).toString()).toEqual("hi"); + done(); + }); + }); + + it("should fail to write to file", (done) => { + fs.write(100000, Buffer.from("wowow"), undefined, undefined, undefined, (err) => { + expect(err).toBeDefined(); + done(); + }); + }); + }); + + describe("writeFile", () => { + it("should write file", (done) => { + fs.writeFile(createTmpFile(), "howdy", undefined, (err) => { + expect(err).toBeUndefined(); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/server/yarn.lock b/packages/protocol/yarn.lock similarity index 100% rename from packages/server/yarn.lock rename to packages/protocol/yarn.lock diff --git a/packages/rules/package.json b/packages/rules/package.json deleted file mode 100644 index 5a0624cc..00000000 --- a/packages/rules/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@coder/rules", - "description": "Custom linting rules.", - "scripts": { - "build": "tsc -p ." - } -} diff --git a/packages/rules/yarn.lock b/packages/rules/yarn.lock deleted file mode 100644 index fb57ccd1..00000000 --- a/packages/rules/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/packages/vscode/src/index.ts b/packages/vscode/src/index.ts index a7dfb832..33e3ce82 100644 --- a/packages/vscode/src/index.ts +++ b/packages/vscode/src/index.ts @@ -13,14 +13,14 @@ const load = (): Promise => { parse: (raw: string): IURI => URI.parse(raw), }); - reject(new Error("not finished")); + const client = new Client({ + mkDirs: [ + "~/vscode/extensions", + "~/.config/User", + ], + }); - // export const client = new Client({ - // mkDirs: [ - // "~/vscode/extensions", - // "~/.config/User", - // ], - // }); + resolve(); // const importTime = time(1500); // import(/* webpackPrefetch: true */ "./workbench").then((module) => { diff --git a/packages/app/src/index.html b/packages/web/src/index.html similarity index 100% rename from packages/app/src/index.html rename to packages/web/src/index.html diff --git a/packages/app/src/index.scss b/packages/web/src/index.scss similarity index 100% rename from packages/app/src/index.scss rename to packages/web/src/index.scss diff --git a/packages/app/src/index.ts b/packages/web/src/index.ts similarity index 100% rename from packages/app/src/index.ts rename to packages/web/src/index.ts diff --git a/packages/yarn.lock b/packages/yarn.lock index 9f48807f..1e60eae3 100644 --- a/packages/yarn.lock +++ b/packages/yarn.lock @@ -165,7 +165,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -arrify@^1.0.0, arrify@^1.0.1: +arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= @@ -224,7 +224,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== -babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= @@ -469,12 +469,12 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.0: +buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -builtin-modules@^1.0.0, builtin-modules@^1.1.1: +builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -527,7 +527,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: +chalk@^2.0.0, chalk@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -602,11 +602,6 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.12.1: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -783,7 +778,7 @@ detect-newline@^2.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= -diff@^3.1.0, diff@^3.2.0: +diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -1126,7 +1121,7 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -get-caller-file@^1.0.1, get-caller-file@^1.0.2: +get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== @@ -2189,7 +2184,7 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -make-error@1.x, make-error@^1.1.1: +make-error@1.x: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== @@ -2344,14 +2339,6 @@ mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1: dependencies: minimist "0.0.8" -mock-require@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.2.tgz#7ce759b559e3b194be5f20a5b1cece0eb363f53d" - integrity sha512-aD/Y1ZFHqw5pHg3HVQ50dLbfaAAcytS6sqLuhP51Dk3TSPdFb2VkSAa3mjrHifLIlGAtwQHJHINafAyqAne7vA== - dependencies: - get-caller-file "^1.0.2" - normalize-path "^2.1.1" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2961,7 +2948,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.3.2: +resolve@1.x: version "1.9.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ== @@ -3435,50 +3422,6 @@ ts-jest@^23.10.5: semver "^5.5" yargs-parser "10.x" -ts-node@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" - integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== - dependencies: - arrify "^1.0.0" - buffer-from "^1.1.0" - diff "^3.1.0" - make-error "^1.1.1" - minimist "^1.2.0" - mkdirp "^0.5.1" - source-map-support "^0.5.6" - yn "^2.0.0" - -tslib@^1.8.0, tslib@^1.8.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== - -tslint@^5.12.1: - version "5.12.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" - integrity sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== - dependencies: - babel-code-frame "^6.22.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^3.2.0" - glob "^7.1.1" - js-yaml "^3.7.0" - minimatch "^3.0.4" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.8.0" - tsutils "^2.27.2" - -tsutils@^2.27.2: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3498,15 +3441,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -typescript-tslint-plugin@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/typescript-tslint-plugin/-/typescript-tslint-plugin-0.2.1.tgz#6a0361cd311bdc9dcec2e70c8a54cab16829e47f" - integrity sha512-j0Tn/2GlAwnaklSNMOZRNpv96j6IWkQF6RuTJ5WowfNlgdYqnJpSaVFwT22INwJiPDDGKNe/aATT0qkU0pWM4w== - dependencies: - minimatch "^3.0.4" - mock-require "^3.0.2" - vscode-languageserver "^5.1.0" - uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" @@ -3585,37 +3519,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== - -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== - dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" - -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== - -vscode-languageserver@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz#0d2feddd33f92aadf5da32450df498d52f6f14eb" - integrity sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A== - dependencies: - vscode-languageserver-protocol "3.14.1" - vscode-uri "^1.0.6" - -vscode-uri@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" - integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== - w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" @@ -3787,8 +3690,3 @@ yargs@^11.0.0: which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^9.0.2" - -yn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= diff --git a/packages/rules/src/curlyStatementNewlinesRule.ts b/rules/src/curlyStatementNewlinesRule.ts similarity index 100% rename from packages/rules/src/curlyStatementNewlinesRule.ts rename to rules/src/curlyStatementNewlinesRule.ts diff --git a/packages/rules/tsconfig.json b/rules/tsconfig.json similarity index 100% rename from packages/rules/tsconfig.json rename to rules/tsconfig.json diff --git a/packages/scripts/src/dummy.js b/scripts/dummy.js similarity index 100% rename from packages/scripts/src/dummy.js rename to scripts/dummy.js diff --git a/packages/scripts/src/install-packages.ts b/scripts/install-packages.ts similarity index 89% rename from packages/scripts/src/install-packages.ts rename to scripts/install-packages.ts index 7b9fda2e..3569f6b4 100644 --- a/packages/scripts/src/install-packages.ts +++ b/scripts/install-packages.ts @@ -1,7 +1,7 @@ import { exec } from "child_process"; import { existsSync, readdirSync } from "fs"; import { join, resolve } from "path"; -import { logger, field } from "../../logger"; +import { logger, field } from "../packages/logger"; /** * Install dependencies for a single package. @@ -39,4 +39,4 @@ const handlePackages = (dir: string): void => { }); }; -handlePackages(resolve(__dirname, "..", "..")); +handlePackages(resolve(__dirname, "..", "packages")); diff --git a/packages/scripts/src/test-setup.js b/scripts/test-setup.js similarity index 100% rename from packages/scripts/src/test-setup.js rename to scripts/test-setup.js diff --git a/scripts/vscode.css.patch b/scripts/vscode.css.patch new file mode 100644 index 00000000..92c98b26 --- /dev/null +++ b/scripts/vscode.css.patch @@ -0,0 +1,32 @@ +diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css +index 651843fcc9..aa31b52cb9 100644 +--- a/src/vs/base/browser/ui/iconLabel/iconlabel.css ++++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css +@@ -32,6 +32,7 @@ + .monaco-icon-label > .monaco-icon-label-description-container { + overflow: hidden; /* this causes the label/description to shrink first if decorations are enabled */ + text-overflow: ellipsis; ++ margin-right: auto; + } + + .monaco-icon-label > .monaco-icon-label-description-container > .label-name { +@@ -39,6 +40,12 @@ + white-space: pre; /* enable to show labels that include multiple whitespaces */ + } + ++.monaco-icon-label > .decorations-wrapper { ++ display: flex; ++ flex-direction: row; ++ padding-right: 12px; ++} ++ + .monaco-icon-label > .monaco-icon-label-description-container > .label-description { + opacity: .7; + margin-left: 0.5em; +@@ -56,6 +63,5 @@ + font-size: 90%; + font-weight: 600; + padding: 0 12px 0 5px; +- margin-left: auto; + text-align: center; + } diff --git a/scripts/vscode.patch b/scripts/vscode.patch new file mode 100644 index 00000000..f707e36a --- /dev/null +++ b/scripts/vscode.patch @@ -0,0 +1,8653 @@ +\ No newline at end of file +diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js +index e30cadb0fc..120de744ca 100644 +--- a/src/bootstrap-fork.js ++++ b/src/bootstrap-fork.js +@@ -19,6 +19,11 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') { + // Disable IO if configured + if (!process.env['VSCODE_ALLOW_IO']) { + disableSTDIO(); ++} else { ++ process.stdin.resume(); ++ process.stdin.on("close", () => { ++ process.exit(0); ++ }); + } + + // Handle Exceptions +diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts +index 74be56db00..98e82ddd8a 100644 +--- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts ++++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts +@@ -122,7 +122,7 @@ export class IconLabel extends Disposable { + ]); + } + +- setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void { ++ setValue(label?: string, description?: string, options?: IIconLabelValueOptions, element?: HTMLElement): void { + const classes = ['monaco-icon-label']; + if (options) { + if (options.extraClasses) { +@@ -161,6 +161,12 @@ export class IconLabel extends Disposable { + this.descriptionNode.empty = !description; + } + } ++ ++ const oldElements = this.domNode.element.querySelectorAll(".decorations-wrapper"); ++ oldElements.forEach((e) => e.remove()); ++ if (element) { ++ this.domNode.element.appendChild(element); ++ } + } + } + +diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts +index 362a2e1e6f..64c7289377 100644 +--- a/src/vs/base/common/amd.ts ++++ b/src/vs/base/common/amd.ts +@@ -8,5 +8,17 @@ + import { URI } from 'vs/base/common/uri'; + + export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { ++ requirefn.toUrl = () => { ++ return ""; ++ }; ++ ++ // TODO@coder: Not sure if this is correct. Webpack makes things tricky. ++ if (process.mainModule && process.mainModule.filename) { ++ const index = process.mainModule.filename.lastIndexOf("/"); ++ return process.mainModule.filename.slice(0, index); ++ } ++ ++ return URI.parse("").fsPath; ++ + return URI.parse(requirefn.toUrl(relativePath)).fsPath; + } +diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts +index f6a2fee985..45d77884dc 100644 +--- a/src/vs/base/common/async.ts ++++ b/src/vs/base/common/async.ts +@@ -45,6 +45,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) + }); + }); + ++ // @ts-ignore + return new class implements CancelablePromise { + cancel() { + source.cancel(); +diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts +index 41dd989c5f..012d265683 100644 +--- a/src/vs/base/common/labels.ts ++++ b/src/vs/base/common/labels.ts +@@ -358,11 +358,14 @@ export function template(template: string, values: { [key: string]: string | ISe + * - macOS: Unsupported (replace && with empty string) + */ + export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean): string { +- if (isMacintosh || forceDisableMnemonics) { +- return label.replace(/\(&&\w\)|&&/g, ''); +- } ++ // NOTE@coder: We could write some custom support for this. For now, disable. ++ return label.replace(/&&/g, ''); ++ ++ // if (isMacintosh || forceDisableMnemonics) { ++ // return label.replace(/\(&&\w\)|&&/g, ''); ++ // } + +- return label.replace(/&&/g, '&'); ++ // return label.replace(/&&/g, '&'); + } + + /** +@@ -372,11 +375,14 @@ export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean + * - macOS: Unsupported (replace && with empty string) + */ + export function mnemonicButtonLabel(label: string): string { +- if (isMacintosh) { +- return label.replace(/\(&&\w\)|&&/g, ''); +- } ++ // NOTE@coder: We could write some custom support for this. For now, disable. ++ return label.replace(/&&/g, ''); ++ ++ // if (isMacintosh) { ++ // return label.replace(/\(&&\w\)|&&/g, ''); ++ // } + +- return label.replace(/&&/g, isWindows ? '&' : '_'); ++ // return label.replace(/&&/g, isWindows ? '&' : '_'); + } + + export function unmnemonicLabel(label: string): string { +diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts +index fc6b2c285b..58d311c5bf 100644 +--- a/src/vs/base/common/platform.ts ++++ b/src/vs/base/common/platform.ts +@@ -67,8 +67,11 @@ if (typeof process === 'object' && typeof process.nextTick === 'function' && typ + _isMacintosh = userAgent.indexOf('Macintosh') >= 0; + _isLinux = userAgent.indexOf('Linux') >= 0; + _isWeb = true; +- _locale = navigator.language; ++ // NOTE@coder: use en instead of en-US since that's vscode default and it uses ++ // that to determine whether to output aliases which will be pointless because ++ // they are the same language. ++ _locale = navigator.language === "en-US" ? "en" : navigator.language; + _language = _locale; + } + +diff --git a/src/vs/base/common/winjs.polyfill.promise.ts b/src/vs/base/common/winjs.polyfill.promise.ts +index fb80ee9569..378aa7d689 100644 +--- a/src/vs/base/common/winjs.polyfill.promise.ts ++++ b/src/vs/base/common/winjs.polyfill.promise.ts +@@ -73,6 +73,8 @@ export class PolyfillPromise implements Promise { + } + } + ++ readonly [Symbol.toStringTag] = "Promise"; ++ + then(onFulfilled?: any, onRejected?: any): PolyfillPromise { + let sync = true; + // To support chaining, we need to return the value of the +@@ -126,4 +128,4 @@ export class PolyfillPromise implements Promise { + catch(onRejected?: any): PolyfillPromise { + return this.then(null, onRejected); + } +-} ++}; +diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts +index 31eea31a6b..67330b9db3 100644 +--- a/src/vs/base/node/config.ts ++++ b/src/vs/base/node/config.ts +@@ -13,6 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event'; + import * as json from 'vs/base/common/json'; + import * as extfs from 'vs/base/node/extfs'; + import { isWindows } from 'vs/base/common/platform'; ++import { isBrowserEnvironment } from 'coder/common'; + + export interface IConfigurationChangeEvent { + config: T; +@@ -61,7 +62,7 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { + this.disposables.push(this._onDidUpdateConfiguration); + + this.registerWatcher(); +- this.initAsync(); ++ this.init(); + } + + public get path(): string { +@@ -76,15 +77,20 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { + return this._onDidUpdateConfiguration.event; + } + +- private initAsync(): void { +- this.loadAsync(config => { ++ private init(): void { ++ const callback = (config: T): void => { + if (!this.loaded) { + this.updateCache(config); // prevent race condition if config was loaded sync already + } + if (this.options.initCallback) { + this.options.initCallback(this.getConfig()); + } +- }); ++ }; ++ if (isBrowserEnvironment()) { ++ this.loadAsync(callback); ++ } else { ++ this.loadSync(callback); ++ } + } + + private updateCache(value: T): void { +@@ -92,11 +98,11 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { + this.loaded = true; + } + +- private loadSync(): T { ++ private loadSync(callback: (config: T) => void): void { + try { +- return this.parse(fs.readFileSync(this._path).toString()); ++ callback(this.parse(fs.readFileSync(this._path).toString())); + } catch (error) { +- return this.options.defaultConfig; ++ callback(this.options.defaultConfig); + } + } + +@@ -216,7 +222,12 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { + + private ensureLoaded(): void { + if (!this.loaded) { +- this.updateCache(this.loadSync()); ++ // NOTE@coder: since loading asynchronously can sometimes take a while, ++ // this can come in, fail (since we can't support sync), then set itself ++ // to the default, which means the user's settings or keybinds don't take ++ // effect when the asynchronous call finally comes back. ++ // Commenting it out. ++ // this.updateCache(this.loadSync()); + } + } + +@@ -224,4 +235,4 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { + this.disposed = true; + this.disposables = dispose(this.disposables); + } +-} +\ No newline at end of file ++} +diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts +index d1ed967b22..b182c9f969 100644 +--- a/src/vs/base/node/encoding.ts ++++ b/src/vs/base/node/encoding.ts +@@ -10,7 +10,8 @@ import * as iconv from 'iconv-lite'; + import { TPromise } from 'vs/base/common/winjs.base'; + import { isLinux, isMacintosh } from 'vs/base/common/platform'; + import { exec } from 'child_process'; +-import { Readable, Writable, WritableOptions } from 'stream'; ++import { Readable, Transform, Writable, WritableOptions } from 'stream'; ++import { IconvLiteDecoderStream } from './iconv'; + + export const UTF8 = 'utf8'; + export const UTF8_with_bom = 'utf8bom'; +@@ -132,7 +133,7 @@ export function encodingExists(encoding: string): boolean { + } + + export function decodeStream(encoding: string): NodeJS.ReadWriteStream { +- return iconv.decodeStream(toNodeEncoding(encoding)); ++ return new IconvLiteDecoderStream((iconv as any).getDecoder(encoding, undefined), undefined); + } + + export function encodeStream(encoding: string, options?: { addBOM?: boolean }): NodeJS.ReadWriteStream { +@@ -194,7 +195,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; + * Guesses the encoding from buffer. + */ + export function guessEncodingByBuffer(buffer: Buffer): TPromise { +- return TPromise.wrap(import('jschardet')).then(jschardet => { ++ return TPromise.wrap(require('jschardet')).then(jschardet => { + jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; + + const guessed = jschardet.detect(buffer); +diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts +index c4c5a8ee43..cee84772fc 100644 +--- a/src/vs/base/node/id.ts ++++ b/src/vs/base/node/id.ts +@@ -84,7 +84,7 @@ export function getMachineId(): TPromise { + + function getMacMachineId(): TPromise { + return new TPromise(resolve => { +- TPromise.join([import('crypto'), import('getmac')]).then(([crypto, getmac]) => { ++ TPromise.join([Promise.resolve(require('crypto')), Promise.resolve(require('getmac'))]).then(([crypto, getmac]) => { + try { + getmac.getMac((error, macAddress) => { + if (!error) { +diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts +index 66930cdaf4..4f00a4982e 100644 +--- a/src/vs/base/node/paths.ts ++++ b/src/vs/base/node/paths.ts +@@ -3,14 +3,14 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-import { getPathFromAmdModule } from 'vs/base/common/amd'; ++// import { getPathFromAmdModule } from 'vs/base/common/amd'; + +-interface IPaths { +- getAppDataPath(platform: string): string; +- getDefaultUserDataPath(platform: string): string; +-} ++// interface IPaths { ++// getAppDataPath(platform: string): string; ++// getDefaultUserDataPath(platform: string): string; ++// } + +-const pathsPath = getPathFromAmdModule(require, 'paths'); +-const paths = require.__$__nodeRequire(pathsPath); +-export const getAppDataPath = paths.getAppDataPath; +-export const getDefaultUserDataPath = paths.getDefaultUserDataPath; ++// const pathsPath = getPathFromAmdModule(require, 'paths'); ++// const paths = require.__$__nodeRequire(pathsPath); ++export const getAppDataPath = () => "/tmp" // paths.getAppDataPath; ++export const getDefaultUserDataPath = (arg?: string) => "/tmp" // paths.getDefaultUserDataPath; +diff --git a/src/vs/base/node/proxy.ts b/src/vs/base/node/proxy.ts +index d3d525ce0d..58ab1dd489 100644 +--- a/src/vs/base/node/proxy.ts ++++ b/src/vs/base/node/proxy.ts +@@ -46,8 +46,8 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = { + }; + + const Ctor = requestURL.protocol === 'http:' +- ? await import('http-proxy-agent') +- : await import('https-proxy-agent'); ++ ? require('http-proxy-agent') ++ : require('https-proxy-agent'); + + return new Ctor(opts); + } +diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts +index 488f8bf9bb..3e8df64db9 100644 +--- a/src/vs/base/node/ps.ts ++++ b/src/vs/base/node/ps.ts +@@ -134,7 +134,7 @@ export function listProcesses(rootPid: number): Promise { + } + }; + +- (import('windows-process-tree')).then(windowsProcessTree => { ++ Promise.resolve(require('windows-process-tree')).then(windowsProcessTree => { + windowsProcessTree.getProcessList(rootPid, (processList) => { + windowsProcessTree.getProcessCpuUsage(processList, (completeProcessList) => { + const processItems: Map = new Map(); +diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts +index 23da75d32a..645c8d5ba3 100644 +--- a/src/vs/base/node/request.ts ++++ b/src/vs/base/node/request.ts +@@ -53,7 +53,7 @@ export interface IRequestFunction { + + async function getNodeRequest(options: IRequestOptions): Promise { + const endpoint = parseUrl(options.url); +- const module = endpoint.protocol === 'https:' ? await import('https') : await import('http'); ++ const module = endpoint.protocol === 'https:' ? require('https') : require('http'); + return module.request; + } + +diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +index 5afeaad9c1..3529dbb97b 100644 +--- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts ++++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +@@ -5,60 +5,46 @@ + + 'use strict'; + +-import { Menu, MenuItem, BrowserWindow, Event, ipcMain } from 'electron'; ++import { Event, ipcMain } from 'electron'; + import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; ++import { ContextMenuManager, ContextMenu } from 'coder/element/contextmenu'; ++ ++const manager = new ContextMenuManager(); + + export function registerContextMenuListener(): void { + ipcMain.on(CONTEXT_MENU_CHANNEL, (event: Event, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { + const menu = createMenu(event, onClickChannel, items); + +- menu.popup({ +- window: BrowserWindow.fromWebContents(event.sender), +- x: options ? options.x : void 0, +- y: options ? options.y : void 0, +- positioningItem: options ? options.positioningItem : void 0, +- callback: () => { +- event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL); +- } ++ manager.displayMenuAtPoint(menu, { ++ top: options ? options.y : void 0, ++ left: options ? options.x : void 0, ++ }); ++ manager.onceClose(() => { ++ event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL); + }); + }); + } + +-function createMenu(event: Event, onClickChannel: string, items: ISerializableContextMenuItem[]): Menu { +- const menu = new Menu(); ++function createMenu(event: Event, onClickChannel: string, items: ISerializableContextMenuItem[]): ContextMenu { ++ const menu = new ContextMenu(onClickChannel, manager); + + items.forEach(item => { +- let menuitem: MenuItem; +- + // Separator + if (item.type === 'separator') { +- menuitem = new MenuItem({ +- type: item.type, +- }); ++ menu.addSpacer(item.id); + } + + // Sub Menu + else if (Array.isArray(item.submenu)) { +- menuitem = new MenuItem({ +- submenu: createMenu(event, onClickChannel, item.submenu), +- label: item.label +- }); ++ menu.addSubMenu(item.id, createMenu(event, onClickChannel, item.submenu), item.label); + } + + // Normal Menu Item + else { +- menuitem = new MenuItem({ +- label: item.label, +- type: item.type, +- accelerator: item.accelerator, +- checked: item.checked, +- enabled: item.enabled, +- visible: item.visible, +- click: (menuItem, win, contextmenuEvent) => event.sender.send(onClickChannel, item.id, contextmenuEvent) ++ menu.addEntry(item.id, item.label, item.accelerator, item.enabled, () => { ++ event.sender.send(onClickChannel, item.id); + }); + } +- +- menu.append(menuitem); + }); + + return menu; +diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts +index c802f0bd42..ea6ef313ac 100644 +--- a/src/vs/base/parts/ipc/node/ipc.net.ts ++++ b/src/vs/base/parts/ipc/node/ipc.net.ts +@@ -5,6 +5,7 @@ + + 'use strict'; + ++import { EventEmitter } from "events"; + import { Socket, Server as NetServer, createConnection, createServer } from 'net'; + import { Event, Emitter, once, mapEvent, fromNodeEventEmitter } from 'vs/base/common/event'; + import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/node/ipc'; +@@ -13,6 +14,7 @@ import { tmpdir } from 'os'; + import { generateUuid } from 'vs/base/common/uuid'; + import { IDisposable } from 'vs/base/common/lifecycle'; + import { TimeoutTimer } from 'vs/base/common/async'; ++import { Session } from '../../../../../../../../wush/src/index'; + + export function generateRandomPipeName(): string { + const randomSuffix = generateUuid(); +@@ -34,6 +36,100 @@ export function generateRandomPipeName(): string { + + export class Protocol implements IDisposable, IMessagePassingProtocol { + ++ public static fromSession(session: Session, onExit: (code: number) => void): Protocol { ++ session.onDone((exitCode) => onExit(exitCode)); ++ return Protocol.fromStdio({ ++ onMessage: (cb) => { ++ session.onStdout((data) => { ++ cb(Buffer.from(data as any)); ++ }, true); ++ }, ++ sendMessage: (data) => { ++ session.sendStdin(data); ++ }, ++ }); ++ } ++ ++ public static fromStream( ++ inStream: { on: (event: "data", cb: (b: Buffer) => void) => void }, ++ outStream: { write: (b: Buffer) => void }, ++ ): Protocol { ++ return Protocol.fromStdio({ ++ onMessage: (cb) => { ++ inStream.on("data", (data) => { ++ cb(Buffer.from(data)); ++ }); ++ }, ++ sendMessage: (data: string) => { ++ outStream.write(Buffer.from(data)); ++ }, ++ }); ++ } ++ ++ public static fromWorker(worker: { ++ onmessage: (event: MessageEvent) => void; ++ postMessage: (data: string, origin?: string | string[]) => void; ++ }, ignoreFirst: boolean = false): Protocol { ++ return Protocol.fromStdio({ ++ onMessage: (cb) => { ++ worker.onmessage = (event: MessageEvent) => { ++ cb(event.data); ++ }; ++ }, ++ sendMessage: (data: string) => { ++ worker.postMessage(data); ++ }, ++ }, ignoreFirst); ++ } ++ ++ public static fromStdio(stdio: { ++ onMessage: (cb: (data: string | Uint8Array) => void) => void; ++ sendMessage: (data: string | Uint8Array) => void; ++ }, ignoreFirst: boolean = false): Protocol { ++ return new Protocol(new (class WorkerSocket { ++ ++ private readonly emitter: EventEmitter; ++ ++ public constructor() { ++ this.emitter = new EventEmitter(); ++ ++ let first = true; ++ stdio.onMessage((data) => { ++ if (ignoreFirst && first) { ++ first = false; ++ return; ++ } ++ this.emitter.emit("data", typeof data === "string" ? data : Buffer.from(data as any)); ++ }); ++ } ++ ++ public removeListener(event: string, listener: () => void): void { ++ this.emitter.removeListener(event, listener); ++ } ++ ++ public once(event: string, listener: () => void): void { ++ this.emitter.once(event, listener); ++ } ++ ++ public on(event: string, listener: () => void): void { ++ this.emitter.on(event, listener); ++ } ++ ++ public end(): void { ++ // TODO: figure it out ++ } ++ ++ public get destroyed() { ++ return false; ++ } ++ ++ public write(data: any): void { ++ stdio.sendMessage(data); ++ } ++ ++ }) as any); ++ } ++ + private static readonly _headerLen = 4; + + private _isDisposed: boolean; +diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts +index 9bf45f0b20..763bbd09c8 100644 +--- a/src/vs/base/worker/workerMain.ts ++++ b/src/vs/base/worker/workerMain.ts +@@ -3,6 +3,7 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + ++ + (function () { + 'use strict'; + +@@ -10,6 +11,7 @@ + let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; + + if (typeof (self).define !== 'function' || !(self).define.amd) { ++ // @ts-ignore + importScripts(monacoBaseUrl + 'vs/loader.js'); + } + +diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts +index 790b2886a7..9f440b754c 100644 +--- a/src/vs/code/electron-browser/issue/issueReporterMain.ts ++++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts +@@ -5,7 +5,6 @@ + + 'use strict'; + +-import 'vs/css!./media/issueReporter'; + import { shell, ipcRenderer, webFrame, clipboard } from 'electron'; + import { localize } from 'vs/nls'; + import { $ } from 'vs/base/browser/dom'; +diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +index ff48ec7cb6..9c65419c8e 100644 +--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts ++++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +@@ -6,7 +6,8 @@ + 'use strict'; + + import * as fs from 'fs'; +-import * as platform from 'vs/base/common/platform'; ++import { join } from 'path'; ++// import * as platform from 'vs/base/common/platform'; + import product from 'vs/platform/node/product'; + import pkg from 'vs/platform/node/package'; + import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; +@@ -23,7 +24,9 @@ import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/ex + import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; + import { IRequestService } from 'vs/platform/request/node/request'; +-import { RequestService } from 'vs/platform/request/electron-browser/requestService'; ++// NOTE@coder: Use the node request instead of the xhr-based one. ++import { RequestService } from 'vs/platform/request/node/requestService'; ++// import { RequestService } from 'vs/platform/request/electron-browser/requestService'; + import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; + import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; +@@ -32,7 +35,7 @@ import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry + import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; + import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows'; + import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; +-import { ipcRenderer } from 'electron'; ++// import { ipcRenderer } from 'electron'; + import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions'; + import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; + import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +@@ -46,13 +49,52 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + import { IDownloadService } from 'vs/platform/download/common/download'; + import { DownloadServiceChannelClient } from 'vs/platform/download/node/downloadIpc'; + import { DefaultURITransformer } from 'vs/base/common/uriIpc'; ++import { readdir, rimraf } from 'vs/base/node/pfs'; ++ ++// NOTE:@coder: provide ipcRenderer. ++const ipcRenderer = { ++ once: (_: string, cb: (_: any, r: IInitData) => void) => { ++ process.on('message', (message) => { ++ // Just going to assume the first message is the one we want for now. ++ const initData = JSON.parse(message) as IInitData; ++ process.env.VSCODE_LOGS = initData.logsDir; ++ product.extensionsGallery.serviceUrl = initData.serviceUrl; ++ // NOTE@coder: clean up old logs. This is mostly to reset the logs for the ++ // shared process itself and to account for logs sent after a shared ++ // client exits, but also cleans up logs that haven't been cleaned up due ++ // to an unexpected exit. ++ readdir(initData.logsDir) ++ .then((cs) => cs.filter((c) => !c.includes(`${initData.windowId}`))) ++ .then((cs) => TPromise.join(cs.map((c) => rimraf(join(initData.logsDir, c))))) ++ .then(() => cb(undefined, initData)); ++ }); ++ }, ++ send: (message: string) => { ++ process.send(message); ++ }, ++}; ++ ++// NOTE@coder: custom data for initialization. ++export interface IInitData extends ISharedProcessInitData { ++ serviceUrl: string; ++ logsDir: string; ++ windowId: number; ++} ++ ++// NOTE@coder: start immediately. ++startup({ ++ machineId: "1", ++}); + + export interface ISharedProcessConfiguration { + readonly machineId: string; + } + + export function startup(configuration: ISharedProcessConfiguration) { +- handshake(configuration); ++ // NOTE:@coder: exit on error. ++ handshake(configuration).then(null, (error) => { ++ process.exit(1); ++ }); + } + + interface ISharedProcessInitData { +@@ -148,7 +190,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I + function setupIPC(hook: string): Thenable { + function setup(retry: boolean): Thenable { + return serve(hook).then(null, err => { +- if (!retry || platform.isWindows || err.code !== 'EADDRINUSE') { ++ // NOTE@coder: it's never Windows. ++ if (!retry || /*platform.isWindows ||*/ err.code !== 'EADDRINUSE') { + return Promise.reject(err); + } + +@@ -190,4 +233,4 @@ function handshake(configuration: ISharedProcessConfiguration): TPromise { + return startHandshake() + .then(data => setupIPC(data.sharedIPCHandle).then(server => main(server, data, configuration))) + .then(() => ipcRenderer.send('handshake:im ready')); +-} +\ No newline at end of file ++} +diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts +index 4ade1e0175..549b95ca34 100644 +--- a/src/vs/code/electron-main/app.ts ++++ b/src/vs/code/electron-main/app.ts +@@ -14,7 +14,7 @@ import { WindowsService } from 'vs/platform/windows/electron-main/windowsService + import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; + import { getShellEnvironment } from 'vs/code/node/shellEnv'; + import { IUpdateService } from 'vs/platform/update/common/update'; +-import { UpdateChannel } from 'vs/platform/update/node/updateIpc'; ++// import { UpdateChannel } from 'vs/platform/update/node/updateIpc'; + import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; + import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net'; + import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; +@@ -192,7 +192,7 @@ export class CodeApplication { + macOpenFileURIs = []; + runningTimeout = null; + } +- }, 100); ++ }, 100) as any; + }); + + app.on('new-window-for-tab', () => { +@@ -427,9 +427,9 @@ export class CodeApplication { + this.mainIpcServer.registerChannel('launch', launchChannel); + + // Register more Electron IPC services +- const updateService = accessor.get(IUpdateService); +- const updateChannel = new UpdateChannel(updateService); +- this.electronIpcServer.registerChannel('update', updateChannel); ++ // const updateService = accessor.get(IUpdateService); ++ // const updateChannel = new UpdateChannel(updateService); ++ // this.electronIpcServer.registerChannel('update', updateChannel); + + const issueService = accessor.get(IIssueService); + const issueChannel = new IssueChannel(issueService); +diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts +index 46007f71cf..0954448ff4 100644 +--- a/src/vs/code/electron-main/main.ts ++++ b/src/vs/code/electron-main/main.ts +@@ -336,8 +336,13 @@ function main() { + assign(process.env, instanceEnv); + + // Startup +- return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService))) +- .then(() => instantiationService.invokeFunction(setupIPC)) ++ console.log("GEtting here"); ++ return instantiationService.invokeFunction(a => { ++ return createPaths(a.get(IEnvironmentService)); ++ }).then(() => { ++ console.log("INVoked stetup ipc"); ++ return instantiationService.invokeFunction(setupIPC); ++ }) + .then(mainIpcServer => { + bufferLogService.logger = createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath); + return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv).startup(); +diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts +index a67b887acd..c6d82e01e3 100644 +--- a/src/vs/code/electron-main/windows.ts ++++ b/src/vs/code/electron-main/windows.ts +@@ -1718,6 +1718,7 @@ export class WindowsManager implements IWindowsMainService { + } + + showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): TPromise { ++ console.log("GETTING HERE R0135 showOpen"); + return this.dialogs.showOpenDialog(options, win); + } + +@@ -1877,6 +1878,7 @@ class Dialogs { + } + + showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): TPromise { ++ console.log("8ugr8w GETTING OPEN DIALOG CALLD"); + + function normalizePaths(paths: string[]): string[] { + if (paths && paths.length > 0 && isMacintosh) { +diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts +index 94b66896ed..6112b83ab2 100644 +--- a/src/vs/code/node/cli.ts ++++ b/src/vs/code/node/cli.ts +@@ -273,7 +273,7 @@ export async function main(argv: string[]): Promise { + processCallbacks.push(async _child => { + try { + // load and start profiler +- const profiler = await import('v8-inspect-profiler'); ++ const profiler = require('v8-inspect-profiler'); + const main = await profiler.startProfiling({ port: portMain }); + const renderer = await profiler.startProfiling({ port: portRenderer, tries: 200 }); + const extHost = await profiler.startProfiling({ port: portExthost, tries: 300 }); +diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts +index 7b006da43e..d69df8f07f 100644 +--- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts ++++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts +@@ -433,7 +433,8 @@ class DecorationCSSRules { + + if (typeof opts.gutterIconPath !== 'undefined') { + if (typeof opts.gutterIconPath === 'string') { +- cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.file(opts.gutterIconPath).toString())); ++ // NOTE@coder: change file() to parse() to allow HTTP URIs. ++ cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.parse(opts.gutterIconPath).toString())); + } else { + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); + } +diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts +index 5df5b9eca1..77b0d0e6c0 100644 +--- a/src/vs/editor/common/model/textModel.ts ++++ b/src/vs/editor/common/model/textModel.ts +@@ -71,6 +71,9 @@ export function createTextBufferFactoryFromStream(stream: IStringStream, filter? + c(builder.finish()); + } + }); ++ // We know internally we're passing this an EventEmitter. ++ // Definitely not the best way to handle this. ++ (stream as any).emit('ready'); + }); + } + +diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts +index 4d0ec22210..acdf47482f 100644 +--- a/src/vs/editor/contrib/clipboard/clipboard.ts ++++ b/src/vs/editor/contrib/clipboard/clipboard.ts +@@ -18,6 +18,10 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; + import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; + import { MenuId } from 'vs/platform/actions/common/actions'; ++import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; ++import { Handler } from 'vs/editor/common/editorCommon'; ++import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; ++import { nativeClipboard } from 'coder/workbench'; + + const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; + +@@ -28,7 +32,7 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE) + // Chrome incorrectly returns true for document.queryCommandSupported('paste') + // when the paste feature is available but the calling script has insufficient + // privileges to actually perform the action +-const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste'))); ++const supportsPaste = true; + + type ExecCommand = 'cut' | 'copy' | 'paste'; + +@@ -166,7 +170,7 @@ class ExecCommandPasteAction extends ExecCommandAction { + id: 'editor.action.clipboardPasteAction', + label: nls.localize('actions.clipboard.pasteLabel', "Paste"), + alias: 'Paste', +- precondition: EditorContextKeys.writable, ++ precondition: ContextKeyExpr.and(EditorContextKeys.writable, nativeClipboard.contextKey), + kbOpts: kbOpts, + menuOpts: { + group: CLIPBOARD_CONTEXT_MENU_GROUP, +@@ -180,6 +184,20 @@ class ExecCommandPasteAction extends ExecCommandAction { + } + }); + } ++ ++ public async run(accessor, editor: ICodeEditor): Promise { ++ if (editor instanceof CodeEditorWidget) { ++ try { ++ editor.trigger('', Handler.Paste, { ++ text: await nativeClipboard.instance.readText(), ++ }); ++ } catch (ex) { ++ super.run(accessor, editor); ++ } ++ } else { ++ super.run(accessor, editor); ++ } ++ } + } + + class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { +diff --git a/src/vs/loader.js b/src/vs/loader.js +index d02070659c..de85aefcdb 100644 +--- a/src/vs/loader.js ++++ b/src/vs/loader.js +@@ -652,10 +652,10 @@ var AMDLoader; + } + this._didInitialize = true; + // capture node modules +- this._fs = nodeRequire('fs'); +- this._vm = nodeRequire('vm'); +- this._path = nodeRequire('path'); +- this._crypto = nodeRequire('crypto'); ++ this._fs = require('fs'); ++ this._vm = require('vm'); ++ this._path = require('path'); ++ this._crypto = require('crypto'); + // js-flags have an impact on cached data + this._jsflags = ''; + for (var _i = 0, _a = process.argv; _i < _a.length; _i++) { +@@ -725,11 +725,18 @@ var AMDLoader; + this._init(nodeRequire); + this._initNodeRequire(nodeRequire, moduleManager); + var recorder = moduleManager.getRecorder(); ++ const context = require.context("../", true, /.*/); ++ if (scriptSrc.indexOf("file:///") !== -1) { ++ const vsSrc = scriptSrc.split("file:///")[1].split(".js")[0]; ++ if (vsSrc && vsSrc.startsWith("vs/")) { ++ scriptSrc = `node|./${vsSrc}`; ++ } ++ } + if (/^node\|/.test(scriptSrc)) { + var pieces = scriptSrc.split('|'); + var moduleExports_1 = null; + try { +- moduleExports_1 = nodeRequire(pieces[1]); ++ moduleExports_1 = context(pieces[1]); + } + catch (err) { + errorback(err); +@@ -823,14 +830,14 @@ var AMDLoader; + path: cachedDataPath + }); + NodeScriptLoader._runSoon(function () { return _this._fs.unlink(cachedDataPath, function (err) { +- if (err) { +- moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ +- errorCode: 'unlink', +- path: cachedDataPath, +- detail: err +- }); +- } +- }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); ++ if (err) { ++ moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ ++ errorCode: 'unlink', ++ path: cachedDataPath, ++ detail: err ++ }); ++ } ++ }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); + } + else if (script.cachedDataProduced) { + // data produced => tell outside world +@@ -840,14 +847,14 @@ var AMDLoader; + }); + // data produced => write cache file + NodeScriptLoader._runSoon(function () { return _this._fs.writeFile(cachedDataPath, script.cachedData, function (err) { +- if (err) { +- moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ +- errorCode: 'writeFile', +- path: cachedDataPath, +- detail: err +- }); +- } +- }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); ++ if (err) { ++ moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ ++ errorCode: 'writeFile', ++ path: cachedDataPath, ++ detail: err ++ }); ++ } ++ }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); + } + }; + NodeScriptLoader._runSoon = function (callback, minTimeout) { +diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts +index a4ef78d6f0..eaa348e689 100644 +--- a/src/vs/platform/actions/browser/menuItemActionItem.ts ++++ b/src/vs/platform/actions/browser/menuItemActionItem.ts +@@ -18,6 +18,7 @@ import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemA + import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; + import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + import { INotificationService } from 'vs/platform/notification/common/notification'; ++import { getFetchUri } from 'coder/api'; + + // The alternative key on all platforms is alt. On windows we also support shift as an alternative key #44136 + class AlternativeKeyEmitter extends Emitter { +@@ -231,8 +232,8 @@ export class MenuItemActionItem extends ActionItem { + iconClass = MenuItemActionItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey); + } else { + iconClass = ids.nextId(); +- createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`); +- createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${item.iconLocation.dark.toString()}")`); ++ createCSSRule(`.icon.${iconClass}`, `background-image: url("${(getFetchUri(item.iconLocation.light || item.iconLocation.dark)).toString(true)}")`); ++ createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${getFetchUri(item.iconLocation.dark).toString(true)}")`); + MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); + } + +diff --git a/src/vs/platform/configuration/node/configuration.ts b/src/vs/platform/configuration/node/configuration.ts +index 925ea03a57..44207f8afa 100644 +--- a/src/vs/platform/configuration/node/configuration.ts ++++ b/src/vs/platform/configuration/node/configuration.ts +@@ -9,23 +9,30 @@ import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/config + import { ConfigWatcher } from 'vs/base/node/config'; + import { Event, Emitter } from 'vs/base/common/event'; + import { TPromise } from 'vs/base/common/winjs.base'; ++import { isBrowserEnvironment } from 'coder/common'; + + export class UserConfiguration extends Disposable { + + private userConfigModelWatcher: ConfigWatcher; + + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); ++ private readonly loadPromise: Promise; + readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor(settingsPath: string) { + super(); ++ let initRes: () => void; ++ this.loadPromise = new Promise((r) => initRes = r); + this.userConfigModelWatcher = new ConfigWatcher(settingsPath, { + changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(settingsPath), parse: (content: string, parseErrors: any[]) => { + const userConfigModelParser = new ConfigurationModelParser(settingsPath); + userConfigModelParser.parse(content); + parseErrors = [...userConfigModelParser.errors]; + return userConfigModelParser; +- } ++ }, ++ initCallback: isBrowserEnvironment ? () => { ++ initRes(); ++ } : undefined, + }); + this._register(this.userConfigModelWatcher); + +@@ -37,6 +44,10 @@ export class UserConfiguration extends Disposable { + return this.userConfigModelWatcher.getConfig().configurationModel; + } + ++ get loaded(): Promise { ++ return this.loadPromise; ++ } ++ + reload(): TPromise { + return new TPromise(c => this.userConfigModelWatcher.reload(() => c(null))); + } +diff --git a/src/vs/platform/credentials/test/node/keytar.test.ts b/src/vs/platform/credentials/test/node/keytar.test.ts +index f75689a147..6b3a63cb9b 100644 +--- a/src/vs/platform/credentials/test/node/keytar.test.ts ++++ b/src/vs/platform/credentials/test/node/keytar.test.ts +@@ -16,7 +16,7 @@ suite('Keytar', () => { + return; + } + (async () => { +- const keytar = await import('keytar'); ++ const keytar = require('keytar'); + const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`; + try { + await keytar.setPassword(name, 'foo', 'bar'); +diff --git a/src/vs/platform/dialogs/node/dialogService.ts b/src/vs/platform/dialogs/node/dialogService.ts +index 4304af5518..b12d48b18d 100644 +--- a/src/vs/platform/dialogs/node/dialogService.ts ++++ b/src/vs/platform/dialogs/node/dialogService.ts +@@ -3,13 +3,15 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-import * as readline from 'readline'; ++// import * as readline from 'readline'; + import { TPromise } from 'vs/base/common/winjs.base'; + import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; + import Severity from 'vs/base/common/severity'; + import { localize } from 'vs/nls'; + import { canceled } from 'vs/base/common/errors'; + ++declare var readline: any; ++ + export class CommandLineDialogService implements IDialogService { + + _serviceBrand: any; +diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts +index ee381bbe35..698076bb89 100644 +--- a/src/vs/platform/extensionManagement/common/extensionManagement.ts ++++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts +@@ -188,6 +188,7 @@ export interface IGalleryExtension { + publisherDisplayName: string; + description: string; + installCount: number; ++ stars: number; + rating: number; + ratingCount: number; + assets: IGalleryExtensionAssets; +diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +index ce457e5e9f..ee73e348a6 100644 +--- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts ++++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +@@ -103,7 +103,7 @@ enum FilterType { + ExcludeWithFlags = 12 + } + +-const AssetType = { ++export const AssetType = { + Icon: 'Microsoft.VisualStudio.Services.Icons.Default', + Details: 'Microsoft.VisualStudio.Services.Content.Details', + Changelog: 'Microsoft.VisualStudio.Services.Content.Changelog', +@@ -311,6 +311,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller + publisherDisplayName: galleryExtension.publisher.displayName, + description: galleryExtension.shortDescription || '', + installCount: getStatistic(galleryExtension.statistics, 'install') + getStatistic(galleryExtension.statistics, 'updateCount'), ++ stars: getStatistic(galleryExtension.statistics, 'stars'), + rating: getStatistic(galleryExtension.statistics, 'averagerating'), + ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'), + assets, +@@ -474,29 +475,104 @@ export class ExtensionGalleryService implements IExtensionGalleryService { + private queryGallery(query: Query, token: CancellationToken): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> { + return this.commonHeadersPromise.then(commonHeaders => { + const data = JSON.stringify(query.raw); +- const headers = assign({}, commonHeaders, { ++ const headers = assign({}, {}, { + 'Content-Type': 'application/json', + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Accept-Encoding': 'gzip', + 'Content-Length': data.length + }); + ++ let ids: string[]; ++ try { ++ const criteria = query.raw.filters[0].criteria; ++ const newIds = []; ++ for (let i = 0; i < criteria.length; i++) { ++ const crit = criteria[i]; ++ if (crit.filterType === FilterType.ExtensionName) { ++ newIds.push(crit.value); ++ } ++ } ++ if (newIds.length > 0) { ++ ids = newIds; ++ } ++ } catch (ex) { ++ ++ } ++ ++ const queryString = `?${query.searchText ? `name=${query.searchText}&` : ""}page=${query.pageNumber - 1}&page_size=${query.pageSize}&sort=downloads${ids ? `&${ids.map((id) => `id=${id}`).join("&")}` : ""}` ++ + return this.requestService.request({ +- type: 'POST', +- url: this.api('/extensionquery'), ++ type: 'GET', ++ url: this.api(`/search${queryString}`), + data, +- headers + }, token).then(context => { + + if (context.res.statusCode >= 400 && context.res.statusCode < 500) { + return { galleryExtensions: [], total: 0 }; + } + +- return asJson(context).then(result => { +- const r = result.results[0]; +- const galleryExtensions = r.extensions; +- const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0]; +- const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0; ++ return asJson(context).then((result: Array<{ ++ readonly author: string; ++ readonly category: string; ++ readonly downloads: number; ++ readonly forks: number; ++ readonly id: string; ++ readonly name: string; ++ readonly short_description: string; ++ readonly source_url: string; ++ readonly stars: number; ++ readonly version: string; ++ readonly watchers: number; ++ readonly metadata: { ++ readonly files: IRawGalleryExtensionFile[]; ++ readonly properties: IRawGalleryExtensionProperty[]; ++ } ++ }>) => { ++ ++ const galleryExtensions: IRawGalleryExtension[] = result.filter((ext) => ext.metadata).map((ext): IRawGalleryExtension => { ++ const assetUrl = ext.source_url.replace("/Microsoft.VisualStudio.Services.VSIXPackage", ""); ++ ++ return { ++ displayName: ext.name, ++ extensionId: ext.id, ++ extensionName: ext.id, ++ flags: "", ++ publisher: { ++ displayName: ext.author, ++ publisherId: undefined, ++ publisherName: ext.author, ++ }, ++ shortDescription: ext.short_description, ++ statistics: [{ ++ statisticName: 'stars', ++ value: ext.stars, ++ }, { ++ statisticName: 'forks', ++ value: ext.forks, ++ }, { ++ statisticName: 'watchers', ++ value: ext.watchers, ++ }, { ++ statisticName: 'install', ++ value: ext.downloads, ++ }], ++ versions: [{ ++ assetUri: assetUrl, ++ fallbackAssetUri: assetUrl, ++ files: [ ++ ...ext.metadata.files, ++ { ++ assetType: AssetType.VSIX, ++ source: ext.source_url, ++ }, ++ ], ++ properties: ext.metadata.properties, ++ version: ext.version, ++ lastUpdated: undefined, ++ }], ++ }; ++ }); ++ const total = result.filter(ext => ext.metadata).length; + + return { galleryExtensions, total }; + }); +@@ -667,7 +743,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { + private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): TPromise { + return this.commonHeadersPromise.then(commonHeaders => { + const baseOptions = { type: 'GET' }; +- const headers = assign({}, commonHeaders, options.headers || {}); ++ const headers = assign({}, {}, options.headers || {}); + options = assign({}, options, baseOptions, { headers }); + + const url = asset.uri; +diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +index 79c5558257..5bd7d0112d 100644 +--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts ++++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +@@ -12,7 +12,7 @@ import * as errors from 'vs/base/common/errors'; + import { assign } from 'vs/base/common/objects'; + import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; + import { flatten } from 'vs/base/common/arrays'; +-import { extract, buffer, ExtractError, zip, IFile } from 'vs/platform/node/zip'; ++import { buffer, extract, tar, IFile } from 'vs/platform/node/nodeTar'; + import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; + import { + IExtensionManagementService, IExtensionGalleryService, ILocalExtension, +@@ -80,8 +80,8 @@ function parseManifest(raw: string): TPromise<{ manifest: IExtensionManifest; me + }); + } + +-export function validateLocalExtension(zipPath: string): TPromise { +- return buffer(zipPath, 'extension/package.json') ++export function validateLocalExtension(tarPath: string): TPromise { ++ return buffer(tarPath, 'extension/package.json') + .then(buffer => parseManifest(buffer.toString('utf8'))) + .then(({ manifest }) => TPromise.as(manifest)); + } +@@ -162,7 +162,7 @@ export class ExtensionManagementService extends Disposable implements IExtension + + zip(extension: ILocalExtension): TPromise { + return TPromise.wrap(this.collectFiles(extension)) +- .then(files => zip(path.join(tmpdir(), generateUuid()), files)) ++ .then(files => tar(path.join(tmpdir(), generateUuid()), files)) + .then(path => URI.file(path)); + } + +@@ -471,15 +471,15 @@ export class ExtensionManagementService extends Disposable implements IExtension + })); + } + +- private extract(id: string, zipPath: string, extractPath: string, token: CancellationToken): TPromise { +- this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`); ++ private extract(id: string, tarPath: string, extractPath: string, token: CancellationToken): TPromise { ++ this.logService.trace(`Started extracting the extension from ${tarPath} to ${extractPath}`); + return pfs.rimraf(extractPath) + .then( +- () => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService, token) ++ () => extract(tarPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService, token) + .then( + () => this.logService.info(`Extracted extension to ${extractPath}:`, id), + e => always(pfs.rimraf(extractPath), () => null) +- .then(() => TPromise.wrapError(new ExtensionManagementError(e.message, e instanceof ExtractError ? e.type : INSTALL_ERROR_EXTRACTING)))), ++ .then(() => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))), + e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING))); + } + +diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts +index e2d951ec24..b00a24ec11 100644 +--- a/src/vs/platform/files/common/files.ts ++++ b/src/vs/platform/files/common/files.ts +@@ -15,6 +15,7 @@ import { startsWithIgnoreCase } from 'vs/base/common/strings'; + import { IDisposable } from 'vs/base/common/lifecycle'; + import { isEqualOrParent, isEqual } from 'vs/base/common/resources'; + import { isUndefinedOrNull } from 'vs/base/common/types'; ++import { ConflictResolution } from 'coder/common'; // NOTE@coder. + + export const IFileService = createDecorator('fileService'); + +@@ -22,6 +23,12 @@ export interface IResourceEncodings { + getWriteEncoding(resource: URI, preferredEncoding?: string): string; + } + ++export interface IContentProvider { ++ resolve: (uri: URI) => TPromise; ++ save: (uri: URI, accept?: ConflictResolution) => TPromise; ++ willHandle: (uri: URI) => boolean; ++} ++ + export interface IFileService { + _serviceBrand: any; + +@@ -51,6 +58,13 @@ export interface IFileService { + */ + registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable; + ++ /** ++ * Registers a content provider for a scheme. ++ * Multiple content providers can be registered for a scheme. ++ * The file service will use the first that resolves with content. ++ */ ++ registerContentProvider(scheme: string, provider: IContentProvider): IDisposable; ++ + /** + * Checks if this file service can handle the given resource. + */ +@@ -602,6 +616,17 @@ export interface IUpdateContentOptions { + * Run mkdirp before saving. + */ + mkdirp?: boolean; ++ ++ /** ++ * NOTE@coder: which side to accept in case of conflict. ++ */ ++ accept?: ConflictResolution; ++ ++ /** ++ * NOTE@coder: whether we are creating the file, so we can bypass Clide since ++ * saving to Clide will not create files that don't exist. ++ */ ++ createFile?: boolean; + } + + export interface IResolveFileOptions { +diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts +index 46bd114b4c..0b502acfcf 100644 +--- a/src/vs/platform/localizations/node/localizations.ts ++++ b/src/vs/platform/localizations/node/localizations.ts +@@ -17,7 +17,7 @@ import product from 'vs/platform/node/product'; + import { distinct, equals } from 'vs/base/common/arrays'; + import { Event, Emitter } from 'vs/base/common/event'; + import { Schemas } from 'vs/base/common/network'; +-import { posix } from 'path'; ++import * as path from 'path'; + + interface ILanguagePack { + hash: string; +@@ -107,7 +107,7 @@ class LanguagePacksCache extends Disposable { + @ILogService private logService: ILogService + ) { + super(); +- this.languagePacksFilePath = posix.join(environmentService.userDataPath, 'languagepacks.json'); ++ this.languagePacksFilePath = path.join(environmentService.userDataPath, 'languagepacks.json'); + this.languagePacksFileLimiter = new Limiter(1); + } + +@@ -152,7 +152,7 @@ class LanguagePacksCache extends Disposable { + languagePack.extensions.push({ extensionIdentifier, version: extension.manifest.version }); + } + for (const translation of localizationContribution.translations) { +- languagePack.translations[translation.id] = posix.join(extension.location.fsPath, translation.path); ++ languagePack.translations[translation.id] = path.join(extension.location.fsPath, translation.path); + } + } + } +diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts +index d2bd5c1965..da7bef4c00 100644 +--- a/src/vs/platform/log/node/spdlogService.ts ++++ b/src/vs/platform/log/node/spdlogService.ts +@@ -107,4 +107,4 @@ class SpdLogService extends AbstractLogService implements ILogService { + + return result; + } +-} +\ No newline at end of file ++} +diff --git a/src/vs/platform/node/package.ts b/src/vs/platform/node/package.ts +index 93c32bc711..a66aa939ec 100644 +--- a/src/vs/platform/node/package.ts ++++ b/src/vs/platform/node/package.ts +@@ -3,14 +3,13 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-import * as path from 'path'; +-import { getPathFromAmdModule } from 'vs/base/common/amd'; ++// import * as path from 'path'; ++// import { getPathFromAmdModule } from 'vs/base/common/amd'; + + export interface IPackageConfiguration { + name: string; + version: string; + } + +-const rootPath = path.dirname(getPathFromAmdModule(require, '')); +-const packageJsonPath = path.join(rootPath, 'package.json'); +-export default require.__$__nodeRequire(packageJsonPath) as IPackageConfiguration; ++// TODO: obtain this version in a reasonable way ++export default { name: "vscode", version: "1.28.0" } +diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts +index dbb84fd473..3fba12657d 100644 +--- a/src/vs/platform/node/product.ts ++++ b/src/vs/platform/node/product.ts +@@ -3,8 +3,10 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-import * as path from 'path'; +-import { getPathFromAmdModule } from 'vs/base/common/amd'; ++// import * as path from 'path'; ++// import { getPathFromAmdModule } from 'vs/base/common/amd'; ++import { ExtensionHostExecutionEnvironment } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; ++import { api } from 'coder/api'; + + export interface IProductConfiguration { + nameShort: string; +@@ -34,12 +36,14 @@ export interface IProductConfiguration { + controlUrl: string; + recommendationsUrl: string; + }; ++ extensionExecutionEnvironments?: { [id: string]: ExtensionHostExecutionEnvironment }; + extensionTips: { [id: string]: string; }; + extensionImportantTips: { [id: string]: { name: string; pattern: string; }; }; + exeBasedExtensionTips: { [id: string]: { friendlyName: string, windowsPath?: string, recommendations: string[] }; }; + extensionKeywords: { [extension: string]: string[]; }; + extensionAllowedBadgeProviders: string[]; + extensionAllowedProposedApi: string[]; ++ fetchUrl: string; + keymapExtensionTips: string[]; + crashReporter: { + companyName: string; +@@ -90,9 +94,24 @@ export interface ISurveyData { + userProbability: number; + } + +-const rootPath = path.dirname(getPathFromAmdModule(require, '')); +-const productJsonPath = path.join(rootPath, 'product.json'); +-const product = require.__$__nodeRequire(productJsonPath) as IProductConfiguration; ++const product = { ++ nameShort: "VSCoder", ++ nameLong: "coder + vscode", ++ dataFolderName: "vscode", ++ extensionsGallery: { ++ "serviceUrl": undefined, ++ }, ++ extensionExecutionEnvironments: { ++ "wayou.vscode-todo-highlight": "worker", ++ "vscodevim.vim": "worker", ++ "coenraads.bracket-pair-colorizer": "worker", ++ }, ++ fetchUrl: "http://9080-untidyfeatheredmandrill.cdr-develop.co", ++} as any as IProductConfiguration; ++ ++if (typeof api !== "undefined") { ++ product.extensionsGallery.serviceUrl = api.environment.appURL("extensions-api"); ++} + + if (process.env['VSCODE_DEV']) { + product.nameShort += ' Dev'; +diff --git a/src/vs/platform/output/node/outputAppender.ts b/src/vs/platform/output/node/outputAppender.ts +index a7ffc9cd3d..72a251a3c9 100644 +--- a/src/vs/platform/output/node/outputAppender.ts ++++ b/src/vs/platform/output/node/outputAppender.ts +@@ -21,4 +21,4 @@ export class OutputAppender { + flush(): void { + this.appender.flush(); + } +-} +\ No newline at end of file ++} +diff --git a/src/vs/platform/request/electron-browser/requestService.ts b/src/vs/platform/request/electron-browser/requestService.ts +index c66762f4d4..36b8c9fed6 100644 +--- a/src/vs/platform/request/electron-browser/requestService.ts ++++ b/src/vs/platform/request/electron-browser/requestService.ts +@@ -32,6 +32,11 @@ export const xhrRequest: IRequestFunction = (options: IRequestOptions, token: Ca + xhr.responseType = 'arraybuffer'; + xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText))); + xhr.onload = (e) => { ++ if (!xhr.response) { ++ const { TextEncoder } = require("text-encoding"); ++ Object.defineProperty(xhr, "response", new TextEncoder().encode(xhr.responseText)); ++ } ++ + resolve({ + res: { + statusCode: xhr.status, +@@ -45,7 +50,7 @@ export const xhrRequest: IRequestFunction = (options: IRequestOptions, token: Ca + + constructor(arraybuffer: ArrayBuffer) { + super(); +- this._buffer = Buffer.from(new Uint8Array(arraybuffer)); ++ this._buffer = Buffer.from(arraybuffer); + this._offset = 0; + this._length = this._buffer.length; + } +diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts +index 5238541eac..668a4c656e 100644 +--- a/src/vs/platform/state/node/stateService.ts ++++ b/src/vs/platform/state/node/stateService.ts +@@ -66,6 +66,9 @@ export class FileStorage { + } + + private loadSync(): object { ++ // Cannot load files sync ++ return {}; ++ + try { + return JSON.parse(fs.readFileSync(this.dbPath).toString()); // invalid JSON or permission issue can happen here + } catch (error) { +@@ -78,6 +81,9 @@ export class FileStorage { + } + + private saveSync(): void { ++ // Cannot save sync ++ return; ++ + try { + writeFileAndFlushSync(this.dbPath, JSON.stringify(this.database, null, 4)); // permission issue can happen here + } catch (error) { +diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts +index 72b7d85693..a2c0831da8 100644 +--- a/src/vs/platform/telemetry/common/telemetryService.ts ++++ b/src/vs/platform/telemetry/common/telemetryService.ts +@@ -164,11 +164,11 @@ Registry.as(Extensions.Configuration).registerConfigurat + 'type': 'object', + 'title': localize('telemetryConfigurationTitle', "Telemetry"), + 'properties': { +- 'telemetry.enableTelemetry': { +- 'type': 'boolean', +- 'description': localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to a Microsoft online service."), +- 'default': true, +- 'tags': ['usesOnlineServices'] +- } ++ // 'telemetry.enableTelemetry': { ++ // 'type': 'boolean', ++ // 'description': localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to a Microsoft online service."), ++ // 'default': true, ++ // 'tags': ['usesOnlineServices'] ++ // } + } + }); +\ No newline at end of file +diff --git a/src/vs/platform/update/node/update.config.contribution.ts b/src/vs/platform/update/node/update.config.contribution.ts +index 61fefc9506..6d4c9fe8ed 100644 +--- a/src/vs/platform/update/node/update.config.contribution.ts ++++ b/src/vs/platform/update/node/update.config.contribution.ts +@@ -16,21 +16,21 @@ configurationRegistry.registerConfiguration({ + 'title': nls.localize('updateConfigurationTitle', "Update"), + 'type': 'object', + 'properties': { +- 'update.channel': { +- 'type': 'string', +- 'enum': ['none', 'default'], +- 'default': 'default', +- 'scope': ConfigurationScope.APPLICATION, +- 'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change. The updates are fetched from a Microsoft online service."), +- 'tags': ['usesOnlineServices'] +- }, +- 'update.enableWindowsBackgroundUpdates': { +- 'type': 'boolean', +- 'default': true, +- 'scope': ConfigurationScope.APPLICATION, +- 'description': nls.localize('enableWindowsBackgroundUpdates', "Enables Windows background updates. The updates are fetched from a Microsoft online service."), +- 'tags': ['usesOnlineServices'] +- }, ++ // 'update.channel': { ++ // 'type': 'string', ++ // 'enum': ['none', 'default'], ++ // 'default': 'default', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change. The updates are fetched from a Microsoft online service."), ++ // 'tags': ['usesOnlineServices'] ++ // }, ++ // 'update.enableWindowsBackgroundUpdates': { ++ // 'type': 'boolean', ++ // 'default': true, ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'description': nls.localize('enableWindowsBackgroundUpdates', "Enables Windows background updates. The updates are fetched from a Microsoft online service."), ++ // 'tags': ['usesOnlineServices'] ++ // }, + 'update.showReleaseNotes': { + 'type': 'boolean', + 'default': true, +diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts +index ab83d870ea..321c7f0a17 100644 +--- a/src/vs/platform/windows/electron-browser/windowService.ts ++++ b/src/vs/platform/windows/electron-browser/windowService.ts +@@ -67,6 +67,12 @@ export class WindowService implements IWindowService { + } + + reloadWindow(args?: ParsedArgs): TPromise { ++ if (typeof location !== "undefined") { ++ location.reload(); ++ ++ return Promise.resolve(); ++ } ++ + return this.windowsService.reloadWindow(this.windowId, args); + } + +@@ -159,6 +165,7 @@ export class WindowService implements IWindowService { + } + + showOpenDialog(options: Electron.OpenDialogOptions): TPromise { ++ console.log("ODKAJ(%(% GETTING CALLED OPEN", this.windowsService); + return this.windowsService.showOpenDialog(this.windowId, options); + } + +diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts +index 0deb6db442..8c7f849e48 100644 +--- a/src/vs/platform/windows/electron-main/windowsService.ts ++++ b/src/vs/platform/windows/electron-main/windowsService.ts +@@ -104,6 +104,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable + } + + showOpenDialog(windowId: number, options: Electron.OpenDialogOptions): TPromise { ++ console.log("WE ARTE OUT HERE 092ti24t", windowId); + this.logService.trace('windowsService#showOpenDialog', windowId); + const codeWindow = this.windowsMainService.getWindowById(windowId); + +diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts +index f5546a0161..df12e2e5d5 100644 +--- a/src/vs/platform/windows/node/windowsIpc.ts ++++ b/src/vs/platform/windows/node/windowsIpc.ts +@@ -114,7 +114,10 @@ export class WindowsChannel implements IWindowsChannel { + case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg); + case 'pickFolderAndOpen': return this.service.pickFolderAndOpen(arg); + case 'pickWorkspaceAndOpen': return this.service.pickWorkspaceAndOpen(arg); +- case 'showMessageBox': return this.service.showMessageBox(arg[0], arg[1]); ++ case 'showMessageBox': { ++ console.log("CAlling service showMsgBox", this.service); ++ return this.service.showMessageBox(arg[0], arg[1]); ++ } + case 'showSaveDialog': return this.service.showSaveDialog(arg[0], arg[1]); + case 'showOpenDialog': return this.service.showOpenDialog(arg[0], arg[1]); + case 'reloadWindow': return this.service.reloadWindow(arg[0], arg[1]); +@@ -185,6 +188,12 @@ export class WindowsChannel implements IWindowsChannel { + } + } + ++/** ++ * Instead of sending messages through the sharedProcess, we intercept this ++ * to provide our own client functionality. ++ * ++ * In most cases we'll access our electron filler. ++ */ + export class WindowsChannelClient implements IWindowsService { + + _serviceBrand: any; +@@ -215,11 +224,28 @@ export class WindowsChannelClient implements IWindowsService { + } + + showMessageBox(windowId: number, options: MessageBoxOptions): TPromise { +- return TPromise.wrap(this.channel.call('showMessageBox', [windowId, options])); ++ return new TPromise((resolve) => { ++ const { dialog } = require('electron'); ++ dialog.showMessageBox(undefined, options, (response: number, checked: boolean) => { ++ resolve({ ++ button: response, ++ checkboxChecked: checked, ++ }); ++ }); ++ }); ++ ++ // return TPromise.wrap(this.channel.call('showMessageBox', [windowId, options])); + } + + showSaveDialog(windowId: number, options: SaveDialogOptions): TPromise { +- return TPromise.wrap(this.channel.call('showSaveDialog', [windowId, options])); ++ return new TPromise((resolve) => { ++ const { dialog } = require('electron'); ++ dialog.showSaveDialog(undefined, options, (filename: string) => { ++ resolve(filename); ++ }); ++ }); ++ ++ // return TPromise.wrap(this.channel.call('showSaveDialog', [windowId, options])); + } + + showOpenDialog(windowId: number, options: OpenDialogOptions): TPromise { +@@ -255,7 +281,11 @@ export class WindowsChannelClient implements IWindowsService { + } + + toggleFullScreen(windowId: number): TPromise { +- return TPromise.wrap(this.channel.call('toggleFullScreen', windowId)); ++ return new TPromise((resolve) => { ++ const { toggleFullScreen } = require("electron"); ++ toggleFullScreen(); ++ resolve(undefined); ++ }); + } + + setRepresentedFilename(windowId: number, fileName: string): TPromise { +@@ -308,7 +338,11 @@ export class WindowsChannelClient implements IWindowsService { + } + + focusWindow(windowId: number): TPromise { +- return TPromise.wrap(this.channel.call('focusWindow', windowId)); ++ return new TPromise((resolve) => { ++ const { focusWindow } = require("electron"); ++ focusWindow(); ++ resolve(undefined); ++ }); + } + + closeWindow(windowId: number): TPromise { +diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +index b603f45eac..d62b599e34 100644 +--- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts ++++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +@@ -30,6 +30,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; + import { URI } from 'vs/base/common/uri'; ++import { getFetchUri } from 'coder/api'; + + export interface IUserFriendlyViewsContainerDescriptor { + id: string; +@@ -174,6 +175,8 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { + super(id, `${id}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + } + } ++ ++ descriptor.icon = getFetchUri(descriptor.icon); + const viewletDescriptor = new ViewletDescriptor( + CustomViewlet, + id, +@@ -205,7 +208,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { + + // Generate CSS to show the icon in the activity bar + const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${cssClass}`; +- createCSSRule(iconClass, `-webkit-mask: url('${descriptor.icon}') no-repeat 50% 50%`); ++ createCSSRule(iconClass, `-webkit-mask: url('${descriptor.icon.toString(true)}') no-repeat 50% 50%`); + } + + } +diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +index d95f314af6..0a034e4459 100644 +--- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts ++++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +@@ -30,6 +30,8 @@ import './mainThreadEditor'; + import './mainThreadEditors'; + import './mainThreadErrors'; + import './mainThreadExtensionService'; ++import './mainThreadFastTime'; ++import './mainThreadApi'; + import './mainThreadFileSystem'; + import './mainThreadFileSystemEventService'; + import './mainThreadHeapService'; +diff --git a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +index ce8dfa1af4..ed0d8c503b 100644 +--- a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts ++++ b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +@@ -57,11 +57,14 @@ export class HeapService implements IHeapService { + + private _doTrackRecursive(obj: any): Promise { + ++ // NOTE@coder: cannot control GC in the browser. ++ return Promise.resolve(obj); ++ + if (isNullOrUndefined(obj)) { + return Promise.resolve(obj); + } + +- return import('gc-signals').then(({ GCSignal, consumeSignals }) => { ++ return Promise.resolve(require('gc-signals')).then(({ GCSignal, consumeSignals }) => { + + if (this._consumeHandle === void 0) { + // ensure that there is one consumer of signals +diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts +index e8ba986c4b..6cdbd8a4bd 100644 +--- a/src/vs/workbench/api/node/extHost.api.impl.ts ++++ b/src/vs/workbench/api/node/extHost.api.impl.ts +@@ -44,6 +44,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS + import { TPromise } from 'vs/base/common/winjs.base'; + import { CancellationTokenSource } from 'vs/base/common/cancellation'; + import * as vscode from 'vscode'; ++import * as coder from 'coder'; + import * as paths from 'vs/base/common/paths'; + import * as files from 'vs/platform/files/common/files'; + import { MainContext, ExtHostContext, IInitData, IMainContext } from './extHost.protocol'; +@@ -61,12 +62,19 @@ import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview'; + import { ExtHostComments } from './extHostComments'; + import { ExtHostSearch } from './extHostSearch'; + import { ExtHostUrls } from './extHostUrls'; ++import { ExtHostFastTime } from './extHostFastTime'; ++import { ExtHostApi } from './extHostApi'; + import { localize } from 'vs/nls'; ++import { IInitData as ICoderInitData, isBrowserEnvironment } from 'coder/common'; + + export interface IExtensionApiFactory { + (extension: IExtensionDescription): typeof vscode; + } + ++export interface ICoderExtensionApiFactory { ++ (extension: IExtensionDescription): typeof coder; ++} ++ + export function checkProposedApiEnabled(extension: IExtensionDescription): void { + if (!extension.enableProposedApi) { + throwProposedApiError(extension); +@@ -85,6 +93,35 @@ function proposedApiFunction(extension: IExtensionDescription, fn: T): T { + } + } + ++/** ++ * Instantiate and return the coder extension API surface. ++ */ ++export function createCoderApiFactory( ++ initData: ICoderInitData, ++ rpcProtocol: IMainContext, ++): ICoderExtensionApiFactory { ++ const extHostFastTime = rpcProtocol.set(ExtHostContext.ExtHostFastTime, new ExtHostFastTime(rpcProtocol)); ++ const extHostApi = rpcProtocol.set(ExtHostContext.ExtHostApi, new ExtHostApi(rpcProtocol)); ++ ++ return function (_extension: IExtensionDescription): typeof coder { ++ return { ++ apiDetails: initData.api, ++ api: extHostApi.createApiProxy(), ++ extensions: { ++ isInstalled: (id) => extHostApi.extensionIsInstalled(id), ++ install: (id) => extHostApi.installExtension(id), ++ }, ++ fastTime: { ++ getUpdater: () => extHostFastTime.getUpdater(), ++ setUpdater: (updater) => { ++ extHostFastTime.setUpdater(updater); ++ }, ++ }, ++ }; ++ }; ++} ++ ++ + /** + * This method instantiates and returns the extension API surface + */ +@@ -141,7 +178,9 @@ export function createApiFactory( + const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); + + // Register an output channel for exthost log +- extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile); ++ // NOTE@coder: add suffix to distinguish between Node and web worker logs. ++ const suffix = typeof process === 'undefined' || typeof process.stdout === 'undefined' ? 'Worker' : 'Node'; ++ extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog' + suffix, suffix + " Extension Host"), extHostLogService.logFile); + + // Register API-ish commands + ExtHostApiCommands.register(extHostCommands); +@@ -832,30 +871,85 @@ class Extension implements vscode.Extension { + } + } + +-export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise { +- return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); ++export function initializeExtensionApis( ++ extensionService: ExtHostExtensionService, vsFactory: IExtensionApiFactory, coderFactory: ICoderExtensionApiFactory, ++): TPromise { ++ return extensionService.getExtensionPathIndex().then((trie) => { ++ defineCoderApi(coderFactory, trie); ++ defineVSApi(vsFactory, trie); ++ }); ++} ++ ++const extApiImpl = new Map(); ++let lastFactory: IExtensionApiFactory; ++ ++let lastCoderFactory: ICoderExtensionApiFactory; ++const extCoderApiImpl = new Map(); ++ ++/** ++ * Fetches the coder API for an extension. ++ */ ++export function fetchCoderApi(description: IExtensionDescription): typeof coder { ++ return fetchApi(description, extCoderApiImpl, lastCoderFactory); ++} ++ ++/** ++ * Fetches the vscode api for an extension ++ */ ++export function fetchVSApi(description: IExtensionDescription): typeof vscode { ++ return fetchApi(description, extApiImpl, lastFactory); ++} ++ ++function fetchApi(description: IExtensionDescription, _extApiImpl: Map, _lastFactory: (d: IExtensionDescription) => T): T { ++ if (!_lastFactory) { ++ throw new Error("the api has not been defined"); ++ } ++ ++ if (_extApiImpl.has(description.id)) { ++ return _extApiImpl.get(description.id); ++ } ++ ++ const newAPI = _lastFactory(description); ++ _extApiImpl.set(description.id, newAPI); ++ return newAPI; ++} ++ ++function defineCoderApi(factory: ICoderExtensionApiFactory, extensionPaths: TernarySearchTree): void { ++ if (isBrowserEnvironment()) { ++ lastCoderFactory = factory; ++ } else { ++ defineApi(factory, extensionPaths, extCoderApiImpl, "coder"); ++ } + } + +-function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree): void { ++function defineVSApi(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree): void { ++ if (isBrowserEnvironment()) { ++ lastFactory = factory; ++ } else { ++ defineApi(factory, extensionPaths, extApiImpl, "vscode"); ++ } ++} + +- // each extension is meant to get its own api implementation +- const extApiImpl = new Map(); +- let defaultApiImpl: typeof vscode; ++function defineApi( ++ factory: (d: IExtensionDescription) => T, extensionPaths: TernarySearchTree, ++ _extApiImpl: Map, moduleName: string, ++): void { ++ let defaultApiImpl: T; + + const node_module = require.__$__nodeRequire('module'); + const original = node_module._load; + node_module._load = function load(request: string, parent: any, isMain: any) { +- if (request !== 'vscode') { ++ if (request !== moduleName) { + return original.apply(this, arguments); + } + + // get extension id from filename and api for extension + const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); + if (ext) { +- let apiImpl = extApiImpl.get(ext.id); ++ let apiImpl = _extApiImpl.get(ext.id); + if (!apiImpl) { + apiImpl = factory(ext); +- extApiImpl.set(ext.id, apiImpl); ++ _extApiImpl.set(ext.id, apiImpl); + } + return apiImpl; + } +@@ -864,7 +958,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT + if (!defaultApiImpl) { + let extensionPathsPretty = ''; + extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`); +- console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`); ++ console.warn(`Could not identify extension for '${moduleName}' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`); + defaultApiImpl = factory(nullExtensionDescription); + } + return defaultApiImpl; +diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts +index 200aa028b5..39f600f5b7 100644 +--- a/src/vs/workbench/api/node/extHost.protocol.ts ++++ b/src/vs/workbench/api/node/extHost.protocol.ts +@@ -42,6 +42,7 @@ import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/ + import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; + import * as vscode from 'vscode'; + import { CancellationToken } from 'vs/base/common/cancellation'; ++import { FastTimeState, IContainer, IUpdateContainer, IFastTimeBalanceResponse, IIDResponse } from 'coder/common'; + + export interface IEnvironment { + isExtensionDevelopmentDebug: boolean; +@@ -87,6 +88,20 @@ export interface IMainContext extends IRPCProtocol { + + // --- main thread + ++export interface MainThreadFastTimeShape extends IDisposable { ++ $setState(state: FastTimeState): void; ++} ++ ++export interface MainThreadApiShape extends IDisposable { ++ $getContainer(orgId: string, contId: string): Promise; ++ $getAvailableVolumes(orgId: string, contId: string): Promise; ++ $updateContainer(orgId: string, contId: string, update: IUpdateContainer): Promise; ++ $renewContainer(orgId: string, contId: string): Promise; ++ $getFastTime(orgId: string): Promise; ++ $extensionIsInstalled(extId: string): Promise; ++ $installExtension(extId: string): Promise; ++} ++ + export interface MainThreadCommandsShape extends IDisposable { + $registerCommand(id: string): void; + $unregisterCommand(id: string): void; +@@ -593,6 +608,14 @@ export interface MainThreadWindowShape extends IDisposable { + + // -- extension host + ++export interface ExtHostFastTimeShape { ++ $updateState(state: FastTimeState): Thenable; ++} ++ ++export interface ExtHostApiShape { ++ // Nothing needed since requests are always one-way. ++} ++ + export interface ExtHostCommandsShape { + $executeContributedCommand(id: string, ...args: any[]): Thenable; + $getContributedCommandHandlerDescriptions(): Thenable<{ [id: string]: string | ICommandHandlerDescription }>; +@@ -1004,6 +1027,8 @@ export interface ExtHostCommentsShape { + // --- proxy identifiers + + export const MainContext = { ++ MainThreadFastTime: createMainId('MainThreadFastTime'), ++ MainThreadApi: createMainId('MainThreadApi'), + MainThreadCommands: >createMainId('MainThreadCommands'), + MainThreadComments: createMainId('MainThreadComments'), + MainThreadConfiguration: createMainId('MainThreadConfiguration'), +@@ -1038,6 +1063,8 @@ export const MainContext = { + }; + + export const ExtHostContext = { ++ ExtHostFastTime: createExtId('ExtHostFastTime'), ++ ExtHostApi: createExtId('ExtHostApi'), + ExtHostCommands: createExtId('ExtHostCommands'), + ExtHostConfiguration: createExtId('ExtHostConfiguration'), + ExtHostDiagnostics: createExtId('ExtHostDiagnostics'), +diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts +index 36cdb4c70d..62fec93a61 100644 +--- a/src/vs/workbench/api/node/extHostExtensionService.ts ++++ b/src/vs/workbench/api/node/extHostExtensionService.ts +@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; + import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; + import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; + import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; +-import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; ++import { createCoderApiFactory, createApiFactory, fetchCoderApi, fetchVSApi, initializeExtensionApis } from 'vs/workbench/api/node/extHost.api.impl'; + import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol'; + import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent, ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator'; + import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +@@ -22,6 +22,12 @@ import { Barrier } from 'vs/base/common/async'; + import { ILogService } from 'vs/platform/log/common/log'; + import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; + import { URI } from 'vs/base/common/uri'; ++import { IInitData as ICoderInitData, isBrowserEnvironment } from 'coder/common'; ++// import * as path from 'path'; ++import { RequireFS } from "../../../../../../../requirefs/src/index"; ++// NOTE@coder for extensions to use the fetch URL. ++import product from 'vs/platform/node/product'; ++import * as resources from 'vs/base/common/resources'; + + class ExtensionMemento implements IExtensionMemento { + +@@ -136,6 +142,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + * This class is constructed manually because it is a service, so it doesn't use any ctor injection + */ + constructor(initData: IInitData, ++ coderInitData: ICoderInitData, + extHostContext: IMainContext, + extHostWorkspace: ExtHostWorkspace, + extHostConfiguration: ExtHostConfiguration, +@@ -151,9 +158,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + this._activator = null; + + // initialize API first (i.e. do not release barrier until the API is initialized) +- const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService); ++ const coderFactory = createCoderApiFactory(coderInitData, extHostContext); ++ const vsFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService); + +- initializeExtensionApi(this, apiFactory).then(() => { ++ initializeExtensionApis(this, vsFactory, coderFactory).then(() => { + + this._activator = new ExtensionsActivator(this._registry, { + showMessage: (severity: Severity, message: string): void => { +@@ -315,7 +323,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + "${TelemetryActivationEvent}" + ] + } +- */ ++ */ + this._mainThreadTelemetry.$publicLog('activatePlugin', event); + if (!extensionDescription.main) { + // Treat the extension as being empty => NOT AN ERROR CASE +@@ -326,7 +334,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + + const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); + return TPromise.join([ +- loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), ++ loadCommonJSModule(this._extHostLogService, extensionDescription, activationTimesBuilder), + this._loadExtensionContext(extensionDescription) + ]).then(values => { + return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, values[0], values[1], activationTimesBuilder); +@@ -358,9 +366,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + globalState, + workspaceState, + subscriptions: [], +- get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, ++ get extensionPath() { ++ return extensionDescription.extensionLocation.path; ++ }, + storagePath: this._storagePath.value(extensionDescription), +- asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); }, ++ asAbsolutePath: (relativePath: string) => { ++ return resources.joinPath(extensionDescription.extensionLocation, relativePath).path; ++ }, + logPath: that._extHostLogService.getLogDirectory(extensionDescription.id) + }); + }); +@@ -407,16 +419,139 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + } + } + +-function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise { ++// This variable is injected at the beginning of the output file. ++// @ts-ignore ++const extRequireMap = typeof externalRequireMap !== 'undefined' ? externalRequireMap : undefined; ++if (extRequireMap) { ++ // This is required because of https://github.com/PowerShell/vscode-powershell/blob/master/src/features/Folding.ts#L588 ++ // Uncertain why this became acceptable instead of bundling its own version of vscode-textmate or having an api func for it ++ // but regardless this is a way to support it ++ extRequireMap['/./node_modules/vscode-textmate'] = require('vscode-textmate'); ++ extRequireMap['vscode-extension-telemetry'] = { ++ default: class TelemetryReporter { ++ ++ public sendTelemetryEvent(): void { ++ ++ } ++ ++ public async dispose(): Promise { ++ ++ } ++ ++ }, ++ }; ++} ++ ++const oldErr = global.console.error; ++ ++function loadCommonJSModule(logService: ILogService, extensionDescription: IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise { + let r: T = null; ++ const modulePath = extensionDescription.main; + activationTimesBuilder.codeLoadingStart(); + logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); +- try { +- r = require.__$__nodeRequire(modulePath); +- } catch (e) { +- return TPromise.wrapError(e); +- } finally { +- activationTimesBuilder.codeLoadingStop(); ++ if (isBrowserEnvironment()) { ++ return new Promise(async (res, rej) => { ++ const { fromTar } = require("../../../../../../../requirefs/src/index"); ++ /** ++ * Returns a requirefs archive ++ */ ++ const fetchArchive = async (): Promise => { ++ if (extensionDescription.download) { ++ return fetch(extensionDescription.download).then((resp) => { ++ if (resp.status !== 200) { ++ return Promise.reject("failed to download extension"); ++ } ++ return resp.arrayBuffer(); ++ }).then((arrayBuffer) => { ++ return fromTar(Buffer.from(arrayBuffer)); ++ }).then((requirefs) => { ++ requirefs.basedir("extension"); ++ return Promise.resolve(requirefs); ++ }); ++ } else { ++ const { wush } = require('coder/server'); ++ const resp = await wush.execute({ ++ command: `cd ${extensionDescription.extensionLocation.path} && tar cvf - ./*`, ++ }).done(undefined, true); ++ if (!resp.wasSuccessful()) { ++ throw new Error(`failed to tar: ${resp.stderr}`); ++ } ++ ++ return fromTar(resp.stdout); ++ } ++ }; ++ ++ const requireFs = await fetchArchive(); ++ const pathModule = require("path"); ++ const mainPath = pathModule.relative(extensionDescription.extensionLocation.path, modulePath); ++ pathModule.PATHS = { ++ VSIX_DIR: "/", ++ }; ++ pathModule.posix = pathModule; ++ const childProcessModule = require("child_process"); ++ childProcessModule.execSync = () => { ++ return ""; ++ }; ++ ++ self["process"] = { ++ // TODO: update this to users platform ++ argv: [], ++ browser: true, ++ binding: () => ({}), ++ env: { ++ HOME: "/root", ++ }, ++ on: () => { ++ // Nothing yet ++ }, ++ nextTick: function (cb, ...args) { ++ setTimeout(cb, 0, ...args); ++ }, ++ platform: "linux", ++ version: "1.0.0", ++ }; ++ ++ // @ts-ignore ++ self["window"] = self; ++ self["global"] = self; ++ self["Buffer"] = require("buffer"); ++ ++ requireFs.provide("coder", fetchCoderApi(extensionDescription)); ++ requireFs.provide("vscode", fetchVSApi(extensionDescription)); ++ requireFs.provide("assert", require("assert")); ++ requireFs.provide("buffer", require("buffer")); ++ requireFs.provide("child_process", childProcessModule); ++ requireFs.provide("fs", require("fs")); ++ requireFs.provide("http", require("http")); ++ requireFs.provide("os", require("os")); ++ requireFs.provide("path", pathModule); ++ requireFs.provide("util", require("util")); ++ requireFs.provide("stream", require("stream")); ++ requireFs.provide("events", require("events")); ++ requireFs.provide("tty", require("tty")); ++ ++ res(requireFs.require(mainPath)); ++ }); ++ } else { ++ try { ++ // This is done to remove extensions ++ // logging getting caught in stdio ++ Object.keys(global.console).forEach((k) => { ++ global.console[k] = () => { ++ // oldErr(...msg); ++ }; ++ }); ++ ++ // This variable is injected at the beginning of the output file. ++ // @ts-ignore ++ r = externalNodeRequire(modulePath); ++ } catch (e) { ++ logService.error("Module:", modulePath, e.stack); ++ oldErr(`Module: ${modulePath}\n` + e.stack + "\n"); ++ return TPromise.wrapError(e); ++ } finally { ++ activationTimesBuilder.codeLoadingStop(); ++ } + } + return TPromise.as(r); + } +diff --git a/src/vs/workbench/api/node/extHostOutputService.ts b/src/vs/workbench/api/node/extHostOutputService.ts +index 1fd525bf8a..f2ff8ed7e7 100644 +--- a/src/vs/workbench/api/node/extHostOutputService.ts ++++ b/src/vs/workbench/api/node/extHostOutputService.ts +@@ -7,7 +7,7 @@ + import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol'; + import * as vscode from 'vscode'; + import { URI } from 'vs/base/common/uri'; +-import { posix } from 'path'; ++import * as path from 'path'; + import { OutputAppender } from 'vs/platform/output/node/outputAppender'; + import { toLocalISOString } from 'vs/base/common/date'; + +@@ -84,7 +84,7 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann + + constructor(name: string, outputDir: string, proxy: MainThreadOutputServiceShape) { + const fileName = `${ExtHostOutputChannelBackedByFile._namePool++}-${name}`; +- const file = URI.file(posix.join(outputDir, `${fileName}.log`)); ++ const file = URI.file(path.join(outputDir, `${fileName}.log`)); + + super(name, false, file, proxy); + this._appender = new OutputAppender(fileName, file.fsPath); +@@ -113,7 +113,7 @@ export class ExtHostOutputService { + private _outputDir: string; + + constructor(logsLocation: URI, mainContext: IMainContext) { +- this._outputDir = posix.join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); ++ this._outputDir = path.join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); + this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService); + } + +diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +index 4cc7e4d150..543c47931c 100644 +--- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts ++++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +@@ -682,10 +682,12 @@ export class FileIndexSearchManager { + } + + private preventCancellation(promise: CancelablePromise): CancelablePromise { ++ // @ts-ignore + return new class implements CancelablePromise { + cancel() { + // Do nothing + } ++ // @ts-ignore + then(resolve, reject) { + return promise.then(resolve, reject); + } +diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts +index 82b27ae509..02d90fe31d 100644 +--- a/src/vs/workbench/api/node/extHostTerminalService.ts ++++ b/src/vs/workbench/api/node/extHostTerminalService.ts +@@ -14,6 +14,7 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration + import { ILogService } from 'vs/platform/log/common/log'; + import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal'; + import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess'; ++import { generate as generateTerminalId } from 'vs/workbench/parts/terminal/common/terminalId'; + + const RENDERER_NO_PROCESS_ID = -1; + +@@ -396,7 +397,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { + + // Fork the process and listen for messages + this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); +- this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env); ++ this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, generateTerminalId()); + this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); + this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); + this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data)); +diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts +index 94b601a515..c989563804 100644 +--- a/src/vs/workbench/browser/dnd.ts ++++ b/src/vs/workbench/browser/dnd.ts +@@ -34,6 +34,7 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co + import { Disposable } from 'vs/base/common/lifecycle'; + import { addDisposableListener, EventType } from 'vs/base/browser/dom'; + import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; ++import { Upload } from 'coder/upload'; + + export interface IDraggedResource { + resource: URI; +@@ -164,14 +165,43 @@ export class ResourcesDropHandler { + @IBackupFileService private backupFileService: IBackupFileService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IEditorService private editorService: IEditorService, +- @IConfigurationService private configurationService: IConfigurationService ++ @IConfigurationService private configurationService: IConfigurationService, ++ @IInstantiationService private instantiationService: IInstantiationService + ) { + } + + handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void { ++ // NOTE@coder: modified to work with browser uploads. ++ const handleBrowserUploads = (): void => { ++ const uploader = this.instantiationService.createInstance(Upload); ++ uploader.uploadDropped(event).then((paths) => { ++ // Add external ones to recently open list unless dropped resource is a workspace ++ const uris = paths.map((p) => URI.file(p)); ++ if (uris.length) { ++ this.windowsService.addRecentlyOpened(uris); ++ } ++ ++ const editors: IResourceEditor[] = uris.map(uri => ({ ++ resource: uri, ++ options: { ++ pinned: true, ++ index: targetIndex, ++ } ++ })); ++ ++ // Open in Editor ++ const targetGroup = resolveTargetGroup(); ++ return this.editorService.openEditors(editors, targetGroup).then(() => { ++ ++ // Finish with provided function ++ afterDrop(targetGroup); ++ }); ++ }); ++ }; ++ + const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled); + if (!untitledOrFileResources.length) { +- return; ++ return handleBrowserUploads(); + } + + // Make the window active to handle the drop properly within +diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts +index 2aee2d4e31..dfeb5cf5dc 100644 +--- a/src/vs/workbench/browser/labels.ts ++++ b/src/vs/workbench/browser/labels.ts +@@ -211,6 +211,7 @@ export class ResourceLabel extends IconLabel { + iconLabelOptions.extraClasses.push(...this.options.extraClasses); + } + ++ let element: HTMLElement; + if (this.options && this.options.fileDecorations && resource) { + const deco = this.decorationsService.getDecoration( + resource, +@@ -230,10 +231,14 @@ export class ResourceLabel extends IconLabel { + if (this.options.fileDecorations.badges) { + iconLabelOptions.extraClasses.push(deco.badgeClassName); + } ++ ++ if (deco.element) { ++ element = deco.element; ++ } + } + } + +- this.setValue(label, this.label.description, iconLabelOptions); ++ this.setValue(label, this.label.description, iconLabelOptions, element); + + this._onDidRender.fire(); + } +diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts +index 750950e953..3efcb01518 100644 +--- a/src/vs/workbench/browser/layout.ts ++++ b/src/vs/workbench/browser/layout.ts +@@ -28,7 +28,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; + import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; + import { getZoomFactor } from 'vs/base/browser/browser'; + +-const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30; ++const TITLE_BAR_HEIGHT = /*isMacintosh ? 22 : */30; + const STATUS_BAR_HEIGHT = 22; + const ACTIVITY_BAR_WIDTH = 50; + +diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css +index fc87a52eaf..e3d9e6bc5d 100644 +--- a/src/vs/workbench/browser/media/part.css ++++ b/src/vs/workbench/browser/media/part.css +@@ -34,8 +34,8 @@ + font-size: 11px; + cursor: default; + font-weight: normal; +- -webkit-margin-before: 0; +- -webkit-margin-after: 0; ++ margin-top: 0; ++ margin-bottom: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +index 02499b9f40..69102d2ea8 100644 +--- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts ++++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +@@ -172,7 +172,7 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction { + super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, partService, telemetryService); + + const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar +- DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl || ''}') no-repeat 50% 50%`); ++ DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl ? iconUrl.toString(true) : ''}') no-repeat 50% 50%`); + } + + setActivity(activity: IActivity): void { +@@ -253,4 +253,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + `); + } + } +-}); +\ No newline at end of file ++}); +diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +index 4ef749c3fd..45f003e2fd 100644 +--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts ++++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +@@ -32,6 +32,9 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten + import { URI } from 'vs/base/common/uri'; + import { ToggleCompositePinnedAction, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; + import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; ++import { getFetchUri } from 'coder/api'; ++import { ToggleFasttimeAction } from 'coder/contributions/fasttime'; ++import { CreateNewTerminalAction, ToggleTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; + + interface IPlaceholderComposite { + id: string; +@@ -73,7 +76,11 @@ export class ActivitybarPart extends Part { + getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction, + getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, + getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)), +- getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))], ++ getContextMenuActions: () => [ ++ this.instantiationService.createInstance(ToggleTerminalAction, ToggleTerminalAction.ID, nls.localize('createNewTerminal', "Toggle Terminal")), ++ this.instantiationService.createInstance(ToggleFasttimeAction, ToggleFasttimeAction.ID, nls.localize('toggleFasttime', "Toggle Fast Time")), ++ this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivityBar', "Hide Activity Bar")), ++ ], + getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(), + hidePart: () => this.partService.setSideBarHidden(true), + compositeSize: 50, +@@ -85,7 +92,7 @@ export class ActivitybarPart extends Part { + this.placeholderComposites = JSON.parse(previousState); + this.placeholderComposites.forEach((s) => { + if (typeof s.iconUrl === 'object') { +- s.iconUrl = URI.revive(s.iconUrl); ++ s.iconUrl = getFetchUri(URI.revive(s.iconUrl)); + } else { + s.iconUrl = void 0; + } +diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +index 5a92b2e1f5..1d3c735e75 100644 +--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css ++++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +@@ -4,25 +4,130 @@ + *--------------------------------------------------------------------------------------------*/ + + .monaco-workbench > .part.activitybar { +- width: 50px; ++ width: 50px; + } + + .monaco-workbench > .activitybar > .content { +- height: 100%; +- display: flex; +- flex-direction: column; +- justify-content: space-between; ++ height: 100%; ++ display: flex; ++ flex-direction: column; ++ justify-content: space-between; + } + + .monaco-workbench > .activitybar > .content .monaco-action-bar { +- text-align: left; +- background-color: inherit; ++ text-align: left; ++ background-color: inherit; + } + + .monaco-workbench > .activitybar .action-item:focus { +- outline: 0 !important; /* activity bar indicates focus custom */ ++ outline: 0 !important; /* activity bar indicates focus custom */ + } + + .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-label.toggle-more { +- -webkit-mask: url('ellipsis-global.svg') no-repeat 50% 50%; +-} +\ No newline at end of file ++ -webkit-mask: url("ellipsis-global.svg") no-repeat 50% 50%; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar { ++ flex: 1; ++ display: flex; ++ flex-direction: column; ++ overflow: visible; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal { ++ transition: 500ms color ease; ++ opacity: 0.65; ++ filter: brightness(115%); ++ padding-top: 10px; ++ padding-bottom: 10px; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal:hover { ++ opacity: 1; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal.disabled { ++ cursor: disabled; ++ opacity: 0.45 !important; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal > .icon { ++ text-align: center; ++ display: block; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal > .icon > svg { ++ width: 29px; ++ fill: currentColor; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime { ++ transition: 500ms color ease; ++ opacity: 0.65; ++ filter: brightness(115%); ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime:hover { ++ opacity: 1; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime.disabled { ++ cursor: disabled; ++ opacity: 0.45 !important; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .icon { ++ text-align: center; ++ display: block; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .icon > svg { ++ width: 22px; ++ fill: currentColor; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .text { ++ font-size: 12px; ++ text-align: center; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .text.unknown { ++ font-size: 8px; ++ opacity: 0; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar > .feedback { ++ transition: 500ms color ease; ++ padding-top: 10px; ++ padding-bottom: 10px; ++ margin-left: 0px; ++ margin-top: auto; ++ flex: 0; ++ cursor: default; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .feedback > .icon { ++ text-align: center; ++ display: block; ++ opacity: 0.65; ++ filter: brightness(115%); ++ cursor: pointer; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .feedback .feedback { ++ position: initial; ++ margin-left: 0px; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .feedback .feedback-dropdown { ++ bottom: -63px; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .feedback:hover > .icon { ++ opacity: 1; ++} ++ ++.monaco-workbench .activitybar > .content > .extras-bar .feedback > .icon > svg { ++ width: 29px; ++ fill: currentColor; ++} +diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts +index 3cc6b830a8..51cf0560ac 100644 +--- a/src/vs/workbench/browser/parts/compositeBar.ts ++++ b/src/vs/workbench/browser/parts/compositeBar.ts +@@ -22,7 +22,16 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView + import { Widget } from 'vs/base/browser/ui/widget'; + import { isUndefinedOrNull } from 'vs/base/common/types'; + import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +-import { ITheme } from 'vs/platform/theme/common/themeService'; ++import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; ++import { IFasttimeService, ToggleFasttimeAction } from 'coder/contributions/fasttime'; ++import { personalAccount } from 'coder/api'; ++import { fasttimeIcon, feedbackIcon, terminalIcon } from 'coder/asset'; ++import { Feedback, FastTimeState } from 'coder/common'; ++import { errorForeground, descriptionForeground, focusBorder, inputValidationWarningBorder } from 'vs/platform/theme/common/colorRegistry'; ++import { Color } from 'vs/base/common/color'; ++import { ICommandService } from 'vs/platform/commands/common/commands'; ++import { ToggleTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; ++import { IFeedbackService, ShowFeedbackAction, FeedbackState } from 'coder/contributions/feedback'; + + export interface ICompositeBarOptions { + icon: boolean; +@@ -58,7 +67,11 @@ export class CompositeBar extends Widget implements ICompositeBar { + private options: ICompositeBarOptions, + @IInstantiationService private instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, +- @IContextMenuService private contextMenuService: IContextMenuService ++ @IContextMenuService private contextMenuService: IContextMenuService, ++ @IFasttimeService private fasttimeService: IFasttimeService, ++ @IFeedbackService private feedbackService: IFeedbackService, ++ @IThemeService private themeService: IThemeService, ++ @ICommandService private commandService: ICommandService, + ) { + super(); + +@@ -105,6 +118,101 @@ export class CompositeBar extends Widget implements ICompositeBar { + } + })); + ++ if (this.options.orientation === ActionsOrientation.VERTICAL) { ++ const extrasDiv = parent.appendChild($('.extras-bar.monaco-action-bar')); ++ { ++ // Terminal Icon ++ const toggleItem = extrasDiv.appendChild($('.action-item.toggle-terminal')); ++ toggleItem.title = "Toggle Terminal"; ++ const toggleIcon = toggleItem.appendChild($('.icon')); ++ toggleIcon.innerHTML = terminalIcon; ++ toggleItem.addEventListener("click", () => { ++ this.instantiationService.createInstance(ToggleTerminalAction, ToggleTerminalAction.ID, "Toggle Terminal").run(); ++ }); ++ } ++ { ++ // Fasttime Icon ++ const toggleItem = extrasDiv.appendChild($('.action-item.fasttime')); ++ toggleItem.title = "Toggle Fast Time"; ++ const toggleIcon = toggleItem.appendChild($('.icon')); ++ toggleIcon.innerHTML = fasttimeIcon; ++ const toggleText = toggleItem.appendChild($('.text')); ++ toggleItem.addEventListener("click", async () => { ++ if (toggleItem.classList.contains("disabled")) { ++ return; ++ } ++ toggleItem.classList.add("disabled"); ++ try { ++ await this.commandService.executeCommand(ToggleFasttimeAction.ID); ++ } catch (ex) { ++ toggleItem.classList.remove("disabled"); ++ } ++ }); ++ ++ const updateLook = (state: FastTimeState): void => { ++ let fill: Color | string; ++ let text: string; ++ const theme = this.themeService.getTheme(); ++ switch (state) { ++ case FastTimeState.Enabled: ++ fill = "rgb(82, 139, 255)"; // theme.getColor(focusBorder); ++ text = "On"; ++ break; ++ case FastTimeState.Disabled: ++ fill = theme.getColor(descriptionForeground); ++ text = "Off"; ++ break; ++ case FastTimeState.Glowing: ++ fill = theme.getColor(inputValidationWarningBorder); ++ text = "Off"; ++ break; ++ case FastTimeState.Out: ++ fill = theme.getColor(errorForeground); ++ text = "Out"; ++ break; ++ case FastTimeState.NotSet: ++ fill = "transparent"; ++ text = "Unknown"; ++ break; ++ default: ++ throw new Error("not sure how to handle look for state"); ++ } ++ ++ if (state === FastTimeState.NotSet) { ++ toggleText.classList.add("unknown"); ++ } else { ++ toggleText.classList.remove("unknown"); ++ } ++ ++ toggleText.innerText = text; ++ toggleItem.classList.remove("disabled"); ++ toggleItem.style.color = fill.toString(); ++ }; ++ this.fasttimeService.onDidStateChange(updateLook); ++ this.themeService.onThemeChange(() => updateLook(this.fasttimeService.state)); ++ updateLook(this.fasttimeService.state); ++ } ++ ++ { ++ // Feedback Icon ++ const toggleItem = extrasDiv.appendChild($('.action-item.feedback')); ++ toggleItem.title = "Show/Hide Feedback"; ++ const toggleIcon = toggleItem.appendChild($('.icon')); ++ toggleIcon.innerHTML = feedbackIcon; ++ const feedback = new Feedback({ ++ account: personalAccount, ++ }); ++ toggleItem.addEventListener("click", async () => { ++ await this.commandService.executeCommand(ShowFeedbackAction.ID); ++ }); ++ toggleItem.appendChild(feedback.domNode); ++ ++ this.feedbackService.onDidShow(() => { ++ feedback.open(); ++ }); ++ } ++ } ++ + return actionBarDiv; + } + +@@ -285,7 +393,8 @@ export class CompositeBar extends Widget implements ICompositeBar { + let overflows = false; + let maxVisible = compositesToShow.length; + let size = 0; +- const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height : this.dimension.width; ++ // Subtract 100 from this so toggle terminal and fasttime show ++ const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height - 100 : this.dimension.width; + for (let i = 0; i < compositesToShow.length && size <= limit; i++) { + size += this.compositeSizeInBar.get(compositesToShow[i]); + if (size > limit) { +@@ -368,6 +477,7 @@ export class CompositeBar extends Widget implements ICompositeBar { + () => this.getOverflowingComposites(), + () => this.model.activeItem ? this.model.activeItem.id : void 0, + (compositeId: string) => { ++ console.log("processing", compositeId); + const item = this.model.findItem(compositeId); + return item && item.activity[0] && item.activity[0].badge; + }, +diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts +index 5363857d45..05f22f5d14 100644 +--- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts ++++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts +@@ -442,22 +442,23 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands. + + interface IEditorToolItem { id: string; title: string; iconDark: string; iconLight: string; } + ++const context = (require as any).context("./media/", true, /.*/); + function appendEditorToolItem(primary: IEditorToolItem, alternative: IEditorToolItem, when: ContextKeyExpr, order: number): void { + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: primary.id, + title: primary.title, + iconLocation: { +- dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconDark}`)), +- light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconLight}`)) ++ dark: URI.parse(context(`./${primary.iconDark}`)), ++ light: URI.parse(context(`./${primary.iconLight}`)) + } + }, + alt: { + id: alternative.id, + title: alternative.title, + iconLocation: { +- dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconDark}`)), +- light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconLight}`)) ++ dark: URI.parse(context(`./${alternative.iconDark}`)), ++ light: URI.parse(context(`./${alternative.iconLight}`)) + } + }, + group: 'navigation', +diff --git a/src/vs/workbench/browser/parts/editor/media/editorstatus.css b/src/vs/workbench/browser/parts/editor/media/editorstatus.css +index 204d01c7f1..46a3443c94 100644 +--- a/src/vs/workbench/browser/parts/editor/media/editorstatus.css ++++ b/src/vs/workbench/browser/parts/editor/media/editorstatus.css +@@ -67,6 +67,7 @@ + padding-right: 12px; + margin-right: 5px; + max-width: fit-content; ++ max-width: -moz-fit-content; + } + + .monaco-shell.vs .screen-reader-detected-explanation .cancel { +diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +index 020706de84..3657992030 100644 +--- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css ++++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +@@ -49,6 +49,7 @@ + .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { + width: 120px; + min-width: fit-content; ++ min-width: -moz-fit-content; + } + + .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink { +@@ -56,6 +57,7 @@ + flex-basis: 0; /* all tabs are even */ + flex-grow: 1; /* all tabs grow even */ + max-width: fit-content; ++ max-width: -moz-fit-content; + } + + .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after, +diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +index 24552a04bd..44b6ca9162 100644 +--- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css ++++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +@@ -130,6 +130,7 @@ + + .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button { + max-width: fit-content; ++ max-width: -moz-fit-content; + padding: 5px 10px; + margin-left: 10px; + font-size: 12px; +diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +index aae7522e58..733800a5bb 100644 +--- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css ++++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +@@ -60,6 +60,84 @@ + font-size: inherit; + } + ++.monaco-workbench > .part.titlebar > .collaborators { ++ display: flex; ++ flex-direction: row; ++} ++ ++@keyframes collaborator-in { ++ from { ++ opacity: 0; ++ transform: translateX(15px); ++ } ++ to { ++ opacity: 0.75; ++ transform: translateX(0px); ++ } ++} ++ ++.monaco-workbench > .part.titlebar > .collaborators > .collaborator { ++ width: 27px; ++ height: 27px; ++ box-sizing: border-box; ++ border-radius: 50%; ++ border-color: currentColor; ++ border-width: 3px; ++ border-style: solid; ++ opacity: 0.75; ++ margin-right: 5px; ++ transition: 150ms opacity ease; ++ box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.5); ++ position: relative; ++} ++ ++@keyframes collaborator-active { ++ 0% { ++ transform: scale(1); ++ } ++ 50% { ++ transform: scale(1.1); ++ } ++ 100% { ++ transform: scale(1); ++ } ++} ++ ++.monaco-workbench > .part.titlebar > .collaborators > .collaborator.active { ++ animation-name: collaborator-active; ++ animation-duration: 0.3s; ++} ++ ++@keyframes collaborator-inner-active { ++ 0% { ++ transform: scale(0); ++ opacity: 1; ++ } ++ 100% { ++ transform: scale(1); ++ opacity: 0; ++ } ++} ++ ++.monaco-workbench > .part.titlebar > .collaborators > .collaborator.active::before { ++ position: absolute; ++ top: 0px; ++ left: 0px; ++ right: 0px; ++ bottom: 0px; ++ border-radius: 50%; ++ background-color: currentColor; ++ content: " "; ++ animation-name: collaborator-inner-active; ++ animation-fill-mode: forwards; ++ animation-duration: 0.3s; ++} ++ ++.monaco-workbench > .part.titlebar > .collaborators > .collaborator > img { ++ border-radius: 50%; ++ width: 100%; ++} ++ + .monaco-workbench.windows > .part.titlebar > .resizer, + .monaco-workbench.linux > .part.titlebar > .resizer { + -webkit-app-region: no-drag; +@@ -76,17 +154,28 @@ + + + .monaco-workbench > .part.titlebar > .window-appicon { +- width: 35px; ++ width: 45px; ++ padding-top: 5px; ++ padding-left: 3px; ++ padding-right: 3px; + height: 100%; + position: relative; + z-index: 99; +- background-image: url('code-icon.svg'); +- background-repeat: no-repeat; +- background-position: center center; +- background-size: 16px; ++ opacity: 0.5; ++ transition: 150ms opacity ease; ++ cursor: pointer; ++ /* fill: white; */ ++ /* background-image: url('code-icon.svg'); */ ++ /* background-repeat: no-repeat; */ ++ /* background-position: center center; */ ++ /* background-size: 16px; */ + flex-shrink: 0; + } + ++.monaco-workbench > .part.titlebar > .window-appicon:hover { ++ opacity: 1; ++} ++ + .monaco-workbench.fullscreen > .part.titlebar > .window-appicon { + display: none; + } +diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +index ec136a4539..1f45dfec44 100644 +--- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts ++++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +@@ -141,9 +141,9 @@ export class MenubarControl extends Disposable { + 'Help': this._register(this.menuService.createMenu(MenuId.MenubarHelpMenu, this.contextKeyService)) + }; + +- if (isMacintosh) { +- this.topLevelMenus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); +- } ++ // if (isMacintosh) { ++ // this.topLevelMenus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); ++ // } + + this.menuUpdater = this._register(new RunOnceScheduler(() => this.doSetupMenubar(), 200)); + +@@ -155,7 +155,7 @@ export class MenubarControl extends Disposable { + this._onVisibilityChange = this._register(new Emitter()); + this._onFocusStateChange = this._register(new Emitter()); + +- if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') { ++ if (this.currentTitlebarStyleSetting !== 'custom') { + for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { + this._register(this.topLevelMenus[topLevelMenuName].onDidChange(() => this.setupMenubar())); + } +@@ -454,7 +454,7 @@ export class MenubarControl extends Disposable { + this._register(this.keybindingService.onDidUpdateKeybindings(() => this.setupMenubar())); + + // These listeners only apply when the custom menubar is being used +- if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { ++ if (this.currentTitlebarStyleSetting === 'custom') { + // Listen to fullscreen changes + this._register(browser.onDidChangeFullscreen(() => this.onDidChangeFullscreen())); + +@@ -467,7 +467,7 @@ export class MenubarControl extends Disposable { + } + + private doSetupMenubar(): void { +- if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { ++ if (this.currentTitlebarStyleSetting === 'custom') { + this.setupCustomMenubar(); + } + +@@ -652,7 +652,7 @@ export class MenubarControl extends Disposable { + return; + } + +- this.container.attributes['role'] = 'menubar'; ++ this.container.setAttribute('role', 'menubar'); + + const firstTimeSetup = this.customMenus === undefined; + if (firstTimeSetup) { +@@ -1136,7 +1136,7 @@ export class MenubarControl extends Disposable { + if (this.container) { + this.doSetupMenubar(); + +- if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { ++ if (this.currentTitlebarStyleSetting === 'custom') { + this.setUnfocusedState(); + } + } +diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +index 8159c8e4eb..ad2d1ab6a9 100644 +--- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts ++++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +@@ -35,6 +35,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti + import { template, getBaseLabel } from 'vs/base/common/labels'; + import { ILabelService } from 'vs/platform/label/common/label'; + import { Event } from 'vs/base/common/event'; ++import { logoArrow } from 'coder/asset'; ++import { client } from 'coder/client'; ++import { ICollaborator } from 'coder/common'; + + export class TitlebarPart extends Part implements ITitleService { + +@@ -48,6 +51,7 @@ export class TitlebarPart extends Part implements ITitleService { + + private titleContainer: HTMLElement; + private title: HTMLElement; ++ private collaborators: HTMLElement; + private dragRegion: HTMLElement; + private windowControls: HTMLElement; + private maxRestoreControl: HTMLElement; +@@ -120,7 +124,7 @@ export class TitlebarPart extends Part implements ITitleService { + } + + private onMenubarVisibilityChanged(visible: boolean) { +- if (isWindows || isLinux) { ++ if (isMacintosh || isWindows || isLinux) { + // Hide title when toggling menu bar + if (this.configurationService.getValue('window.menuBarVisibility') === 'toggle' && visible) { + // Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor +@@ -133,7 +137,7 @@ export class TitlebarPart extends Part implements ITitleService { + } + + private onMenubarFocusChanged(focused: boolean) { +- if (isWindows || isLinux) { ++ if (isMacintosh || isWindows || isLinux) { + if (focused) { + hide(this.dragRegion); + } else { +@@ -291,9 +295,15 @@ export class TitlebarPart extends Part implements ITitleService { + this.dragRegion = append(this.titleContainer, $('div.titlebar-drag-region')); + + // App Icon (Windows/Linux) +- if (!isMacintosh) { +- this.appIcon = append(this.titleContainer, $('div.window-appicon')); +- } ++ // if (!isMacintosh) { ++ // this.appIcon = append(this.titleContainer, $('div.window-appicon')); ++ // } ++ ++ this.appIcon = append(this.titleContainer, $('div.window-appicon')); ++ this.appIcon.innerHTML = logoArrow; ++ this.appIcon.addEventListener("click", () => { ++ window.location.href = "/projects"; ++ }); + + // Menubar: the menubar part which is responsible for populating both the custom and native menubars + this.menubarPart = this.instantiationService.createInstance(MenubarControl); +@@ -302,10 +312,10 @@ export class TitlebarPart extends Part implements ITitleService { + + this.menubarPart.create(this.menubar); + +- if (!isMacintosh) { ++ // if (!isMacintosh) { + this._register(this.menubarPart.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); + this._register(this.menubarPart.onFocusStateChange(e => this.onMenubarFocusChanged(e))); +- } ++ // } + + // Title + this.title = append(this.titleContainer, $('div.window-title')); +@@ -315,6 +325,50 @@ export class TitlebarPart extends Part implements ITitleService { + this.doUpdateTitle(); + } + ++ this.collaborators = append(this.titleContainer, $('div.collaborators')); ++ const collaboratorDivs = new Map(); ++ ++ Promise.all([client.api, client.localCollaboratorsService]).then(([api, service]) => { ++ const registerCollaborator = (collaborator: ICollaborator) => { ++ const cd = append(this.collaborators, $('div.collaborator')); ++ cd.style.color = collaborator.color; ++ cd.id = collaborator.nonce; ++ const img = append(cd, $('img')) as HTMLImageElement; ++ img.src = api.avatars.get(collaborator.organizationId, 32); ++ ++ collaboratorDivs.set(collaborator.nonce, cd); ++ }; ++ service.collaborators.forEach((c) => registerCollaborator(c)); ++ const pulseActive = (id: string): void => { ++ const cd = collaboratorDivs.get(id); ++ if (cd) { ++ cd.classList.remove("active"); ++ setTimeout(() => { ++ cd.classList.add("active"); ++ }); ++ } ++ }; ++ this._register(service.onAdded(registerCollaborator)); ++ this._register(service.onRemoved((collaborator) => { ++ if (collaboratorDivs.has(collaborator.nonce)) { ++ collaboratorDivs.get(collaborator.nonce).remove(); ++ collaboratorDivs.delete(collaborator.nonce); ++ } ++ })); ++ this._register(service.onChangeActiveFile((event) => { ++ pulseActive(event.collaborator.nonce); ++ })); ++ this._register(service.onChangeCursorPosition((event) => { ++ pulseActive(event.collaborator.nonce); ++ })); ++ this._register(service.onOpenFile((event) => { ++ pulseActive(event.collaborator.nonce); ++ })); ++ this._register(service.onCloseFile((event) => { ++ pulseActive(event.collaborator.nonce); ++ })); ++ }); ++ + // Maximize/Restore on doubleclick + if (isMacintosh) { + this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, e => { +@@ -336,48 +390,48 @@ export class TitlebarPart extends Part implements ITitleService { + }); + + // Window Controls (Windows/Linux) +- if (!isMacintosh) { +- this.windowControls = append(this.titleContainer, $('div.window-controls-container')); +- +- +- // Minimize +- const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg')); +- const minimizeIcon = append(minimizeIconContainer, $('div.window-icon')); +- addClass(minimizeIcon, 'window-minimize'); +- this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { +- this.windowService.minimizeWindow(); +- })); +- +- // Restore +- const restoreIconContainer = append(this.windowControls, $('div.window-icon-bg')); +- this.maxRestoreControl = append(restoreIconContainer, $('div.window-icon')); +- addClass(this.maxRestoreControl, 'window-max-restore'); +- this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, e => { +- this.windowService.isMaximized().then((maximized) => { +- if (maximized) { +- return this.windowService.unmaximizeWindow(); +- } +- +- return this.windowService.maximizeWindow(); +- }); +- })); +- +- // Close +- const closeIconContainer = append(this.windowControls, $('div.window-icon-bg')); +- addClass(closeIconContainer, 'window-close-bg'); +- const closeIcon = append(closeIconContainer, $('div.window-icon')); +- addClass(closeIcon, 'window-close'); +- this._register(addDisposableListener(closeIcon, EventType.CLICK, e => { +- this.windowService.closeWindow(); +- })); +- +- // Resizer +- this.resizer = append(this.titleContainer, $('div.resizer')); +- +- const isMaximized = this.windowService.getConfiguration().maximized ? true : false; +- this.onDidChangeMaximized(isMaximized); +- this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this); +- } ++ // if (!isMacintosh) { ++ // this.windowControls = append(this.titleContainer, $('div.window-controls-container')); ++ ++ ++ // // Minimize ++ // const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg')); ++ // const minimizeIcon = append(minimizeIconContainer, $('div.window-icon')); ++ // addClass(minimizeIcon, 'window-minimize'); ++ // this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { ++ // this.windowService.minimizeWindow(); ++ // })); ++ ++ // // Restore ++ // const restoreIconContainer = append(this.windowControls, $('div.window-icon-bg')); ++ // this.maxRestoreControl = append(restoreIconContainer, $('div.window-icon')); ++ // addClass(this.maxRestoreControl, 'window-max-restore'); ++ // this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, e => { ++ // this.windowService.isMaximized().then((maximized) => { ++ // if (maximized) { ++ // return this.windowService.unmaximizeWindow(); ++ // } ++ ++ // return this.windowService.maximizeWindow(); ++ // }); ++ // })); ++ ++ // // Close ++ // const closeIconContainer = append(this.windowControls, $('div.window-icon-bg')); ++ // addClass(closeIconContainer, 'window-close-bg'); ++ // const closeIcon = append(closeIconContainer, $('div.window-icon')); ++ // addClass(closeIcon, 'window-close'); ++ // this._register(addDisposableListener(closeIcon, EventType.CLICK, e => { ++ // this.windowService.closeWindow(); ++ // })); ++ ++ // // Resizer ++ // this.resizer = append(this.titleContainer, $('div.resizer')); ++ ++ // const isMaximized = this.windowService.getConfiguration().maximized ? true : false; ++ // this.onDidChangeMaximized(isMaximized); ++ // this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this); ++ // } + + // Since the title area is used to drag the window, we do not want to steal focus from the + // currently active element. So we restore focus after a timeout back to where it was. +@@ -529,14 +583,14 @@ export class TitlebarPart extends Part implements ITitleService { + } + + // Only prevent zooming behavior on macOS or when the menubar is not visible +- if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { ++ if (false && this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { + // To prevent zooming we need to adjust the font size with the zoom factor + const newHeight = this.initialSizing.titlebarHeight / getZoomFactor(); + this.title.style.fontSize = `${this.initialSizing.titleFontSize / getZoomFactor()}px`; + this.title.style.lineHeight = `${newHeight}px`; + + // Windows/Linux specific layout +- if (isWindows || isLinux) { ++ if ((isWindows || isLinux) && this.appIcon) { + if (typeof this.initialSizing.controlsWidth !== 'number') { + this.initialSizing.controlsWidth = parseInt(getComputedStyle(this.windowControls).width, 10); + } +@@ -569,12 +623,16 @@ export class TitlebarPart extends Part implements ITitleService { + this.title.style.fontSize = null; + this.title.style.lineHeight = null; + +- this.appIcon.style.width = null; +- this.appIcon.style.backgroundSize = null; +- this.appIcon.style.paddingTop = null; +- this.appIcon.style.paddingBottom = null; ++ if (this.appIcon) { ++ this.appIcon.style.width = null; ++ this.appIcon.style.backgroundSize = null; ++ this.appIcon.style.paddingTop = null; ++ this.appIcon.style.paddingBottom = null; ++ } + +- this.windowControls.style.width = null; ++ if (this.windowControls) { ++ this.windowControls.style.width = null; ++ } + } + + if (this.menubarPart) { +@@ -605,8 +663,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); + if (titlebarActiveFg) { + collector.addRule(` +- .monaco-workbench > .part.titlebar > .window-controls-container .window-icon { +- background-color: ${titlebarActiveFg}; ++ .monaco-workbench > .part.titlebar > .window-appicon { ++ fill: ${titlebarActiveFg}; + } + `); + } +@@ -614,8 +672,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const titlebarInactiveFg = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND); + if (titlebarInactiveFg) { + collector.addRule(` +- .monaco-workbench > .part.titlebar.inactive > .window-controls-container .window-icon { +- background-color: ${titlebarInactiveFg}; ++ .monaco-workbench > .part.titlebar.inactive > .window-appicon { ++ fill: ${titlebarInactiveFg}; + } + `); + } +diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts +index c913fc9e6c..3b73e420a0 100644 +--- a/src/vs/workbench/browser/parts/views/customView.ts ++++ b/src/vs/workbench/browser/parts/views/customView.ts +@@ -35,6 +35,7 @@ import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; + import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; + import { IMouseEvent } from 'vs/base/browser/mouseEvent'; + import { localize } from 'vs/nls'; ++import { getFetchUri } from 'coder/api'; + + export class CustomTreeViewPanel extends ViewletPanel { + +@@ -508,7 +509,7 @@ class TreeRenderer implements IRenderer { + const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; + const label = node.label ? node.label : resource ? basename(resource.path) : ''; + const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; +- const iconUrl = icon ? URI.revive(icon) : null; ++ const iconUrl = icon ? getFetchUri(URI.revive(icon)) : null; + const title = node.tooltip ? node.tooltip : resource ? void 0 : label; + + // reset +diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css +index 9691ecbdeb..0c34aee9c9 100644 +--- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css ++++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css +@@ -8,6 +8,6 @@ + text-overflow: ellipsis; + overflow: hidden; + font-size: 11px; +- -webkit-margin-before: 0; +- -webkit-margin-after: 0; ++ margin-top: 0; ++ margin-bottom: 0; + } +diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts +index b4a27fe133..e0fceac656 100644 +--- a/src/vs/workbench/electron-browser/main.contribution.ts ++++ b/src/vs/workbench/electron-browser/main.contribution.ts +@@ -40,14 +40,14 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickS + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); + + const isLocal = FileDialogContext.isEqualTo('local'); +-if (isMacintosh) { +- workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, isLocal), 'File: Open...', fileCategory, isLocal); +-} else { +- workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, isLocal), 'File: Open File...', fileCategory, isLocal); +- workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, isLocal), 'File: Open Folder...', fileCategory, isLocal); +-} +- +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); ++// if (isMacintosh) { ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, isLocal), 'File: Open...', fileCategory, isLocal); ++// } else { ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, isLocal), 'File: Open File...', fileCategory, isLocal); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, isLocal), 'File: Open Folder...', fileCategory, isLocal); ++// } ++ ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); + if (!!product.reportIssueUrl) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenIssueReporterAction, OpenIssueReporterAction.ID, OpenIssueReporterAction.LABEL), 'Help: Open Issue Reporter', helpCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory); +@@ -73,7 +73,7 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTw + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), 'Help: About', helpCategory); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), 'Help: About', helpCategory); + + workbenchActionsRegistry.registerWorkbenchAction( + new SyncActionDescriptor(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { +@@ -108,28 +108,28 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(Increa + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, null), 'View: Decrease Current View Size', viewCategory); + + const workspacesCategory = nls.localize('workspaces', "Workspaces"); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, isLocal); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, isLocal); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory, isLocal); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); +- +-CommandsRegistry.registerCommand(OpenWorkspaceConfigFileAction.ID, serviceAccessor => { +- serviceAccessor.get(IInstantiationService).createInstance(OpenWorkspaceConfigFileAction, OpenWorkspaceConfigFileAction.ID, OpenWorkspaceConfigFileAction.LABEL).run(); +-}); +-MenuRegistry.appendMenuItem(MenuId.CommandPalette, { +- command: { +- id: OpenWorkspaceConfigFileAction.ID, +- title: { value: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`, original: 'Workspaces: Open Workspace Configuration File' }, +- }, +- when: new RawContextKey('workbenchState', '').isEqualTo('workspace') +-}); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, isLocal); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, isLocal); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory, isLocal); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); ++ ++// CommandsRegistry.registerCommand(OpenWorkspaceConfigFileAction.ID, serviceAccessor => { ++// serviceAccessor.get(IInstantiationService).createInstance(OpenWorkspaceConfigFileAction, OpenWorkspaceConfigFileAction.ID, OpenWorkspaceConfigFileAction.LABEL).run(); ++// }); ++// MenuRegistry.appendMenuItem(MenuId.CommandPalette, { ++// command: { ++// id: OpenWorkspaceConfigFileAction.ID, ++// title: { value: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`, original: 'Workspaces: Open Workspace Configuration File' }, ++// }, ++// when: new RawContextKey('workbenchState', '').isEqualTo('workspace') ++// }); + + // Developer related actions + const developerCategory = nls.localize('developer', "Developer"); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); +-workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); ++// workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); + + const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); + +@@ -155,92 +155,92 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ + + // Menu registration - file menu + +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '1_new', +- command: { +- id: NewWindowAction.ID, +- title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") +- }, +- order: 2 +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '2_open', +- command: { +- id: OpenFileAction.ID, +- title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") +- }, +- order: 1, +- when: ContextKeyExpr.and(IsMacContext.toNegated(), isLocal) +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '2_open', +- command: { +- id: OpenFolderAction.ID, +- title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") +- }, +- order: 2, +- when: ContextKeyExpr.and(IsMacContext.toNegated(), isLocal) +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '2_open', +- command: { +- id: OpenFileFolderAction.ID, +- title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") +- }, +- order: 1, +- when: ContextKeyExpr.and(IsMacContext, isLocal) +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '2_open', +- command: { +- id: OpenWorkspaceAction.ID, +- title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") +- }, +- order: 3, +- when: isLocal +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), +- submenu: MenuId.MenubarRecentMenu, +- group: '2_open', +- order: 4 +-}); +- +- +-// More +-MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { +- group: 'y_more', +- command: { +- id: OpenRecentAction.ID, +- title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") +- }, +- order: 1 +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '3_workspace', +- command: { +- id: ADD_ROOT_FOLDER_COMMAND_ID, +- title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") +- }, +- order: 1, +- when: isLocal +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '3_workspace', +- command: { +- id: SaveWorkspaceAsAction.ID, +- title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") +- }, +- order: 2, +- when: isLocal +-}); ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '1_new', ++// command: { ++// id: NewWindowAction.ID, ++// title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") ++// }, ++// order: 2 ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '2_open', ++// command: { ++// id: OpenFileAction.ID, ++// title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") ++// }, ++// order: 1, ++// when: ContextKeyExpr.and(IsMacContext.toNegated(), isLocal) ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '2_open', ++// command: { ++// id: OpenFolderAction.ID, ++// title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") ++// }, ++// order: 2, ++// when: ContextKeyExpr.and(IsMacContext.toNegated(), isLocal) ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '2_open', ++// command: { ++// id: OpenFileFolderAction.ID, ++// title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") ++// }, ++// order: 1, ++// when: ContextKeyExpr.and(IsMacContext, isLocal) ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '2_open', ++// command: { ++// id: OpenWorkspaceAction.ID, ++// title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") ++// }, ++// order: 3, ++// when: isLocal ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), ++// submenu: MenuId.MenubarRecentMenu, ++// group: '2_open', ++// order: 4 ++// }); ++ ++ ++// // More ++// MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { ++// group: 'y_more', ++// command: { ++// id: OpenRecentAction.ID, ++// title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") ++// }, ++// order: 1 ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '3_workspace', ++// command: { ++// id: ADD_ROOT_FOLDER_COMMAND_ID, ++// title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") ++// }, ++// order: 1, ++// when: isLocal ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '3_workspace', ++// command: { ++// id: SaveWorkspaceAsAction.ID, ++// title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") ++// }, ++// order: 2, ++// when: isLocal ++// }); + + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), +@@ -250,45 +250,45 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + when: IsMacContext.toNegated() + }); + +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '6_close', +- command: { +- id: CloseWorkspaceAction.ID, +- title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), +- precondition: new RawContextKey('workspaceFolderCount', 0).notEqualsTo('0') +- }, +- order: 3, +- when: new RawContextKey('workbenchState', '').notEqualsTo('workspace') +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '6_close', +- command: { +- id: CloseWorkspaceAction.ID, +- title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") +- }, +- order: 3, +- when: new RawContextKey('workbenchState', '').isEqualTo('workspace') +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: '6_close', +- command: { +- id: CloseCurrentWindowAction.ID, +- title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") +- }, +- order: 4 +-}); +- +-MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- group: 'z_Exit', +- command: { +- id: QUIT_ID, +- title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") +- }, +- order: 1, +- when: IsMacContext.toNegated() +-}); ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '6_close', ++// command: { ++// id: CloseWorkspaceAction.ID, ++// title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), ++// precondition: new RawContextKey('workspaceFolderCount', 0).notEqualsTo('0') ++// }, ++// order: 3, ++// when: new RawContextKey('workbenchState', '').notEqualsTo('workspace') ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '6_close', ++// command: { ++// id: CloseWorkspaceAction.ID, ++// title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") ++// }, ++// order: 3, ++// when: new RawContextKey('workbenchState', '').isEqualTo('workspace') ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: '6_close', ++// command: { ++// id: CloseCurrentWindowAction.ID, ++// title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") ++// }, ++// order: 4 ++// }); ++ ++// MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { ++// group: 'z_Exit', ++// command: { ++// id: QUIT_ID, ++// title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") ++// }, ++// order: 1, ++// when: IsMacContext.toNegated() ++// }); + + // Appereance menu + MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { +@@ -450,25 +450,25 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + order: 1 + }); + +-MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { +- group: '5_tools', +- command: { +- id: 'workbench.action.openProcessExplorer', +- title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer") +- }, +- order: 2 +-}); ++// MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { ++// group: '5_tools', ++// command: { ++// id: 'workbench.action.openProcessExplorer', ++// title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer") ++// }, ++// order: 2 ++// }); + + // About +-MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { +- group: 'z_about', +- command: { +- id: 'workbench.action.showAboutDialog', +- title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") +- }, +- order: 1, +- when: IsMacContext.toNegated() +-}); ++// MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { ++// group: 'z_about', ++// command: { ++// id: 'workbench.action.showAboutDialog', ++// title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") ++// }, ++// order: 1, ++// when: IsMacContext.toNegated() ++// }); + + // Configuration: Workbench + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +@@ -629,13 +629,13 @@ configurationRegistry.registerConfiguration({ + ], + 'included': isMacintosh + }, +- 'workbench.settings.enableNaturalLanguageSearch': { +- 'type': 'boolean', +- 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."), +- 'default': true, +- 'scope': ConfigurationScope.WINDOW, +- 'tags': ['usesOnlineServices'] +- }, ++ // 'workbench.settings.enableNaturalLanguageSearch': { ++ // 'type': 'boolean', ++ // 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."), ++ // 'default': true, ++ // 'scope': ConfigurationScope.WINDOW, ++ // 'tags': ['usesOnlineServices'] ++ // }, + 'workbench.settings.settingsSearchTocBehavior': { + 'type': 'string', + 'enum': ['hide', 'filter'], +@@ -658,12 +658,12 @@ configurationRegistry.registerConfiguration({ + 'default': 'ui', + 'scope': ConfigurationScope.WINDOW + }, +- 'workbench.enableExperiments': { +- 'type': 'boolean', +- 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), +- 'default': true, +- 'tags': ['usesOnlineServices'] +- } ++ // 'workbench.enableExperiments': { ++ // 'type': 'boolean', ++ // 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), ++ // 'default': true, ++ // 'tags': ['usesOnlineServices'] ++ // } + } + }); + +@@ -675,59 +675,59 @@ configurationRegistry.registerConfiguration({ + 'title': nls.localize('windowConfigurationTitle', "Window"), + 'type': 'object', + 'properties': { +- 'window.openFilesInNewWindow': { +- 'type': 'string', +- 'enum': ['on', 'off', 'default'], +- 'enumDescriptions': [ +- nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."), +- nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."), +- isMacintosh ? +- nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") : +- nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).") +- ], +- 'default': 'off', +- 'scope': ConfigurationScope.APPLICATION, +- 'markdownDescription': +- isMacintosh ? +- nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : +- nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") +- }, +- 'window.openFoldersInNewWindow': { +- 'type': 'string', +- 'enum': ['on', 'off', 'default'], +- 'enumDescriptions': [ +- nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), +- nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), +- nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") +- ], +- 'default': 'default', +- 'scope': ConfigurationScope.APPLICATION, +- 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") +- }, +- 'window.openWithoutArgumentsInNewWindow': { +- 'type': 'string', +- 'enum': ['on', 'off'], +- 'enumDescriptions': [ +- nls.localize('window.openWithoutArgumentsInNewWindow.on', "Open a new empty window."), +- nls.localize('window.openWithoutArgumentsInNewWindow.off', "Focus the last active running instance.") +- ], +- 'default': isMacintosh ? 'off' : 'on', +- 'scope': ConfigurationScope.APPLICATION, +- 'markdownDescription': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") +- }, +- 'window.restoreWindows': { +- 'type': 'string', +- 'enum': ['all', 'folders', 'one', 'none'], +- 'enumDescriptions': [ +- nls.localize('window.reopenFolders.all', "Reopen all windows."), +- nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), +- nls.localize('window.reopenFolders.one', "Reopen the last active window."), +- nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") +- ], +- 'default': 'one', +- 'scope': ConfigurationScope.APPLICATION, +- 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") +- }, ++ // 'window.openFilesInNewWindow': { ++ // 'type': 'string', ++ // 'enum': ['on', 'off', 'default'], ++ // 'enumDescriptions': [ ++ // nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."), ++ // nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."), ++ // isMacintosh ? ++ // nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") : ++ // nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).") ++ // ], ++ // 'default': 'off', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'markdownDescription': ++ // isMacintosh ? ++ // nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : ++ // nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") ++ // }, ++ // 'window.openFoldersInNewWindow': { ++ // 'type': 'string', ++ // 'enum': ['on', 'off', 'default'], ++ // 'enumDescriptions': [ ++ // nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), ++ // nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), ++ // nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") ++ // ], ++ // 'default': 'default', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") ++ // }, ++ // 'window.openWithoutArgumentsInNewWindow': { ++ // 'type': 'string', ++ // 'enum': ['on', 'off'], ++ // 'enumDescriptions': [ ++ // nls.localize('window.openWithoutArgumentsInNewWindow.on', "Open a new empty window."), ++ // nls.localize('window.openWithoutArgumentsInNewWindow.off', "Focus the last active running instance.") ++ // ], ++ // 'default': isMacintosh ? 'off' : 'on', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'markdownDescription': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") ++ // }, ++ // 'window.restoreWindows': { ++ // 'type': 'string', ++ // 'enum': ['all', 'folders', 'one', 'none'], ++ // 'enumDescriptions': [ ++ // nls.localize('window.reopenFolders.all', "Reopen all windows."), ++ // nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), ++ // nls.localize('window.reopenFolders.one', "Reopen the last active window."), ++ // nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") ++ // ], ++ // 'default': 'one', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") ++ // }, + 'window.restoreFullscreen': { + 'type': 'boolean', + 'default': false, +@@ -745,19 +745,19 @@ configurationRegistry.registerConfiguration({ + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, + "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") + }, +- 'window.newWindowDimensions': { +- 'type': 'string', +- 'enum': ['default', 'inherit', 'maximized', 'fullscreen'], +- 'enumDescriptions': [ +- nls.localize('window.newWindowDimensions.default', "Open new windows in the center of the screen."), +- nls.localize('window.newWindowDimensions.inherit', "Open new windows with same dimension as last active one."), +- nls.localize('window.newWindowDimensions.maximized', "Open new windows maximized."), +- nls.localize('window.newWindowDimensions.fullscreen', "Open new windows in full screen mode.") +- ], +- 'default': 'default', +- 'scope': ConfigurationScope.APPLICATION, +- 'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.") +- }, ++ // 'window.newWindowDimensions': { ++ // 'type': 'string', ++ // 'enum': ['default', 'inherit', 'maximized', 'fullscreen'], ++ // 'enumDescriptions': [ ++ // nls.localize('window.newWindowDimensions.default', "Open new windows in the center of the screen."), ++ // nls.localize('window.newWindowDimensions.inherit', "Open new windows with same dimension as last active one."), ++ // nls.localize('window.newWindowDimensions.maximized', "Open new windows maximized."), ++ // nls.localize('window.newWindowDimensions.fullscreen', "Open new windows in full screen mode.") ++ // ], ++ // 'default': 'default', ++ // 'scope': ConfigurationScope.APPLICATION, ++ // 'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.") ++ // }, + 'window.closeWhenEmpty': { + 'type': 'boolean', + 'default': false, +@@ -792,8 +792,8 @@ configurationRegistry.registerConfiguration({ + }, + 'window.titleBarStyle': { + 'type': 'string', +- 'enum': ['native', 'custom'], +- 'default': isLinux ? 'native' : 'custom', ++ 'enum': ['custom'], ++ 'default': 'custom', + 'scope': ConfigurationScope.APPLICATION, + 'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.") + }, +diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts +index 5323ed87b6..646c942be2 100644 +--- a/src/vs/workbench/electron-browser/main.ts ++++ b/src/vs/workbench/electron-browser/main.ts +@@ -15,19 +15,19 @@ import * as errors from 'vs/base/common/errors'; + import * as comparer from 'vs/base/common/comparers'; + import * as platform from 'vs/base/common/platform'; + import { URI as uri } from 'vs/base/common/uri'; +-import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; ++// import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; + import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; + import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; + import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; + import { stat } from 'vs/base/node/pfs'; + import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +-import * as gracefulFs from 'graceful-fs'; ++// import * as gracefulFs from 'graceful-fs'; + import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; + import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows'; + import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; + import { IStorageService } from 'vs/platform/storage/common/storage'; + import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +-import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService'; ++// import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService'; + import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; + import { webFrame } from 'electron'; + import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc'; +@@ -37,7 +37,6 @@ import { IURLService } from 'vs/platform/url/common/url'; + import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc'; + import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; +-import * as fs from 'fs'; + import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; + import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc'; + import { IIssueService } from 'vs/platform/issue/common/issue'; +@@ -47,10 +46,11 @@ import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc'; + import { IMenubarService } from 'vs/platform/menubar/common/menubar'; + import { Schemas } from 'vs/base/common/network'; + import { sanitizeFilePath } from 'vs/base/node/extfs'; ++import { getStorageService } from 'coder/workbench'; + +-gracefulFs.gracefulify(fs); // enable gracefulFs ++// gracefulFs.gracefulify(fs); // enable gracefulFs + +-export function startup(configuration: IWindowConfiguration): TPromise { ++export function startup(configuration: IWindowConfiguration): TPromise { + + revive(configuration); + +@@ -92,7 +92,7 @@ function revive(workbench: IWindowConfiguration) { + }); + } + +-function openWorkbench(configuration: IWindowConfiguration): TPromise { ++function openWorkbench(configuration: IWindowConfiguration): TPromise { + const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`); + const mainServices = createMainProcessServices(mainProcessClient, configuration); + +@@ -103,9 +103,11 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { + // Since the configuration service is one of the core services that is used in so many places, we initialize it + // right before startup of the workbench shell to have its data ready for consumers + return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => { +- const storageService = createStorageService(workspaceService, environmentService); +- +- return domContentLoaded().then(() => { ++ let storageService: IStorageService; ++ return getStorageService.then((gottenStorageService) => { ++ storageService = gottenStorageService; ++ return domContentLoaded(); ++ }).then(() => { + + // Open Shell + perf.mark('willStartWorkbench'); +@@ -119,6 +121,8 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { + shell.open(); + + // Inform user about loading issues from the loader ++ return shell; ++ + (self).require.config({ + onError: (err: any) => { + if (err.errorCode === 'load') { +@@ -158,47 +162,47 @@ function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: + }); + } + +-function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService { +- let workspaceId: string; +- let secondaryWorkspaceId: number; +- +- switch (workspaceService.getWorkbenchState()) { +- +- // in multi root workspace mode we use the provided ID as key for workspace storage +- case WorkbenchState.WORKSPACE: +- workspaceId = uri.from({ path: workspaceService.getWorkspace().id, scheme: 'root' }).toString(); +- break; +- +- // in single folder mode we use the path of the opened folder as key for workspace storage +- // the ctime is used as secondary workspace id to clean up stale UI state if necessary +- case WorkbenchState.FOLDER: +- const workspace: Workspace = workspaceService.getWorkspace(); +- workspaceId = workspace.folders[0].uri.toString(); +- secondaryWorkspaceId = workspace.ctime; +- break; +- +- // finaly, if we do not have a workspace open, we need to find another identifier for the window to store +- // workspace UI state. if we have a backup path in the configuration we can use that because this +- // will be a unique identifier per window that is stable between restarts as long as there are +- // dirty files in the workspace. +- // We use basename() to produce a short identifier, we do not need the full path. We use a custom +- // scheme so that we can later distinguish these identifiers from the workspace one. +- case WorkbenchState.EMPTY: +- workspaceId = workspaceService.getWorkspace().id; +- break; +- } +- +- const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests! +- +- let storage: IStorage; +- if (disableStorage) { +- storage = inMemoryLocalStorageInstance; +- } else { +- storage = window.localStorage; +- } +- +- return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId); +-} ++// function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService { ++// let workspaceId: string; ++// let secondaryWorkspaceId: number; ++ ++// switch (workspaceService.getWorkbenchState()) { ++ ++// // in multi root workspace mode we use the provided ID as key for workspace storage ++// case WorkbenchState.WORKSPACE: ++// workspaceId = uri.from({ path: workspaceService.getWorkspace().id, scheme: 'root' }).toString(); ++// break; ++ ++// // in single folder mode we use the path of the opened folder as key for workspace storage ++// // the ctime is used as secondary workspace id to clean up stale UI state if necessary ++// case WorkbenchState.FOLDER: ++// const workspace: Workspace = workspaceService.getWorkspace(); ++// workspaceId = workspace.folders[0].uri.toString(); ++// secondaryWorkspaceId = workspace.ctime; ++// break; ++ ++// // finaly, if we do not have a workspace open, we need to find another identifier for the window to store ++// // workspace UI state. if we have a backup path in the configuration we can use that because this ++// // will be a unique identifier per window that is stable between restarts as long as there are ++// // dirty files in the workspace. ++// // We use basename() to produce a short identifier, we do not need the full path. We use a custom ++// // scheme so that we can later distinguish these identifiers from the workspace one. ++// case WorkbenchState.EMPTY: ++// workspaceId = workspaceService.getWorkspace().id; ++// break; ++// } ++ ++// const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests! ++ ++// let storage: IStorage; ++// if (disableStorage) { ++// storage = inMemoryLocalStorageInstance; ++// } else { ++// storage = window.localStorage; ++// } ++ ++// return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId); ++// } + + function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService { + const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, configuration.logLevel, environmentService.logsPath); +diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts +index 55f91cfa0e..a4b6422562 100644 +--- a/src/vs/workbench/electron-browser/shell.ts ++++ b/src/vs/workbench/electron-browser/shell.ts +@@ -24,7 +24,7 @@ import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry + import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; + import { ElectronWindow } from 'vs/workbench/electron-browser/window'; + import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; +-import { IWindowsService, IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; ++import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; + import { WindowService } from 'vs/platform/windows/electron-browser/windowService'; + import { IRequestService } from 'vs/platform/request/node/request'; + import { RequestService } from 'vs/platform/request/electron-browser/requestService'; +@@ -58,7 +58,8 @@ import { IModeService } from 'vs/editor/common/services/modeService'; + import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; + import { ICrashReporterService, NullCrashReporterService, CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; + import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/node/ipc'; +-import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; ++// @ts-ignore ++import { connect as connectNet, Client, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; + import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; + import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; + import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +@@ -100,6 +101,8 @@ import { DefaultURITransformer } from 'vs/base/common/uriIpc'; + import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; + import { ILabelService } from 'vs/platform/label/common/label'; + ++import { protocolPromise } from 'coder/workbench'; ++ + /** + * Services that we require for the Shell + */ +@@ -135,7 +138,7 @@ export class WorkbenchShell extends Disposable { + private previousErrorTime: number; + + private configuration: IWindowConfiguration; +- private workbench: Workbench; ++ public workbench: Workbench; + + constructor(container: HTMLElement, coreServices: ICoreServices, mainProcessServices: ServiceCollection, private mainProcessClient: IPCClient, configuration: IWindowConfiguration) { + super(); +@@ -344,8 +347,9 @@ export class WorkbenchShell extends Disposable { + + serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, this.configuration.windowId, this.configuration)); + +- const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() +- .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); ++ const sharedProcess = protocolPromise.then((proto) => { ++ return new Client(proto, "1"); ++ }); + + sharedProcess.then(client => { + client.registerChannel('download', new DownloadServiceChannel()); +@@ -362,7 +366,7 @@ export class WorkbenchShell extends Disposable { + // Telemetry + + if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { +- const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); ++ const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender')) as any); + const config: ITelemetryServiceConfig = { + appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)), + commonProperties: resolveWorkbenchCommonProperties(this.storageService, product.commit, pkg.version, this.configuration.machineId, this.environmentService.installSourcePath), +@@ -394,7 +398,7 @@ export class WorkbenchShell extends Disposable { + serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); + serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + +- const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); ++ const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions')) as any); + const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer); + serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, extensionManagementChannelClient)); + serviceCollection.set(IExtensionManagementService, new SyncDescriptor(MulitExtensionManagementService)); +@@ -440,7 +444,7 @@ export class WorkbenchShell extends Disposable { + + serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); + +- const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations'))); ++ const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations')) as any); + serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, localizationsChannel)); + + return [instantiationService, serviceCollection]; +diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts +index 83cc52d8a4..09dca6d008 100644 +--- a/src/vs/workbench/electron-browser/window.ts ++++ b/src/vs/workbench/electron-browser/window.ts +@@ -44,6 +44,33 @@ import { INotificationService } from 'vs/platform/notification/common/notificati + import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; + import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; + import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; ++import { nativeClipboard } from 'coder/workbench'; ++ ++const pasteAction = new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, nativeClipboard.isEnabled, async () => { ++ if (nativeClipboard.isEnabled) { ++ try { ++ const element = document.activeElement as HTMLInputElement | HTMLTextAreaElement; ++ const start = element.selectionStart; ++ const end = element.selectionEnd; ++ const allText = element.value; ++ const newText = allText.substring(0, start) + (await nativeClipboard.instance.readText()) + allText.substring(end); ++ element.value = newText; ++ } catch (ex) { ++ document.execCommand('paste') && TPromise.as(true); ++ } ++ } else { ++ document.execCommand('paste') && TPromise.as(true); ++ } ++}); ++ ++nativeClipboard.onChange((v) => { ++ if (v) { ++ pasteAction.label = "Paste"; ++ } else { ++ pasteAction.label = "Paste (Must use keybind)"; ++ } ++ pasteAction.enabled = v; ++}); + + const TextInputActions: IAction[] = [ + new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)), +@@ -51,7 +78,7 @@ const TextInputActions: IAction[] = [ + new Separator(), + new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && TPromise.as(true)), + new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && TPromise.as(true)), +- new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && TPromise.as(true)), ++ pasteAction, + new Separator(), + new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && TPromise.as(true)) + ]; +@@ -273,13 +300,14 @@ export class ElectronWindow extends Themable { + + private create(): void { + ++ // Commented out (just use native window.open). + // Handle window.open() calls +- const $this = this; +- (window).open = function (url: string, target: string, features: string, replace: boolean): any { +- $this.windowsService.openExternal(url); ++ // const $this = this; ++ // (window).open = function (url: string, target: string, features: string, replace: boolean): any { ++ // $this.windowsService.openExternal(url); + +- return null; +- }; ++ // return null; ++ // }; + + // Emit event when vscode has loaded + this.lifecycleService.when(LifecyclePhase.Running).then(() => { +@@ -292,8 +320,8 @@ export class ElectronWindow extends Themable { + // Root warning + this.lifecycleService.when(LifecyclePhase.Running).then(() => { + let isAdminPromise: Promise; +- if (isWindows) { +- isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); ++ if (isWindows && false) { ++ isAdminPromise = require('native-is-elevated').then(isElevated => isElevated()); + } else { + isAdminPromise = Promise.resolve(isRootUser()); + } +diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts +index 04474dc883..4d55c884cd 100644 +--- a/src/vs/workbench/electron-browser/workbench.ts ++++ b/src/vs/workbench/electron-browser/workbench.ts +@@ -115,6 +115,8 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS + import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService'; + import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; + import { LabelService, ILabelService } from 'vs/platform/label/common/label'; ++import { IFasttimeService, FasttimeService } from 'coder/contributions/fasttime'; ++import { FeedbackService, IFeedbackService } from 'coder/contributions/feedback'; + + interface WorkbenchParams { + configuration: IWindowConfiguration; +@@ -186,7 +188,7 @@ export class Workbench extends Disposable implements IPartService { + + _serviceBrand: any; + +- private workbenchParams: WorkbenchParams; ++ public workbenchParams: WorkbenchParams; + private workbench: HTMLElement; + private workbenchStarted: boolean; + private workbenchCreated: boolean; +@@ -295,7 +297,7 @@ export class Workbench extends Disposable implements IPartService { + private createWorkbench(): void { + this.workbench = document.createElement('div'); + this.workbench.id = Identifiers.WORKBENCH_CONTAINER; +- DOM.addClasses(this.workbench, 'monaco-workbench', isWindows ? 'windows' : isLinux ? 'linux' : 'mac'); ++ DOM.addClasses(this.workbench, 'monaco-workbench', 'linux'); + + this._register(DOM.addDisposableListener(this.workbench, DOM.EventType.SCROLL, () => { + this.workbench.scrollTop = 0; // Prevent workbench from scrolling #55456 +@@ -360,12 +362,16 @@ export class Workbench extends Disposable implements IPartService { + serviceCollection.set(IContextViewService, this.contextViewService); + + // Use themable context menus when custom titlebar is enabled to match custom menubar +- if (!isMacintosh && this.getCustomTitleBarStyle() === 'custom') { ++ if (this.getCustomTitleBarStyle() === 'custom') { + serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, null)); + } else { + serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService)); + } + ++ // Coder services ++ serviceCollection.set(IFasttimeService, new SyncDescriptor(FasttimeService)); ++ serviceCollection.set(IFeedbackService, new SyncDescriptor(FeedbackService)); ++ + // Menus/Actions + serviceCollection.set(IMenuService, new SyncDescriptor(MenuService)); + +@@ -1190,8 +1196,6 @@ export class Workbench extends Disposable implements IPartService { + return false; + } else if (!browser.isFullscreen()) { + return true; +- } else if (isMacintosh) { +- return false; + } else if (this.menubarVisibility === 'visible') { + return true; + } else if (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default') { +@@ -1216,7 +1220,7 @@ export class Workbench extends Disposable implements IPartService { + let offset = 0; + if (this.isVisible(Parts.TITLEBAR_PART)) { + offset = this.workbenchLayout.partLayoutInfo.titlebar.height; +- if (isMacintosh || this.menubarVisibility === 'hidden') { ++ if (this.menubarVisibility === 'hidden') { + offset /= browser.getZoomFactor(); + } + } +diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts +index a1ba3700a3..03ac5a2822 100644 +--- a/src/vs/workbench/node/extensionHostMain.ts ++++ b/src/vs/workbench/node/extensionHostMain.ts +@@ -24,6 +24,15 @@ import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; + import { timeout } from 'vs/base/common/async'; + import { Counter } from 'vs/base/common/numbers'; + import { CancellationTokenSource } from 'vs/base/common/cancellation'; ++import { IInitData as ICoderInitData } from 'coder/common'; ++ ++if (!process.exit) { ++ process.exit = { ++ bind: () => { ++ ++ }, ++ } as any; ++} + + const nativeExit = process.exit.bind(process); + function patchProcess(allowExit: boolean) { +@@ -65,7 +74,7 @@ export class ExtensionHostMain { + private _searchRequestIdProvider: Counter; + private _mainThreadWorkspace: MainThreadWorkspaceShape; + +- constructor(protocol: IMessagePassingProtocol, initData: IInitData) { ++ constructor(protocol: IMessagePassingProtocol, initData: IInitData, coderInitData: ICoderInitData) { + const rpcProtocol = new RPCProtocol(protocol); + + // ensure URIs are transformed and revived +@@ -87,7 +96,7 @@ export class ExtensionHostMain { + this._extHostLogService.trace('initData', initData); + + this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration); +- this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService); ++ this._extensionService = new ExtHostExtensionService(initData, coderInitData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService); + + // error forwarding and stack trace scanning + Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) +@@ -127,7 +136,9 @@ export class ExtensionHostMain { + + start(): TPromise { + return this._extensionService.onExtensionAPIReady() +- .then(() => this.handleEagerExtensions()) ++ .then(() => { ++ this.handleEagerExtensions(); ++ }) + .then(() => this.handleExtensionTests()) + .then(() => { + this._extHostLogService.info(`eager extensions activated`); +diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts +index ba0e95ec5c..676f5c4e41 100644 +--- a/src/vs/workbench/node/extensionHostProcess.ts ++++ b/src/vs/workbench/node/extensionHostProcess.ts +@@ -2,19 +2,22 @@ + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +- + 'use strict'; + ++import { IInitData as ICoderInitData, isBrowserEnvironment, API } from 'coder/common'; + import { onUnexpectedError } from 'vs/base/common/errors'; + import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; + import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; + import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; + import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +-import { createConnection } from 'net'; ++// import { createConnection } from 'net'; + import { Event, filterEvent } from 'vs/base/common/event'; + import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +-import * as nativeWatchdog from 'native-watchdog'; + import product from 'vs/platform/node/product'; ++import connectToWush from '../../../../../../wush/src/index'; ++import 'coder/setup'; ++import { set as setApi } from 'coder/api'; ++import { setWush } from 'coder/server'; + + // With Electron 2.x and node.js 8.x the "natives" module + // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and +@@ -45,45 +48,61 @@ let onTerminate = function () { + exit(); + }; + +-function createExtHostProtocol(): Promise { ++function createExtHostProtocol(): IMessagePassingProtocol { ++ const protocol = isBrowserEnvironment() ++ ? Protocol.fromWorker(self) ++ : Protocol.fromStream(process.stdin, process.stdout); + +- const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST; ++ return new class implements IMessagePassingProtocol { + +- return new Promise((resolve, reject) => { ++ private _terminating = false; + +- const socket = createConnection(pipeName, () => { +- socket.removeListener('error', reject); +- resolve(new Protocol(socket)); ++ readonly onMessage: Event = filterEvent(protocol.onMessage, msg => { ++ if (!isMessageOfType(msg, MessageType.Terminate)) { ++ return true; ++ } ++ this._terminating = true; ++ onTerminate(); ++ return false; + }); +- socket.once('error', reject); +- +- }).then(protocol => { + +- return new class implements IMessagePassingProtocol { ++ send(msg: any): void { ++ if (!this._terminating) { ++ protocol.send(msg); ++ } ++ } ++ }; ++} + +- private _terminating = false; ++function getCoderInitData(protocol: IMessagePassingProtocol): Promise { ++ return new Promise((resolve, reject) => { ++ const first = protocol.onMessage((raw) => { ++ first.dispose(); ++ const initData = JSON.parse(raw.toString()); + +- readonly onMessage: Event = filterEvent(protocol.onMessage, msg => { +- if (!isMessageOfType(msg, MessageType.Terminate)) { +- return true; +- } +- this._terminating = true; +- onTerminate(); +- return false; +- }); ++ product.fetchUrl = initData.fetchUrl; + +- send(msg: any): void { +- if (!this._terminating) { +- protocol.send(msg); ++ if (isBrowserEnvironment()) { ++ if (initData.api.key) { ++ const api = new API(); ++ api.setAPIKey(initData.api.key); ++ setApi(api, { id_str: initData.api.organization } as any, { organization: { id_str: initData.api.organization} } as any, { id_str: initData.api.container } as any, undefined, undefined); + } ++ connectToWush(initData.wush).then((wush) => { ++ setWush(wush, initData.wush); ++ resolve(initData); ++ }).catch((error) => { ++ reject(error); ++ }); ++ } else { ++ resolve(initData); + } +- }; ++ }); + }); + } + + function connectToRenderer(protocol: IMessagePassingProtocol): Promise { + return new Promise((c, e) => { +- + // Listen init data message + const first = protocol.onMessage(raw => { + first.dispose(); +@@ -129,25 +148,33 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise { +- // connect to main side +- return connectToRenderer(protocol); +-}).then(renderer => { +- // setup things +- const extensionHostMain = new ExtensionHostMain(renderer.protocol, renderer.initData); +- onTerminate = () => extensionHostMain.terminate(); +- return extensionHostMain.start(); ++const protocol = createExtHostProtocol(); ++getCoderInitData(protocol).then((coderInitData) => { ++ return connectToRenderer(protocol).then((renderer) => { ++ // setup things ++ const extensionHostMain = new ExtensionHostMain(renderer.protocol, renderer.initData, coderInitData); ++ if (!isBrowserEnvironment()) { ++ process.stdin.on('close', () => { ++ extensionHostMain.terminate(); ++ }); ++ } ++ onTerminate = () => extensionHostMain.terminate(); ++ return extensionHostMain.start(); ++ }); + }).catch(err => console.error(err)); + + function patchExecArgv() { +diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +index 11b46982bd..0ced196436 100644 +--- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts ++++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +@@ -16,7 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur + import { WorkbenchTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; + import { renderViewTree, twistiePixels } from 'vs/workbench/parts/debug/browser/baseDebugView'; + import { IAccessibilityProvider, ITree, IRenderer, IDataSource } from 'vs/base/parts/tree/browser/tree'; +-import { IDebugSession, IDebugService, IModel, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; ++import { IDebugSession, IDebugService, IDebugModel, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; + import { Source } from 'vs/workbench/parts/debug/common/debugSource'; + import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +@@ -201,7 +201,7 @@ class RootFolderTreeItem extends BaseTreeItem { + + class RootTreeItem extends BaseTreeItem { + +- constructor(private _debugModel: IModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) { ++ constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) { + super(undefined, 'Root'); + this._debugModel.getSessions().forEach(session => { + this.add(session); +diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +index 48ab4d9099..542b2026e4 100644 +--- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css ++++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +@@ -216,6 +216,7 @@ + flex: 1; + flex-shrink: 0; + min-width: fit-content; ++ min-width: -moz-fit-content; + } + + .debug-viewlet .debug-call-stack .stack-frame.subtle { +diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts +index ba31334f87..528f843221 100644 +--- a/src/vs/workbench/parts/debug/common/debug.ts ++++ b/src/vs/workbench/parts/debug/common/debug.ts +@@ -365,7 +365,7 @@ export interface IViewModel extends ITreeElement { + onDidSelectExpression: Event; + } + +-export interface IModel extends ITreeElement { ++export interface IDebugModel extends ITreeElement { + getSessions(): ReadonlyArray; + getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number, enabledOnly?: boolean }): ReadonlyArray; + areBreakpointsActivated(): boolean; +@@ -776,7 +776,7 @@ export interface IDebugService { + /** + * Gets the current debug model. + */ +- getModel(): IModel; ++ getModel(): IDebugModel; + + /** + * Gets the current view model. +diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts +index ec1f0121bd..1d1db88a38 100644 +--- a/src/vs/workbench/parts/debug/common/debugModel.ts ++++ b/src/vs/workbench/parts/debug/common/debugModel.ts +@@ -16,7 +16,7 @@ import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types'; + import { distinct } from 'vs/base/common/arrays'; + import { Range, IRange } from 'vs/editor/common/core/range'; + import { +- ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource, ++ ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource, + IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint + } from 'vs/workbench/parts/debug/common/debug'; + import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +@@ -723,7 +723,7 @@ export class ThreadAndSessionIds implements ITreeElement { + } + } + +-export class Model implements IModel { ++export class DebugModel implements IDebugModel { + + private sessions: IDebugSession[]; + private toDispose: lifecycle.IDisposable[]; +diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +index df72e464cf..dead98d5e6 100644 +--- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts ++++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +@@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom'; + import { TPromise } from 'vs/base/common/winjs.base'; + import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; + import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; +-import { Thread, StackFrame, ThreadAndSessionIds, Model } from 'vs/workbench/parts/debug/common/debugModel'; ++import { Thread, StackFrame, ThreadAndSessionIds, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; + import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; + import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + import { MenuId } from 'vs/platform/actions/common/actions'; +@@ -313,14 +313,14 @@ class CallStackDataSource implements IDataSource { + } + + public hasChildren(tree: ITree, element: any): boolean { +- return element instanceof Model || element instanceof DebugSession || (element instanceof Thread && (element).stopped); ++ return element instanceof DebugModel || element instanceof DebugSession || (element instanceof Thread && (element).stopped); + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof Thread) { + return this.getThreadChildren(element); + } +- if (element instanceof Model) { ++ if (element instanceof DebugModel) { + return TPromise.as(element.getSessions()); + } + +diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts +index f004ab8879..bef854be3f 100644 +--- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts ++++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts +@@ -22,7 +22,7 @@ import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/file + import { IWindowService } from 'vs/platform/windows/common/windows'; + import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +-import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel'; ++import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel'; + import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; + import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions'; + import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager'; +@@ -47,7 +47,7 @@ import { IAction, Action } from 'vs/base/common/actions'; + import { deepClone, equals } from 'vs/base/common/objects'; + import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSession'; + import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +-import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IModel, IReplElementSource, IEnablement, IBreakpoint, IBreakpointData, IExpression, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug'; ++import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IReplElementSource, IEnablement, IBreakpoint, IBreakpointData, IExpression, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug'; + import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils'; + + const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; +@@ -63,7 +63,7 @@ export class DebugService implements IDebugService { + private readonly _onDidNewSession: Emitter; + private readonly _onWillNewSession: Emitter; + private readonly _onDidEndSession: Emitter; +- private model: Model; ++ private model: DebugModel; + private viewModel: ViewModel; + private configurationManager: ConfigurationManager; + private allSessions = new Map(); +@@ -114,7 +114,7 @@ export class DebugService implements IDebugService { + this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); + +- this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), ++ this.model = new DebugModel(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), + this.loadExceptionBreakpoints(), this.loadWatchExpressions(), this.textFileService); + this.toDispose.push(this.model); + +@@ -158,7 +158,7 @@ export class DebugService implements IDebugService { + return this.allSessions.get(sessionId); + } + +- getModel(): IModel { ++ getModel(): IDebugModel { + return this.model; + } + +diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +index 3118d64989..6d7c1c7bf2 100644 +--- a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts ++++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +@@ -16,7 +16,7 @@ import * as aria from 'vs/base/browser/ui/aria/aria'; + import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, ActualBreakpoints, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug'; + import { Source } from 'vs/workbench/parts/debug/common/debugSource'; + import { mixin } from 'vs/base/common/objects'; +-import { Thread, ExpressionContainer, Model } from 'vs/workbench/parts/debug/common/debugModel'; ++import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; + import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession'; + import product from 'vs/platform/node/product'; + import { INotificationService } from 'vs/platform/notification/common/notification'; +@@ -49,7 +49,7 @@ export class DebugSession implements IDebugSession { + constructor( + private _configuration: { resolved: IConfig, unresolved: IConfig }, + public root: IWorkspaceFolder, +- private model: Model, ++ private model: DebugModel, + @INotificationService private notificationService: INotificationService, + @IDebugService private debugService: IDebugService, + @ITelemetryService private telemetryService: ITelemetryService, +diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +index 008bb89550..3dcd1f4fc3 100644 +--- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts ++++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +@@ -15,7 +15,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent'; + import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; + import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; + import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; +-import { Model, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; ++import { DebugModel, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; + import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView'; + import { ClearReplAction, ReplCollapseAllAction } from 'vs/workbench/parts/debug/browser/debugActions'; + import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; +@@ -34,11 +34,11 @@ export class ReplExpressionsDataSource implements IDataSource { + } + + public hasChildren(tree: ITree, element: any): boolean { +- return element instanceof Model || (element).hasChildren; ++ return element instanceof DebugModel || (element).hasChildren; + } + + public getChildren(tree: ITree, element: any): TPromise { +- if (element instanceof Model) { ++ if (element instanceof DebugModel) { + return TPromise.as(element.getReplElements()); + } + if (element instanceof RawObjectReplElement) { +diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +index 36de5bfdf7..9727aea0fa 100644 +--- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts ++++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +@@ -12,7 +12,7 @@ import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, + import { CollapseAction } from 'vs/workbench/browser/viewlet'; + import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; + import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/parts/debug/common/debug'; +-import { Expression, Variable, Model } from 'vs/workbench/parts/debug/common/debugModel'; ++import { Expression, Variable, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; + import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, EditWatchExpressionAction, RemoveWatchExpressionAction } from 'vs/workbench/parts/debug/browser/debugActions'; + import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; + import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +@@ -202,7 +202,7 @@ class WatchExpressionsDataSource implements IDataSource { + } + + public hasChildren(tree: ITree, element: any): boolean { +- if (element instanceof Model) { ++ if (element instanceof DebugModel) { + return true; + } + +@@ -211,7 +211,7 @@ class WatchExpressionsDataSource implements IDataSource { + } + + public getChildren(tree: ITree, element: any): TPromise { +- if (element instanceof Model) { ++ if (element instanceof DebugModel) { + const viewModel = this.debugService.getViewModel(); + return TPromise.join(element.getWatchExpressions().map(we => + we.name ? we.evaluate(viewModel.focusedSession, viewModel.focusedStackFrame, 'watch').then(() => we) : TPromise.as(we))); +@@ -344,7 +344,7 @@ class WatchExpressionsController extends BaseDebugController { + const expression = element; + this.debugService.getViewModel().setSelectedExpression(expression); + return true; +- } else if (element instanceof Model && event.detail === 2) { ++ } else if (element instanceof DebugModel && event.detail === 2) { + // Double click in watch panel triggers to add a new watch expression + this.debugService.addWatchExpression(); + return true; +@@ -376,8 +376,8 @@ class WatchExpressionsDragAndDrop extends DefaultDragAndDrop { + return elements[0].name; + } + +- public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): IDragOverReaction { +- if (target instanceof Expression || target instanceof Model) { ++ public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | DebugModel, originalEvent: DragMouseEvent): IDragOverReaction { ++ if (target instanceof Expression || target instanceof DebugModel) { + return { + accept: true, + autoExpand: false +@@ -387,12 +387,12 @@ class WatchExpressionsDragAndDrop extends DefaultDragAndDrop { + return DRAG_OVER_REJECT; + } + +- public drop(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): void { ++ public drop(tree: ITree, data: IDragAndDropData, target: Expression | DebugModel, originalEvent: DragMouseEvent): void { + const draggedData = data.getData(); + if (Array.isArray(draggedData)) { + const draggedElement = draggedData[0]; + const watches = this.debugService.getModel().getWatchExpressions(); +- const position = target instanceof Model ? watches.length - 1 : watches.indexOf(target); ++ const position = target instanceof DebugModel ? watches.length - 1 : watches.indexOf(target); + this.debugService.moveWatchExpression(draggedElement.getId(), position); + } + } +diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts +index cb6808251c..f88e404209 100644 +--- a/src/vs/workbench/parts/debug/node/debugAdapter.ts ++++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts +@@ -334,9 +334,10 @@ export class DebugAdapter extends StreamDebugAdapter { + options.cwd = this.adapterExecutable.cwd; + } + const child = cp.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), options); +- if (!child.pid) { +- reject(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); +- } ++ // NOTE@coder: our version doesn't provide a pid ATM. ++ // if (!child.pid) { ++ // reject(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); ++ // } + this.serverProcess = child; + resolve(null); + } else { +diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts +index 3dcd2bfe3e..09d4b06ed4 100644 +--- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts ++++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts +@@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event'; + import { TPromise } from 'vs/base/common/winjs.base'; + import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; + import { Position } from 'vs/editor/common/core/position'; +-import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, ActualBreakpoints, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug'; ++import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, ActualBreakpoints, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug'; + import { Source } from 'vs/workbench/parts/debug/common/debugSource'; + import { ISuggestion } from 'vs/editor/common/modes'; + +@@ -109,7 +109,7 @@ export class MockDebugService implements IDebugService { + return TPromise.as(null); + } + +- public getModel(): IModel { ++ public getModel(): IDebugModel { + return null; + } + +diff --git a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts +index 60007a799e..bc88b90a30 100644 +--- a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts ++++ b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts +@@ -6,18 +6,18 @@ + import * as assert from 'assert'; + import { URI as uri } from 'vs/base/common/uri'; + import severity from 'vs/base/common/severity'; +-import { SimpleReplElement, Model, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; ++import { SimpleReplElement, DebugModel, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; + import * as sinon from 'sinon'; + import { MockRawSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; + import { Source } from 'vs/workbench/parts/debug/common/debugSource'; + import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSession'; + + suite('Debug - Model', () => { +- let model: Model; ++ let model: DebugModel; + let rawSession: MockRawSession; + + setup(() => { +- model = new Model([], true, [], [], [], { isDirty: (e: any) => false }); ++ model = new DebugModel([], true, [], [], [], { isDirty: (e: any) => false }); + rawSession = new MockRawSession(); + }); + +diff --git a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts +index 208b281d85..9d833fc8a5 100644 +--- a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts ++++ b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts +@@ -130,12 +130,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ + } + }); + +-MenuRegistry.appendMenuItem(MenuId.CommandPalette, { +- command: { +- id: OPEN_NATIVE_CONSOLE_COMMAND_ID, +- title: { value: nls.localize('globalConsoleAction', "Open New Terminal"), original: 'Open New Terminal' } +- } +-}); ++// MenuRegistry.appendMenuItem(MenuId.CommandPalette, { ++// command: { ++// id: OPEN_NATIVE_CONSOLE_COMMAND_ID, ++// title: { value: nls.localize('globalConsoleAction', "Open New Terminal"), original: 'Open New Terminal' } ++// } ++// }); + + const openConsoleCommand = { + id: OPEN_IN_TERMINAL_COMMAND_ID, +diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +index f8e86e08d8..7402bfcc6e 100644 +--- a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts ++++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +@@ -128,15 +128,16 @@ export class RatingsWidget implements IDisposable { + return; + } + +- const rating = Math.round(this.extension.rating * 2) / 2; ++ const rating = this.extension.stars; ++ // const rating = Math.round(this.extension.rating * 2) / 2; + +- if (this.extension.rating === null) { +- return; +- } ++ // if (this.extension.rating === null) { ++ // return; ++ // } + +- if (this.options.small && this.extension.ratingCount === 0) { +- return; +- } ++ // if (this.options.small && this.extension.ratingCount === 0) { ++ // return; ++ // } + + if (this.options.small) { + append(this.container, $('span.full.star')); +diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts +index ca6f8e8204..d9d82efe34 100644 +--- a/src/vs/workbench/parts/extensions/common/extensions.ts ++++ b/src/vs/workbench/parts/extensions/common/extensions.ts +@@ -46,6 +46,7 @@ export interface IExtension { + licenseUrl: string; + installCount: number; + rating: number; ++ stars: number; + ratingCount: number; + outdated: boolean; + enablementState: EnablementState; +diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +index 487436a01e..1d4cb6ddf9 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts ++++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +@@ -52,7 +52,7 @@ import { ShowCurrentReleaseNotesAction } from 'vs/workbench/parts/update/electro + import { KeybindingParser } from 'vs/base/common/keybindingParser'; + + function renderBody(body: string): string { +- const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://'); ++ const styleSheetPath = ""; // require.toUrl('css-loader!style-loader!./media/markdown.css').replace('file://', 'vscode-core-resource://'); + return ` + + +diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +index 89f50e43e3..2dc5fc09d4 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts ++++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +@@ -443,7 +443,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe + // group ids by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] + this._availableRecommendations = Object.create(null); + forEach(extensionTips, entry => { +- let { key: id, value: pattern } = entry; ++ let { key: id, value: pattern }: { [key: string]: any } = entry; + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id.toLowerCase()]; +@@ -454,7 +454,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe + + forEach(product.extensionImportantTips, entry => { + let { key: id, value } = entry; +- const { pattern } = value; ++ const { pattern }: any = value; + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id.toLowerCase()]; +@@ -631,6 +631,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe + ('extensionsAssistant/fileExtensionsSuggestionIgnore', StorageScope.GLOBAL, '[]')); + const mimeTypes = result[1]; + ++ // NOTE@coder we are handling these notifications. ++ fileExtensionSuggestionIgnoreList.push(...[ ++ "c", "cpp", "cc", "go", "php", "py", "rb", "rs", ++ ]); ++ + if (fileExtension) { + fileExtension = fileExtension.substr(1); // Strip the dot + } +diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +index a38def022f..216ef27590 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts ++++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +@@ -171,8 +171,8 @@ actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: U + const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL); + actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); + +-const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); +-actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel); ++// const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); ++// actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel); + + const disableAllAction = new SyncActionDescriptor(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL); + actionRegistry.registerWorkbenchAction(disableAllAction, 'Extensions: Disable All Installed Extensions', ExtensionsLabel); +diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +index e8e1133868..3dc3c07c04 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css ++++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +@@ -142,6 +142,7 @@ + padding-left: 6px; + flex: 1; + min-width: fit-content; ++ min-width: -moz-fit-content; + } + + .extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) { +diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/markdown.css b/src/vs/workbench/parts/extensions/electron-browser/media/markdown.css +index 521ae95b08..9c0cfb035d 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/media/markdown.css ++++ b/src/vs/workbench/parts/extensions/electron-browser/media/markdown.css +@@ -3,21 +3,21 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-body { ++.vscode-body { + padding: 10px 20px; + line-height: 22px; + } + +-img { ++.vscode-body img { + max-width: 100%; + max-height: 100%; + } + +-a { ++.vscode-body a { + text-decoration: none; + } + +-a:hover { ++.vscode-body a:hover { + text-decoration: underline; + } + +diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +index fb9af88c96..cea8b08112 100644 +--- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts ++++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +@@ -614,7 +614,7 @@ class SaveExtensionHostProfileAction extends Action { + let dataToWrite: object = profileInfo.data; + + if (this._environmentService.isBuilt) { +- const profiler = await import('v8-inspect-profiler'); ++ const profiler = require('v8-inspect-profiler'); + // when running from a not-development-build we remove + // absolute filenames because we don't want to reveal anything + // about users. We also append the `.txt` suffix to make it +diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +index b4ab2011a4..7de8be820f 100644 +--- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts ++++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +@@ -39,6 +39,8 @@ import { groupBy } from 'vs/base/common/collections'; + import { Schemas } from 'vs/base/common/network'; + import * as resources from 'vs/base/common/resources'; + import { CancellationToken } from 'vs/base/common/cancellation'; ++import { getFetchUrl } from 'coder/api'; ++import { events } from 'coder/analytics'; + + interface IExtensionStateProvider { + (extension: Extension): T; +@@ -122,7 +124,7 @@ class Extension implements IExtension { + } + + get iconUrl(): string { +- return this.galleryIconUrl || this.localIconUrl || this.defaultIconUrl; ++ return this.galleryIconUrl || this.localIconUrl ? getFetchUrl(this.galleryIconUrl || this.localIconUrl) : this.defaultIconUrl; + } + + get iconUrlFallback(): string { +@@ -144,18 +146,20 @@ class Extension implements IExtension { + return this.gallery && this.gallery.assets.icon.fallbackUri; + } + ++ // NOTE@coder: changed from require.toUrl to require.context. + private get defaultIconUrl(): string { ++ const context = (require as any).context("../electron-browser/media/", true, /.*/); + if (this.type === LocalExtensionType.System) { + if (this.local.manifest && this.local.manifest.contributes) { + if (Array.isArray(this.local.manifest.contributes.themes) && this.local.manifest.contributes.themes.length) { +- return require.toUrl('../electron-browser/media/theme-icon.png'); ++ return context('./theme-icon.png'); + } + if (Array.isArray(this.local.manifest.contributes.grammars) && this.local.manifest.contributes.grammars.length) { +- return require.toUrl('../electron-browser/media/language-icon.svg'); ++ return context('./language-icon.png'); + } + } + } +- return require.toUrl('../electron-browser/media/defaultIcon.png'); ++ return context('./defaultIcon.png'); + } + + get repository(): string { +@@ -176,6 +180,10 @@ class Extension implements IExtension { + return this.gallery ? this.gallery.installCount : null; + } + ++ get stars(): number { ++ return this.gallery ? this.gallery.stars : null; ++ } ++ + get rating(): number { + return this.gallery ? this.gallery.rating : null; + } +@@ -621,11 +629,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, + const ids = [], names = []; + for (const installed of this.installed) { + if (installed.type === LocalExtensionType.User) { +- if (installed.uuid) { +- ids.push(installed.uuid); +- } else { ++ // if (installed.uuid) { ++ // ids.push(installed.uuid); ++ // } else { + names.push(installed.id); +- } ++ // } + } + } + +@@ -690,6 +698,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, + return TPromise.wrapError(new Error('Missing gallery')); + } + ++ events.ide.installExtension.track(extension.id); + return this.progressService.withProgress({ + location: ProgressLocation.Extensions, + title: nls.localize('installingMarketPlaceExtension', 'Installing extension from Marketplace....'), +@@ -714,6 +723,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, + return TPromise.wrapError(new Error('Missing local')); + } + ++ events.ide.uninstallExtension.track(ext.id); + this.logService.info(`Requested uninstalling the extension ${extension.id} from window ${this.windowService.getCurrentWindowId()}`); + return this.progressService.withProgress({ + location: ProgressLocation.Extensions, +@@ -741,7 +751,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, + } + + private checkAndEnableDisabledDependencies(extensionIdentifier: IExtensionIdentifier): TPromise { +- const extension = this.local.filter(e => areSameExtensions(extensionIdentifier, e.local.identifier))[0]; ++ // NOTE@coder: When installing multiple extensions, sometimes other extensions can have an undefined `local`. ++ const extension = this.local.filter(e => e.local && areSameExtensions(extensionIdentifier, e.local.identifier))[0]; + if (extension) { + const disabledDepencies = this.getExtensionsRecursively([extension], this.local, EnablementState.Enabled, { dependencies: true, pack: false }); + if (disabledDepencies.length) { +@@ -1048,4 +1059,4 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, + this.syncDelayer.cancel(); + this.disposables = dispose(this.disposables); + } +-} +\ No newline at end of file ++} +diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts +index 9db7cbedda..b9f65b0c2b 100644 +--- a/src/vs/workbench/parts/files/common/files.ts ++++ b/src/vs/workbench/parts/files/common/files.ts +@@ -157,7 +157,9 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { + } + + provideTextContent(resource: URI): TPromise { +- const fileOnDiskResource = URI.file(resource.fsPath); ++ // NOTE@coder: keep using conflict scheme so we can load it differently. ++ // const fileOnDiskResource = URI.file(resource.fsPath); ++ const fileOnDiskResource = resource; + + // Make sure our file from disk is resolved up to date + return this.resolveEditorModel(resource).then(codeEditorModel => { +@@ -181,7 +183,9 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { + } + + private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise { +- const fileOnDiskResource = URI.file(resource.fsPath); ++ // NOTE@coder: keep using conflict scheme so we can load it differently. ++ // const fileOnDiskResource = URI.file(resource.fsPath); ++ const fileOnDiskResource = resource; + + return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => { + let codeEditorModel = this.modelService.getModel(resource); +diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +index 4ed5466ae8..3c21272563 100644 +--- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts ++++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +@@ -142,13 +142,15 @@ function appendEditorTitleContextMenuItem(id: string, title: string, when: Conte + } + + // Editor Title Menu for Conflict Resolution ++// NOTE@coder use context to resolve icons. ++const context = (require as any).context("./media/", true, /.*/); + appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite disk contents"), { +- light: URI.parse(require.toUrl(`vs/workbench/parts/files/electron-browser/media/check.svg`)), +- dark: URI.parse(require.toUrl(`vs/workbench/parts/files/electron-browser/media/check-inverse.svg`)) ++ light: URI.parse(context(`./check.svg`)), ++ dark: URI.parse(context(`./check-inverse.svg`)) + }, -10, acceptLocalChangesCommand); + appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to content on disk"), { +- light: URI.parse(require.toUrl(`vs/workbench/parts/files/electron-browser/media/undo.svg`)), +- dark: URI.parse(require.toUrl(`vs/workbench/parts/files/electron-browser/media/undo-inverse.svg`)) ++ light: URI.parse(context(`./undo.svg`)), ++ dark: URI.parse(context(`./undo-inverse.svg`)) + }, -9, revertLocalChangesCommand); + + function appendSaveConflictEditorTitleAction(id: string, title: string, iconLocation: { dark: URI; light?: URI; }, order: number, command: ICommandHandler): void { +diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts +index 46d4ba300f..834f277486 100644 +--- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts ++++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts +@@ -546,7 +546,8 @@ class BaseDeleteFileAction extends BaseFileAction { + super('moveFileToTrash', MOVE_FILE_TO_TRASH_LABEL, fileService, notificationService, textFileService); + + this.tree = tree; +- this.useTrash = useTrash && elements.every(e => !paths.isUNC(e.resource.fsPath)); // on UNC shares there is no trash ++ // NOTE@coder: skip the trash for now. ++ this.useTrash = false; //useTrash && elements.every(e => !paths.isUNC(e.resource.fsPath)); // on UNC shares there is no trash + + this._updateEnablement(); + } +diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +index c3fc2504f7..8c7992e852 100644 +--- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css ++++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +@@ -52,6 +52,7 @@ + + .explorer-viewlet .panel-header .count { + min-width: fit-content; ++ min-width: -moz-fit-content; + } + + .explorer-viewlet .panel-header .monaco-count-badge.hidden { +diff --git a/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts +index 27a3136bda..21ec2a0c67 100644 +--- a/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts ++++ b/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts +@@ -33,6 +33,7 @@ import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; + import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + import { once } from 'vs/base/common/event'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; ++import { ConflictResolution } from 'coder/common'; // NOTE@coder. + + export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; + export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; +@@ -80,8 +81,11 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I + let isActiveEditorSaveConflictResolution = false; + let activeConflictResolutionResource: URI; + ++ // NOTE@coder: show buttons if the resource is a conflict resource. ++ // `originalInput` will *not* be `ResourceEditorInput` because we registered ++ // a provider for the conflict scheme which results in it becoming a `FileEditorInput`. + const activeInput = this.editorService.activeEditor; +- if (activeInput instanceof DiffEditorInput && activeInput.originalInput instanceof ResourceEditorInput && activeInput.modifiedInput instanceof FileEditorInput) { ++ if (activeInput instanceof DiffEditorInput) { // && activeInput.originalInput instanceof ResourceEditorInput && activeInput.modifiedInput instanceof FileEditorInput) { + const resource = activeInput.originalInput.getResource(); + if (resource && resource.scheme === CONFLICT_RESOLUTION_SCHEME) { + isActiveEditorSaveConflictResolution = true; +@@ -328,7 +332,8 @@ export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: + modelService.updateModel(model.textEditorModel, createTextBufferFactoryFromSnapshot(localModelSnapshot)); + + // Trigger save +- return model.save().then(() => { ++ // NOTE@coder: add conflict resolution. ++ return model.save({ accept: ConflictResolution.ACCEPT_LOCAL, force: true }).then(() => { + + // Reopen file input + return editorService.openEditor({ resource: model.getResource() }, group).then(() => { +@@ -357,15 +362,20 @@ export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource: + clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + + // Revert on model +- return model.revert().then(() => { ++ // NOTE@coder: add conflict resolution. ++ return model.save({ accept: ConflictResolution.ACCEPT_DISK, force: true }).then(() => { + +- // Reopen file input +- return editorService.openEditor({ resource: model.getResource() }, group).then(() => { ++ // NOTE@coder: revert so dirty indicator goes away. ++ return model.revert().then(() => { + +- // Clean up +- group.closeEditor(editor); +- editor.dispose(); +- reference.dispose(); ++ // Reopen file input ++ return editorService.openEditor({ resource: model.getResource() }, group).then(() => { ++ ++ // Clean up ++ group.closeEditor(editor); ++ editor.dispose(); ++ reference.dispose(); ++ }); + }); + }); + }); +diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +index f64cf9a303..898b8269e4 100644 +--- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts ++++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +@@ -56,6 +56,7 @@ import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } + import { INotificationService } from 'vs/platform/notification/common/notification'; + import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; + import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; ++import { Upload } from 'coder/upload'; + + export class FileDataSource implements IDataSource { + constructor( +@@ -762,7 +763,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { + @IInstantiationService instantiationService: IInstantiationService, + @ITextFileService private textFileService: ITextFileService, + @IWindowService private windowService: IWindowService, +- @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService ++ @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + ) { + super(stat => this.statToResource(stat), instantiationService); + +@@ -921,6 +922,11 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { + } + + private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { ++ // NOTE@coder: modified to work with browser uploads. ++ const uri = (target instanceof ExplorerItem ? target : target.roots[0]).resource; ++ const uploader = this.instantiationService.createInstance(Upload); ++ return uploader.uploadDropped(originalEvent.browserEvent as DragEvent, uri); ++ + const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); + + // Check for dropped external files to be folders +diff --git a/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts +index b0df3ba7f1..7b68143490 100644 +--- a/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts ++++ b/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts +@@ -26,7 +26,10 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { + ) { + super(); + let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); +- outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Main"), file: URI.file(join(environmentService.logsPath, `main.log`)), log: true }); ++ // NOTE@coder: This channel only seems to be used when loading the app ++ // (electron-main/main.ts and electron-main/app.ts) and we skip all of that, ++ // so it is never actually created or written to. ++ // outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Main"), file: URI.file(join(environmentService.logsPath, `main.log`)), log: true }); + outputChannelRegistry.registerChannel({ id: Constants.sharedLogChannelId, label: nls.localize('sharedLog', "Shared"), file: URI.file(join(environmentService.logsPath, `sharedprocess.log`)), log: true }); + outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: URI.file(join(environmentService.logsPath, `renderer${windowService.getCurrentWindowId()}.log`)), log: true }); + +@@ -37,4 +40,4 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { + } + } + +-Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Starting); +\ No newline at end of file ++Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Starting); +diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts +index 54d7402f8a..514e5e14ef 100644 +--- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts ++++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts +@@ -78,6 +78,8 @@ export class OutputLinkProvider { + } + + private provideLinks(modelUri: URI): TPromise { ++ // TODO@coder: get this working. ++ return TPromise.wrap([]); + return this.getOrCreateWorker().withSyncedResources([modelUri]).then(linkComputer => { + return linkComputer.computeLinks(modelUri.toString()); + }); +diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts +index 37b449ca94..c488706bd0 100644 +--- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts ++++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts +@@ -183,6 +183,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out + + // Use one rotating file to check for main file reset + this.appender = new OutputAppender(this.id, this.file.fsPath); ++ + this.rotatingFilePath = `${outputChannelDescriptor.id}.1.log`; + this._register(watchOutputDirectory(paths.dirname(this.file.fsPath), logService, (eventType, file) => this.onFileChangedInOutputDirector(eventType, file))); + +diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +index b694bf98a1..f258e1051e 100644 +--- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts ++++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +@@ -955,8 +955,7 @@ export class DirtyDiffModel { + private _editorModel: ITextModel, + @ISCMService private scmService: ISCMService, + @IEditorWorkerService private editorWorkerService: IEditorWorkerService, +- @ITextModelService private textModelResolverService: ITextModelService, +- @IConfigurationService private configurationService: IConfigurationService ++ @ITextModelService private textModelResolverService: ITextModelService + ) { + this.diffDelayer = new ThrottledDelayer(200); + +@@ -998,7 +997,7 @@ export class DirtyDiffModel { + changes = []; + } + +- const diff = sortedDiff(this._changes, changes, compareChanges); ++ const diff = sortedDiff(this._changes || [], changes || [], compareChanges); + this._changes = changes; + + if (diff.length > 0) { +@@ -1017,9 +1016,7 @@ export class DirtyDiffModel { + return TPromise.as([]); // Files too large + } + +- const ignoreTrimWhitespace = this.configurationService.getValue('diffEditor.ignoreTrimWhitespace'); +- +- return this.editorWorkerService.computeDirtyDiff(originalURI, this._editorModel.uri, ignoreTrimWhitespace); ++ return this.editorWorkerService.computeDirtyDiff(originalURI, this._editorModel.uri, false); + }); + } + +diff --git a/src/vs/workbench/parts/terminal/browser/terminalTab.ts b/src/vs/workbench/parts/terminal/browser/terminalTab.ts +index 651f27fb81..a4132b433f 100644 +--- a/src/vs/workbench/parts/terminal/browser/terminalTab.ts ++++ b/src/vs/workbench/parts/terminal/browser/terminalTab.ts +@@ -9,6 +9,7 @@ import { Event, Emitter, anyEvent } from 'vs/base/common/event'; + import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; + import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; + import { IPartService, Position } from 'vs/workbench/services/part/common/partService'; ++import { generate } from 'vs/workbench/parts/terminal/common/terminalId'; + + const SPLIT_PANE_MIN_SIZE = 120; + +@@ -224,6 +225,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { + configHelper: ITerminalConfigHelper, + private _container: HTMLElement, + shellLaunchConfig: IShellLaunchConfig, ++ initialId: number = generate(), + @ITerminalService private readonly _terminalService: ITerminalService, + @IPartService private readonly _partService: IPartService + ) { +@@ -236,6 +238,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { + configHelper, + undefined, + shellLaunchConfig, ++ initialId, + true); + this._terminalInstances.push(instance); + this._initInstanceListeners(instance); +@@ -359,13 +362,15 @@ export class TerminalTab extends Disposable implements ITerminalTab { + public split( + terminalFocusContextKey: IContextKey, + configHelper: ITerminalConfigHelper, +- shellLaunchConfig: IShellLaunchConfig ++ shellLaunchConfig: IShellLaunchConfig, ++ id: number, + ): ITerminalInstance { + const instance = this._terminalService.createInstance( + terminalFocusContextKey, + configHelper, + undefined, + shellLaunchConfig, ++ id, + true); + this._terminalInstances.splice(this._activeInstanceIndex + 1, 0, instance); + this._initInstanceListeners(instance); +diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts +index f44f184e92..e687cea879 100644 +--- a/src/vs/workbench/parts/terminal/common/terminal.ts ++++ b/src/vs/workbench/parts/terminal/common/terminal.ts +@@ -208,7 +208,7 @@ export interface ITerminalService { + /** + * Creates a raw terminal instance, this should not be used outside of the terminal part. + */ +- createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; ++ createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, id:number, doCreateProcess: boolean): ITerminalInstance; + getInstanceFromId(terminalId: number): ITerminalInstance; + getInstanceFromIndex(terminalIndex: number): ITerminalInstance; + getTabLabels(): string[]; +@@ -233,6 +233,11 @@ export interface ITerminalService { + selectDefaultWindowsShell(): TPromise; + setWorkspaceShellAllowed(isAllowed: boolean): void; + ++ /** ++ * Restores terminals stored ++ */ ++ restore(): void; ++ + requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): void; + } + +@@ -258,7 +263,7 @@ export interface ITerminalTab { + setVisible(visible: boolean): void; + layout(width: number, height: number): void; + addDisposable(disposable: IDisposable): void; +- split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; ++ split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig, id: number): ITerminalInstance; + } + + export interface ITerminalDimensions { +@@ -483,7 +488,7 @@ export interface ITerminalInstance { + /** + * Focuses and pastes the contents of the clipboard into the terminal instance. + */ +- paste(): void; ++ paste(): Promise; + + /** + * Send text to the terminal instance. The text is written to the stdin of the underlying pty +@@ -594,7 +599,7 @@ export interface ITerminalProcessManager extends IDisposable { + + addDisposable(disposable: IDisposable); + dispose(immediate?: boolean); +- createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number); ++ createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, id: number); + write(data: string): void; + setDimensions(cols: number, rows: number): void; + } +diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts +index 667e5e94f3..4cbebc3c8b 100644 +--- a/src/vs/workbench/parts/terminal/common/terminalService.ts ++++ b/src/vs/workbench/parts/terminal/common/terminalService.ts +@@ -10,10 +10,19 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; + import { IPartService } from 'vs/workbench/services/part/common/partService'; + import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN } from 'vs/workbench/parts/terminal/common/terminal'; + import { TPromise } from 'vs/base/common/winjs.base'; +-import { IStorageService } from 'vs/platform/storage/common/storage'; ++import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; + import { FindReplaceState } from 'vs/editor/contrib/find/findState'; ++import { generate as generateTerminalId } from 'vs/workbench/parts/terminal/common/terminalId'; ++ ++export type TerminalStorage = ReadonlyArray<{ ++ // The name of the socket ++ readonly terminals: ReadonlyArray; ++}>; + + export abstract class TerminalService implements ITerminalService { ++ ++ private static readonly workbenchTerminalState = "workbench.terminal.state"; ++ + public _serviceBrand: any; + + protected _isShuttingDown: boolean; +@@ -23,7 +32,7 @@ export abstract class TerminalService implements ITerminalService { + protected _terminalTabs: ITerminalTab[]; + protected abstract _terminalInstances: ITerminalInstance[]; + private _findState: FindReplaceState; +- ++ private _restored: boolean; + private _activeTabIndex: number; + + public get activeTabIndex(): number { return this._activeTabIndex; } +@@ -48,6 +57,8 @@ export abstract class TerminalService implements ITerminalService { + public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } + protected readonly _onActiveInstanceChanged: Emitter = new Emitter(); + public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } ++ protected readonly _onTabAdded: Emitter = new Emitter(); ++ public get onTabAdded(): Event { return this._onTabAdded.event; } + protected readonly _onTabDisposed: Emitter = new Emitter(); + public get onTabDisposed(): Event { return this._onTabDisposed.event; } + +@@ -58,7 +69,7 @@ export abstract class TerminalService implements ITerminalService { + @IPanelService protected readonly _panelService: IPanelService, + @IPartService private readonly _partService: IPartService, + @ILifecycleService lifecycleService: ILifecycleService, +- @IStorageService protected readonly _storageService: IStorageService ++ @IStorageService protected readonly _storageService: IStorageService, + ) { + this._activeTabIndex = 0; + this._isShuttingDown = false; +@@ -70,6 +81,59 @@ export abstract class TerminalService implements ITerminalService { + this.onTabDisposed(tab => this._removeTab(tab)); + + this._handleContextKeys(); ++ ++ } ++ ++ public restore(): void { ++ if (this._restored) { ++ return; ++ } ++ this._restored = true; ++ let beingRestored: boolean = false; ++ const updateStorage = () => { ++ if (beingRestored) { ++ return; ++ } ++ const storage: TerminalStorage = this._terminalTabs.map((tab) => { ++ return { ++ terminals: tab.terminalInstances.map((instance) => instance.id), ++ }; ++ }); ++ ++ this._storageService.store(TerminalService.workbenchTerminalState, JSON.stringify(storage), StorageScope.GLOBAL); ++ }; ++ ++ this.onTabAdded((tab) => { ++ tab.onInstancesChanged(() => { ++ updateStorage(); ++ }); ++ ++ updateStorage(); ++ }); ++ ++ try { ++ const values: TerminalStorage = JSON.parse(this._storageService.get(TerminalService.workbenchTerminalState, StorageScope.GLOBAL, "[]")); ++ ++ beingRestored = true; ++ values.forEach((value, index) => { ++ const terminals = value.terminals; ++ if (terminals.length > 0) { ++ let terminal: ITerminalInstance; ++ for (let i = 0; i < terminals.length; i++) { ++ const terminalId = terminals[i]; ++ if (!terminal) { ++ terminal = this.createTerminal(undefined, undefined, terminalId); ++ } else { ++ this.splitInstance(terminal, undefined, terminalId); ++ } ++ } ++ } ++ }); ++ beingRestored = false; ++ } catch (ex) { ++ console.error(`Failed to restore terminals: ${ex}`); ++ } ++ + } + + private _handleContextKeys(): void { +@@ -83,9 +147,9 @@ export abstract class TerminalService implements ITerminalService { + } + + protected abstract _showTerminalCloseConfirmation(): TPromise; +- public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; ++ public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean, id?: number): ITerminalInstance; + public abstract createTerminalRenderer(name: string): ITerminalInstance; +- public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; ++ public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, id: number, doCreateProcess: boolean): ITerminalInstance; + public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; + public abstract selectDefaultWindowsShell(): TPromise; + public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; +@@ -256,13 +320,13 @@ export abstract class TerminalService implements ITerminalService { + this.setActiveTabByIndex(newIndex); + } + +- public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): void { ++ public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}, id?: number): void { + const tab = this._getTabForInstance(instanceToSplit); + if (!tab) { + return; + } + +- const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig); ++ const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig, id || generateTerminalId()); + this._initInstanceListeners(instance); + this._onInstancesChanged.fire(); + +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +index 378144e25f..f3547a7a45 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +@@ -12,7 +12,7 @@ import * as nls from 'vs/nls'; + import * as panel from 'vs/workbench/browser/panel'; + import * as platform from 'vs/base/common/platform'; + import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +-import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, DEFAULT_LINE_HEIGHT, DEFAULT_LETTER_SPACING, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED } from 'vs/workbench/parts/terminal/common/terminal'; ++import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, DEFAULT_LINE_HEIGHT, DEFAULT_LETTER_SPACING, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/parts/terminal/common/terminal'; + import { getTerminalDefaultShellUnixLike, getTerminalDefaultShellWindows } from 'vs/workbench/parts/terminal/node/terminal'; + import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; + import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +@@ -38,6 +38,7 @@ import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/term + import { TerminalPickerHandler } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; + import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; + import { setupTerminalMenu } from 'vs/workbench/parts/terminal/common/terminalMenu'; ++import { nativeClipboard } from 'coder/workbench'; + + const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); + +@@ -404,9 +405,11 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTer + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, ++ win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V }, + // Don't apply to Mac since cmd+v works + mac: { primary: null } +-}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); ++ // Conflicting expressions so never enabled ++}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, nativeClipboard.contextKey)), 'Terminal: Paste into Active Terminal', category); + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { + // Don't use ctrl+a by default as that would override the common go to start + // of prompt shell binding +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +index c051ff13e4..f6876322aa 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +@@ -30,6 +30,7 @@ import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminal + import { Command } from 'vs/editor/browser/editorExtensions'; + import { timeout } from 'vs/base/common/async'; + import { FindReplaceState } from 'vs/editor/contrib/find/findState'; ++import { nativeClipboard } from 'coder/workbench'; + + export const TERMINAL_PICKER_PREFIX = 'term '; + +@@ -556,19 +557,31 @@ export class TerminalPasteAction extends Action { + + public static readonly ID = TERMINAL_COMMAND_ID.PASTE; + public static readonly LABEL = nls.localize('workbench.action.terminal.paste', "Paste into Active Terminal"); +- public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.paste.short', "Paste"); ++ public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.paste.short', nativeClipboard.instance ? "Paste" : "Paste (Must use keybind)"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); ++ const update = (v: boolean) => { ++ if (v) { ++ this._setLabel("Paste"); ++ } else { ++ this._setLabel("Paste (Must use keybind)"); ++ } ++ this._setEnabled(v); ++ }; ++ update(nativeClipboard.isEnabled); ++ nativeClipboard.onChange((e) => { ++ update(e); ++ }); + } + + public run(event?: any): TPromise { + const instance = this.terminalService.getActiveOrCreateInstance(); + if (instance) { +- instance.paste(); ++ return instance.paste(); + } + return TPromise.as(void 0); + } +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +index 4d4f6645ad..ede8ff0c32 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +@@ -214,7 +214,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { + // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's + // safe to assume that this was used by accident as Sysnative does not + // exist and will break the terminal in non-WoW64 environments. +- if (platform.isWindows && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')) { ++ if (false && platform.isWindows && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')) { + const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase(); + if (shell.executable.toLowerCase().indexOf(sysnativePath) === 0) { + shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length)); +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +index f0ba1ed3a7..b38c87ab25 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +@@ -34,6 +34,7 @@ import { ILogService } from 'vs/platform/log/common/log'; + import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; + import { TerminalProcessManager } from './terminalProcessManager'; + import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; ++import { nativeClipboard } from 'coder/workbench'; + + // How long in milliseconds should an average frame take to render for a notification to appear + // which suggests the fallback DOM-based renderer +@@ -117,6 +118,7 @@ export class TerminalInstance implements ITerminalInstance { + private readonly _configHelper: TerminalConfigHelper, + private _container: HTMLElement, + private _shellLaunchConfig: IShellLaunchConfig, ++ id: number, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @INotificationService private readonly _notificationService: INotificationService, +@@ -134,7 +136,7 @@ export class TerminalInstance implements ITerminalInstance { + this._hadFocusOnExit = false; + this._isVisible = false; + this._isDisposed = false; +- this._id = TerminalInstance._idCounter++; ++ this._id = typeof id !== "undefined" ? id : TerminalInstance._idCounter++; + this._terminalHasTextContextKey = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.bindTo(this._contextKeyService); + this.disableLayout = false; + +@@ -263,7 +265,7 @@ export class TerminalInstance implements ITerminalInstance { + */ + protected async _createXterm(): Promise { + if (!Terminal) { +- Terminal = (await import('vscode-xterm')).Terminal; ++ Terminal = (require('vscode-xterm')).Terminal; + // Enable xterm.js addons + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); +@@ -615,9 +617,19 @@ export class TerminalInstance implements ITerminalInstance { + return this._xtermReadyPromise.then(() => this.focus(force)); + } + +- public paste(): void { ++ public async paste(): Promise { + this.focus(); +- document.execCommand('paste'); ++ ++ if (nativeClipboard.instance) { ++ try { ++ const text = await nativeClipboard.instance.readText(); ++ this.sendText(text, false); ++ } catch (ex) { ++ this._notificationService.error(`You must allow clipboard access to paste!`); ++ } ++ } else { ++ document.execCommand('paste'); ++ } + } + + public write(text: string): void { +@@ -730,7 +742,7 @@ export class TerminalInstance implements ITerminalInstance { + this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', true)); + } + +- if (platform.isWindows) { ++ if (false && platform.isWindows) { + this._processManager.ptyProcessReady.then(() => { + this._xtermReadyPromise.then(() => { + if (!this._isDisposed) { +@@ -743,7 +755,7 @@ export class TerminalInstance implements ITerminalInstance { + // Create the process asynchronously to allow the terminal's container + // to be created so dimensions are accurate + setTimeout(() => { +- this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows); ++ this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this.id); + }, 0); + } + +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +index d5ccc52fb7..bed7794981 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +@@ -114,7 +114,8 @@ export class TerminalPanel extends Panel { + this._updateTheme(); + } else { + return super.setVisible(visible).then(() => { +- // Check if instances were already restored as part of workbench restore ++ this._terminalService.restore(); ++ + if (this._terminalService.terminalInstances.length === 0) { + this._terminalService.createTerminal(); + } +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +index 818b359ea9..d8645c3369 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +@@ -86,7 +86,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { + public createProcess( + shellLaunchConfig: IShellLaunchConfig, + cols: number, +- rows: number ++ rows: number, ++ id: number, + ): void { + const extensionHostOwned = (this._configHelper.config).extHostProcess; + if (extensionHostOwned) { +@@ -120,7 +121,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { + terminalEnvironment.addTerminalEnvironmentKeys(env, locale); + + this._logService.debug(`Terminal process launching`, shellLaunchConfig, this.initialCwd, cols, rows, env); +- this._process = new TerminalProcess(shellLaunchConfig, this.initialCwd, cols, rows, env); ++ this._process = new TerminalProcess(shellLaunchConfig, this.initialCwd, cols, rows, env, id); + } + this.processState = ProcessState.LAUNCHING; + +diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +index fb61123763..b4b0e39d86 100644 +--- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts ++++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +@@ -29,6 +29,7 @@ import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; + import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; + import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; + import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; ++import { generate } from 'vs/workbench/parts/terminal/common/terminalId'; + + export class TerminalService extends AbstractTerminalService implements ITerminalService { + private _configHelper: TerminalConfigHelper; +@@ -71,13 +72,14 @@ export class TerminalService extends AbstractTerminalService implements ITermina + }); + } + +- public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { ++ public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean, id?: number): ITerminalInstance { + const terminalTab = this._instantiationService.createInstance(TerminalTab, + this._terminalFocusContextKey, + this._configHelper, + this._terminalContainer, +- shell); ++ shell, id || generate()); + this._terminalTabs.push(terminalTab); ++ this._onTabAdded.fire(terminalTab); + const instance = terminalTab.terminalInstances[0]; + terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); + terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); +@@ -95,8 +97,8 @@ export class TerminalService extends AbstractTerminalService implements ITermina + return this.createTerminal({ name, isRendererOnly: true }); + } + +- public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { +- const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); ++ public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, id: number, doCreateProcess: boolean): ITerminalInstance { ++ const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig, id); + this._onInstanceCreated.fire(instance); + return instance; + } +diff --git a/src/vs/workbench/parts/terminal/node/terminal.ts b/src/vs/workbench/parts/terminal/node/terminal.ts +index 7eaadc133a..c6ea0a7fb7 100644 +--- a/src/vs/workbench/parts/terminal/node/terminal.ts ++++ b/src/vs/workbench/parts/terminal/node/terminal.ts +@@ -33,7 +33,7 @@ export interface ITerminalChildProcess { + let _TERMINAL_DEFAULT_SHELL_UNIX_LIKE: string = null; + export function getTerminalDefaultShellUnixLike(): string { + if (!_TERMINAL_DEFAULT_SHELL_UNIX_LIKE) { +- let unixLikeTerminal = 'sh'; ++ let unixLikeTerminal = 'eval $SHELL'; + if (!platform.isWindows && process.env.SHELL) { + unixLikeTerminal = process.env.SHELL; + // Some systems have $SHELL set to /bin/false which breaks the terminal +@@ -57,7 +57,8 @@ export function getTerminalDefaultShellWindows(): string { + return _TERMINAL_DEFAULT_SHELL_WINDOWS; + } + +-if (platform.isLinux) { ++// Add this because we are always on a linux machine ++if (true || platform.isLinux) { + const file = '/etc/os-release'; + fileExists(file).then(exists => { + if (!exists) { +diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts +index 03beafef97..c48cd6528b 100644 +--- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts ++++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts +@@ -6,16 +6,19 @@ + import * as os from 'os'; + import * as path from 'path'; + import * as platform from 'vs/base/common/platform'; +-import * as pty from 'node-pty'; ++import * as ptyTypes from 'node-pty'; + import { Event, Emitter } from 'vs/base/common/event'; + import { ITerminalChildProcess } from 'vs/workbench/parts/terminal/node/terminal'; + import { IDisposable } from 'vs/base/common/lifecycle'; + import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; ++import { toString } from 'vs/workbench/parts/terminal/common/terminalId'; ++import { dtachLocation } from 'coder/server'; + + export class TerminalProcess implements ITerminalChildProcess, IDisposable { + private _exitCode: number; + private _closeTimeout: number; +- private _ptyProcess: pty.IPty; ++ // @ts-ignore ++ private _ptyProcess: ptyTypes.IPty; + private _currentTitle: string = ''; + + private readonly _onProcessData: Emitter = new Emitter(); +@@ -32,7 +35,8 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { + cwd: string, + cols: number, + rows: number, +- env: platform.IProcessEnvironment ++ env: platform.IProcessEnvironment, ++ id: number, + ) { + let shellName: string; + if (os.platform() === 'win32') { +@@ -43,7 +47,8 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { + shellName = 'xterm-256color'; + } + +- const options: pty.IPtyForkOptions = { ++ // @ts-ignore ++ const options: ptyTypes.IPtyForkOptions = { + name: shellName, + cwd, + env, +@@ -51,7 +56,9 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { + rows + }; + +- this._ptyProcess = pty.spawn(shellLaunchConfig.executable, shellLaunchConfig.args, options); ++ // This is here so the extensionHostProcess doesn't require node-pty ++ const pty = require("node-pty"); ++ this._ptyProcess = pty.spawn(`${dtachLocation} -A /tmp/${toString(id)} bash -c '${shellLaunchConfig.executable.replace(/'/g, "\\'")}'`, shellLaunchConfig.args, options); + this._ptyProcess.on('data', (data) => { + this._onProcessData.fire(data); + if (this._closeTimeout) { +@@ -97,7 +104,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { + if (this._closeTimeout) { + clearTimeout(this._closeTimeout); + } +- this._closeTimeout = setTimeout(() => this._kill(), 250); ++ this._closeTimeout = setTimeout(() => this._kill(), 250) as any; + } + + private _kill(): void { +diff --git a/src/vs/workbench/parts/terminal/node/windowsShellHelper.ts b/src/vs/workbench/parts/terminal/node/windowsShellHelper.ts +index 246480377f..6d5002d6bd 100644 +--- a/src/vs/workbench/parts/terminal/node/windowsShellHelper.ts ++++ b/src/vs/workbench/parts/terminal/node/windowsShellHelper.ts +@@ -42,7 +42,7 @@ export class WindowsShellHelper { + + this._isDisposed = false; + +- (import('windows-process-tree')).then(mod => { ++ Promise.resolve(require('windows-process-tree')).then(mod => { + if (this._isDisposed) { + return; + } +diff --git a/src/vs/workbench/parts/update/electron-browser/media/markdown.css b/src/vs/workbench/parts/update/electron-browser/media/markdown.css +index 2c2e0bb924..6f5b832a07 100644 +--- a/src/vs/workbench/parts/update/electron-browser/media/markdown.css ++++ b/src/vs/workbench/parts/update/electron-browser/media/markdown.css +@@ -3,21 +3,21 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-body { ++.vscode-body { + padding: 10px 20px; + line-height: 22px; + } + +-img { ++.vscode-body img { + max-width: 100%; + max-height: 100%; + } + +-a { ++.vscode-body a { + text-decoration: none; + } + +-a:hover { ++.vscode-body a:hover { + text-decoration: underline; + } + +diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +index 0d7075217d..fd32d7a3a3 100644 +--- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts ++++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +@@ -33,7 +33,7 @@ function renderBody( + body: string, + css: string + ): string { +- const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://'); ++ const styleSheetPath = ""; // require.toUrl('css-loader!style-loader!./media/markdown.css').replace('file://', 'vscode-core-resource://'); + return ` + + +diff --git a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts +index 4e79126712..a406c9c6b5 100644 +--- a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts ++++ b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts +@@ -20,7 +20,7 @@ const workbench = Registry.as(WorkbenchExtensio + + workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running); + +-if (platform.isWindows) { ++if (platform.isWindows && false) { + if (process.arch === 'ia32') { + workbench.registerWorkbenchContribution(Win3264BitContribution, LifecyclePhase.Running); + } +diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts +index 53ba1b4913..51c6b5d0a8 100644 +--- a/src/vs/workbench/parts/update/electron-browser/update.ts ++++ b/src/vs/workbench/parts/update/electron-browser/update.ts +@@ -377,7 +377,7 @@ export class UpdateContribution implements IGlobalActivity { + + // windows fast updates + private onUpdateUpdating(update: IUpdate): void { +- if (isWindows && product.target === 'user') { ++ if (false && isWindows && product.target === 'user') { + return; + } + +diff --git a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js +index 6cfbf3f08f..070ce0c803 100644 +--- a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js ++++ b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js +@@ -7,7 +7,23 @@ + 'use strict'; + + // @ts-ignore +- const ipcRenderer = require('electron').ipcRenderer; ++ // const ipcRenderer = require('electron').ipcRenderer; ++ const ipcRenderer = { ++ on: (channel, callback) => { ++ window.addEventListener("message", (event) => { ++ if (event.data.channel === channel) { ++ callback(event.data.channel, ...event.data.data); ++ } ++ }); ++ }, ++ sendToHost: (channel, ...args) => { ++ window.parent.postMessage({ ++ channel, ++ data: args, ++ id: document.body.id, ++ }, "*"); ++ }, ++ }; + + const registerVscodeResourceScheme = (function () { + let hasRegistered = false; +@@ -19,13 +35,13 @@ + hasRegistered = true; + + // @ts-ignore +- require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', { +- secure: true, +- bypassCSP: false, +- allowServiceWorkers: false, +- supportFetchAPI: true, +- corsEnabled: true +- }); ++ // require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', { ++ // secure: true, ++ // bypassCSP: false, ++ // allowServiceWorkers: false, ++ // supportFetchAPI: true, ++ // corsEnabled: true ++ // }); + }; + }()); + +@@ -115,66 +131,65 @@ + }); + }; + +- document.addEventListener('DOMContentLoaded', () => { +- ipcRenderer.on('baseUrl', (_event, value) => { +- initData.baseUrl = value; +- }); +- +- ipcRenderer.on('styles', (_event, variables, activeTheme) => { +- initData.styles = variables; +- initData.activeTheme = activeTheme; ++ ipcRenderer.on('baseUrl', (_event, value) => { ++ initData.baseUrl = value; ++ }); + +- // webview +- var target = getActiveFrame(); +- if (!target) { +- return; +- } +- var body = target.contentDocument.getElementsByTagName('body'); +- styleBody(body[0]); ++ ipcRenderer.on('styles', (_event, variables, activeTheme) => { ++ initData.styles = variables; ++ initData.activeTheme = activeTheme; + +- // iframe +- Object.keys(variables).forEach((variable) => { +- target.contentDocument.documentElement.style.setProperty(`--${variable}`, variables[variable]); +- }); +- }); ++ // webview ++ var target = getActiveFrame(); ++ if (!target) { ++ return; ++ } ++ var body = target.contentDocument.getElementsByTagName('body'); ++ styleBody(body[0]); + +- // propagate focus +- ipcRenderer.on('focus', () => { +- const target = getActiveFrame(); +- if (target) { +- target.contentWindow.focus(); +- } ++ // iframe ++ Object.keys(variables).forEach((variable) => { ++ target.contentDocument.documentElement.style.setProperty(`--${variable}`, variables[variable]); + }); ++ }); + +- // update iframe-contents +- ipcRenderer.on('content', (_event, data) => { +- const options = data.options; +- enableWrappedPostMessage = options && options.enableWrappedPostMessage; ++ // propagate focus ++ ipcRenderer.on('focus', () => { ++ const target = getActiveFrame(); ++ if (target) { ++ target.contentWindow.focus(); ++ } ++ }); + +- if (enableWrappedPostMessage) { +- registerVscodeResourceScheme(); +- } ++ // update iframe-contents ++ ipcRenderer.on('content', (_event, data) => { ++ const options = data.options; ++ enableWrappedPostMessage = options && options.enableWrappedPostMessage; + +- const text = data.contents; +- const newDocument = new DOMParser().parseFromString(text, 'text/html'); ++ if (enableWrappedPostMessage) { ++ registerVscodeResourceScheme(); ++ } + +- newDocument.querySelectorAll('a').forEach(a => { +- if (!a.title) { +- a.title = a.href; +- } +- }); ++ const text = data.contents; ++ const newDocument = new DOMParser().parseFromString(text, 'text/html'); + +- // set base-url if applicable +- if (initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) { +- const baseElement = newDocument.createElement('base'); +- baseElement.href = initData.baseUrl; +- newDocument.head.appendChild(baseElement); ++ newDocument.querySelectorAll('a').forEach(a => { ++ if (!a.title) { ++ a.title = a.href; + } ++ }); ++ ++ // set base-url if applicable ++ if (initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) { ++ const baseElement = newDocument.createElement('base'); ++ baseElement.href = initData.baseUrl; ++ newDocument.head.appendChild(baseElement); ++ } + +- // apply default script +- if (enableWrappedPostMessage && options.allowScripts) { +- const defaultScript = newDocument.createElement('script'); +- defaultScript.textContent = ` ++ // apply default script ++ if (enableWrappedPostMessage && options.allowScripts) { ++ const defaultScript = newDocument.createElement('script'); ++ defaultScript.textContent = ` + const acquireVsCodeApi = (function() { + const originalPostMessage = window.parent.postMessage.bind(window.parent); + let acquired = false; +@@ -206,21 +221,21 @@ + delete window.frameElement; + `; + +- if (newDocument.head.hasChildNodes()) { +- newDocument.head.insertBefore(defaultScript, newDocument.head.firstChild); +- } else { +- newDocument.head.appendChild(defaultScript); +- } ++ if (newDocument.head.hasChildNodes()) { ++ newDocument.head.insertBefore(defaultScript, newDocument.head.firstChild); ++ } else { ++ newDocument.head.appendChild(defaultScript); + } ++ } + +- // apply default styles +- const defaultStyles = newDocument.createElement('style'); +- defaultStyles.id = '_defaultStyles'; ++ // apply default styles ++ const defaultStyles = newDocument.createElement('style'); ++ defaultStyles.id = '_defaultStyles'; + +- const vars = Object.keys(initData.styles || {}).map(variable => { +- return `--${variable}: ${initData.styles[variable].replace(/[^\#\"\'\,\. a-z0-9\-\(\)]/gi, '')};`; +- }); +- defaultStyles.innerHTML = ` ++ const vars = Object.keys(initData.styles || {}).map(variable => { ++ return `--${variable}: ${initData.styles[variable].replace(/[^\#\"\'\,\. a-z0-9\-\(\)]/gi, '')};`; ++ }); ++ defaultStyles.innerHTML = ` + :root { ${vars.join('\n')} } + + body { +@@ -298,143 +313,145 @@ + background-color: rgba(111, 195, 223, 0.8); + } + `; +- if (newDocument.head.hasChildNodes()) { +- newDocument.head.insertBefore(defaultStyles, newDocument.head.firstChild); +- } else { +- newDocument.head.appendChild(defaultStyles); +- } ++ if (newDocument.head.hasChildNodes()) { ++ newDocument.head.insertBefore(defaultStyles, newDocument.head.firstChild); ++ } else { ++ newDocument.head.appendChild(defaultStyles); ++ } + +- styleBody(newDocument.body); ++ styleBody(newDocument.body); + +- const frame = getActiveFrame(); ++ const frame = getActiveFrame(); + +- // keep current scrollY around and use later +- var setInitialScrollPosition; +- if (firstLoad) { +- firstLoad = false; +- setInitialScrollPosition = (body, window) => { +- if (!isNaN(initData.initialScrollProgress)) { +- if (window.scrollY === 0) { +- window.scroll(0, body.clientHeight * initData.initialScrollProgress); +- } +- } +- }; +- } else { +- const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; +- setInitialScrollPosition = (body, window) => { ++ // keep current scrollY around and use later ++ var setInitialScrollPosition; ++ if (firstLoad) { ++ firstLoad = false; ++ setInitialScrollPosition = (body, window) => { ++ if (!isNaN(initData.initialScrollProgress)) { + if (window.scrollY === 0) { +- window.scroll(0, scrollY); ++ window.scroll(0, body.clientHeight * initData.initialScrollProgress); + } +- }; +- } +- +- // Clean up old pending frames and set current one as new one +- const previousPendingFrame = getPendingFrame(); +- if (previousPendingFrame) { +- previousPendingFrame.setAttribute('id', ''); +- document.body.removeChild(previousPendingFrame); +- } +- pendingMessages = []; +- +- const newFrame = document.createElement('iframe'); +- newFrame.setAttribute('id', 'pending-frame'); +- newFrame.setAttribute('frameborder', '0'); +- newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); +- newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; +- document.body.appendChild(newFrame); +- +- // write new content onto iframe +- newFrame.contentDocument.open('text/html', 'replace'); +- newFrame.contentWindow.onbeforeunload = () => { +- if (isInDevelopmentMode) { // Allow reloads while developing a webview +- ipcRenderer.sendToHost('do-reload'); +- return false; + } +- +- // Block navigation when not in development mode +- console.log('prevented webview navigation'); +- return false; + }; +- +- var onLoad = (contentDocument, contentWindow) => { +- if (contentDocument.body) { +- // Workaround for https://github.com/Microsoft/vscode/issues/12865 +- // check new scrollY and reset if neccessary +- setInitialScrollPosition(contentDocument.body, contentWindow); ++ } else { ++ const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; ++ setInitialScrollPosition = (body, window) => { ++ if (window.scrollY === 0) { ++ window.scroll(0, scrollY); + } ++ }; ++ } + +- const newFrame = getPendingFrame(); +- if (newFrame && newFrame.contentDocument === contentDocument) { +- const oldActiveFrame = getActiveFrame(); +- if (oldActiveFrame) { +- document.body.removeChild(oldActiveFrame); +- } +- newFrame.setAttribute('id', 'active-frame'); +- newFrame.style.visibility = 'visible'; +- newFrame.contentWindow.focus(); ++ // Clean up old pending frames and set current one as new one ++ const previousPendingFrame = getPendingFrame(); ++ if (previousPendingFrame) { ++ previousPendingFrame.setAttribute('id', ''); ++ document.body.removeChild(previousPendingFrame); ++ } ++ pendingMessages = []; ++ ++ const newFrame = document.createElement('iframe'); ++ newFrame.onerror = () => { ++ // Suppress errors ++ }; ++ newFrame.setAttribute('id', 'pending-frame'); ++ newFrame.setAttribute('frameborder', '0'); ++ newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-scripts allow-same-origin'); ++ newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; ++ document.body.appendChild(newFrame); ++ ++ // write new content onto iframe ++ newFrame.contentDocument.open('text/html', 'replace'); ++ newFrame.contentWindow.onbeforeunload = () => { ++ if (isInDevelopmentMode) { // Allow reloads while developing a webview ++ ipcRenderer.sendToHost('do-reload'); ++ return false; ++ } ++ ++ // Block navigation when not in development mode ++ console.log('prevented webview navigation'); ++ return false; ++ }; + +- contentWindow.addEventListener('scroll', handleInnerScroll); ++ var onLoad = (contentDocument, contentWindow) => { ++ if (contentDocument.body) { ++ // Workaround for https://github.com/Microsoft/vscode/issues/12865 ++ // check new scrollY and reset if neccessary ++ setInitialScrollPosition(contentDocument.body, contentWindow); ++ } + +- pendingMessages.forEach((data) => { +- contentWindow.postMessage(data, '*'); +- }); +- pendingMessages = []; ++ const newFrame = getPendingFrame(); ++ if (newFrame && newFrame.contentDocument === contentDocument) { ++ const oldActiveFrame = getActiveFrame(); ++ if (oldActiveFrame) { ++ document.body.removeChild(oldActiveFrame); + } +- }; ++ newFrame.setAttribute('id', 'active-frame'); ++ newFrame.style.visibility = 'visible'; ++ // newFrame.contentWindow.focus(); ++ ++ contentWindow.addEventListener('scroll', handleInnerScroll); ++ ++ pendingMessages.forEach((data) => { ++ contentWindow.postMessage(data, '*'); ++ }); ++ pendingMessages = []; ++ } ++ }; + ++ clearTimeout(loadTimeout); ++ loadTimeout = undefined; ++ loadTimeout = setTimeout(() => { + clearTimeout(loadTimeout); + loadTimeout = undefined; +- loadTimeout = setTimeout(() => { ++ onLoad(newFrame.contentDocument, newFrame.contentWindow); ++ }, 200); ++ ++ newFrame.contentWindow.addEventListener('load', function (e) { ++ if (loadTimeout) { + clearTimeout(loadTimeout); + loadTimeout = undefined; +- onLoad(newFrame.contentDocument, newFrame.contentWindow); +- }, 200); +- +- newFrame.contentWindow.addEventListener('load', function (e) { +- if (loadTimeout) { +- clearTimeout(loadTimeout); +- loadTimeout = undefined; +- onLoad(e.target, this); +- } +- }); ++ onLoad(e.target, this); ++ } ++ }); + +- // Bubble out link clicks +- newFrame.contentWindow.addEventListener('click', handleInnerClick); ++ // Bubble out link clicks ++ newFrame.contentWindow.addEventListener('click', handleInnerClick); + +- // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off +- // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden +- newFrame.contentDocument.write(''); +- newFrame.contentDocument.write(newDocument.documentElement.innerHTML); +- newFrame.contentDocument.close(); ++ // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off ++ // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden ++ newFrame.contentDocument.write(''); ++ newFrame.contentDocument.write(newDocument.documentElement.innerHTML); ++ newFrame.contentDocument.close(); + +- ipcRenderer.sendToHost('did-set-content'); +- }); ++ ipcRenderer.sendToHost('did-set-content'); ++ }); + +- // Forward message to the embedded iframe +- ipcRenderer.on('message', (_event, data) => { +- const pending = getPendingFrame(); +- if (pending) { +- pendingMessages.push(data); +- } else { +- const target = getActiveFrame(); +- if (target) { +- target.contentWindow.postMessage(data, '*'); +- } ++ // Forward message to the embedded iframe ++ ipcRenderer.on('message', (_event, data) => { ++ const pending = getPendingFrame(); ++ if (pending) { ++ pendingMessages.push(data); ++ } else { ++ const target = getActiveFrame(); ++ if (target) { ++ target.contentWindow.postMessage(data, '*'); + } +- }); ++ } ++ }); + +- ipcRenderer.on('initial-scroll-position', (_event, progress) => { +- initData.initialScrollProgress = progress; +- }); ++ ipcRenderer.on('initial-scroll-position', (_event, progress) => { ++ initData.initialScrollProgress = progress; ++ }); + +- ipcRenderer.on('devtools-opened', () => { +- isInDevelopmentMode = true; +- }); ++ ipcRenderer.on('devtools-opened', () => { ++ isInDevelopmentMode = true; ++ }); + +- // Forward messages from the embedded iframe +- window.onmessage = onMessage; ++ // Forward messages from the embedded iframe ++ // window.onmessage = onMessage; + +- // signal ready +- ipcRenderer.sendToHost('webview-ready', process.pid); +- }); ++ // signal ready ++ ipcRenderer.sendToHost('webview-ready'); + }()); +\ No newline at end of file +diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +index 898a782901..0da931ed6a 100644 +--- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts ++++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +@@ -54,8 +54,8 @@ export class WebviewElement extends Disposable { + this._webview.style.height = '0'; + this._webview.style.outline = '0'; + +- this._webview.preload = require.toUrl('./webview-pre.js'); +- this._webview.src = this._options.useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; ++ this._webview.preload = require('!!file-loader!./webview-pre.js'); ++ // this._webview.src = this._options.useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; + + this._ready = new Promise(resolve => { + const subscription = this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { +diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +index 56ffc97bcb..d5624d748d 100644 +--- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts ++++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +@@ -13,38 +13,151 @@ export function used() { + export default () => ` +
+
+-
+-

${escape(localize('welcomePage.vscode', "Visual Studio Code"))}

+-

${escape(localize({ key: 'welcomePage.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))}

+-
+-
++ ++
++
++
++

${escape(localize('welcomePage.customize', "Cloud Computing"))}

++
    ++
  • ++ ++
  • ++
  • ++ ++
  • ++
  • ++ ++
  • ++
++
++
++

${escape(localize('welcomePage.customize', "Keep Coding"))}

++
    ++
  • ++ ++
  • ++
  • ++ ++
  • ++
  • ++ ++
  • +
+
+

+diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css +index ee97160653..20793fde44 100644 +--- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css ++++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css +@@ -22,6 +22,13 @@ + flex-flow: row; + } + ++ ++@media(max-width:1000px) { ++ .monaco-workbench > .part.editor > .content .welcomePage .row { ++ display: unset; ++ } ++} ++ + .monaco-workbench > .part.editor > .content .welcomePage .row .section { + overflow: hidden; + } +@@ -76,6 +83,13 @@ + display: block; + } + ++.monaco-workbench > .part.editor > .content .welcomePage .description { ++ font-size: 1.8em; ++ display: block; ++ margin-top: 20px; ++ margin-bottom: 20px; ++} ++ + .hc-black .monaco-workbench > .part.editor > .content .welcomePage .subtitle { + font-weight: 200; + } +@@ -94,7 +108,7 @@ + } + + .monaco-workbench > .part.editor > .content .welcomePage .splash .section { +- margin-bottom: 5em; ++ margin-bottom: 2em; + } + + .monaco-workbench > .part.editor > .content .welcomePage .splash ul { +@@ -214,6 +228,65 @@ + display: inline; + } + ++.monaco-workbench > .part.editor > .content .welcomePage .commands { ++ margin-right: 5px; ++} ++ ++.monaco-workbench > .part.editor > .content .welcomePage .toprow { ++ display: flex; ++ flex-direction: row; ++} ++ ++@media (max-width:1000px) { ++ .monaco-workbench > .part.editor > .content .welcomePage .toprow { ++ display: unset; ++ } ++} ++ ++.monaco-workbench > .part.editor > .content .welcomePage .toprow .splash { ++ display: flex; ++ flex-direction: row; ++ max-width: fit-content; ++ max-width: -moz-fit-content; ++ margin-left: auto; ++} ++ ++@media (max-width:1000px) { ++ .monaco-workbench > .part.editor > .content .welcomePage .toprow .splash { ++ margin-left: 0; ++ } ++} ++ ++.monaco-workbench > .part.editor > .content .welcomePage .toprow .splash .section { ++ margin-left: 4em; ++} ++ ++@media (max-width:1000px) { ++ .monaco-workbench > .part.editor > .content .welcomePage .toprow .splash .section { ++ margin-left: 0; ++ margin-right: 8em; ++ } ++} ++ ++.monaco-workbench > .part.editor > .content .welcomePage .toprow .splash .section h2 { ++ margin-top: 0px; ++} ++ ++@keyframes fasttime-fade { ++ from {fill:#6c5829;} ++ to {fill:#f1d183;} ++} ++ ++.monaco-workbench > .part.editor > .content .welcomePage .commands .resources svg { ++ /* animation: fasttime-fade 1.5s 0s linear infinite alternate; */ ++ width: 25px; ++ margin-right: 10px; ++ float: left; ++ opacity: 0.5; ++ fill: currentColor; ++ height: 100%; ++} ++ + .monaco-workbench > .part.editor > .content .welcomePageContainer.max-height-685px .title { + display: none; + } +diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts +index 5599f8459e..d5c481f1af 100644 +--- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts ++++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts +@@ -18,7 +18,7 @@ const typeId = 'workbench.editors.walkThroughInput'; + const inputOptions: WalkThroughInputOptions = { + typeId, + name: localize('editorWalkThrough.title', "Interactive Playground"), +- resource: URI.parse(require.toUrl('./vs_code_editor_walkthrough.md')) ++ resource: URI.parse(require('file-loader!./vs_code_editor_walkthrough.md')) + .with({ scheme: Schemas.walkThrough }), + telemetryFrom: 'walkThrough' + }; +diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts +index 412a4cd9ba..791ae4a41c 100644 +--- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts ++++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts +@@ -16,6 +16,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; + import * as marked from 'vs/base/common/marked/marked'; + import { Schemas } from 'vs/base/common/network'; + import { Range } from 'vs/editor/common/core/range'; ++import 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page'; + + export class WalkThroughContentProvider implements ITextModelContentProvider, IWorkbenchContribution { + +@@ -31,13 +32,22 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW + public provideTextContent(resource: URI): TPromise { + const query = resource.query ? JSON.parse(resource.query) : {}; + const content: TPromise = (query.moduleId ? new TPromise((resolve, reject) => { +- require([query.moduleId], content => { +- try { +- resolve(content.default()); +- } catch (err) { +- reject(err); +- } ++ import("vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page").then((content) => { ++ resolve(content.default()); ++ }).catch((err) => { ++ reject(err); + }); ++ // require([query.moduleId], content => { ++ // try { ++ // resolve(content.default()); ++ // } catch (err) { ++ // reject(err); ++ // } ++ // }); ++ }) : resource.scheme !== "file" ? new TPromise((resolve, reject) => { ++ return fetch(resource.path).then((res) => res.text()).then((str) => { ++ resolve(str); ++ }).catch((err) => reject(err)); + }) : this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => content.value)); + return content.then(content => { + let codeEditorModel = this.modelService.getModel(resource); +@@ -64,7 +74,11 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi + } + + public provideTextContent(resource: URI): TPromise { +- return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => { ++ return (new TPromise((resolve, reject) => { ++ return fetch(resource.path).then((res) => res.text()).then((str) => { ++ resolve(str); ++ }).catch((err) => reject(err)); ++ })).then(content => { + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + const j = parseInt(resource.fragment); +@@ -81,17 +95,17 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi + return ''; + }; + +- const textBuffer = content.value.create(DefaultEndOfLine.LF); +- const lineCount = textBuffer.getLineCount(); +- const range = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1); +- const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); +- marked(markdown, { renderer }); ++ // const textBuffer = content.value.create(DefaultEndOfLine.LF); ++ // const lineCount = textBuffer.getLineCount(); ++ // const range = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1); ++ // const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); ++ marked(content, { renderer }); + + const modeId = this.modeService.getModeIdForLanguageName(languageName); + const mode = this.modeService.getOrCreateMode(modeId); + codeEditorModel = this.modelService.createModel(codeSnippet, mode, resource); + } else { +- this.modelService.updateModel(codeEditorModel, content.value); ++ this.modelService.updateModel(codeEditorModel, content); + } + + return codeEditorModel; +diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts +index 782c8d13dc..252fbb35e1 100644 +--- a/src/vs/workbench/services/configuration/node/configurationService.ts ++++ b/src/vs/workbench/services/configuration/node/configurationService.ts +@@ -349,7 +349,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat + return stat(folder.fsPath) + .then(workspaceStat => { + let ctime: number; +- if (isLinux) { ++ // Adding this because the stat will always be a linux stat ++ if (true || isLinux) { + ctime = workspaceStat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! + } else if (isMacintosh) { + ctime = workspaceStat.birthtime.getTime(); // macOS: birthtime is fine to use as is +@@ -461,23 +462,25 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat + this.cachedFolderConfigs = new ResourceMap(); + + const folders = this.workspace.folders; +- return this.loadFolderConfigurations(folders) +- .then((folderConfigurations) => { +- +- let workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations); +- const folderConfigurationModels = new ResourceMap(); +- folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); +- +- const currentConfiguration = this._configuration; +- this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null +- +- if (currentConfiguration) { +- const changedKeys = this._configuration.compare(currentConfiguration); +- this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE); +- } else { +- this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE))); +- } +- }); ++ return Promise.all([ ++ this.loadFolderConfigurations(folders), ++ this.userConfiguration.loaded, ++ ]).then((values) => { ++ const folderConfigurations = values[0]; ++ let workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations); ++ const folderConfigurationModels = new ResourceMap(); ++ folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); ++ ++ const currentConfiguration = this._configuration; ++ this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null ++ ++ if (currentConfiguration) { ++ const changedKeys = this._configuration.compare(currentConfiguration); ++ this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE); ++ } else { ++ this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE))); ++ } ++ }); + } + + private getWorkspaceConfigurationModel(folderConfigurations: ConfigurationModel[]): ConfigurationModel { +diff --git a/src/vs/workbench/services/configurationResolver/node/variableResolver.ts b/src/vs/workbench/services/configurationResolver/node/variableResolver.ts +index b91edb9639..e63dc5863b 100644 +--- a/src/vs/workbench/services/configurationResolver/node/variableResolver.ts ++++ b/src/vs/workbench/services/configurationResolver/node/variableResolver.ts +@@ -36,7 +36,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe + private _context: IVariableResolveContext, + private _envVariables: IProcessEnvironment = process.env + ) { +- if (isWindows) { ++ if (false && isWindows) { + this._envVariables = Object.create(null); + Object.keys(_envVariables).forEach(key => { + this._envVariables[key.toLowerCase()] = _envVariables[key]; +@@ -56,11 +56,11 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe + const result = objects.deepClone(config) as any; + + // hoist platform specific attributes to top level +- if (isWindows && result.windows) { ++ if (false && isWindows && result.windows) { + Object.keys(result.windows).forEach(key => result[key] = result.windows[key]); + } else if (isMacintosh && result.osx) { + Object.keys(result.osx).forEach(key => result[key] = result.osx[key]); +- } else if (isLinux && result.linux) { ++ } else if ((isWindows || isLinux) && result.linux) { + Object.keys(result.linux).forEach(key => result[key] = result.linux[key]); + } + +diff --git a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts +index 71239496da..f7ed8c56ab 100644 +--- a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts ++++ b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts +@@ -33,12 +33,12 @@ configurationRegistry.registerConfiguration({ + title: nls.localize('telemetryConfigurationTitle', "Telemetry"), + 'type': 'object', + 'properties': { +- 'telemetry.enableCrashReporter': { +- 'type': 'boolean', +- 'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to a Microsoft online service. \nThis option requires restart to take effect."), +- 'default': true, +- 'tags': ['usesOnlineServices'] +- } ++ // 'telemetry.enableCrashReporter': { ++ // 'type': 'boolean', ++ // 'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to a Microsoft online service. \nThis option requires restart to take effect."), ++ // 'default': true, ++ // 'tags': ['usesOnlineServices'] ++ // } + } + }); + +diff --git a/src/vs/workbench/services/decorations/browser/decorations.ts b/src/vs/workbench/services/decorations/browser/decorations.ts +index 3b5e58e218..5112c14575 100644 +--- a/src/vs/workbench/services/decorations/browser/decorations.ts ++++ b/src/vs/workbench/services/decorations/browser/decorations.ts +@@ -20,12 +20,15 @@ export interface IDecorationData { + readonly tooltip?: string; + readonly bubble?: boolean; + readonly source?: string; ++ ++ readonly element?: HTMLElement; + } + + export interface IDecoration { + readonly tooltip: string; + readonly labelClassName: string; + readonly badgeClassName: string; ++ readonly element?: HTMLElement; + update(data: IDecorationData): IDecoration; + } + +diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts +index b2f151ec7b..4597ed60a6 100644 +--- a/src/vs/workbench/services/decorations/browser/decorationsService.ts ++++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts +@@ -130,6 +130,17 @@ class DecorationStyles { + let labelClassName = rule.itemColorClassName; + let badgeClassName = rule.itemBadgeClassName; + let tooltip = data.filter(d => !isFalsyOrWhitespace(d.tooltip)).map(d => d.tooltip).join(' • '); ++ let wrapper: HTMLElement; ++ data.forEach((d) => { ++ if (d.element) { ++ if (!wrapper) { ++ wrapper = document.createElement("div"); ++ wrapper.classList.add("decorations-wrapper"); ++ } ++ ++ wrapper.append(d.element); ++ } ++ }); + + if (onlyChildren) { + // show items from its children only +@@ -140,6 +151,7 @@ class DecorationStyles { + return { + labelClassName, + badgeClassName, ++ element: wrapper, + tooltip, + update: (replace) => { + let newData = data.slice(); +diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +index c2c1ee52f5..d7e7090cb8 100644 +--- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts ++++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +@@ -110,7 +110,7 @@ export class DialogService implements IDialogService { + + // Linux: order of buttons is reverse + // macOS: also reverse, but the OS handles this for us! +- if (isLinux) { ++ if (isLinux || isWindows) { + buttons = buttons.reverse(); + buttonIndexMap = buttonIndexMap.reverse(); + } +@@ -126,7 +126,7 @@ export class DialogService implements IDialogService { + + // macOS/Linux: the cancel button should always be to the left of the primary action + // if we see more than 2 buttons, move the cancel one to the left of the primary +- if (!isWindows && buttons.length > 2 && cancelId !== 1) { ++ if (/*!isWindows && */buttons.length > 2 && cancelId !== 1) { + const cancelButton = buttons[cancelId]; + buttons.splice(cancelId, 1); + buttons.splice(1, 0, cancelButton); +diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts +index 0a5f2cfcea..5624c60241 100644 +--- a/src/vs/workbench/services/extensions/common/extensions.ts ++++ b/src/vs/workbench/services/extensions/common/extensions.ts +@@ -10,6 +10,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' + import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; + import { Event } from 'vs/base/common/event'; + import { URI } from 'vs/base/common/uri'; ++import { ExtensionHostExecutionEnvironment } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; + + export interface IExtensionDescription { + readonly id: string; +@@ -20,6 +21,7 @@ export interface IExtensionDescription { + readonly publisher: string; + readonly isBuiltin: boolean; + readonly isUnderDevelopment: boolean; ++ readonly executionEnvironment?: ExtensionHostExecutionEnvironment; + readonly extensionLocation: URI; + readonly extensionDependencies?: string[]; + readonly activationEvents?: string[]; +@@ -33,6 +35,7 @@ export interface IExtensionDescription { + url: string; + }; + enableProposedApi?: boolean; ++ download?: string; + } + + export const IExtensionService = createDecorator('extensionService'); +@@ -116,7 +119,9 @@ export class ExtensionPointContribution { + } + } + +-export const ExtensionHostLogFileName = 'exthost'; ++// NOTE@coder: add suffix to distinguish between Node and web worker logs. ++const suffix = typeof process === 'undefined' || typeof process.stdout === 'undefined' ? 'worker' : 'node'; ++export const ExtensionHostLogFileName = 'exthost.' + suffix; + + export interface IExtensionService { + _serviceBrand: any; +diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +index a9017211fd..e574656333 100644 +--- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts ++++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +@@ -5,6 +5,7 @@ + + 'use strict'; + ++import { Buffer } from "buffer"; + import * as nls from 'vs/nls'; + import { toErrorMessage } from 'vs/base/common/errorMessage'; + import * as objects from 'vs/base/common/objects'; +@@ -15,13 +16,13 @@ import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/l + import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; + import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; + import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +-import { ChildProcess, fork } from 'child_process'; ++import { ChildProcess } from 'child_process'; + import { ipcRenderer as ipc } from 'electron'; + import product from 'vs/platform/node/product'; + import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +-import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +-import { createServer, Server, Socket } from 'net'; ++import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; ++import { Socket } from 'net'; + import { Event, Emitter, debounceEvent, mapEvent, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event'; + import { IInitData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; + import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +@@ -35,13 +36,16 @@ import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; + import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; + import { ILogService } from 'vs/platform/log/common/log'; + import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +-import { getPathFromAmdModule } from 'vs/base/common/amd'; + import { timeout } from 'vs/base/common/async'; + import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; + import { ILabelService } from 'vs/platform/label/common/label'; + import { URI } from 'vs/base/common/uri'; + import { Schemas } from 'vs/base/common/network'; + import { onUnexpectedError } from 'vs/base/common/errors'; ++import { EventEmitter } from "events"; ++import { IInitData as ICoderInitData } from 'coder/common'; ++import { api, personalAccount, container } from 'coder/api'; ++import { nodePath, bootstrapForkLocation, wush, wushCredentials } from 'coder/server'; + + export interface IExtensionHostStarter { + readonly onCrashed: Event<[number, string]>; +@@ -50,6 +54,11 @@ export interface IExtensionHostStarter { + dispose(): void; + } + ++export enum ExtensionHostExecutionEnvironment { ++ Worker = "worker", ++ Node = "node", ++} ++ + export class ExtensionHostProcessWorker implements IExtensionHostStarter { + + private readonly _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); +@@ -67,7 +76,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + private _terminating: boolean; + + // Resources, in order they get acquired/created when .start() is called: +- private _namedPipeServer: Server; + private _inspectPort: number; + private _extensionHostProcess: ChildProcess; + private _extensionHostConnection: Socket; +@@ -76,6 +84,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + constructor( + private readonly _extensions: TPromise, + private readonly _extensionHostLogsLocation: URI, ++ private readonly _executionEnvironment: ExtensionHostExecutionEnvironment, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @INotificationService private readonly _notificationService: INotificationService, + @IWindowsService private readonly _windowsService: IWindowsService, +@@ -84,6 +93,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, ++ // @ts-ignore + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICrashReporterService private readonly _crashReporterService: ICrashReporterService, + @ILogService private readonly _logService: ILogService, +@@ -100,7 +110,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + this._lastExtensionHostError = null; + this._terminating = false; + +- this._namedPipeServer = null; + this._extensionHostProcess = null; + this._extensionHostConnection = null; + this._messageProtocol = null; +@@ -147,16 +156,14 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + } + + if (!this._messageProtocol) { +- this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { +- const pipeName = data[0]; +- const portData = data[1]; ++ this._messageProtocol = TPromise.join([this._tryFindDebugPort()]).then(async (data) => { ++ const portData = data[0]; + + const opts = { + env: objects.mixin(objects.deepClone(process.env), { + AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true, +- VSCODE_IPC_HOOK_EXTHOST: pipeName, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose), + VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : this._environmentService.log +@@ -188,7 +195,121 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + } + + // Run Extension Host as fork of current process +- this._extensionHostProcess = fork(getPathFromAmdModule(require, 'bootstrap-fork'), ['--type=extensionHost'], opts); ++ // Here we decide to run the extension process as a command via wush ++ // or in a web worker ++ ++ const setExtensionHostProcess = (dataListener: (cb: (data: Uint8Array) => void) => void, exitListener: (cb: (code: number) => void) => void): void => { ++ this._extensionHostProcess = new (class { ++ ++ public readonly stdout: EventEmitter; ++ public readonly stderr: EventEmitter; ++ private readonly emitter: EventEmitter; ++ ++ public constructor() { ++ this.emitter = new EventEmitter(); ++ ++ this.stdout = new EventEmitter(); ++ this.stderr = new EventEmitter(); ++ ++ [this.stdout, this.stderr].forEach((e) => { ++ (e as any).setEncoding = () => { ++ ++ }; ++ }); ++ ++ exitListener((code: number) => { ++ this.emitter.emit("exit", code); ++ }); ++ ++ dataListener((msg: Uint8Array) => { ++ this.stderr.emit("data", msg); ++ }); ++ } ++ ++ public on(event: string, listener: () => void) { ++ this.emitter.on(event, listener); ++ } ++ ++ }) as any; ++ }; ++ ++ let protocol: Protocol; ++ let shouldSendApiKey: boolean = false; ++ ++ switch (this._executionEnvironment) { ++ case ExtensionHostExecutionEnvironment.Worker: { ++ const ExtensionProcessWorker = require('worker-loader!vs/workbench/node/extensionHostProcess'); ++ const extensionProcessWorker = new ExtensionProcessWorker() as Worker; ++ extensionProcessWorker.onerror = (err) => { ++ console.log("Got worker err", err); ++ }; ++ setExtensionHostProcess((cb) => { ++ extensionProcessWorker.addEventListener("message", (event) => { ++ cb(event.data); ++ }); ++ }, (cb) => { ++ // We cannot tell if a worker dies ++ }); ++ shouldSendApiKey = true; ++ protocol = Protocol.fromWorker(extensionProcessWorker); ++ break; ++ } ++ case ExtensionHostExecutionEnvironment.Node: { ++ // The flag is to help identify it for debugging. ++ const sess = wush.execute({ ++ command: `bash -c 'VSCODE_ALLOW_IO=true` ++ + ` AMD_ENTRYPOINT=vs/workbench/node/extensionHostProcess` ++ + ` nice -n -18 ${nodePath} ${bootstrapForkLocation} --host'`, ++ }); ++ sess.onStderr((err) => { ++ console.error("Got stderr from node extension host:", err); ++ }); ++ protocol = Protocol.fromStdio({ ++ onMessage: (cb) => { ++ sess.onStdout((data: Buffer) => { ++ cb(Buffer.from(data)); ++ }, true); ++ }, ++ sendMessage: (data: Uint8Array) => { ++ while (data.length > 32000) { ++ const part = data.slice(0, 32000); ++ sess.sendStdin(part); ++ data = data.slice(32000); ++ } ++ sess.sendStdin(data); ++ }, ++ }); ++ setExtensionHostProcess((cb) => { ++ sess.onStdout((data) => { ++ cb(data); ++ }, true); ++ }, (cb) => { ++ sess.onDone((exitCode) => { ++ console.log("The session exited", exitCode); ++ cb(exitCode); ++ }); ++ }); ++ break; ++ } ++ default: { ++ throw new Error("invalid execution environment provided"); ++ } ++ } ++ ++ const initMessage: ICoderInitData = { ++ api: { ++ organization: personalAccount.organization.id_str, ++ container: container.id_str, ++ isProduction: api.environment.isProduction(), ++ key: shouldSendApiKey ? api.apiKey.key : undefined, ++ }, ++ // Only the worker needs Wush credentials. ++ wush: this._executionEnvironment === ExtensionHostExecutionEnvironment.Worker ++ ? wushCredentials ++ : undefined, ++ fetchUrl: product.fetchUrl, ++ }; ++ protocol.send(Buffer.from(JSON.stringify(initMessage))); + + // Catch all output coming from the extension host process + type Output = { data: string, format: string[] }; +@@ -214,9 +335,9 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + if (inspectorUrlMatch) { + console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black'); + } else { +- console.group('Extension Host'); +- console.log(output.data, ...output.format); +- console.groupEnd(); ++ // console.group('Extension Host'); ++ // console.log(output.data, ...output.format); ++ // console.groupEnd(); + } + }); + +@@ -257,11 +378,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + run: () => this._windowService.reloadWindow() + }] + ); +- }, 10000); ++ }, 10000) as any as number; + } + + // Initialize extension host process with hand shakes +- return this._tryExtHostHandshake().then((protocol) => { ++ return this._tryExtHostHandshake(protocol).then((protocol) => { + clearTimeout(startupTimeoutHandle); + return protocol; + }); +@@ -271,22 +392,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + return this._messageProtocol; + } + +- /** +- * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. +- */ +- private _tryListenOnPipe(): Promise { +- return new Promise((resolve, reject) => { +- const pipeName = generateRandomPipeName(); +- +- this._namedPipeServer = createServer(); +- this._namedPipeServer.on('error', reject); +- this._namedPipeServer.listen(pipeName, () => { +- this._namedPipeServer.removeListener('error', reject); +- resolve(pipeName); +- }); +- }); +- } +- + /** + * Find a free port if extension host debugging is enabled. + */ +@@ -317,28 +422,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + }); + } + +- private _tryExtHostHandshake(): Promise { ++ private _tryExtHostHandshake(protocol: Protocol): Promise { + + return new Promise((resolve, reject) => { +- +- // Wait for the extension host to connect to our named pipe +- // and wrap the socket in the message passing protocol +- let handle = setTimeout(() => { +- this._namedPipeServer.close(); +- this._namedPipeServer = null; +- reject('timeout'); +- }, 60 * 1000); +- +- this._namedPipeServer.on('connection', socket => { +- clearTimeout(handle); +- this._namedPipeServer.close(); +- this._namedPipeServer = null; +- this._extensionHostConnection = socket; +- resolve(new Protocol(this._extensionHostConnection)); +- }); +- ++ resolve(protocol); + }).then((protocol) => { +- + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. + return new Promise((resolve, reject) => { +@@ -348,10 +436,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + }, 60 * 1000); + + const disposable = protocol.onMessage(msg => { +- + if (isMessageOfType(msg, MessageType.Ready)) { + // 1) Extension Host is ready to receive messages, initialize it +- this._createExtHostInitData().then(data => protocol.send(Buffer.from(JSON.stringify(data)))); ++ this._createExtHostInitData().then(data => { ++ protocol.send(Buffer.from(JSON.stringify(data))); ++ }); + return; + } + +@@ -380,8 +469,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + } + + private _createExtHostInitData(): TPromise { +- return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensions]) +- .then(([telemetryInfo, extensionDescriptions]) => { ++ return TPromise.join([this._extensions]) ++ .then(([extensionDescriptions]) => { + const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; + const workspace = this._contextService.getWorkspace(); + const r: IInitData = { +@@ -403,7 +492,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + extensions: extensionDescriptions, + // Send configurations scopes only in development mode. + configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData, +- telemetryInfo, ++ telemetryInfo: { ++ instanceId: "1", ++ machineId: "1", ++ sessionId: "1", ++ }, + logLevel: this._logService.getLevel(), + logsLocation: this._extensionHostLogsLocation + }; +@@ -504,10 +597,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { + } + + private _cleanResources(): void { +- if (this._namedPipeServer) { +- this._namedPipeServer.close(); +- this._namedPipeServer = null; +- } ++ // if (this._namedPipeServer) { ++ // this._namedPipeServer.close(); ++ // this._namedPipeServer = null; ++ // } + if (this._extensionHostConnection) { + this._extensionHostConnection.end(); + this._extensionHostConnection = null; +diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +index cff2054ab5..5302b01786 100644 +--- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts ++++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +@@ -17,7 +17,7 @@ export class ExtensionHostProfiler { + } + + public start(): TPromise { +- return TPromise.wrap(import('v8-inspect-profiler')).then(profiler => { ++ return TPromise.wrap(require('v8-inspect-profiler')).then(profiler => { + return profiler.startProfiling({ port: this._port }).then(session => { + return { + stop: () => { +diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +index de147469de..e6d86ea493 100644 +--- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts ++++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +@@ -27,7 +27,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + import { IStorageService } from 'vs/platform/storage/common/storage'; + import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +-import { ExtensionHostProcessWorker, IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; ++import { ExtensionHostProcessWorker, IExtensionHostStarter, ExtensionHostExecutionEnvironment } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; + import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; + import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; + import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +@@ -47,6 +47,9 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; + import { isEqualOrParent } from 'vs/base/common/resources'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; ++import { AssetType as ExtensionAssetType } from 'vs/platform/extensionManagement/node/extensionGalleryService'; ++import { api } from 'coder/api'; ++import { systemExtensionsLocation } from 'coder/workbench'; + + // Enable to see detailed message communication between window and extension host + const LOG_EXTENSION_HOST_COMMUNICATION = false; +@@ -55,7 +58,9 @@ const LOG_USE_COLORS = true; + let _SystemExtensionsRoot: string = null; + function getSystemExtensionsRoot(): string { + if (!_SystemExtensionsRoot) { +- _SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); ++ // TODO: update this to a realistic path ++ _SystemExtensionsRoot = systemExtensionsLocation; ++ // _SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); + } + return _SystemExtensionsRoot; + } +@@ -297,7 +302,7 @@ export class ExtensionService extends Disposable implements IExtensionService { + @IExtensionManagementService private extensionManagementService: IExtensionManagementService + ) { + super(); +- this._extensionHostLogsLocation = URI.file(path.posix.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`)); ++ this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`)); + this._registry = null; + this._installedExtensionsReady = new Barrier(); + this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; +@@ -392,11 +397,27 @@ export class ExtensionService extends Disposable implements IExtensionService { + private _startExtensionHostProcess(initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + +- const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions(), this._extensionHostLogsLocation); +- const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents); +- extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); +- extHostProcessManager.onDidChangeResponsiveState((responsiveState) => this._onResponsiveStateChanged(responsiveState)); +- this._extensionHostProcessManagers.push(extHostProcessManager); ++ Object.keys(ExtensionHostExecutionEnvironment).forEach((key) => { ++ const executionValue = ExtensionHostExecutionEnvironment[key]; ++ ++ const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions().then((extensions) => { ++ const validExts = extensions.filter((extension) => { ++ let env = extension.executionEnvironment || ExtensionHostExecutionEnvironment.Node; ++ if (product.extensionExecutionEnvironments && product.extensionExecutionEnvironments[extension.id]) { ++ env = product.extensionExecutionEnvironments[extension.id]; ++ } ++ ++ return env === executionValue; ++ }); ++ ++ return validExts; ++ }), this._extensionHostLogsLocation, executionValue); ++ const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents); ++ extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); ++ extHostProcessManager.onDidChangeResponsiveState((responsiveState) => this._onResponsiveStateChanged(responsiveState)); ++ this._extensionHostProcessManagers.push(extHostProcessManager); ++ }); ++ + } + + private _onExtensionHostCrashed(code: number, signal: string): void { +@@ -583,9 +604,10 @@ export class ExtensionService extends Disposable implements IExtensionService { + // --- impl + + private _scanAndHandleExtensions(): void { +- + this._scanExtensions() +- .then(allExtensions => this._getRuntimeExtensions(allExtensions)) ++ .then(allExtensions => { ++ return this._getRuntimeExtensions(allExtensions); ++ }) + .then(allExtensions => { + this._registry = new ExtensionDescriptionRegistry(allExtensions); + +@@ -620,6 +642,7 @@ export class ExtensionService extends Disposable implements IExtensionService { + if (result.hasOwnProperty(userExtension.id)) { + log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); + } ++ userExtension.download = api.environment.publicBucket(`extensions/${userExtension.id}/${userExtension.version}/${ExtensionAssetType.VSIX}`); + result[userExtension.id] = userExtension; + }); + development.forEach(developedExtension => { +@@ -654,7 +677,7 @@ export class ExtensionService extends Disposable implements IExtensionService { + }); + } + +- const enableProposedApiForAll = !this._environmentService.isBuilt || ++ const enableProposedApiForAll = true || !this._environmentService.isBuilt || + (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong.indexOf('Insiders') >= 0) || + (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); + +@@ -686,14 +709,14 @@ export class ExtensionService extends Disposable implements IExtensionService { + + if (extensionsToDisable.length) { + return this.extensionManagementService.getInstalled(LocalExtensionType.User) +- .then(installed => { +- const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); +- return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); +- }) +- .then(() => { +- this._storageService.store(BetterMergeDisabledNowKey, true); +- return runtimeExtensions; +- }); ++ .then(installed => { ++ const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); ++ return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); ++ }) ++ .then(() => { ++ this._storageService.store(BetterMergeDisabledNowKey, true); ++ return runtimeExtensions; ++ }); + } else { + return runtimeExtensions; + } +@@ -958,7 +981,6 @@ export class ExtensionService extends Disposable implements IExtensionService { + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentLocationURI.fsPath, false, true, translations), log + ); + } +- + return TPromise.join([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { + const system = extensionDescriptions[0]; + const user = extensionDescriptions[1]; +diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts +index 4e45241c0b..cee4b45ed3 100644 +--- a/src/vs/workbench/services/files/electron-browser/fileService.ts ++++ b/src/vs/workbench/services/files/electron-browser/fileService.ts +@@ -10,7 +10,7 @@ import * as fs from 'fs'; + import * as os from 'os'; + import * as crypto from 'crypto'; + import * as assert from 'assert'; +-import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot, IFilesConfiguration, IFileSystemProviderRegistrationEvent, IFileSystemProvider } from 'vs/platform/files/common/files'; ++import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot, IFilesConfiguration, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IContentProvider } from 'vs/platform/files/common/files'; + import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/files'; + import { isEqualOrParent } from 'vs/base/common/paths'; + import { ResourceMap } from 'vs/base/common/map'; +@@ -28,7 +28,7 @@ import * as pfs from 'vs/base/node/pfs'; + import * as encoding from 'vs/base/node/encoding'; + import * as flow from 'vs/base/node/flow'; + import { FileWatcher as UnixWatcherService } from 'vs/workbench/services/files/node/watcher/unix/watcherService'; +-import { FileWatcher as WindowsWatcherService } from 'vs/workbench/services/files/node/watcher/win32/watcherService'; ++// import { FileWatcher as WindowsWatcherService } from 'vs/workbench/services/files/node/watcher/win32/watcherService'; + import { toFileChangesEvent, normalize, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; + import { Event, Emitter } from 'vs/base/common/event'; + import { FileWatcher as NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/watcherService'; +@@ -45,6 +45,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; + import product from 'vs/platform/node/product'; + import { IEncodingOverride, ResourceEncodings } from 'vs/workbench/services/files/electron-browser/encoding'; + import { createReadableOfSnapshot } from 'vs/workbench/services/files/electron-browser/streams'; ++import { ConflictResolution } from 'coder/common'; // NOTE@coder. + + class BufferPool { + +@@ -76,6 +77,12 @@ export interface IFileServiceTestOptions { + encodingOverride?: IEncodingOverride[]; + } + ++let instantiationListeners: Array<(fileService: FileService) => void> = []; ++ ++export function onInstantiation(cb: (fileService: FileService) => void): void { ++ instantiationListeners.push(cb); ++} ++ + export class FileService extends Disposable implements IFileService { + + _serviceBrand: any; +@@ -104,6 +111,7 @@ export class FileService extends Disposable implements IFileService { + private undeliveredRawFileChangesEvents: IRawFileChange[]; + + private _encoding: ResourceEncodings; ++ private readonly contentProviders: Map; + + constructor( + private contextService: IWorkspaceContextService, +@@ -120,10 +128,13 @@ export class FileService extends Disposable implements IFileService { + this.activeFileChangesWatchers = new ResourceMap<{ unwatch: Function, count: number }>(); + this.fileChangesWatchDelayer = new ThrottledDelayer(FileService.FS_EVENT_DELAY); + this.undeliveredRawFileChangesEvents = []; ++ this.contentProviders = new Map(); + + this._encoding = new ResourceEncodings(textResourceConfigurationService, environmentService, contextService, this.options.encodingOverride); + + this.registerListeners(); ++ ++ instantiationListeners.forEach((i) => i(this)); + } + + get encoding(): ResourceEncodings { +@@ -214,18 +225,39 @@ export class FileService extends Disposable implements IFileService { + + // legacy watcher + else { +- let watcherIgnoredPatterns: string[] = []; +- if (configuration.files && configuration.files.watcherExclude) { +- watcherIgnoredPatterns = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); +- } +- +- if (isWindows) { +- const legacyWindowsWatcher = new WindowsWatcherService(this.contextService, watcherIgnoredPatterns, e => this._onFileChanges.fire(e), err => this.handleError(err), this.environmentService.verbose); +- this.activeWorkspaceFileChangeWatcher = toDisposable(legacyWindowsWatcher.startWatching()); +- } else { +- const legacyUnixWatcher = new UnixWatcherService(this.contextService, this.configurationService, e => this._onFileChanges.fire(e), err => this.handleError(err), this.environmentService.verbose); +- this.activeWorkspaceFileChangeWatcher = toDisposable(legacyUnixWatcher.startWatching()); +- } ++ // NOTE@coder: no windows. ++ // let watcherIgnoredPatterns: string[] = []; ++ // if (configuration.files && configuration.files.watcherExclude) { ++ // watcherIgnoredPatterns = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); ++ // } ++ ++ // if (isWindows) { ++ // const legacyWindowsWatcher = new WindowsWatcherService(this.contextService, watcherIgnoredPatterns, e => this._onFileChanges.fire(e), err => this.handleError(err), this.environmentService.verbose); ++ // this.activeWorkspaceFileChangeWatcher = toDisposable(legacyWindowsWatcher.startWatching()); ++ // } else { ++ const legacyUnixWatcher = new UnixWatcherService( ++ this.contextService, ++ this.configurationService, ++ e => this._onFileChanges.fire(e), ++ (err, final) => { ++ this.handleError(err); ++ if (final) { ++ this.notificationService.prompt( ++ Severity.Error, ++ "File watcher failed to start after retrying for some time.", ++ [{ ++ label: "Restart File Watcher", ++ run: () => { ++ legacyUnixWatcher.startWatching(); ++ }, ++ }], ++ ); ++ } ++ }, ++ this.environmentService.verbose, ++ ); ++ this.activeWorkspaceFileChangeWatcher = toDisposable(legacyUnixWatcher.startWatching()); ++ // } + } + } + +@@ -233,6 +265,24 @@ export class FileService extends Disposable implements IFileService { + throw new Error('not implemented'); + } + ++ registerContentProvider(scheme: string, provider: IContentProvider): IDisposable { ++ if (this.contentProviders.has(scheme)) { ++ this.contentProviders.get(scheme).push(provider); ++ } else { ++ this.contentProviders.set(scheme, [provider]); ++ } ++ ++ return { ++ dispose: (): void => { ++ const arr = this.contentProviders.get(scheme); ++ const index = arr.indexOf(provider); ++ if (index !== -1) { ++ arr.splice(index, 1); ++ } ++ }, ++ }; ++ } ++ + canHandleResource(resource: uri): boolean { + return resource.scheme === Schemas.file; + } +@@ -268,13 +318,15 @@ export class FileService extends Disposable implements IFileService { + streamContent.value.on('error', err => reject(err)); + streamContent.value.on('end', _ => resolve(result)); + ++ // NOTE@coder: internally we wait for the ready event. ++ (streamContent.value as any).emit('ready'); ++ + return result; + }); + }); + } + + resolveStreamContent(resource: uri, options?: IResolveContentOptions): TPromise { +- + // Guard early against attempts to resolve an invalid file path + if (resource.scheme !== Schemas.file || !resource.fsPath) { + return TPromise.wrapError(new FileOperationError( +@@ -415,6 +467,16 @@ export class FileService extends Disposable implements IFileService { + } + + private resolveFileData(resource: uri, options: IResolveContentOptions, token: CancellationToken): TPromise { ++ if (this.contentProviders.has(resource.scheme)) { ++ const providers = this.contentProviders.get(resource.scheme); ++ for (let i = 0; i < providers.length; i++) { ++ const provider = providers[i]; ++ const data = provider.resolve(resource); ++ if (typeof data !== "undefined") { ++ return data; ++ } ++ } ++ } + + const chunkBuffer = BufferPool._64K.acquire(); + +@@ -572,61 +634,56 @@ export class FileService extends Disposable implements IFileService { + return this.doUpdateContent(resource, value, options); + } + ++ // NOTE@coder: restructured for better performance. + private doUpdateContent(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): TPromise { + const absolutePath = this.toAbsolutePath(resource); ++ const encodingToWrite = this._encoding.getWriteEncoding(resource, options.encoding); + +- // 1.) check file for writing +- return this.checkFileBeforeWriting(absolutePath, options).then(exists => { +- let createParentsPromise: TPromise; +- if (exists) { +- createParentsPromise = TPromise.as(null); +- } else { +- createParentsPromise = pfs.mkdirp(paths.dirname(absolutePath)); +- } +- +- // 2.) create parents as needed +- return createParentsPromise.then(() => { ++ // NOTE@coder: our provider doesn't use the encoding and has its own ++ // conflict resolution, so we can skip checking both here. ++ let checkPromise: TPromise; ++ if (!options.createFile && this.contentProviders.has(resource.scheme)) { ++ checkPromise = TPromise.as(false); ++ } else { ++ checkPromise = this.checkFileBeforeWriting(absolutePath, options).then((exists) => { + const encodingToWrite = this._encoding.getWriteEncoding(resource, options.encoding); +- let addBomPromise: TPromise = TPromise.as(false); +- + // UTF_16 BE and LE as well as UTF_8 with BOM always have a BOM + if (encodingToWrite === encoding.UTF16be || encodingToWrite === encoding.UTF16le || encodingToWrite === encoding.UTF8_with_bom) { +- addBomPromise = TPromise.as(true); ++ return TPromise.as(true); + } + + // Existing UTF-8 file: check for options regarding BOM + else if (exists && encodingToWrite === encoding.UTF8) { + if (options.overwriteEncoding) { +- addBomPromise = TPromise.as(false); // if we are to overwrite the encoding, we do not preserve it if found ++ return TPromise.as(false); // if we are to overwrite the encoding, we do not preserve it if found + } else { +- addBomPromise = encoding.detectEncodingByBOM(absolutePath).then(enc => enc === encoding.UTF8); // otherwise preserve it if found ++ return encoding.detectEncodingByBOM(absolutePath).then(enc => enc === encoding.UTF8); // otherwise preserve it if found + } + } + +- // 3.) check to add UTF BOM +- return addBomPromise.then(addBom => { +- +- // 4.) set contents and resolve +- if (!exists || !isWindows) { +- return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); +- } ++ return TPromise.as(false); ++ }); ++ } + +- // On Windows and if the file exists, we use a different strategy of saving the file +- // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows +- // (see https://github.com/Microsoft/vscode/issues/931) and prevent removing alternate data streams +- // (see https://github.com/Microsoft/vscode/issues/6363) +- else { ++ const doUpdate = (addBom: boolean, createFile: boolean): TPromise => { ++ // NOTE@coder: add conflict resolution with options.accept. ++ // Also the Windows check has been entirely removed. ++ return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, undefined, options.accept, createFile); ++ }; + +- // 4.) truncate +- return pfs.truncate(absolutePath, 0).then(() => { ++ // NOTE@coder: Save first and then handle errors afterward. This lets us ++ // skip a bit of work for most saves. ++ return checkPromise.then((addBom) => { ++ return doUpdate(addBom, options.createFile).then(null, (error) => { ++ if (error.code === 'ENOENT') { ++ return pfs.mkdirp(paths.dirname(absolutePath)).then(() => { ++ return doUpdate(addBom, true); ++ }); ++ } + +- // 5.) set contents (with r+ mode) and resolve +- return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }); +- }); +- } +- }); ++ return TPromise.wrapError(error); + }); +- }).then(null, error => { ++ }).then(null, (error) => { + if (error.code === 'EACCES' || error.code === 'EPERM') { + return TPromise.wrapError(new FileOperationError( + nls.localize('filePermission', "Permission denied writing to file ({0})", resource.toString(true)), +@@ -639,7 +696,12 @@ export class FileService extends Disposable implements IFileService { + }); + } + +- private doSetContentsAndResolve(resource: uri, absolutePath: string, value: string | ITextSnapshot, addBOM: boolean, encodingToWrite: string, options?: { mode?: number; flag?: string; }): TPromise { ++ // NOTE@coder: add conflict resolution. ++ private doSetContentsAndResolve( ++ resource: uri, absolutePath: string, value: string | ITextSnapshot, ++ addBOM: boolean, encodingToWrite: string, options?: { mode?: number; flag?: string; }, ++ accept?: ConflictResolution, createFile?: boolean, ++ ): TPromise { + let writeFilePromise: TPromise; + + // Configure encoding related options as needed +@@ -651,10 +713,24 @@ export class FileService extends Disposable implements IFileService { + }; + } + +- if (typeof value === 'string') { +- writeFilePromise = pfs.writeFile(absolutePath, value, writeFileOptions); +- } else { +- writeFilePromise = pfs.writeFile(absolutePath, createReadableOfSnapshot(value), writeFileOptions); ++ // NOTE@coder: use custom content provider for saving if one exists. ++ if (!createFile && this.contentProviders.has(resource.scheme)) { ++ const providers = this.contentProviders.get(resource.scheme); ++ for (let i = 0; i < providers.length; i++) { ++ const provider = providers[i]; ++ writeFilePromise = provider.save(resource, accept); ++ if (typeof writeFilePromise !== "undefined") { ++ break; ++ } ++ } ++ } ++ ++ if (typeof writeFilePromise === "undefined") { ++ if (typeof value === 'string') { ++ writeFilePromise = pfs.writeFile(absolutePath, value, writeFileOptions); ++ } else { ++ writeFilePromise = pfs.writeFile(absolutePath, createReadableOfSnapshot(value), writeFileOptions); ++ } + } + + // set contents +@@ -679,7 +755,7 @@ export class FileService extends Disposable implements IFileService { + return this.updateContent(uri.file(tmpPath), value, writeOptions).then(() => { + + // 3.) invoke our CLI as super user +- return TPromise.wrap(import('sudo-prompt')).then(sudoPrompt => { ++ return TPromise.wrap(require('sudo-prompt')).then(sudoPrompt => { + return new TPromise((c, e) => { + const promptOptions = { + name: this.environmentService.appNameLong.replace('-', ''), +@@ -748,7 +824,7 @@ export class FileService extends Disposable implements IFileService { + } + + // Create file +- return this.updateContent(resource, content).then(result => { ++ return this.updateContent(resource, content, { createFile: true }).then(result => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); +@@ -776,8 +852,9 @@ export class FileService extends Disposable implements IFileService { + } + + private checkFileBeforeWriting(absolutePath: string, options: IUpdateContentOptions = Object.create(null), ignoreReadonly?: boolean): TPromise { +- return pfs.exists(absolutePath).then(exists => { +- if (exists) { ++ // NOTE@coder: we can just use the stat to see if the file exists. ++ // return pfs.exists(absolutePath).then(exists => { ++ // if (exists) { + return pfs.stat(absolutePath).then(stat => { + if (stat.isDirectory()) { + return TPromise.wrapError(new Error('Expected file is actually a directory')); +@@ -795,7 +872,17 @@ export class FileService extends Disposable implements IFileService { + // check for size is a weaker check because it can return a false negative if the file has changed + // but to the same length. This is a compromise we take to avoid having to produce checksums of + // the file content for comparison which would be much slower to compute. +- if (typeof options.mtime === 'number' && typeof options.etag === 'string' && options.mtime < stat.mtime.getTime() && options.etag !== etag(stat.size, options.mtime)) { ++ const providers = this.contentProviders.get("file"); ++ let handled = false; ++ for (let i = 0; i < providers.length; i++) { ++ const provider = providers[i]; ++ if (provider.willHandle(uri.file(absolutePath))) { ++ handled = true; ++ break; ++ } ++ } ++ ++ if (!handled && typeof options.mtime === 'number' && typeof options.etag === 'string' && options.mtime < stat.mtime.getTime() && options.etag !== etag(stat.size, options.mtime)) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE, options)); + } + +@@ -816,17 +903,22 @@ export class FileService extends Disposable implements IFileService { + return this.readOnlyError(options); + } + +- return exists; ++ return true; + }); + }); + } + +- return TPromise.as(exists); ++ return TPromise.as(true); ++ }, (error) => { ++ if (error.code === "ENOENT") { ++ return TPromise.as(false); ++ } ++ return TPromise.wrapError(error); + }); +- } ++ // } + +- return TPromise.as(exists); +- }); ++ // return TPromise.as(exists); ++ // }); + } + + private readOnlyError(options: IUpdateContentOptions): TPromise { +@@ -916,11 +1008,11 @@ export class FileService extends Disposable implements IFileService { + return this.doDelete(resource, options && options.recursive); + } + +- private doMoveItemToTrash(resource: uri): TPromise { ++ private async doMoveItemToTrash(resource: uri): TPromise { + const absolutePath = resource.fsPath; + + const shell = (require('electron') as Electron.RendererInterface).shell; // workaround for being able to run tests out of VSCode debugger +- const result = shell.moveItemToTrash(absolutePath); ++ const result = await shell.moveItemToTrash(absolutePath); + if (!result) { + return TPromise.wrapError(new Error(isWindows ? nls.localize('binFailed', "Failed to move '{0}' to the recycle bin", paths.basename(absolutePath)) : nls.localize('trashFailed', "Failed to move '{0}' to the trash", paths.basename(absolutePath)))); + } +diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts +index 7c91d6c73e..d3346595c1 100644 +--- a/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts ++++ b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts +@@ -4,11 +4,13 @@ + *--------------------------------------------------------------------------------------------*/ + 'use strict'; + +-import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; + import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; + import { ChokidarWatcherService } from 'vs/workbench/services/files/node/watcher/unix/chokidarWatcherService'; ++import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; ++import { ChannelServer } from 'vs/base/parts/ipc/node/ipc'; + +-const server = new Server(); ++const protocol = Protocol.fromStream(process.stdin, process.stdout); ++const server = new ChannelServer(protocol); + const service = new ChokidarWatcherService(); + const channel = new WatcherChannel(service); +-server.registerChannel('watcher', channel); +\ No newline at end of file ++server.registerChannel('watcher', channel); +diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +index 1c9aff61c6..30b561464f 100644 +--- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts ++++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +@@ -5,7 +5,7 @@ + + 'use strict'; + +-import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; ++import { getNextTickChannel, ChannelClient } from 'vs/base/parts/ipc/node/ipc'; + import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; + import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; + import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; +@@ -16,7 +16,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur + import { Schemas } from 'vs/base/common/network'; + import { filterEvent } from 'vs/base/common/event'; + import { IWatchError } from 'vs/workbench/services/files/node/watcher/unix/watcher'; +-import { getPathFromAmdModule } from 'vs/base/common/amd'; ++import { nodePath, bootstrapForkLocation, wush } from 'coder/server'; ++import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; + + export class FileWatcher { + private static readonly MAX_RESTARTS = 5; +@@ -30,7 +31,7 @@ export class FileWatcher { + private contextService: IWorkspaceContextService, + private configurationService: IConfigurationService, + private onFileChanges: (changes: FileChangesEvent) => void, +- private errorLogger: (msg: string) => void, ++ private errorLogger: (msg: string, final?: boolean) => void, + private verboseLogging: boolean + ) { + this.isDisposed = false; +@@ -39,23 +40,12 @@ export class FileWatcher { + } + + public startWatching(): () => void { +- const args = ['--type=watcherService']; +- +- const client = new Client( +- getPathFromAmdModule(require, 'bootstrap-fork'), +- { +- serverName: 'File Watcher (chokidar)', +- args, +- env: { +- AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/unix/watcherApp', +- PIPE_LOGGING: 'true', +- VERBOSE_LOGGING: this.verboseLogging +- } +- } +- ); +- this.toDispose.push(client); +- +- client.onDidProcessExit(() => { ++ const protocol = Protocol.fromSession(wush.execute({ ++ // The flag is to help identify it for debugging. ++ command: `bash -c 'VSCODE_ALLOW_IO=true` ++ + ` AMD_ENTRYPOINT=vs/workbench/services/files/node/watcher/unix/watcherApp` ++ + ` ${nodePath} ${bootstrapForkLocation} --watcher'`, ++ }), () => { + // our watcher app should never be completed because it keeps on watching. being in here indicates + // that the watcher process died and we want to restart it here. we only do it a max number of times + if (!this.isDisposed) { +@@ -64,10 +54,14 @@ export class FileWatcher { + this.restartCounter++; + this.startWatching(); + } else { +- this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!'); ++ this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!', true); + } + } +- }, null, this.toDispose); ++ ++ this.toDispose.forEach((d) => d.dispose()); ++ }); ++ const client = new ChannelClient(protocol); ++ this.toDispose.push(client); + + const channel = getNextTickChannel(client.getChannel('watcher')); + this.service = new WatcherChannelClient(channel); +diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts +index 0e3f6d5c06..f720bc57cd 100644 +--- a/src/vs/workbench/services/search/node/rawSearchService.ts ++++ b/src/vs/workbench/services/search/node/rawSearchService.ts +@@ -446,16 +446,21 @@ export class SearchService implements IRawSearchService { + * TODO@rob - Is this really needed? + */ + private preventCancellation(promise: CancelablePromise): CancelablePromise { ++ // @ts-ignore + return new class implements CancelablePromise { + cancel() { + // Do nothing + } ++ // @ts-ignore + then(resolve, reject) { + return promise.then(resolve, reject); + } + catch(reject?) { + return this.then(undefined, reject); + } ++ cancelableThen(resolve, reject) { ++ return createCancelablePromise(_ => this.then(resolve, reject)); ++ } + }; + } + } +diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +index 0d2c576b0d..bb8a9aac01 100644 +--- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts ++++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +@@ -4,7 +4,8 @@ + *--------------------------------------------------------------------------------------------*/ + + import * as cp from 'child_process'; +-import { rgPath } from 'vscode-ripgrep'; ++import * as path from 'path'; ++// import { rgPath } from 'vscode-ripgrep'; + + import { isMacintosh as isMac } from 'vs/base/common/platform'; + import * as glob from 'vs/base/common/glob'; +@@ -14,6 +15,9 @@ import { normalizeNFD } from 'vs/base/common/normalization'; + import { IFolderSearch, IRawSearch } from './search'; + import { foldersToIncludeGlobs, foldersToRgExcludeGlobs } from './ripgrepTextSearch'; + ++// NOTE@coder: use rg provided by our mount. ++const rgPath = path.join(path.dirname(process.execPath), "rg"); ++ + // If vscode-ripgrep is in an .asar file, then the binary is unpacked. + const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); + +diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +index 94751dc40b..5f96dd74e1 100644 +--- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts ++++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +@@ -18,9 +18,12 @@ import * as encoding from 'vs/base/node/encoding'; + import * as extfs from 'vs/base/node/extfs'; + import { IRange, Range } from 'vs/editor/common/core/range'; + import { IProgress, ITextSearchPreviewOptions, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; +-import { rgPath } from 'vscode-ripgrep'; ++// import { rgPath } from 'vscode-ripgrep'; + import { FileMatch, IFolderSearch, IRawSearch, ISerializedFileMatch, ISerializedSearchSuccess } from './search'; + ++// NOTE@coder: use rg provided by our mount. ++const rgPath = path.join(path.dirname(process.execPath), "rg"); ++ + // If vscode-ripgrep is in an .asar file, then the binary is unpacked. + const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); + +@@ -63,7 +66,7 @@ export class RipgrepEngine { + this.postProcessExclusions = glob.parseToAsync(rgArgs.siblingClauses, { trimForExclusions: true }); + } + +- const cwd = platform.isWindows ? 'c:/' : '/'; ++ const cwd = /*platform.isWindows ? 'c:/' :*/ '/'; + const escapedArgs = rgArgs.args + .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) + .join(' '); +diff --git a/src/vs/workbench/services/search/node/searchApp.ts b/src/vs/workbench/services/search/node/searchApp.ts +index 293705f38b..a971b19658 100644 +--- a/src/vs/workbench/services/search/node/searchApp.ts ++++ b/src/vs/workbench/services/search/node/searchApp.ts +@@ -5,11 +5,13 @@ + + 'use strict'; + +-import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; + import { SearchChannel } from './searchIpc'; + import { SearchService } from './rawSearchService'; ++import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; ++import { ChannelServer } from 'vs/base/parts/ipc/node/ipc'; + +-const server = new Server(); ++const protocol = Protocol.fromStream(process.stdin, process.stdout); ++const server = new ChannelServer(protocol); + const service = new SearchService(); + const channel = new SearchChannel(service); +-server.registerChannel('search', channel); +\ No newline at end of file ++server.registerChannel('search', channel); +diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts +index f1869966ca..252061caa3 100644 +--- a/src/vs/workbench/services/search/node/searchService.ts ++++ b/src/vs/workbench/services/search/node/searchService.ts +@@ -18,8 +18,7 @@ import * as strings from 'vs/base/common/strings'; + import { URI as uri } from 'vs/base/common/uri'; + import { TPromise } from 'vs/base/common/winjs.base'; + import * as pfs from 'vs/base/node/pfs'; +-import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; +-import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; ++import { getNextTickChannel, ChannelClient } from 'vs/base/parts/ipc/node/ipc'; + import { Range } from 'vs/editor/common/core/range'; + import { FindMatch, ITextModel } from 'vs/editor/common/model'; + import { IModelService } from 'vs/editor/common/services/modelService'; +@@ -33,6 +32,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten + import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; + import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; + import { ISearchChannel, SearchChannelClient } from './searchIpc'; ++import { nodePath, bootstrapForkLocation, wush } from 'coder/server'; ++import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; + + export class SearchService extends Disposable implements ISearchService { + public _serviceBrand: any; +@@ -409,34 +410,15 @@ export class DiskSearch implements ISearchResultProvider { + private raw: IRawSearchService; + + constructor(verboseLogging: boolean, timeout: number = 60 * 60 * 1000, searchDebug?: IDebugParams) { +- const opts: IIPCOptions = { +- serverName: 'Search', +- timeout: timeout, +- args: ['--type=searchService'], +- // See https://github.com/Microsoft/vscode/issues/27665 +- // Pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`. +- // e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host +- // results in the forked process inheriting `--inspect-brk=xxx`. +- freshExecArgv: true, +- env: { +- AMD_ENTRYPOINT: 'vs/workbench/services/search/node/searchApp', +- PIPE_LOGGING: 'true', +- VERBOSE_LOGGING: verboseLogging +- } +- }; +- +- if (searchDebug) { +- if (searchDebug.break && searchDebug.port) { +- opts.debugBrk = searchDebug.port; +- } else if (!searchDebug.break && searchDebug.port) { +- opts.debug = searchDebug.port; +- } +- } +- +- const client = new Client( +- getPathFromAmdModule(require, 'bootstrap-fork'), +- opts); +- ++ const protocol = Protocol.fromSession(wush.execute({ ++ // The flag is to help identify it for debugging. ++ command: `bash -c 'VSCODE_ALLOW_IO=true` ++ + ` AMD_ENTRYPOINT=vs/workbench/services/search/node/searchApp` ++ + ` ${nodePath} ${bootstrapForkLocation} --search'`, ++ }), () => { ++ // Nothin ++ }); ++ const client = new ChannelClient(protocol); + const channel = getNextTickChannel(client.getChannel('search')); + this.raw = new SearchChannelClient(channel); + } +diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +index 8dc3f78076..3c687de502 100644 +--- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts ++++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +@@ -12,9 +12,9 @@ import { Event, Emitter } from 'vs/base/common/event'; + import { TPromise } from 'vs/base/common/winjs.base'; + import { onUnexpectedError } from 'vs/base/common/errors'; + import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +-import { ITokenizationSupport, TokenizationRegistry, IState, LanguageId, TokenMetadata } from 'vs/editor/common/modes'; ++import { ITokenizationSupport, TokenizationRegistry, IState, LanguageId, TokenMetadata, StandardTokenType } from 'vs/editor/common/modes'; + import { IModeService } from 'vs/editor/common/services/modeService'; +-import { StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, ITokenTypeMap, StandardTokenType } from 'vscode-textmate'; ++import { StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, ITokenTypeMap } from 'vscode-textmate'; + import { IWorkbenchThemeService, ITokenColorizationRule } from 'vs/workbench/services/themes/common/workbenchThemeService'; + import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; + import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; +@@ -112,13 +112,13 @@ export class TMLanguageRegistration { + const tokenType = tokenTypes[scope]; + switch (tokenType) { + case 'string': +- this.tokenTypes[scope] = StandardTokenType.String; ++ this.tokenTypes[scope] = StandardTokenType.String as any; + break; + case 'other': +- this.tokenTypes[scope] = StandardTokenType.Other; ++ this.tokenTypes[scope] = StandardTokenType.Other as any; + break; + case 'comment': +- this.tokenTypes[scope] = StandardTokenType.Comment; ++ this.tokenTypes[scope] = StandardTokenType.Comment as any; + break; + } + } +@@ -211,7 +211,7 @@ export class TextMateService implements ITextMateService { + + private _getOrCreateGrammarRegistry(): TPromise<[Registry, StackElement]> { + if (!this._grammarRegistry) { +- this._grammarRegistry = TPromise.wrap(import('vscode-textmate')).then(({ Registry, INITIAL, parseRawGrammar }) => { ++ this._grammarRegistry = TPromise.wrap(require('vscode-textmate')).then(({ Registry, INITIAL, parseRawGrammar }) => { + const grammarRegistry = new Registry({ + loadGrammar: (scopeName: string) => { + const location = this._scopeRegistry.getGrammarLocation(scopeName); +@@ -228,7 +228,23 @@ export class TextMateService implements ITextMateService { + }, + getInjections: (scopeName: string) => { + return this._injections[scopeName]; +- } ++ }, ++ getOnigLib: () => { ++ return new Promise((res, rej) => { ++ const onigasm = require('onigasm'); ++ const wasmUrl = require('!!file-loader!onigasm/lib/onigasm.wasm'); ++ return fetch(wasmUrl).then((resp) => resp.arrayBuffer()).then((buffer) => { ++ return onigasm.loadWASM(buffer); ++ }).then(() => { ++ res({ ++ createOnigScanner: function (patterns) { return new onigasm.OnigScanner(patterns); }, ++ createOnigString: function (s) { return new onigasm.OnigString(s); } ++ }); ++ }).catch((reason) => { ++ rej(reason); ++ }); ++ }); ++ }, + }); + this._updateTheme(grammarRegistry); + this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); +diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +index 5e727d770c..c8041e10f1 100644 +--- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts ++++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +@@ -64,6 +64,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil + private bufferSavedVersionId: number; + private lastResolvedDiskStat: IFileStat; + private blockModelContentChange: boolean; ++ private updatingTextBuffer: boolean; + private autoSaveAfterMillies: number; + private autoSaveAfterMilliesEnabled: boolean; + private autoSaveDisposable: IDisposable; +@@ -425,9 +426,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil + + // Update model value in a block that ignores model content change events + this.blockModelContentChange = true; ++ this.updatingTextBuffer = true; + try { + this.updateTextEditorModel(value); + } finally { ++ this.updatingTextBuffer = false; + this.blockModelContentChange = false; + } + +@@ -687,7 +690,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil + // - the model is not dirty (otherwise we know there are changed which needs to go to the file) + // - the model is not in orphan mode (because in that case we know the file does not exist on disk) + // - the model version did not change due to save participants running +- if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { ++ // - NOTE@coder this is not a conflict resolution ++ if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId && typeof options.accept === "undefined") { + return this.doTouch(newVersionId); + } + +@@ -709,7 +713,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil + mtime: this.lastResolvedDiskStat.mtime, + encoding: this.getEncoding(), + etag: this.lastResolvedDiskStat.etag, +- writeElevated: options.writeElevated ++ writeElevated: options.writeElevated, ++ accept: options.accept, + }).then(stat => { + this.logService.trace(`doSave(${versionId}) - after updateContent()`, this.resource); + +diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts +index 56719225de..d96d9adff1 100644 +--- a/src/vs/workbench/services/textfile/common/textfiles.ts ++++ b/src/vs/workbench/services/textfile/common/textfiles.ts +@@ -14,6 +14,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' + import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; + import { ITextBufferFactory } from 'vs/editor/common/model'; + import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; ++import { ConflictResolution } from 'coder/common'; // NOTE@coder. + + /** + * The save error handler can be installed on the text text file editor model to install code that executes when save errors occur. +@@ -207,6 +208,7 @@ export interface ISaveOptions { + overwriteEncoding?: boolean; + skipSaveParticipants?: boolean; + writeElevated?: boolean; ++ accept?: ConflictResolution; + } + + export interface ILoadOptions { +diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +index 90d988f493..b14745993a 100644 +--- a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts ++++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +@@ -87,8 +87,8 @@ export class TextFileService extends AbstractTextFileService { + : getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm); + + const buttons: string[] = [ +- resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"), +- nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"), ++ resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "Save"), ++ nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Don't Save"), + nls.localize('cancel', "Cancel") + ]; + +@@ -116,7 +116,7 @@ export class TextFileService extends AbstractTextFileService { + const options: Electron.SaveDialogOptions = { defaultPath }; + + // Filters are only enabled on Windows where they work properly +- if (!isWindows) { ++ if (!isWindows || true) { + return options; + } + +diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +index 81c979f3f8..d2806e9227 100644 +--- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts ++++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +@@ -324,7 +324,7 @@ function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRu + + let pListParser: Thenable<{ parse(content: string) }>; + function getPListParser() { +- return pListParser || import('fast-plist'); ++ return pListParser || Promise.resolve(require('fast-plist')); + } + + function _loadSyntaxTokens(fileService: IFileService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise { +diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +index b6d8dd31b1..4b62c4ccf1 100644 +--- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts ++++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +@@ -13,6 +13,8 @@ import { ExtensionData, IThemeExtensionPoint, IFileIconTheme } from 'vs/workbenc + import { TPromise } from 'vs/base/common/winjs.base'; + import { IFileService } from 'vs/platform/files/common/files'; + import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; ++import product from 'vs/platform/node/product'; ++import { api } from 'coder/api'; + + export class FileIconThemeData implements IFileIconTheme { + id: string; +@@ -113,7 +115,7 @@ export class FileIconThemeData implements IFileIconTheme { + description: this.description, + settingsId: this.settingsId, + location: this.location, +- styleSheetContent: this.styleSheetContent, ++ // styleSheetContent: this.styleSheetContent, + hasFileIcons: this.hasFileIcons, + hasFolderIcons: this.hasFolderIcons, + hidesExplorerArrows: this.hidesExplorerArrows +@@ -180,7 +182,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i + + const iconThemeDocumentLocationDirname = resources.dirname(iconThemeDocumentLocation); + function resolvePath(path: string) { +- return resources.joinPath(iconThemeDocumentLocationDirname, path); ++ return URI.parse(product.fetchUrl + resources.joinPath(iconThemeDocumentLocationDirname, path).path) + `?api_key=${api.apiKey.key}`; + } + + function collectSelectors(associations: IconsAssociation, baseThemeClassName?: string) { +diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts +index ce9193eb9c..4da9379e26 100644 +--- a/src/vs/workbench/workbench.main.ts ++++ b/src/vs/workbench/workbench.main.ts +@@ -5,6 +5,8 @@ + + 'use strict'; + ++import 'vs/loader'; ++ + // Base + import 'vs/base/common/strings'; + import 'vs/base/common/errors'; +@@ -65,9 +67,9 @@ import 'vs/workbench/parts/scm/electron-browser/scm.contribution'; + import 'vs/workbench/parts/scm/electron-browser/scmViewlet'; // can be packaged separately + + import 'vs/workbench/parts/debug/electron-browser/debug.contribution'; +-import 'vs/workbench/parts/debug/browser/debugQuickOpen'; +-import 'vs/workbench/parts/debug/electron-browser/repl'; +-import 'vs/workbench/parts/debug/browser/debugViewlet'; // can be packaged separately ++// import 'vs/workbench/parts/debug/browser/debugQuickOpen'; ++// import 'vs/workbench/parts/debug/electron-browser/repl'; ++// import 'vs/workbench/parts/debug/browser/debugViewlet'; // can be packaged separately + + import 'vs/workbench/parts/markers/electron-browser/markers.contribution'; + import 'vs/workbench/parts/comments/electron-browser/comments.contribution'; +@@ -83,6 +85,8 @@ import 'vs/workbench/parts/extensions/electron-browser/extensions.contribution'; + import 'vs/workbench/parts/extensions/browser/extensionsQuickOpen'; + import 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; // can be packaged separately + ++import 'vs/workbench/parts/collaboration/electron-browser/collaboration.contribution'; ++ + import 'vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution'; + + import 'vs/workbench/parts/output/electron-browser/output.contribution'; +@@ -94,7 +98,7 @@ import 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; // can be p + + import 'vs/workbench/electron-browser/workbench'; + +-import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; ++// import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; + + import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; + +@@ -114,25 +118,25 @@ import 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; + + import 'vs/workbench/parts/themes/electron-browser/themes.contribution'; + +-import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution'; ++// import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution'; + + import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; + + import 'vs/workbench/parts/update/electron-browser/update.contribution'; + +-import 'vs/workbench/parts/surveys/electron-browser/nps.contribution'; +-import 'vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution'; ++// import 'vs/workbench/parts/surveys/electron-browser/nps.contribution'; ++// import 'vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution'; + + import 'vs/workbench/parts/performance/electron-browser/performance.contribution'; + +-import 'vs/workbench/parts/cli/electron-browser/cli.contribution'; ++// import 'vs/workbench/parts/cli/electron-browser/cli.contribution'; + + import 'vs/workbench/api/electron-browser/extensionHost.contribution'; + + import 'vs/workbench/electron-browser/main.contribution'; + import 'vs/workbench/electron-browser/main'; + +-import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution'; ++// import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution'; + + import 'vs/workbench/parts/watermark/electron-browser/watermark'; + +@@ -145,3 +149,11 @@ import 'vs/workbench/parts/navigation/common/navigation.contribution'; + // services + import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; + import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; ++ ++import { URI } from 'vs/base/common/uri'; ++import { startup } from 'vs/workbench/electron-browser/main'; ++ ++export { ++ URI, ++ startup, ++}; +diff --git a/test/smoke/tsconfig.json b/test/smoke/tsconfig.json +index 1b0b03c2d6..2b86b7ecc0 100644 +--- a/test/smoke/tsconfig.json ++++ b/test/smoke/tsconfig.json +@@ -13,6 +13,9 @@ + "lib": [ + "es2016", + "dom" ++ ], ++ "typeRoots": [ ++ "./node_modules/@types" + ] + }, + "exclude": [ diff --git a/webpack.config.app.js b/webpack.config.app.js index c14becf2..bff8a0a4 100644 --- a/webpack.config.app.js +++ b/webpack.config.app.js @@ -16,12 +16,12 @@ const PreloadWebpackPlugin = require("preload-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const root = __dirname; -const nodeFills = path.join(root, "packages", "node-browser", "src"); +const fills = path.join(root, "packages", "ide", "src", "fill"); const vscodeFills = path.join(root, "packages", "vscode", "src", "fill"); module.exports = { context: root, - devtool: "eval", + devtool: "source-map", entry: "./packages/app/src/index.ts", mode: isCi ? "production" : "development", output: { @@ -71,25 +71,25 @@ module.exports = { "native-keymap": path.join(vscodeFills, "native-keymap.ts"), "node-pty": path.join(vscodeFills, "node-pty.ts"), - "gc-signals": path.join(nodeFills, "empty.ts"), - "selenium-webdriver": path.join(nodeFills, "empty.ts"), - "vscode": path.join(nodeFills, "empty.ts"), - "vscode-fsevents": path.join(nodeFills, "empty.ts"), - "vsda": path.join(nodeFills, "empty.ts"), - "windows-foreground-love": path.join(nodeFills, "empty.ts"), - "windows-mutex": path.join(nodeFills, "empty.ts"), - "windows-process-tree": path.join(nodeFills, "empty.ts"), + "gc-signals": path.join(fills, "empty.ts"), + "selenium-webdriver": path.join(fills, "empty.ts"), + "vscode": path.join(fills, "empty.ts"), + "vscode-fsevents": path.join(fills, "empty.ts"), + "vsda": path.join(fills, "empty.ts"), + "windows-foreground-love": path.join(fills, "empty.ts"), + "windows-mutex": path.join(fills, "empty.ts"), + "windows-process-tree": path.join(fills, "empty.ts"), "crypto": "crypto-browserify", "http": "http-browserify", "os": "os-browserify", - "util": path.join(root, "node_modules", "util"), - "child_process": path.join(nodeFills, "child_process.ts"), - "fs": path.join(nodeFills, "fs.ts"), - "net": path.join(nodeFills, "net.ts"), + "child_process": path.join(fills, "child_process.ts"), + "fs": path.join(fills, "fs.ts"), + "net": path.join(fills, "net.ts"), + "util": path.join(fills, "util.ts"), - "electron": path.join(root, "packages", "electron-browser", "src", "electron.ts"), + "electron": path.join(fills, "electron.ts"), "@coder": path.join(root, "packages"), "vs": path.join(root, "lib", "vscode", "src", "vs"), diff --git a/yarn.lock b/yarn.lock index 0139022d..960eb091 100644 --- a/yarn.lock +++ b/yarn.lock @@ -268,6 +268,13 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -330,6 +337,11 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -615,7 +627,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -buffer-from@^1.0.0: +buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -639,7 +651,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0: +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -877,7 +889,7 @@ commander@2.17.x, commander@~2.17.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.18.0: +commander@^2.12.1, commander@^2.18.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== @@ -1279,6 +1291,11 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== +diff@^3.1.0, diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -1490,6 +1507,11 @@ eslint-scope@^4.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -1916,7 +1938,7 @@ gaze@^1.0.0: dependencies: globule "^1.0.0" -get-caller-file@^1.0.1: +get-caller-file@^1.0.1, get-caller-file@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== @@ -1958,7 +1980,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -2658,6 +2680,14 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= +js-yaml@^3.7.0: + version "3.12.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" + integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -2900,6 +2930,11 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -3130,6 +3165,14 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: dependencies: minimist "0.0.8" +mock-require@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.2.tgz#7ce759b559e3b194be5f20a5b1cece0eb363f53d" + integrity sha512-aD/Y1ZFHqw5pHg3HVQ50dLbfaAAcytS6sqLuhP51Dk3TSPdFb2VkSAa3mjrHifLIlGAtwQHJHINafAyqAne7vA== + dependencies: + get-caller-file "^1.0.2" + normalize-path "^2.1.1" + moment@^2.22.1: version "2.23.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.23.0.tgz#759ea491ac97d54bac5ad776996e2a58cc1bc225" @@ -3671,6 +3714,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4181,6 +4229,13 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@^1.3.2: + version "1.9.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" + integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ== + dependencies: + path-parse "^1.0.6" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -4496,7 +4551,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.6: +source-map-support@^0.5.6, source-map-support@~0.5.6: version "0.5.10" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== @@ -4582,6 +4637,11 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sshpk@^1.7.0: version "1.16.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de" @@ -4917,11 +4977,50 @@ ts-loader@^5.3.3: micromatch "^3.1.4" semver "^5.0.1" -tslib@^1.9.0: +ts-node@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" + integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" + +tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslint@^5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" + integrity sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -4952,6 +5051,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript-tslint-plugin@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/typescript-tslint-plugin/-/typescript-tslint-plugin-0.2.1.tgz#6a0361cd311bdc9dcec2e70c8a54cab16829e47f" + integrity sha512-j0Tn/2GlAwnaklSNMOZRNpv96j6IWkQF6RuTJ5WowfNlgdYqnJpSaVFwT22INwJiPDDGKNe/aATT0qkU0pWM4w== + dependencies: + minimatch "^3.0.4" + mock-require "^3.0.2" + vscode-languageserver "^5.1.0" + typescript@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" @@ -5135,6 +5243,37 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" +vscode-jsonrpc@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" + integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== + +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== + dependencies: + vscode-jsonrpc "^4.0.0" + vscode-languageserver-types "3.14.0" + +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== + +vscode-languageserver@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz#0d2feddd33f92aadf5da32450df498d52f6f14eb" + integrity sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A== + dependencies: + vscode-languageserver-protocol "3.14.1" + vscode-uri "^1.0.6" + +vscode-uri@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" + integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== + watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" @@ -5472,3 +5611,8 @@ yargs@^7.0.0: which-module "^1.0.0" y18n "^3.2.1" yargs-parser "^5.0.0" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=