diff --git a/README.md b/README.md
index 3a10b463101..df87a4eea0b 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
a code editor with what developers need for their core edit-build-debug cycle. Code
provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools.
-VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.
+VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.
diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js
index 0cbd42b6e0b..68caf689ded 100644
--- a/build/gulpfile.vscode.js
+++ b/build/gulpfile.vscode.js
@@ -43,8 +43,8 @@ const nodeModules = ['electron', 'original-fs']
// Build
const builtInExtensions = [
- { name: 'ms-vscode.node-debug', version: '1.16.2' },
- { name: 'ms-vscode.node-debug2', version: '1.16.0' }
+ { name: 'ms-vscode.node-debug', version: '1.16.5' },
+ { name: 'ms-vscode.node-debug2', version: '1.16.3' }
];
const excludedExtensions = [
diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe
index 096f516afcf..9c2970d508b 100644
--- a/build/monaco/monaco.d.ts.recipe
+++ b/build/monaco/monaco.d.ts.recipe
@@ -5,16 +5,7 @@
declare module monaco {
- interface Thenable {
- /**
- * Attaches callbacks for the resolution and/or rejection of the Promise.
- * @param onfulfilled The callback to execute when the Promise is resolved.
- * @param onrejected The callback to execute when the Promise is rejected.
- * @returns A Promise for the completion of which ever callback is executed.
- */
- then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable;
- then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable;
- }
+ type Thenable = PromiseLike;
export interface IDisposable {
dispose(): void;
@@ -41,7 +32,7 @@ declare module monaco {
Error = 3,
}
-#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, TPromise
+#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise
#include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken
#include(vs/base/common/uri): URI
#include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod
diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js
index 7b156212bec..95205aa1fe1 100644
--- a/build/npm/postinstall.js
+++ b/build/npm/postinstall.js
@@ -24,6 +24,7 @@ npmInstall('extensions'); // node modules shared by all extensions
const extensions = [
'vscode-api-tests',
'vscode-colorize-tests',
+ 'azure-account',
'json',
'configuration-editing',
'extension-editing',
diff --git a/build/tfs/darwin/build.sh b/build/tfs/darwin/build.sh
index 80676602f2e..acbb849d4b3 100755
--- a/build/tfs/darwin/build.sh
+++ b/build/tfs/darwin/build.sh
@@ -16,6 +16,9 @@ echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc
step "Install dependencies" \
npm install
+step "Hygiene" \
+ npm run gulp -- hygiene
+
step "Mix in repository from vscode-distro" \
npm run gulp -- mixin
@@ -23,7 +26,7 @@ step "Install distro dependencies" \
node build/tfs/common/installDistro.js
step "Build minified & upload source maps" \
- npm run gulp -- --max_old_space_size=4096 vscode-darwin-min upload-vscode-sourcemaps
+ npm run gulp -- vscode-darwin-min upload-vscode-sourcemaps
# step "Create loader snapshot"
# node build/lib/snapshotLoader.js
diff --git a/build/tfs/darwin/smoketest.sh b/build/tfs/darwin/smoketest.sh
index e93de88e48a..ad1606c3aad 100755
--- a/build/tfs/darwin/smoketest.sh
+++ b/build/tfs/darwin/smoketest.sh
@@ -19,7 +19,7 @@ step "Install distro dependencies" \
node build/tfs/common/installDistro.js
step "Build minified & upload source maps" \
- npm run gulp -- --max_old_space_size=4096 vscode-darwin-min
+ npm run gulp -- vscode-darwin-min
step "Run smoke test" \
pushd test/smoke
diff --git a/build/tfs/linux/build.sh b/build/tfs/linux/build.sh
index 541130d2b50..b3d1825c2d9 100755
--- a/build/tfs/linux/build.sh
+++ b/build/tfs/linux/build.sh
@@ -18,6 +18,9 @@ echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc
step "Install dependencies" \
npm install --arch=$ARCH --unsafe-perm
+step "Hygiene" \
+ npm run gulp -- hygiene
+
step "Mix in repository from vscode-distro" \
npm run gulp -- mixin
@@ -28,7 +31,7 @@ step "Install distro dependencies" \
node build/tfs/common/installDistro.js --arch=$ARCH
step "Build minified" \
- npm run gulp -- --max_old_space_size=4096 "vscode-linux-$ARCH-min"
+ npm run gulp -- "vscode-linux-$ARCH-min"
# step "Create loader snapshot"
# node build/lib/snapshotLoader.js --arch=$ARCH
diff --git a/build/tfs/linux/release.sh b/build/tfs/linux/release.sh
index 40d68aee73f..41f6d35e675 100755
--- a/build/tfs/linux/release.sh
+++ b/build/tfs/linux/release.sh
@@ -4,10 +4,10 @@
. ./build/tfs/common/common.sh
step "Build Debian package" \
- npm run gulp -- --max_old_space_size=4096 "vscode-linux-$ARCH-build-deb"
+ npm run gulp -- "vscode-linux-$ARCH-build-deb"
step "Build RPM package" \
- npm run gulp -- --max_old_space_size=4096 "vscode-linux-$ARCH-build-rpm"
+ npm run gulp -- "vscode-linux-$ARCH-build-rpm"
(cd $BUILD_SOURCESDIRECTORY/build/tfs/common && \
step "Install build dependencies" \
diff --git a/build/tfs/linux/smoketest.sh b/build/tfs/linux/smoketest.sh
index 579bac638b1..8d71fff1275 100644
--- a/build/tfs/linux/smoketest.sh
+++ b/build/tfs/linux/smoketest.sh
@@ -24,7 +24,7 @@ step "Install distro dependencies" \
node build/tfs/common/installDistro.js --arch=$ARCH
step "Build minified" \
- npm run gulp -- --max_old_space_size=4096 "vscode-linux-$ARCH-min"
+ npm run gulp -- "vscode-linux-$ARCH-min"
function configureEnvironment {
id -u testuser &>/dev/null || (useradd -m testuser; chpasswd <<< testuser:testpassword)
diff --git a/build/tfs/win32/1_build.ps1 b/build/tfs/win32/1_build.ps1
index c27b05caeaf..0090920d506 100644
--- a/build/tfs/win32/1_build.ps1
+++ b/build/tfs/win32/1_build.ps1
@@ -19,6 +19,10 @@ step "Install dependencies" {
exec { & npm install }
}
+step "Hygiene" {
+ exec { & npm run gulp -- hygiene }
+}
+
$env:VSCODE_MIXIN_PASSWORD = $mixinPassword
step "Mix in repository from vscode-distro" {
exec { & npm run gulp -- mixin }
@@ -33,7 +37,7 @@ step "Install distro dependencies" {
}
step "Build minified" {
- exec { & npm run gulp -- --max_old_space_size=4096 "vscode-win32-$global:arch-min" }
+ exec { & npm run gulp -- "vscode-win32-$global:arch-min" }
}
# step "Create loader snapshot" {
diff --git a/build/tfs/win32/2_package.ps1 b/build/tfs/win32/2_package.ps1
index 4cb1978bbad..dcd90611374 100644
--- a/build/tfs/win32/2_package.ps1
+++ b/build/tfs/win32/2_package.ps1
@@ -6,7 +6,7 @@ Param(
. .\build\tfs\win32\lib.ps1
step "Create archive and setup package" {
- exec { & npm run gulp -- --max_old_space_size=4096 "vscode-win32-$global:arch-archive" "vscode-win32-$global:arch-setup" }
+ exec { & npm run gulp -- "vscode-win32-$global:arch-archive" "vscode-win32-$global:arch-setup" }
}
done
\ No newline at end of file
diff --git a/build/tfs/win32/smoketest.ps1 b/build/tfs/win32/smoketest.ps1
index 1817b806071..2a6519aaaf4 100644
--- a/build/tfs/win32/smoketest.ps1
+++ b/build/tfs/win32/smoketest.ps1
@@ -34,7 +34,7 @@ step "Install distro dependencies" {
}
step "Build minified" {
- exec { & npm run gulp -- --max_old_space_size=4096 "vscode-win32-$global:arch-min" }
+ exec { & npm run gulp -- "vscode-win32-$global:arch-min" }
}
step "Run smoke test" {
diff --git a/extensions/azure-account/.vscodeignore b/extensions/azure-account/.vscodeignore
new file mode 100644
index 00000000000..24428a6f758
--- /dev/null
+++ b/extensions/azure-account/.vscodeignore
@@ -0,0 +1,4 @@
+test/**
+src/**
+tsconfig.json
+npm-shrinkwrap.json
\ No newline at end of file
diff --git a/extensions/azure-account/npm-shrinkwrap.json b/extensions/azure-account/npm-shrinkwrap.json
new file mode 100644
index 00000000000..18206b8124a
--- /dev/null
+++ b/extensions/azure-account/npm-shrinkwrap.json
@@ -0,0 +1,521 @@
+{
+ "name": "azure-account",
+ "version": "0.1.0",
+ "dependencies": {
+ "@types/copy-paste": {
+ "version": "1.1.30",
+ "from": "@types/copy-paste@>=1.1.30 <2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/copy-paste/-/copy-paste-1.1.30.tgz",
+ "dev": true
+ },
+ "@types/form-data": {
+ "version": "2.2.0",
+ "from": "@types/form-data@*",
+ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.0.tgz",
+ "dependencies": {
+ "@types/node": {
+ "version": "8.0.24",
+ "from": "@types/node@*",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz"
+ }
+ }
+ },
+ "@types/node": {
+ "version": "6.0.87",
+ "from": "@types/node@>=6.0.40 <7.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.87.tgz",
+ "dev": true
+ },
+ "@types/opn": {
+ "version": "3.0.28",
+ "from": "@types/opn@>=3.0.28 <4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/opn/-/opn-3.0.28.tgz",
+ "dev": true
+ },
+ "@types/request": {
+ "version": "0.0.45",
+ "from": "@types/request@>=0.0.45 <0.0.46",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-0.0.45.tgz",
+ "dependencies": {
+ "@types/node": {
+ "version": "8.0.24",
+ "from": "@types/node@*",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz"
+ }
+ }
+ },
+ "@types/uuid": {
+ "version": "2.0.30",
+ "from": "@types/uuid@>=2.0.29 <3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-2.0.30.tgz",
+ "dependencies": {
+ "@types/node": {
+ "version": "8.0.24",
+ "from": "@types/node@*",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz"
+ }
+ }
+ },
+ "adal-node": {
+ "version": "0.1.22",
+ "from": "adal-node@>=0.1.22 <0.2.0",
+ "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.22.tgz"
+ },
+ "ajv": {
+ "version": "4.11.8",
+ "from": "ajv@>=4.9.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz"
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "from": "asn1@>=0.2.3 <0.3.0",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz"
+ },
+ "assert-plus": {
+ "version": "0.2.0",
+ "from": "assert-plus@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz"
+ },
+ "async": {
+ "version": "2.5.0",
+ "from": "async@>=0.6.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz"
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "from": "asynckit@>=0.4.0 <0.5.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
+ },
+ "aws-sign2": {
+ "version": "0.6.0",
+ "from": "aws-sign2@>=0.6.0 <0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz"
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "from": "aws4@>=1.2.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz"
+ },
+ "azure-arm-resource": {
+ "version": "2.0.0-preview",
+ "from": "azure-arm-resource@>=2.0.0-preview <3.0.0",
+ "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-2.0.0-preview.tgz"
+ },
+ "base64url": {
+ "version": "2.0.0",
+ "from": "base64url@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz"
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.1",
+ "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+ "optional": true
+ },
+ "boom": {
+ "version": "2.10.1",
+ "from": "boom@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz"
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "from": "buffer-equal-constant-time@1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "from": "caseless@>=0.12.0 <0.13.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz"
+ },
+ "co": {
+ "version": "4.6.0",
+ "from": "co@>=4.6.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
+ },
+ "combined-stream": {
+ "version": "1.0.5",
+ "from": "combined-stream@>=1.0.5 <1.1.0",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz"
+ },
+ "copy-paste": {
+ "version": "1.3.0",
+ "from": "copy-paste@>=1.3.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz"
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "from": "core-util-is@1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+ },
+ "cryptiles": {
+ "version": "2.0.5",
+ "from": "cryptiles@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz"
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "from": "dashdash@>=1.12.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "from": "assert-plus@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+ }
+ }
+ },
+ "date-utils": {
+ "version": "1.2.21",
+ "from": "date-utils@*",
+ "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz"
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "from": "delayed-stream@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
+ },
+ "duplexer": {
+ "version": "0.1.1",
+ "from": "duplexer@>=0.1.1 <0.2.0",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz"
+ },
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "from": "ecc-jsbn@>=0.1.1 <0.2.0",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+ "optional": true
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.9",
+ "from": "ecdsa-sig-formatter@1.0.9",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz"
+ },
+ "extend": {
+ "version": "3.0.1",
+ "from": "extend@>=3.0.0 <3.1.0",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz"
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "from": "extsprintf@1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz"
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "from": "forever-agent@>=0.6.1 <0.7.0",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
+ },
+ "form-data": {
+ "version": "2.1.4",
+ "from": "form-data@>=2.1.1 <2.2.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz"
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "from": "getpass@>=0.1.1 <0.2.0",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "from": "assert-plus@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+ }
+ }
+ },
+ "har-schema": {
+ "version": "1.0.5",
+ "from": "har-schema@>=1.0.5 <2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz"
+ },
+ "har-validator": {
+ "version": "4.2.1",
+ "from": "har-validator@>=4.2.1 <4.3.0",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz"
+ },
+ "hawk": {
+ "version": "3.1.3",
+ "from": "hawk@>=3.1.3 <3.2.0",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz"
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "from": "hoek@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz"
+ },
+ "http-signature": {
+ "version": "1.1.1",
+ "from": "http-signature@>=1.1.0 <1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.4.18",
+ "from": "iconv-lite@>=0.4.8 <0.5.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz"
+ },
+ "is-buffer": {
+ "version": "1.1.5",
+ "from": "is-buffer@>=1.1.5 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz"
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "from": "is-stream@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz"
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "from": "is-typedarray@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "from": "is-wsl@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz"
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "from": "isstream@>=0.1.2 <0.2.0",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "from": "jsbn@>=0.1.0 <0.2.0",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "optional": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "from": "json-schema@0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz"
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "from": "json-stable-stringify@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz"
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "from": "json-stringify-safe@>=5.0.1 <5.1.0",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "from": "jsonify@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "from": "jsprim@>=1.2.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "from": "assert-plus@1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+ }
+ }
+ },
+ "jwa": {
+ "version": "1.1.5",
+ "from": "jwa@>=1.1.4 <2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz"
+ },
+ "jws": {
+ "version": "3.1.4",
+ "from": "jws@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz"
+ },
+ "lodash": {
+ "version": "4.17.4",
+ "from": "lodash@>=4.14.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz"
+ },
+ "mime-db": {
+ "version": "1.29.0",
+ "from": "mime-db@>=1.29.0 <1.30.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz"
+ },
+ "mime-types": {
+ "version": "2.1.16",
+ "from": "mime-types@>=2.1.7 <2.2.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz"
+ },
+ "moment": {
+ "version": "2.18.1",
+ "from": "moment@>=2.14.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz"
+ },
+ "ms-rest": {
+ "version": "2.2.1",
+ "from": "ms-rest@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.2.1.tgz",
+ "dependencies": {
+ "@types/node": {
+ "version": "7.0.42",
+ "from": "@types/node@>=7.0.10 <8.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz"
+ },
+ "uuid": {
+ "version": "3.1.0",
+ "from": "uuid@^3.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz"
+ }
+ }
+ },
+ "ms-rest-azure": {
+ "version": "2.2.3",
+ "from": "ms-rest-azure@>=2.2.3 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.2.3.tgz",
+ "dependencies": {
+ "@types/node": {
+ "version": "7.0.42",
+ "from": "@types/node@^7.0.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz"
+ },
+ "async": {
+ "version": "0.2.7",
+ "from": "async@0.2.7",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.7.tgz"
+ },
+ "uuid": {
+ "version": "3.1.0",
+ "from": "uuid@^3.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz"
+ }
+ }
+ },
+ "node-uuid": {
+ "version": "1.4.7",
+ "from": "node-uuid@1.4.7",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz"
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "from": "oauth-sign@>=0.8.1 <0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz"
+ },
+ "opn": {
+ "version": "5.1.0",
+ "from": "opn@>=5.1.0 <6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz"
+ },
+ "performance-now": {
+ "version": "0.2.0",
+ "from": "performance-now@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz"
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "from": "punycode@>=1.4.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz"
+ },
+ "qs": {
+ "version": "6.4.0",
+ "from": "qs@>=6.4.0 <6.5.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz"
+ },
+ "request": {
+ "version": "2.81.0",
+ "from": "request@>=2.52.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+ "dependencies": {
+ "uuid": {
+ "version": "3.1.0",
+ "from": "uuid@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz"
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "from": "safe-buffer@>=5.0.1 <6.0.0",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz"
+ },
+ "sntp": {
+ "version": "1.0.9",
+ "from": "sntp@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz"
+ },
+ "sshpk": {
+ "version": "1.13.1",
+ "from": "sshpk@>=1.7.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "from": "assert-plus@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+ }
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "from": "stringstream@>=0.0.4 <0.1.0",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
+ },
+ "sync-exec": {
+ "version": "0.6.2",
+ "from": "sync-exec@>=0.6.0 <0.7.0",
+ "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz",
+ "optional": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "from": "through@>=2.3.4 <2.4.0",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
+ },
+ "tough-cookie": {
+ "version": "2.3.2",
+ "from": "tough-cookie@>=2.3.0 <2.4.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz"
+ },
+ "tunnel": {
+ "version": "0.0.5",
+ "from": "tunnel@>=0.0.2 <0.1.0",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz"
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "from": "tunnel-agent@>=0.6.0 <0.7.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz"
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "from": "tweetnacl@>=0.14.0 <0.15.0",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "optional": true
+ },
+ "underscore": {
+ "version": "1.8.3",
+ "from": "underscore@>=1.3.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz"
+ },
+ "verror": {
+ "version": "1.10.0",
+ "from": "verror@1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "from": "assert-plus@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+ }
+ }
+ },
+ "vscode-nls": {
+ "version": "2.0.2",
+ "from": "vscode-nls@>=2.0.2 <3.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
+ },
+ "xmldom": {
+ "version": "0.1.27",
+ "from": "xmldom@>=0.1.0",
+ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz"
+ },
+ "xpath.js": {
+ "version": "1.0.7",
+ "from": "xpath.js@>=1.0.5 <1.1.0",
+ "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.0.7.tgz"
+ }
+ }
+}
diff --git a/extensions/azure-account/package.json b/extensions/azure-account/package.json
new file mode 100644
index 00000000000..d98aa059e74
--- /dev/null
+++ b/extensions/azure-account/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "azure-account",
+ "version": "0.1.0",
+ "publisher": "vscode",
+ "engines": {
+ "vscode": "*"
+ },
+ "enableProposedApi": true,
+ "activationEvents": [
+ "*"
+ ],
+ "main": "./out/extension",
+ "contributes": {
+ "commands": [
+ {
+ "command": "azure-account.login",
+ "title": "%azure-account.commands.login%",
+ "category": "%azure-account.commands.azure%"
+ },
+ {
+ "command": "azure-account.logout",
+ "title": "%azure-account.commands.logout%",
+ "category": "%azure-account.commands.azure%"
+ },
+ {
+ "command": "azure-account.addFilter",
+ "title": "%azure-account.commands.addResourceFilter%",
+ "category": "%azure-account.commands.azure%"
+ },
+ {
+ "command": "azure-account.removeFilter",
+ "title": "%azure-account.commands.removeResourceFilter%",
+ "category": "%azure-account.commands.azure%"
+ },
+ {
+ "command": "azure-account.createAccount",
+ "title": "%azure-account.commands.createAccount%",
+ "category": "%azure-account.commands.azure%"
+ }
+ ],
+ "configuration": {
+ "type": "object",
+ "title": "Azure configuration",
+ "properties": {
+ "azure.resourceFilter": {
+ "type": "array",
+ "default": null,
+ "description": "The resource filter, each element is either a subscription id or a subscription id and a resource group name separated by a slash."
+ }
+ }
+ }
+ },
+ "scripts": {
+ "compile": "gulp compile-extension:azure-account",
+ "watch": "gulp watch-extension:azure-account"
+ },
+ "devDependencies": {
+ "@types/copy-paste": "^1.1.30",
+ "@types/node": "^6.0.40",
+ "@types/opn": "^3.0.28"
+ },
+ "dependencies": {
+ "adal-node": "^0.1.22",
+ "azure-arm-resource": "^2.0.0-preview",
+ "copy-paste": "^1.3.0",
+ "ms-rest-azure": "^2.2.3",
+ "vscode-nls": "^2.0.2",
+ "opn": "^5.1.0"
+ }
+}
\ No newline at end of file
diff --git a/extensions/azure-account/package.nls.json b/extensions/azure-account/package.nls.json
new file mode 100644
index 00000000000..4857a376665
--- /dev/null
+++ b/extensions/azure-account/package.nls.json
@@ -0,0 +1,8 @@
+{
+ "azure-account.commands.azure": "Azure",
+ "azure-account.commands.login": "Login",
+ "azure-account.commands.logout": "Logout",
+ "azure-account.commands.addResourceFilter": "Add Resource Filter",
+ "azure-account.commands.removeResourceFilter": "Remove Resource Filter",
+ "azure-account.commands.createAccount": "Create an Account"
+}
\ No newline at end of file
diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts
new file mode 100644
index 00000000000..d5b4b3d9ed1
--- /dev/null
+++ b/extensions/azure-account/src/azure-account.ts
@@ -0,0 +1,472 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+const adal = require('adal-node');
+const MemoryCache = adal.MemoryCache;
+const AuthenticationContext = adal.AuthenticationContext;
+const CacheDriver = require('adal-node/lib/cache-driver');
+const createLogContext = require('adal-node/lib/log').createLogContext;
+
+import { DeviceTokenCredentials, AzureEnvironment } from 'ms-rest-azure';
+import { SubscriptionClient, ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource';
+import * as opn from 'opn';
+import * as copypaste from 'copy-paste';
+import * as nls from 'vscode-nls';
+
+import { window, commands, credentials, EventEmitter, MessageItem, ExtensionContext, workspace, ConfigurationTarget } from 'vscode';
+import { AzureAccount, AzureSession, AzureLoginStatus, AzureResourceFilter } from './typings/azure-account.api';
+
+const localize = nls.loadMessageBundle();
+
+const defaultEnvironment = (AzureEnvironment).Azure;
+const commonTenantId = 'common';
+const authorityHostUrl = defaultEnvironment.activeDirectoryEndpointUrl;
+const clientId = '04b07795-8ddb-461a-bbee-02f9e1bf7b46';
+const authorityUrl = `${authorityHostUrl}${commonTenantId}`;
+const resource = defaultEnvironment.activeDirectoryResourceId;
+
+const credentialsService = 'VSCode Public Azure';
+const credentialsAccount = 'Refresh Token';
+
+interface DeviceLogin {
+ userCode: string;
+ deviceCode: string;
+ verificationUrl: string;
+ expiresIn: number;
+ interval: number;
+ message: string;
+}
+
+interface TokenResponse {
+ tokenType: string;
+ expiresIn: number;
+ expiresOn: string;
+ resource: string;
+ accessToken: string;
+ refreshToken: string;
+ userId: string;
+ isUserIdDisplayable: boolean;
+ familyName: string;
+ givenName: string;
+ oid: string;
+ tenantId: string;
+ isMRRT: boolean;
+ _clientId: string;
+ _authority: string;
+}
+
+interface AzureAccountWriteable extends AzureAccount {
+ status: AzureLoginStatus;
+}
+
+class AzureLoginError extends Error {
+ constructor(message: string, public _reason: any) {
+ super(message);
+ }
+}
+
+export class AzureLoginHelper {
+
+ private onStatusChanged = new EventEmitter();
+ private onSessionsChanged = new EventEmitter();
+ private onFiltersChanged = new EventEmitter();
+ private tokenCache = new MemoryCache();
+ private oldResourceFilter: string;
+
+ constructor(context: ExtensionContext) {
+ const subscriptions = context.subscriptions;
+ subscriptions.push(commands.registerCommand('azure-account.login', () => this.login().catch(console.error)));
+ subscriptions.push(commands.registerCommand('azure-account.logout', () => this.logout().catch(console.error)));
+ subscriptions.push(commands.registerCommand('azure-account.askForLogin', () => this.askForLogin().catch(console.error)));
+ subscriptions.push(commands.registerCommand('azure-account.addFilter', () => this.addFilter().catch(console.error)));
+ subscriptions.push(commands.registerCommand('azure-account.removeFilter', () => this.removeFilter().catch(console.error)));
+ subscriptions.push(this.api.onSessionsChanged(() => this.updateFilters().catch(console.error)));
+ subscriptions.push(workspace.onDidChangeConfiguration(() => this.updateFilters(true).catch(console.error)));
+ this.initialize()
+ .catch(console.error);
+ }
+
+ api: AzureAccount = {
+ status: 'Initializing',
+ onStatusChanged: this.onStatusChanged.event,
+ sessions: [],
+ onSessionsChanged: this.onSessionsChanged.event,
+ filters: [],
+ onFiltersChanged: this.onFiltersChanged.event,
+ credentials
+ };
+
+ async login() {
+ try {
+ this.beginLoggingIn();
+ const deviceLogin = await deviceLogin1();
+ const copyAndOpen: MessageItem = { title: localize('azure-account.copyAndOpen', "Copy & Open") };
+ const close: MessageItem = { title: localize('azure-account.close', "Close"), isCloseAffordance: true };
+ const response = await window.showInformationMessage(deviceLogin.message, copyAndOpen, close);
+ if (response === copyAndOpen) {
+ copypaste.copy(deviceLogin.userCode);
+ opn(deviceLogin.verificationUrl);
+ }
+ const tokenResponse = await deviceLogin2(deviceLogin);
+ const refreshToken = tokenResponse.refreshToken;
+ const tokenResponses = await tokensFromToken(tokenResponse);
+ await credentials.writeSecret(credentialsService, credentialsAccount, refreshToken);
+ await this.updateSessions(tokenResponses);
+ } finally {
+ this.updateStatus();
+ }
+ }
+
+ async logout() {
+ await credentials.deleteSecret(credentialsService, credentialsAccount);
+ await this.updateSessions([]);
+ this.updateStatus();
+ }
+
+ private async initialize() {
+ try {
+ const refreshToken = await credentials.readSecret(credentialsService, credentialsAccount);
+ if (refreshToken) {
+ this.beginLoggingIn();
+ const tokenResponse = await tokenFromRefreshToken(refreshToken);
+ const tokenResponses = await tokensFromToken(tokenResponse);
+ await this.updateSessions(tokenResponses);
+ }
+ } catch (err) {
+ if (!(err instanceof AzureLoginError)) {
+ throw err;
+ }
+ } finally {
+ this.updateStatus();
+ }
+ }
+
+ private beginLoggingIn() {
+ if (this.api.status !== 'LoggedIn') {
+ (this.api).status = 'LoggingIn';
+ this.onStatusChanged.fire(this.api.status);
+ }
+ }
+
+ private updateStatus() {
+ const status = this.api.sessions.length ? 'LoggedIn' : 'LoggedOut';
+ if (this.api.status !== status) {
+ (this.api).status = status;
+ this.onStatusChanged.fire(this.api.status);
+ }
+ }
+
+ private async updateSessions(tokenResponses: TokenResponse[]) {
+ await clearTokenCache(this.tokenCache);
+ for (const tokenResponse of tokenResponses) {
+ await addTokenToCache(this.tokenCache, tokenResponse);
+ }
+ const sessions = this.api.sessions;
+ sessions.splice(0, sessions.length, ...tokenResponses.map(tokenResponse => ({
+ environment: defaultEnvironment,
+ userId: tokenResponse.userId,
+ tenantId: tokenResponse.tenantId,
+ credentials: new DeviceTokenCredentials({ username: tokenResponse.userId, clientId, tokenCache: this.tokenCache, domain: tokenResponse.tenantId })
+ })));
+ this.onSessionsChanged.fire();
+ }
+
+ private async askForLogin() {
+ if (this.api.status === 'LoggedIn') {
+ return;
+ }
+ const login = { title: localize('azure-account.login', "Login") };
+ const cancel = { title: 'Cancel', isCloseAffordance: true };
+ const result = await window.showInformationMessage(localize('azure-account.loginFirst', "Not logged in, log in first."), login, cancel);
+ return result === login && commands.executeCommand('azure-account.login');
+ }
+
+ private async addFilter() {
+ if (this.api.status !== 'LoggedIn') {
+ return commands.executeCommand('azure-account.askForLogin');
+ }
+
+ const azureConfig = workspace.getConfiguration('azure');
+ const resourceFilter = azureConfig.get('resourceFilter') || [];
+
+ const subscriptionItems: { session: AzureSession; subscription: SubscriptionModels.Subscription }[] = [];
+ for (const session of this.api.sessions) {
+ const credentials = session.credentials;
+ const client = new SubscriptionClient(credentials);
+ const subscriptions = await listAll(client.subscriptions, client.subscriptions.list());
+ subscriptionItems.push(...subscriptions.filter(subscription => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}`) === -1)
+ .map(subscription => ({
+ session,
+ subscription
+ })));
+ }
+ subscriptionItems.sort((a, b) => a.subscription.displayName!.localeCompare(b.subscription.displayName!));
+ const subscriptionResult = await window.showQuickPick(subscriptionItems.map(subscription => ({
+ label: subscription.subscription.displayName!,
+ description: subscription.subscription.subscriptionId!,
+ subscription
+ })));
+ if (!subscriptionResult) {
+ return;
+ }
+
+ const { session, subscription } = subscriptionResult.subscription;
+ const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!);
+ const resourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list());
+ const resourceGroupFilters: AzureResourceFilter[] = [
+ {
+ ...subscriptionResult.subscription,
+ allResourceGroups: true,
+ resourceGroups
+ }
+ ];
+ resourceGroupFilters.push(...resourceGroups.filter(resourceGroup => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}/${resourceGroup.name}`) === -1)
+ .map(resourceGroup => ({
+ session,
+ subscription,
+ allResourceGroups: false,
+ resourceGroups: [resourceGroup]
+ })));
+ resourceGroupFilters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : '').localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : ''));
+ const resourceGroupResult = await window.showQuickPick(resourceGroupFilters.map(resourceGroup => (!resourceGroup.allResourceGroups ? {
+ label: resourceGroup.resourceGroups[0].name!,
+ description: resourceGroup.resourceGroups[0].location,
+ resourceGroup
+ } : {
+ label: localize('azure-account.entireSubscription', "Entire Subscription"),
+ description: '',
+ resourceGroup
+ })));
+ if (!resourceGroupResult) {
+ return;
+ }
+
+ const resourceGroup = resourceGroupResult.resourceGroup;
+ if (!resourceGroup.allResourceGroups) {
+ resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/${resourceGroup.resourceGroups[0].name}`);
+ } else {
+ resourceFilter.splice(0, resourceFilter.length, ...resourceFilter.filter(c => !c.startsWith(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/`)));
+ resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}`);
+ }
+
+ const resourceFilterConfig = azureConfig.inspect('resourceFilter');
+ let target = ConfigurationTarget.Global;
+ if (resourceFilterConfig) {
+ if (resourceFilterConfig.workspaceFolderValue) {
+ target = ConfigurationTarget.WorkspaceFolder;
+ } else if (resourceFilterConfig.workspaceValue) {
+ target = ConfigurationTarget.Workspace;
+ } else if (resourceFilterConfig.globalValue) {
+ target = ConfigurationTarget.Global;
+ }
+ }
+ await azureConfig.update('resourceFilter', resourceFilter, target);
+ }
+
+ private async removeFilter() {
+ if (this.api.status !== 'LoggedIn') {
+ return commands.executeCommand('azure-account.askForLogin');
+ }
+
+ const azureConfig = workspace.getConfiguration('azure');
+ let resourceFilter = azureConfig.get('resourceFilter') || [];
+
+ const filters = resourceFilter.length ? this.api.filters.reduce((list, filter) => {
+ if (filter.allResourceGroups) {
+ list.push(filter);
+ } else {
+ list.push(...filter.resourceGroups.map(resourceGroup => ({
+ ...filter,
+ resourceGroups: [resourceGroup]
+ })));
+ }
+ return list;
+ }, []) : [];
+ filters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : `/${a.subscription.displayName}`).localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : `/${b.subscription.displayName}`));
+ const filterResult = await window.showQuickPick(filters.map(filter => (!filter.allResourceGroups ? {
+ label: filter.resourceGroups[0].name!,
+ description: filter.subscription.displayName!,
+ filter
+ } : {
+ label: filter.subscription.displayName!,
+ description: filter.subscription.subscriptionId!,
+ filter
+ })));
+ if (!filterResult) {
+ return;
+ }
+
+ const filter = filterResult.filter;
+ const remove = !filter.allResourceGroups ?
+ `${filter.session.tenantId}/${filter.subscription.subscriptionId}/${filter.resourceGroups[0].name}` :
+ `${filter.session.tenantId}/${filter.subscription.subscriptionId}`;
+ resourceFilter = resourceFilter.filter(e => e !== remove);
+
+ const resourceFilterConfig = azureConfig.inspect('resourceFilter');
+ let target = ConfigurationTarget.Global;
+ if (resourceFilterConfig) {
+ if (resourceFilterConfig.workspaceFolderValue) {
+ target = ConfigurationTarget.WorkspaceFolder;
+ } else if (resourceFilterConfig.workspaceValue) {
+ target = ConfigurationTarget.Workspace;
+ } else if (resourceFilterConfig.globalValue) {
+ target = ConfigurationTarget.Global;
+ }
+ }
+ await azureConfig.update('resourceFilter', resourceFilter.length ? resourceFilter : undefined, target);
+ }
+
+ private async updateFilters(configChange = false) {
+ const azureConfig = workspace.getConfiguration('azure');
+ let resourceFilter = azureConfig.get('resourceFilter');
+ if (configChange && JSON.stringify(resourceFilter) === this.oldResourceFilter) {
+ return;
+ }
+ this.oldResourceFilter = JSON.stringify(resourceFilter);
+ if (resourceFilter && !Array.isArray(resourceFilter)) {
+ resourceFilter = [];
+ }
+ const filters = resourceFilter && resourceFilter.map(s => typeof s === 'string' ? s.split('/') : [])
+ .filter(s => s.length === 2 || s.length === 3)
+ .map(([tenantId, subscriptionId, resourceGroup]) => ({ tenantId, subscriptionId, resourceGroup }));
+ const tenantIds = filters && filters.reduce | boolean>>>((result, filter) => {
+ const tenant = result[filter.tenantId] || (result[filter.tenantId] = {});
+ const resourceGroups = tenant[filter.subscriptionId] || (tenant[filter.subscriptionId] = (filter.resourceGroup ? {} : true));
+ if (typeof resourceGroups === 'object' && filter.resourceGroup) {
+ resourceGroups[filter.resourceGroup] = true;
+ }
+ return result;
+ }, {});
+
+ const newFilters: AzureResourceFilter[] = [];
+ const sessions = tenantIds ? this.api.sessions.filter(session => tenantIds[session.tenantId]) : this.api.sessions;
+ for (const session of sessions) {
+ const client = new SubscriptionClient(session.credentials);
+ const subscriptionIds = tenantIds && tenantIds[session.tenantId];
+ const subscriptions = await listAll(client.subscriptions, client.subscriptions.list());
+ const filteredSubscriptions = subscriptionIds ? subscriptions.filter(subscription => subscriptionIds[subscription.subscriptionId!]) : subscriptions;
+ for (const subscription of filteredSubscriptions) {
+ const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!);
+ const resourceGroupNames = subscriptionIds && subscriptionIds[subscription.subscriptionId!];
+ const allResourceGroups = !(resourceGroupNames && typeof resourceGroupNames === 'object');
+ const unfilteredResourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list());
+ const resourceGroups = allResourceGroups ? unfilteredResourceGroups : unfilteredResourceGroups.filter(resourceGroup => (>resourceGroupNames!)[resourceGroup.name!]);
+ newFilters.push({ session, subscription, allResourceGroups, resourceGroups });
+ }
+ }
+ this.api.filters.splice(0, this.api.filters.length, ...newFilters);
+ this.onFiltersChanged.fire();
+ }
+}
+
+async function deviceLogin1(): Promise {
+ return new Promise((resolve, reject) => {
+ const cache = new MemoryCache();
+ const context = new AuthenticationContext(authorityUrl, null, cache);
+ context.acquireUserCode(resource, clientId, 'en-us', function (err: any, response: any) {
+ if (err) {
+ reject(new AzureLoginError(localize('azure-account.userCodeFailed', "Aquiring user code failed"), err));
+ } else {
+ resolve(response);
+ }
+ });
+ });
+}
+
+async function deviceLogin2(deviceLogin: DeviceLogin) {
+ return new Promise((resolve, reject) => {
+ const tokenCache = new MemoryCache();
+ const context = new AuthenticationContext(authorityUrl, null, tokenCache);
+ context.acquireTokenWithDeviceCode(resource, clientId, deviceLogin, function (err: any, tokenResponse: TokenResponse) {
+ if (err) {
+ reject(new AzureLoginError(localize('azure-account.tokenFailed', "Aquiring token with device code"), err));
+ } else {
+ resolve(tokenResponse);
+ }
+ });
+ });
+}
+
+async function tokenFromRefreshToken(refreshToken: string, tenantId = commonTenantId) {
+ return new Promise((resolve, reject) => {
+ const tokenCache = new MemoryCache();
+ const context = new AuthenticationContext(`${authorityHostUrl}${tenantId}`, null, tokenCache);
+ context.acquireTokenWithRefreshToken(refreshToken, clientId, null, function (err: any, tokenResponse: TokenResponse) {
+ if (err) {
+ reject(new AzureLoginError(localize('azure-account.tokenFromRefreshTokenFailed', "Aquiring token with refresh token"), err));
+ } else {
+ resolve(tokenResponse);
+ }
+ });
+ });
+}
+
+async function tokensFromToken(firstTokenResponse: TokenResponse) {
+ const tokenResponses = [firstTokenResponse];
+ const tokenCache = new MemoryCache();
+ await addTokenToCache(tokenCache, firstTokenResponse);
+ const credentials = new DeviceTokenCredentials({ username: firstTokenResponse.userId, clientId, tokenCache });
+ const client = new SubscriptionClient(credentials);
+ const tenants = await listAll(client.tenants, client.tenants.list());
+ for (const tenant of tenants) {
+ if (tenant.tenantId !== firstTokenResponse.tenantId) {
+ const tokenResponse = await tokenFromRefreshToken(firstTokenResponse.refreshToken, tenant.tenantId);
+ tokenResponses.push(tokenResponse);
+ }
+ }
+ return tokenResponses;
+}
+
+async function addTokenToCache(tokenCache: any, tokenResponse: TokenResponse) {
+ return new Promise((resolve, reject) => {
+ const driver = new CacheDriver(
+ { _logContext: createLogContext('') },
+ `${authorityHostUrl}${tokenResponse.tenantId}`,
+ tokenResponse.resource,
+ clientId,
+ tokenCache,
+ (entry: any, resource: any, callback: (err: any, response: any) => {}) => {
+ callback(null, entry);
+ }
+ );
+ driver.add(tokenResponse, function (err: any) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ });
+ });
+}
+
+async function clearTokenCache(tokenCache: any) {
+ await new Promise((resolve, reject) => {
+ tokenCache.find({}, (err: any, entries: any[]) => {
+ if (err) {
+ reject(err);
+ } else {
+ tokenCache.remove(entries, (err: any) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ });
+ }
+ });
+ });
+}
+
+export interface PartialList extends Array {
+ nextLink?: string;
+}
+
+export async function listAll(client: { listNext(nextPageLink: string): Promise>; }, first: Promise>): Promise {
+ const all: T[] = [];
+ for (let list = await first; list.length || list.nextLink; list = list.nextLink ? await client.listNext(list.nextLink) : []) {
+ all.push(...list);
+ }
+ return all;
+}
\ No newline at end of file
diff --git a/extensions/azure-account/src/extension.ts b/extensions/azure-account/src/extension.ts
new file mode 100644
index 00000000000..df46a761df1
--- /dev/null
+++ b/extensions/azure-account/src/extension.ts
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { window, ExtensionContext, commands, credentials } from 'vscode';
+import { AzureLoginHelper } from './azure-account';
+import { AzureAccount } from './typings/azure-account.api';
+import * as opn from 'opn';
+import * as nls from 'vscode-nls';
+
+const localize = nls.loadMessageBundle();
+
+export function activate(context: ExtensionContext) {
+ if (!credentials) {
+ return; // Proposed API not available.
+ }
+ const azureLogin = new AzureLoginHelper(context);
+ const subscriptions = context.subscriptions;
+ subscriptions.push(createStatusBarItem(azureLogin.api));
+ subscriptions.push(commands.registerCommand('azure-account.createAccount', createAccount));
+ return azureLogin.api;
+}
+
+function createAccount() {
+ opn('https://azure.microsoft.com/en-us/free');
+}
+
+function createStatusBarItem(api: AzureAccount) {
+ const statusBarItem = window.createStatusBarItem();
+ function updateStatusBar() {
+ switch (api.status) {
+ case 'LoggingIn':
+ statusBarItem.text = localize('azure-account.loggingIn', "Azure: Logging in...");
+ statusBarItem.show();
+ break;
+ case 'LoggedIn':
+ statusBarItem.text = localize('azure-account.loggedIn', "Azure: {0}", api.sessions[0].userId);
+ statusBarItem.show();
+ break;
+ default:
+ statusBarItem.hide();
+ break;
+ }
+ }
+ api.onStatusChanged(updateStatusBar);
+ api.onSessionsChanged(updateStatusBar);
+ updateStatusBar();
+ return statusBarItem;
+}
+
+export function deactivate() {
+}
\ No newline at end of file
diff --git a/extensions/azure-account/src/typings/azure-account.api.d.ts b/extensions/azure-account/src/typings/azure-account.api.d.ts
new file mode 100644
index 00000000000..089511dda45
--- /dev/null
+++ b/extensions/azure-account/src/typings/azure-account.api.d.ts
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Event } from 'vscode';
+import { ServiceClientCredentials } from 'ms-rest';
+import { AzureEnvironment } from 'ms-rest-azure';
+import { SubscriptionModels, ResourceModels } from 'azure-arm-resource';
+
+export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut';
+
+export interface AzureAccount {
+ readonly status: AzureLoginStatus;
+ readonly onStatusChanged: Event;
+ readonly sessions: AzureSession[];
+ readonly onSessionsChanged: Event;
+ readonly filters: AzureResourceFilter[];
+ readonly onFiltersChanged: Event;
+ readonly credentials: Credentials;
+}
+
+export interface AzureSession {
+ readonly environment: AzureEnvironment;
+ readonly userId: string;
+ readonly tenantId: string;
+ readonly credentials: ServiceClientCredentials;
+}
+
+export interface AzureResourceFilter {
+ readonly session: AzureSession;
+ readonly subscription: SubscriptionModels.Subscription;
+ readonly allResourceGroups: boolean;
+ readonly resourceGroups: ResourceModels.ResourceGroup[];
+}
+
+export interface Credentials {
+ readSecret(service: string, account: string): Thenable;
+ writeSecret(service: string, account: string, secret: string): Thenable;
+ deleteSecret(service: string, account: string): Thenable;
+}
diff --git a/extensions/azure-account/src/typings/ref.d.ts b/extensions/azure-account/src/typings/ref.d.ts
new file mode 100644
index 00000000000..216911a680e
--- /dev/null
+++ b/extensions/azure-account/src/typings/ref.d.ts
@@ -0,0 +1,6 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+///
diff --git a/extensions/azure-account/src/typings/vscode.rejected.d.ts b/extensions/azure-account/src/typings/vscode.rejected.d.ts
new file mode 100644
index 00000000000..7f6f64df560
--- /dev/null
+++ b/extensions/azure-account/src/typings/vscode.rejected.d.ts
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+// This is the place for API experiments and proposal.
+
+declare module 'vscode' {
+
+ /**
+ * Namespace for handling credentials.
+ */
+ export namespace credentials {
+
+ /**
+ * Read a previously stored secret from the credential store.
+ *
+ * @param service The service of the credential.
+ * @param account The account of the credential.
+ * @return A promise for the secret of the credential.
+ */
+ export function readSecret(service: string, account: string): Thenable;
+
+ /**
+ * Write a secret to the credential store.
+ *
+ * @param service The service of the credential.
+ * @param account The account of the credential.
+ * @param secret The secret of the credential to write to the credential store.
+ * @return A promise indicating completion of the operation.
+ */
+ export function writeSecret(service: string, account: string, secret: string): Thenable;
+
+ /**
+ * Delete a previously stored secret from the credential store.
+ *
+ * @param service The service of the credential.
+ * @param account The account of the credential.
+ * @return A promise resolving to true if there was a secret for that service and account.
+ */
+ export function deleteSecret(service: string, account: string): Thenable;
+ }
+}
diff --git a/extensions/azure-account/tsconfig.json b/extensions/azure-account/tsconfig.json
new file mode 100644
index 00000000000..61265d449a4
--- /dev/null
+++ b/extensions/azure-account/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "strict": true,
+ "noUnusedLocals": true,
+ "outDir": "./out",
+ "lib": [
+ "es6"
+ ],
+ "sourceMap": true
+ },
+ "exclude": [
+ "node_modules"
+ ],
+ "include": [
+ "src/**/*"
+ ]
+}
\ No newline at end of file
diff --git a/extensions/css/client/src/cssMain.ts b/extensions/css/client/src/cssMain.ts
index 1080628cad4..a13dabac0b9 100644
--- a/extensions/css/client/src/cssMain.ts
+++ b/extensions/css/client/src/cssMain.ts
@@ -9,6 +9,7 @@ import * as path from 'path';
import { languages, window, commands, workspace, ExtensionContext } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, Range, TextEdit } from 'vscode-languageclient';
import { activateColorDecorations, ColorProvider } from './colorDecorators';
+import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
@@ -23,7 +24,7 @@ export function activate(context: ExtensionContext) {
// The server is implemented in node
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'cssServerMain.js'));
// The debug options for the server
- let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] };
+ let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
@@ -44,6 +45,7 @@ export function activate(context: ExtensionContext) {
// Create the language client and start the client.
let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions);
+ client.registerFeature(new ConfigurationFeature(client));
let disposable = client.start();
// Push the disposable to the context's subscriptions so that the
diff --git a/extensions/css/npm-shrinkwrap.json b/extensions/css/npm-shrinkwrap.json
index 89c99f09cf0..6a497e0b691 100644
--- a/extensions/css/npm-shrinkwrap.json
+++ b/extensions/css/npm-shrinkwrap.json
@@ -13,19 +13,19 @@
"resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz"
},
"vscode-jsonrpc": {
- "version": "3.2.0",
- "from": "vscode-jsonrpc@>=3.2.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz"
+ "version": "3.3.1",
+ "from": "vscode-jsonrpc@>=3.3.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz"
},
"vscode-languageclient": {
- "version": "3.2.0",
- "from": "vscode-languageclient@3.2.0",
- "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.2.0.tgz"
+ "version": "3.4.0-next.10",
+ "from": "vscode-languageclient@next",
+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.10.tgz"
},
"vscode-languageserver-types": {
- "version": "3.2.0",
- "from": "vscode-languageserver-types@>=3.2.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz"
+ "version": "3.3.0",
+ "from": "vscode-languageserver-types@>=3.3.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz"
},
"vscode-nls": {
"version": "2.0.2",
diff --git a/extensions/css/package.json b/extensions/css/package.json
index ff0bf9ac994..49b8f98ba2f 100644
--- a/extensions/css/package.json
+++ b/extensions/css/package.json
@@ -62,16 +62,19 @@
"properties": {
"css.validate": {
"type": "boolean",
+ "scope": "resource",
"default": true,
"description": "%css.validate.desc%"
},
"css.colorDecorators.enable": {
"type": "boolean",
+ "scope": "window",
"default": true,
"description": "%css.colorDecorators.enable.desc%"
},
"css.lint.compatibleVendorPrefixes": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -82,6 +85,7 @@
},
"css.lint.vendorPrefix": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -92,6 +96,7 @@
},
"css.lint.duplicateProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -102,6 +107,7 @@
},
"css.lint.emptyRules": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -112,6 +118,7 @@
},
"css.lint.importStatement": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -122,6 +129,7 @@
},
"css.lint.boxModel": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -132,6 +140,7 @@
},
"css.lint.universalSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -142,6 +151,7 @@
},
"css.lint.zeroUnits": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -152,6 +162,7 @@
},
"css.lint.fontFaceProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -162,6 +173,7 @@
},
"css.lint.hexColorLength": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -172,6 +184,7 @@
},
"css.lint.argumentsInColorFunction": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -182,6 +195,7 @@
},
"css.lint.unknownProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -192,6 +206,7 @@
},
"css.lint.ieHack": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -202,6 +217,7 @@
},
"css.lint.unknownVendorSpecificProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -212,6 +228,7 @@
},
"css.lint.propertyIgnoredDueToDisplay": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -222,6 +239,7 @@
},
"css.lint.important": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -232,6 +250,7 @@
},
"css.lint.float": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -242,6 +261,7 @@
},
"css.lint.idSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -252,6 +272,7 @@
},
"css.trace.server": {
"type": "string",
+ "scope": "window",
"enum": [
"off",
"messages",
@@ -274,16 +295,19 @@
"properties": {
"scss.validate": {
"type": "boolean",
+ "scope": "resource",
"default": true,
"description": "%scss.validate.desc%"
},
"scss.colorDecorators.enable": {
"type": "boolean",
+ "scope": "window",
"default": true,
"description": "%scss.colorDecorators.enable.desc%"
},
"scss.lint.compatibleVendorPrefixes": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -294,6 +318,7 @@
},
"scss.lint.vendorPrefix": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -304,6 +329,7 @@
},
"scss.lint.duplicateProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -314,6 +340,7 @@
},
"scss.lint.emptyRules": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -324,6 +351,7 @@
},
"scss.lint.importStatement": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -334,6 +362,7 @@
},
"scss.lint.boxModel": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -344,6 +373,7 @@
},
"scss.lint.universalSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -354,6 +384,7 @@
},
"scss.lint.zeroUnits": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -364,6 +395,7 @@
},
"scss.lint.fontFaceProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -374,6 +406,7 @@
},
"scss.lint.hexColorLength": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -384,6 +417,7 @@
},
"scss.lint.argumentsInColorFunction": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -394,6 +428,7 @@
},
"scss.lint.unknownProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -404,6 +439,7 @@
},
"scss.lint.ieHack": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -414,6 +450,7 @@
},
"scss.lint.unknownVendorSpecificProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -424,6 +461,7 @@
},
"scss.lint.propertyIgnoredDueToDisplay": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -434,6 +472,7 @@
},
"scss.lint.important": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -444,6 +483,7 @@
},
"scss.lint.float": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -454,6 +494,7 @@
},
"scss.lint.idSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -477,16 +518,19 @@
"properties": {
"less.validate": {
"type": "boolean",
+ "scope": "resource",
"default": true,
"description": "%less.validate.desc%"
},
"less.colorDecorators.enable": {
"type": "boolean",
+ "scope": "window",
"default": true,
"description": "%less.colorDecorators.enable.desc%"
},
"less.lint.compatibleVendorPrefixes": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -497,6 +541,7 @@
},
"less.lint.vendorPrefix": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -507,6 +552,7 @@
},
"less.lint.duplicateProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -517,6 +563,7 @@
},
"less.lint.emptyRules": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -527,6 +574,7 @@
},
"less.lint.importStatement": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -537,6 +585,7 @@
},
"less.lint.boxModel": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -547,6 +596,7 @@
},
"less.lint.universalSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -557,6 +607,7 @@
},
"less.lint.zeroUnits": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -567,6 +618,7 @@
},
"less.lint.fontFaceProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -577,6 +629,7 @@
},
"less.lint.hexColorLength": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -587,6 +640,7 @@
},
"less.lint.argumentsInColorFunction": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -597,6 +651,7 @@
},
"less.lint.unknownProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -607,6 +662,7 @@
},
"less.lint.ieHack": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -617,6 +673,7 @@
},
"less.lint.unknownVendorSpecificProperties": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -627,6 +684,7 @@
},
"less.lint.propertyIgnoredDueToDisplay": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -637,6 +695,7 @@
},
"less.lint.important": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -647,6 +706,7 @@
},
"less.lint.float": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -657,6 +717,7 @@
},
"less.lint.idSelector": {
"type": "string",
+ "scope": "resource",
"enum": [
"ignore",
"warning",
@@ -674,10 +735,10 @@
},
"dependencies": {
"parse-color": "^1.0.0",
- "vscode-languageclient": "^3.2.0",
+ "vscode-languageclient": "3.4.0-next.10",
"vscode-nls": "^2.0.2"
},
"devDependencies": {
"@types/node": "^6.0.51"
}
-}
\ No newline at end of file
+}
diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json
index 21329db6203..63bed9e999b 100644
--- a/extensions/css/server/npm-shrinkwrap.json
+++ b/extensions/css/server/npm-shrinkwrap.json
@@ -3,29 +3,34 @@
"version": "1.0.0",
"dependencies": {
"vscode-css-languageservice": {
- "version": "2.1.2",
+ "version": "2.1.3",
"from": "vscode-css-languageservice@next",
- "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.2.tgz"
+ "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz"
},
"vscode-jsonrpc": {
- "version": "3.2.0",
- "from": "vscode-jsonrpc@>=3.2.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz"
+ "version": "3.3.1",
+ "from": "vscode-jsonrpc@>=3.3.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz"
},
"vscode-languageserver": {
- "version": "3.2.0",
- "from": "vscode-languageserver@3.2.0",
- "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.2.0.tgz"
+ "version": "3.4.0-next.4",
+ "from": "vscode-languageserver@next",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.4.tgz"
},
"vscode-languageserver-types": {
- "version": "3.2.0",
- "from": "vscode-languageserver-types@>=3.2.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz"
+ "version": "3.3.0",
+ "from": "vscode-languageserver-types@>=3.3.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz"
},
"vscode-nls": {
"version": "2.0.2",
"from": "vscode-nls@>=2.0.1 <3.0.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
+ },
+ "vscode-uri": {
+ "version": "1.0.1",
+ "from": "vscode-uri@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz"
}
}
}
diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json
index c7b93d1573f..d8112cc044a 100644
--- a/extensions/css/server/package.json
+++ b/extensions/css/server/package.json
@@ -8,8 +8,8 @@
"node": "*"
},
"dependencies": {
- "vscode-css-languageservice": "^2.1.2",
- "vscode-languageserver": "^3.2.0"
+ "vscode-css-languageservice": "^2.1.3",
+ "vscode-languageserver": "3.4.0-next.4"
},
"devDependencies": {
"@types/node": "^6.0.51"
diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts
index 573b4b61c5f..4b5c4e4ad2c 100644
--- a/extensions/css/server/src/cssServerMain.ts
+++ b/extensions/css/server/src/cssServerMain.ts
@@ -8,6 +8,7 @@ import {
createConnection, IConnection, Range,
TextDocuments, TextDocument, InitializeParams, InitializeResult, RequestType
} from 'vscode-languageserver';
+import { GetConfigurationRequest } from 'vscode-languageserver/lib/protocol.proposed';
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
@@ -43,10 +44,20 @@ connection.onShutdown(() => {
stylesheets.dispose();
});
+let scopedSettingsSupport = false;
// After the server has started the client sends an initilize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
- let snippetSupport = params.capabilities && params.capabilities.textDocument && params.capabilities.textDocument.completion && params.capabilities.textDocument.completion.completionItem && params.capabilities.textDocument.completion.completionItem.snippetSupport;
+ function hasClientCapability(name: string) {
+ let keys = name.split('.');
+ let c = params.capabilities;
+ for (let i = 0; c && i < keys.length; i++) {
+ c = c[keys[i]];
+ }
+ return !!c;
+ }
+ let snippetSupport = hasClientCapability('textDocument.completion.completionItem.snippetSupport');
+ scopedSettingsSupport = hasClientCapability('workspace.configuration');
return {
capabilities: {
// Tell the client that the server works in FULL text document sync mode
@@ -78,6 +89,20 @@ function getLanguageService(document: TextDocument) {
return service;
}
+let documentSettings: { [key: string]: Thenable } = {};
+function getDocumentSettings(textDocument: TextDocument): Thenable {
+ if (scopedSettingsSupport) {
+ let promise = documentSettings[textDocument.uri];
+ if (!promise) {
+ let configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] };
+ promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => s[0]);
+ documentSettings[textDocument.uri] = promise;
+ }
+ return promise;
+ }
+ return void 0;
+}
+
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration(change => {
updateConfiguration(change.settings);
@@ -87,6 +112,8 @@ function updateConfiguration(settings: Settings) {
for (let languageId in languageServices) {
languageServices[languageId].configure(settings[languageId]);
}
+ // reset all document settings
+ documentSettings = {};
// Revalidate any open text documents
documents.all().forEach(triggerValidation);
}
@@ -123,10 +150,13 @@ function triggerValidation(textDocument: TextDocument): void {
}
function validateTextDocument(textDocument: TextDocument): void {
+ let settingsPromise = getDocumentSettings(textDocument);
let stylesheet = stylesheets.get(textDocument);
- let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet);
- // Send the computed diagnostics to VSCode.
- connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
+ settingsPromise.then(settings => {
+ let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings);
+ // Send the computed diagnostics to VSCode.
+ connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
+ });
}
connection.onCompletion(textDocumentPosition => {
diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json
index 68b2886e6d4..d4dd5915a17 100644
--- a/extensions/emmet/npm-shrinkwrap.json
+++ b/extensions/emmet/npm-shrinkwrap.json
@@ -4,8 +4,8 @@
"dependencies": {
"@emmetio/css-parser": {
"version": "0.4.0",
- "from": "@emmetio/css-parser@>=0.4.0 <0.5.0",
- "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.0.tgz"
+ "from": "ramya-rao-a/css-parser#vscode",
+ "resolved": "git://github.com/ramya-rao-a/css-parser.git#370c480ac103bd17c7bcfb34bf5d577dc40d3660"
},
"@emmetio/extract-abbreviation": {
"version": "0.1.3",
@@ -38,9 +38,9 @@
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz"
},
"vscode-emmet-helper": {
- "version": "1.0.16",
- "from": "vscode-emmet-helper@>=1.0.16 <2.0.0",
- "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.16.tgz"
+ "version": "1.0.17",
+ "from": "vscode-emmet-helper@>=1.0.17 <2.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.17.tgz"
},
"vscode-languageserver-types": {
"version": "3.3.0",
diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json
index f9266ef067a..7bf1a1213b9 100644
--- a/extensions/emmet/package.json
+++ b/extensions/emmet/package.json
@@ -210,9 +210,9 @@
"dependencies": {
"@emmetio/html-matcher": "^0.3.1",
- "@emmetio/css-parser": "^0.4.0",
+ "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode",
"@emmetio/math-expression": "^0.1.1",
- "vscode-emmet-helper": "^1.0.16",
+ "vscode-emmet-helper": "^1.0.17",
"vscode-languageserver-types": "^3.0.3",
"image-size": "^0.5.2",
"vscode-nls": "2.0.2"
diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts
index 399d3807833..df92393e509 100644
--- a/extensions/emmet/src/abbreviationActions.ts
+++ b/extensions/emmet/src/abbreviationActions.ts
@@ -95,10 +95,7 @@ export function expandEmmetAbbreviation(args): Thenable {
const editor = vscode.window.activeTextEditor;
- let rootNode = parseDocument(editor.document);
- if (!rootNode) {
- return fallbackTab();
- }
+ let rootNode = parseDocument(editor.document, false);
// When tabbed on a non empty selection, do not treat it as an emmet abbreviation, and fallback to tab instead
if (vscode.workspace.getConfiguration('emmet')['triggerExpansionOnTab'] === true && editor.selections.find(x => !x.isEmpty)) {
@@ -192,21 +189,31 @@ export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: s
}
if (isStyleSheet(syntax)) {
- if (currentNode.type !== 'rule') {
+
+ // If current node is a rule or at-rule, then perform additional checks to ensure
+ // emmet suggestions are not provided in the rule selector
+ if (currentNode.type !== 'rule' && currentNode.type !== 'at-rule') {
return true;
}
+
const currentCssNode = currentNode;
- // Workaround for https://github.com/Microsoft/vscode/30188
- if (currentCssNode.parent
- && currentCssNode.parent.type === 'rule'
- && currentCssNode.selectorToken
- && currentCssNode.selectorToken.start.line !== currentCssNode.selectorToken.end.line) {
+ // Position is valid if it occurs after the `{` that marks beginning of rule contents
+ if (position.isAfter(currentCssNode.contentStartToken.end)) {
return true;
}
- // Position is valid if it occurs after the `{` that marks beginning of rule contents
- return currentCssNode.selectorToken && position.isAfter(currentCssNode.selectorToken.end);
+ // Workaround for https://github.com/Microsoft/vscode/30188
+ // The line above the rule selector is considered as part of the selector by the css-parser
+ // But we should assume it is a valid location for css properties under the parent rule
+ if (currentCssNode.parent
+ && (currentCssNode.parent.type === 'rule' || currentCssNode.parent.type === 'at-rule')
+ && currentCssNode.selectorToken
+ && position.line !== currentCssNode.selectorToken.end.line) {
+ return true;
+ }
+
+ return false;
}
const currentHtmlNode = currentNode;
diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts
index 28adbadfb7a..cadfd8ef380 100644
--- a/extensions/emmet/src/defaultCompletionProvider.ts
+++ b/extensions/emmet/src/defaultCompletionProvider.ts
@@ -72,9 +72,13 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
const currentHtmlNode = currentNode;
if (currentHtmlNode
&& currentHtmlNode.close
- && currentHtmlNode.name === 'style'
&& getInnerRange(currentHtmlNode).contains(position)) {
- return 'css';
+ if (currentHtmlNode.name === 'style') {
+ return 'css';
+ }
+ if (currentHtmlNode.name === 'script') {
+ return;
+ }
}
}
diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts
index 9d179b8d661..f3e0aec1456 100644
--- a/extensions/emmet/src/test/abbreviationAction.test.ts
+++ b/extensions/emmet/src/test/abbreviationAction.test.ts
@@ -21,6 +21,25 @@ const cssContents = `
}
`;
+const scssContents = `
+.boo {
+ margin: 10px;
+ p10
+ .hoo {
+ p20
+ }
+}
+@include b(alert) {
+
+ margin: 10px;
+ p30
+
+ @include b(alert) {
+ p40
+ }
+}
+`
+
const bemFilterExample = 'ul.search-form._wide>li.-querystring+li.-btn_large|bem';
const expectedBemFilterOutput = `