Merge branch 'master' into misolori/issue97204

This commit is contained in:
Peng Lyu 2020-06-01 15:08:03 -07:00 committed by GitHub
commit 45a80baf12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
801 changed files with 23581 additions and 12710 deletions

View file

@ -63,13 +63,18 @@
"browser": [
"common"
],
"electron-main": [
"electron-sandbox": [
"common",
"node"
"browser"
],
"electron-browser": [
"common",
"browser",
"node",
"electron-sandbox"
],
"electron-main": [
"common",
"node"
]
}
@ -104,6 +109,14 @@
"**/vs/base/{common,browser}/**"
]
},
{
"target": "**/vs/base/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/base/node/**",
"restrictions": [
@ -149,13 +162,22 @@
"*" // node modules
]
},
{
"target": "**/vs/base/parts/*/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,electron-sandbox}/**",
"**/vs/base/parts/*/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/base/parts/*/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,node,electron-browser}/**",
"**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
"**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -185,6 +207,7 @@
"vs/nls",
"**/vs/base/common/**",
"**/vs/base/parts/*/common/**",
"**/vs/base/test/common/**",
"**/vs/platform/*/common/**",
"**/vs/platform/*/test/common/**"
]
@ -209,14 +232,24 @@
"*" // node modules
]
},
{
"target": "**/vs/platform/*/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,electron-sandbox}/**",
"**/vs/base/parts/*/{common,browser,electron-sandbox}/**",
"**/vs/platform/*/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/platform/*/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,node}/**",
"**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
"**/vs/platform/*/{common,browser,node,electron-browser}/**",
"**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -420,18 +453,34 @@
"**/vs/**/{common,worker}/**"
]
},
{
"target": "**/vs/workbench/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,electron-sandbox}/**",
"**/vs/base/parts/*/{common,browser,electron-sandbox}/**",
"**/vs/platform/*/{common,browser,electron-sandbox}/**",
"**/vs/editor/{common,browser,electron-sandbox}/**",
"**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention
"**/vs/workbench/{common,browser,electron-sandbox}/**",
"**/vs/workbench/api/{common,browser,electron-sandbox}/**",
"**/vs/workbench/services/*/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/workbench/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/{common,browser,node,electron-browser}/**",
"**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
"**/vs/platform/*/{common,browser,node,electron-browser}/**",
"**/vs/editor/{common,browser,node,electron-browser}/**",
"**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention
"**/vs/workbench/{common,browser,node,electron-browser,api}/**",
"**/vs/workbench/services/*/{common,browser,node,electron-browser}/**",
"**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/services/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -443,7 +492,7 @@
"**/vs/base/**",
"**/vs/platform/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,node,electron-browser}/**",
"**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"vs/workbench/contrib/files/common/editors/fileEditorInput",
"**/vs/workbench/services/**",
"**/vs/workbench/test/**",
@ -507,16 +556,30 @@
"*" // node modules
]
},
{
"target": "**/vs/workbench/services/**/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/**/{common,browser,worker,electron-sandbox}/**",
"**/vs/platform/**/{common,browser,electron-sandbox}/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,electron-sandbox}/**",
"**/vs/workbench/api/{common,browser,electron-sandbox}/**",
"**/vs/workbench/services/**/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/workbench/services/**/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/**/{common,browser,worker,node,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-browser}/**",
"**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,node,electron-browser,api}/**",
"**/vs/workbench/services/**/{common,browser,node,electron-browser}/**",
"**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -529,7 +592,7 @@
"**/vs/base/**",
"**/vs/platform/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,node,electron-browser}/**",
"**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/services/**",
"**/vs/workbench/contrib/**",
"**/vs/workbench/test/**",
@ -623,17 +686,32 @@
"*" // node modules
]
},
{
"target": "**/vs/workbench/contrib/**/electron-sandbox/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/**/{common,browser,worker,electron-sandbox}/**",
"**/vs/platform/**/{common,browser,electron-sandbox}/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,electron-sandbox}/**",
"**/vs/workbench/api/{common,browser,electron-sandbox}/**",
"**/vs/workbench/services/**/{common,browser,electron-sandbox}/**",
"**/vs/workbench/contrib/**/{common,browser,electron-sandbox}/**"
]
},
{
"target": "**/vs/workbench/contrib/**/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/**/{common,browser,worker,node,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-browser}/**",
"**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/**",
"**/vs/workbench/{common,browser,node,electron-browser,api}/**",
"**/vs/workbench/services/**/{common,browser,node,electron-browser}/**",
"**/vs/workbench/contrib/**/{common,browser,node,electron-browser}/**",
"**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/contrib/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -653,10 +731,10 @@
"restrictions": [
"vs/nls",
"vs/css!./**/*",
"**/vs/base/**/{common,browser,node,electron-browser}/**",
"**/vs/base/parts/**/{common,browser,node,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-browser}/**",
"**/vs/code/**/{common,browser,node,electron-browser}/**",
"**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/code/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@ -684,6 +762,54 @@
"*" // node modules
]
},
{
"target": "**/src/vs/workbench/workbench.common.main.ts",
"restrictions": [
"vs/nls",
"**/vs/base/**/{common,browser}/**",
"**/vs/base/parts/**/{common,browser}/**",
"**/vs/platform/**/{common,browser}/**",
"**/vs/editor/**",
"**/vs/workbench/**/{common,browser}/**"
]
},
{
"target": "**/src/vs/workbench/workbench.web.main.ts",
"restrictions": [
"vs/nls",
"**/vs/base/**/{common,browser}/**",
"**/vs/base/parts/**/{common,browser}/**",
"**/vs/platform/**/{common,browser}/**",
"**/vs/editor/**",
"**/vs/workbench/**/{common,browser}/**",
"**/vs/workbench/workbench.common.main"
]
},
{
"target": "**/src/vs/workbench/workbench.sandbox.main.ts",
"restrictions": [
"vs/nls",
"**/vs/base/**/{common,browser,electron-sandbox}/**",
"**/vs/base/parts/**/{common,browser,electron-sandbox}/**",
"**/vs/platform/**/{common,browser,electron-sandbox}/**",
"**/vs/editor/**",
"**/vs/workbench/**/{common,browser,electron-sandbox}/**",
"**/vs/workbench/workbench.common.main"
]
},
{
"target": "**/src/vs/workbench/workbench.desktop.main.ts",
"restrictions": [
"vs/nls",
"**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/**",
"**/vs/workbench/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/workbench.common.main",
"**/vs/workbench/workbench.sandbox.main"
]
},
{
"target": "**/extensions/**",
"restrictions": "**/*"

View file

@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v16
ref: v18
path: ./actions
- name: Install Actions
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested')
@ -31,6 +31,7 @@ jobs:
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested')
uses: ./actions/author-verified
with:
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!"
pendingReleaseLabel: awaiting-insiders-release
authorVerificationRequestedLabel: author-verification-requested

View file

@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v16
ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions

View file

@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
ref: v16
ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Commands

View file

@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v16
ref: v18
path: ./actions
- name: Install Actions
if: contains(github.event.issue.labels.*.name, '*english-please')

View file

@ -17,7 +17,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
ref: v16
ref: v18
- name: Install Actions
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request')
run: npm install --production --prefix ./actions

View file

@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
ref: v16
ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Locker

View file

@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
ref: v16
ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Needs More Info Closer

View file

@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v16
ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions

View file

@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v16
ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions

View file

@ -0,0 +1,31 @@
name: "Release Pipeline Labeler"
on:
issues:
types: [closed]
schedule:
- cron: 20 14 * * * # 4:20pm Zurich
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
ref: v18
path: ./actions
- name: Checkout Repo
if: github.event_name != 'issues'
uses: actions/checkout@v2
with:
path: ./repo
fetch-depth: 0
- name: Install Actions
run: npm install --production --prefix ./actions
- name: "Run Release Pipeline Labeler"
uses: ./actions/release-pipeline
with:
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
notYetReleasedLabel: unreleased
insidersReleasedLabel: insiders-released

View file

@ -14,7 +14,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
ref: v16
ref: v18
- name: Install Actions
if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item')
run: npm install --production --prefix ./actions

31
.vscode/launch.json vendored
View file

@ -20,10 +20,7 @@
"port": 5870,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"presentation": {
"hidden": true
}
]
},
{
"type": "pwa-chrome",
@ -173,7 +170,7 @@
"${workspaceFolder}/out/**/*.js"
],
"presentation": {
"group": "6_tests",
"group": "5_tests",
"order": 6
}
},
@ -220,10 +217,7 @@
"type": "node",
"request": "launch",
"name": "VS Code (Web)",
"runtimeExecutable": "yarn",
"runtimeArgs": [
"web"
],
"program": "${workspaceFolder}/scripts/code-web.js",
"presentation": {
"group": "0_vscode",
"order": 2
@ -277,7 +271,7 @@
{
"type": "node",
"request": "launch",
"name": "HTML Unit Tests",
"name": "HTML Server Unit Tests",
"program": "${workspaceFolder}/extensions/html-language-features/server/test/index.js",
"stopOnEntry": false,
"cwd": "${workspaceFolder}/extensions/html-language-features/server",
@ -289,6 +283,21 @@
"order": 10
}
},
{
"type": "node",
"request": "launch",
"name": "CSS Server Unit Tests",
"program": "${workspaceFolder}/extensions/css-language-features/server/test/index.js",
"stopOnEntry": false,
"cwd": "${workspaceFolder}/extensions/css-language-features/server",
"outFiles": [
"${workspaceFolder}/extensions/css-language-features/server/out/**/*.js"
],
"presentation": {
"group": "5_tests",
"order": 10
}
},
{
"type": "extensionHost",
"request": "launch",
@ -313,7 +322,7 @@
"name": "TypeScript Extension Tests",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/extensions/typescript-language-features/test-fixtures",
"${workspaceFolder}/extensions/typescript-language-features/test-workspace",
"--extensionDevelopmentPath=${workspaceFolder}/extensions/typescript-language-features",
"--extensionTestsPath=${workspaceFolder}/extensions/typescript-language-features/out/test"
],

View file

@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "7.2.4"
target "7.3.0"
runtime "electron"

View file

@ -6,8 +6,6 @@
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View file

@ -4,7 +4,5 @@
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View file

@ -162,21 +162,13 @@ steps:
- script: |
set -e
APP_ROOT=$(agent.builddirectory)/VSCode-darwin
APP_NAME="`ls $APP_ROOT | head -n 1`"
HELPER_APP_NAME="`echo $APP_NAME | sed -e 's/^Visual Studio //;s/\.app$//'`"
APP_FRAMEWORK_PATH="$APP_ROOT/$APP_NAME/Contents/Frameworks"
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist "$APP_ROOT"/*.app
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper.app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-gpu-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (GPU).app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-plugin-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Plugin).app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-renderer-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Renderer).app"
DEBUG=electron-osx-sign* node build/darwin/sign.js
displayName: Set Hardened Entitlements
- script: |

View file

@ -12,6 +12,8 @@ const es = require('event-stream');
const vfs = require('vinyl-fs');
const fancyLog = require('fancy-log');
const ansiColors = require('ansi-colors');
const fs = require('fs');
const path = require('path');
function main() {
const quality = process.env['VSCODE_QUALITY'];
@ -21,7 +23,7 @@ function main() {
return;
}
const productJsonFilter = filter('product.json', { restore: true });
const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true });
fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`);
return vfs
@ -29,7 +31,32 @@ function main() {
.pipe(filter(f => !f.isDirectory()))
.pipe(productJsonFilter)
.pipe(buffer())
.pipe(json(o => Object.assign({}, require('../product.json'), o)))
.pipe(json(o => {
const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8'));
let builtInExtensions = ossProduct.builtInExtensions;
if (Array.isArray(o.builtInExtensions)) {
fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name));
builtInExtensions = o.builtInExtensions;
} else if (o.builtInExtensions) {
const include = o.builtInExtensions['include'] || [];
const exclude = o.builtInExtensions['exclude'] || [];
fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name));
fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name));
fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude);
builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name));
builtInExtensions = [...builtInExtensions, ...include];
fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name));
} else {
fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name));
}
return { ...o, builtInExtensions };
}))
.pipe(productJsonFilter.restore)
.pipe(es.mapSync(function (f) {
fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎'));
@ -38,4 +65,4 @@ function main() {
.pipe(vfs.dest('.'));
}
main();
main();

View file

@ -36,6 +36,17 @@ jobs:
steps:
- template: win32/product-build-win32.yml
- job: WindowsARM64
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true'))
pool:
vmImage: VS2017-Win2016
variables:
VSCODE_ARCH: arm64
dependsOn:
- Compile
steps:
- template: win32/product-build-win32-arm64.yml
- job: Linux
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true'))
pool:

View file

@ -72,29 +72,6 @@ steps:
vstsFeed: 'npm-vscode'
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
- script: |
set -e
yarn generate-github-config
displayName: Generate GitHub config
env:
OSS_GITHUB_ID: "a5d3c261b032765a78de"
OSS_GITHUB_SECRET: $(oss-github-client-secret)
INSIDERS_GITHUB_ID: "31f02627809389d9f111"
INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret)
STABLE_GITHUB_ID: "baa8a44b5e861d918709"
STABLE_GITHUB_SECRET: $(stable-github-client-secret)
EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea"
EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret)
VSO_GITHUB_ID: "3d4be8f37a0325b5817d"
VSO_GITHUB_SECRET: $(vso-github-client-secret)
VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492"
VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret)
VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c"
VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret)
GITHUB_APP_ID: "Iv1.ae51e546bef24ff1"
GITHUB_APP_SECRET: $(github-app-client-secret)
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
- script: |
set -e
yarn postinstall

View file

@ -0,0 +1,190 @@
steps:
- powershell: |
mkdir .build -ea 0
"$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit
"$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality
displayName: Prepare cache flag
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
inputs:
keyfile: 'build/.cachesalt, .build/commit, .build/quality'
targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min'
vstsFeed: 'npm-vscode'
platformIndependent: true
alias: 'Compilation'
- powershell: |
$ErrorActionPreference = "Stop"
exit 1
displayName: Check RestoreCache
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
- task: NodeTool@0
inputs:
versionSpec: "12.13.0"
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
inputs:
versionSpec: "1.x"
- task: UsePythonVersion@0
inputs:
versionSpec: '2.x'
addToPath: true
- task: AzureKeyVault@1
displayName: 'Azure Key Vault: Get Secrets'
inputs:
azureSubscription: 'vscode-builds-subscription'
KeyVaultName: vscode
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
"machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
exec { git config user.email "vscode@microsoft.com" }
exec { git config user.name "VSCode" }
mkdir .build -ea 0
"$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
displayName: Prepare tooling
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" }
exec { git fetch distro }
exec { git merge $(node -p "require('./package.json').distro") }
displayName: Merge distro
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
inputs:
keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
vstsFeed: 'npm-vscode'
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:npm_config_arch="$(VSCODE_ARCH)"
$env:CHILD_CONCURRENCY="1"
exec { yarn --frozen-lockfile }
displayName: Install dependencies
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
inputs:
keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
vstsFeed: 'npm-vscode'
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn postinstall }
displayName: Run postinstall scripts
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { node build/azure-pipelines/mixin }
displayName: Mix in quality
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" }
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" }
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" }
displayName: Build
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
inputs:
ConnectedServiceName: 'ESRP CodeSign'
FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)'
Pattern: '*.dll,*.exe,*.node'
signConfigType: inlineSignParams
inlineOperation: |
[
{
"keyCode": "CP-230012",
"operationSetCode": "SigntoolSign",
"parameters": [
{
"parameterName": "OpusName",
"parameterValue": "VS Code"
},
{
"parameterName": "OpusInfo",
"parameterValue": "https://code.visualstudio.com/"
},
{
"parameterName": "Append",
"parameterValue": "/as"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"toolName": "sign",
"toolVersion": "1.0"
},
{
"keyCode": "CP-230012",
"operationSetCode": "SigntoolVerify",
"parameters": [
{
"parameterName": "VerifyAll",
"parameterValue": "/all"
}
],
"toolName": "sign",
"toolVersion": "1.0"
}
]
SessionTimeout: 120
- task: NuGetCommand@2
displayName: Install ESRPClient.exe
inputs:
restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config'
feedsToUse: config
nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config'
externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b
restoreDirectory: packages
- task: ESRPImportCertTask@1
displayName: Import ESRP Request Signing Certificate
inputs:
ESRP: 'ESRP CodeSign'
- powershell: |
$ErrorActionPreference = "Stop"
.\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key)
displayName: Import ESRP Auth Certificate
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)"
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)"
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
.\build\azure-pipelines\win32\publish.ps1
displayName: Publish
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
continueOnError: true

View file

@ -16,16 +16,21 @@ $ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
$Build = "$Root\VSCode-win32-$Arch"
# Create server archive
exec { xcopy $LegacyServer $Server /H /E /I }
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
if ("$Arch" -ne "arm64") {
exec { xcopy $LegacyServer $Server /H /E /I }
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
}
# get version
$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json
$Version = $PackageJson.version
$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" }
$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-$Arch" }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Zip }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $SystemExe }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $UserExe }
exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
if ("$Arch" -ne "arm64") {
exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
}

View file

@ -10,11 +10,11 @@ const path = require('path');
let window = null;
app.once('ready', () => {
window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true } });
window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true, enableWebSQL: false } });
window.setMenuBarVisibility(false);
window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true }));
// window.webContents.openDevTools();
window.once('closed', () => window = null);
});
app.on('window-all-closed', () => app.quit());
app.on('window-all-closed', () => app.quit());

61
build/darwin/sign.js Normal file
View file

@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const codesign = require("electron-osx-sign");
const path = require("path");
const util = require("../lib/util");
const product = require("../../product.json");
async function main() {
const buildDir = process.env['AGENT_BUILDDIRECTORY'];
const tempDir = process.env['AGENT_TEMPDIRECTORY'];
if (!buildDir) {
throw new Error('$AGENT_BUILDDIRECTORY not set');
}
if (!tempDir) {
throw new Error('$AGENT_TEMPDIRECTORY not set');
}
const baseDir = path.dirname(__dirname);
const appRoot = path.join(buildDir, 'VSCode-darwin');
const appName = product.nameLong + '.app';
const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks');
const helperAppBaseName = product.nameShort;
const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app';
const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app';
const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app';
const defaultOpts = {
app: path.join(appRoot, appName),
platform: 'darwin',
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
hardenedRuntime: true,
'pre-auto-entitlements': false,
'pre-embed-provisioning-profile': false,
keychain: path.join(tempDir, 'buildagent.keychain'),
version: util.getElectronVersion(),
identity: '99FM488X57',
'gatekeeper-assess': false
};
const appOpts = Object.assign(Object.assign({}, defaultOpts), {
// TODO(deepak1556): Incorrectly declared type in electron-osx-sign
ignore: (filePath) => {
return filePath.includes(gpuHelperAppName) ||
filePath.includes(pluginHelperAppName) ||
filePath.includes(rendererHelperAppName);
} });
const gpuHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, gpuHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist') });
const pluginHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, pluginHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist') });
const rendererHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, rendererHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist') });
await codesign.signAsync(gpuHelperOpts);
await codesign.signAsync(pluginHelperOpts);
await codesign.signAsync(rendererHelperOpts);
await codesign.signAsync(appOpts);
}
if (require.main === module) {
main().catch(err => {
console.error(err);
process.exit(1);
});
}

90
build/darwin/sign.ts Normal file
View file

@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* 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 * as codesign from 'electron-osx-sign';
import * as path from 'path';
import * as util from '../lib/util';
import * as product from '../../product.json';
async function main(): Promise<void> {
const buildDir = process.env['AGENT_BUILDDIRECTORY'];
const tempDir = process.env['AGENT_TEMPDIRECTORY'];
if (!buildDir) {
throw new Error('$AGENT_BUILDDIRECTORY not set');
}
if (!tempDir) {
throw new Error('$AGENT_TEMPDIRECTORY not set');
}
const baseDir = path.dirname(__dirname);
const appRoot = path.join(buildDir, 'VSCode-darwin');
const appName = product.nameLong + '.app';
const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks');
const helperAppBaseName = product.nameShort;
const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app';
const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app';
const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app';
const defaultOpts: codesign.SignOptions = {
app: path.join(appRoot, appName),
platform: 'darwin',
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
hardenedRuntime: true,
'pre-auto-entitlements': false,
'pre-embed-provisioning-profile': false,
keychain: path.join(tempDir, 'buildagent.keychain'),
version: util.getElectronVersion(),
identity: '99FM488X57',
'gatekeeper-assess': false
};
const appOpts = {
...defaultOpts,
// TODO(deepak1556): Incorrectly declared type in electron-osx-sign
ignore: (filePath: string) => {
return filePath.includes(gpuHelperAppName) ||
filePath.includes(pluginHelperAppName) ||
filePath.includes(rendererHelperAppName);
}
};
const gpuHelperOpts: codesign.SignOptions = {
...defaultOpts,
app: path.join(appFrameworkPath, gpuHelperAppName),
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
};
const pluginHelperOpts: codesign.SignOptions = {
...defaultOpts,
app: path.join(appFrameworkPath, pluginHelperAppName),
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'),
};
const rendererHelperOpts: codesign.SignOptions = {
...defaultOpts,
app: path.join(appFrameworkPath, rendererHelperAppName),
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
};
await codesign.signAsync(gpuHelperOpts);
await codesign.signAsync(pluginHelperOpts);
await codesign.signAsync(rendererHelperOpts);
await codesign.signAsync(appOpts as any);
}
if (require.main === module) {
main().catch(err => {
console.error(err);
process.exit(1);
});
}

View file

@ -59,6 +59,7 @@ const indentationFilter = [
// except specific folders
'!test/automation/out/**',
'!test/smoke/out/**',
'!extensions/typescript-language-features/test-workspace/**',
'!extensions/vscode-api-tests/testWorkspace/**',
'!extensions/vscode-api-tests/testWorkspace2/**',
'!build/monaco/**',
@ -84,7 +85,7 @@ const indentationFilter = [
'!src/typings/**/*.d.ts',
'!extensions/**/*.d.ts',
'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
'!build/{lib,download}/**/*.js',
'!build/{lib,download,darwin}/**/*.js',
'!build/**/*.sh',
'!build/azure-pipelines/**/*.js',
'!build/azure-pipelines/**/*.config',

View file

@ -66,6 +66,7 @@ const vscodeResources = [
'out-build/vs/base/node/languagePacks.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',
'out-build/vs/base/browser/ui/codicons/codicon/**',
'out-build/vs/base/parts/sandbox/electron-browser/preload.js',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/contrib/debug/**/*.json',
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
@ -154,6 +155,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
const out = sourceFolderName;
const checksums = computeChecksums(out, [
'vs/base/parts/sandbox/electron-browser/preload.js',
'vs/workbench/workbench.desktop.main.js',
'vs/workbench/workbench.desktop.main.css',
'vs/workbench/services/extensions/node/extensionHostProcess.js',

View file

@ -65,6 +65,7 @@ function buildWin32Setup(arch, target) {
return cb => {
const ia32AppId = target === 'system' ? product.win32AppId : product.win32UserAppId;
const x64AppId = target === 'system' ? product.win32x64AppId : product.win32x64UserAppId;
const arm64AppId = target === 'system' ? product.win32arm64AppId : product.win32arm64UserAppId;
const sourcePath = buildPath(arch);
const outputPath = setupDir(arch, target);
@ -88,12 +89,12 @@ function buildWin32Setup(arch, target) {
ShellNameShort: product.win32ShellNameShort,
AppMutex: product.win32MutexName,
Arch: arch,
AppId: arch === 'ia32' ? ia32AppId : x64AppId,
IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId,
IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId,
AppId: { 'ia32': ia32AppId, 'x64': x64AppId, 'arm64': arm64AppId }[arch],
IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch],
IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch],
AppUserId: product.win32AppUserModelId,
ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64',
ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64',
ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
SourceDir: sourcePath,
RepoDir: repoPath,
OutputDir: outputPath,
@ -112,8 +113,10 @@ function defineWin32SetupTasks(arch, target) {
defineWin32SetupTasks('ia32', 'system');
defineWin32SetupTasks('x64', 'system');
defineWin32SetupTasks('arm64', 'system');
defineWin32SetupTasks('ia32', 'user');
defineWin32SetupTasks('x64', 'user');
defineWin32SetupTasks('arm64', 'user');
function archiveWin32Setup(arch) {
return cb => {
@ -145,6 +148,7 @@ function updateIcon(executablePath) {
gulp.task(task.define('vscode-win32-ia32-inno-updater', task.series(copyInnoUpdater('ia32'), updateIcon(path.join(buildPath('ia32'), 'tools', 'inno_updater.exe')))));
gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdater('x64'), updateIcon(path.join(buildPath('x64'), 'tools', 'inno_updater.exe')))));
gulp.task(task.define('vscode-win32-arm64-inno-updater', task.series(copyInnoUpdater('arm64'), updateIcon(path.join(buildPath('arm64'), 'tools', 'inno_updater.exe')))));
// CodeHelper.exe icon

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = exports.getElectronVersion = void 0;
exports.config = void 0;
const fs = require("fs");
const path = require("path");
const vfs = require("vinyl-fs");
@ -16,12 +16,6 @@ const electron = require('gulp-atom-electron');
const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
function getElectronVersion() {
const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
const target = /^target "(.*)"$/m.exec(yarnrc)[1];
return target;
}
exports.getElectronVersion = getElectronVersion;
const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8'));
function darwinBundleDocumentType(extensions, icon) {
return {
@ -33,7 +27,7 @@ function darwinBundleDocumentType(extensions, icon) {
};
}
exports.config = {
version: getElectronVersion(),
version: util.getElectronVersion(),
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2019 Microsoft. All rights reserved',
@ -70,7 +64,7 @@ exports.config = {
darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'),
darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'),
darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'),
darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "containerfile", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
],
darwinBundleURLTypes: [{
role: 'Viewer',
@ -100,7 +94,7 @@ function getElectron(arch) {
};
}
async function main(arch = process.arch) {
const version = getElectronVersion();
const version = util.getElectronVersion();
const electronPath = path.join(root, '.build', 'electron');
const versionFile = path.join(electronPath, 'version');
const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`;

View file

@ -19,12 +19,6 @@ const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
export function getElectronVersion(): string {
const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
const target = /^target "(.*)"$/m.exec(yarnrc)![1];
return target;
}
const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8'));
function darwinBundleDocumentType(extensions: string[], icon: string) {
@ -38,7 +32,7 @@ function darwinBundleDocumentType(extensions: string[], icon: string) {
}
export const config = {
version: getElectronVersion(),
version: util.getElectronVersion(),
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2019 Microsoft. All rights reserved',
@ -75,7 +69,7 @@ export const config = {
darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'),
darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'),
darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'),
darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "containerfile", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
],
darwinBundleURLTypes: [{
role: 'Viewer',
@ -108,7 +102,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream {
}
async function main(arch = process.arch): Promise<void> {
const version = getElectronVersion();
const version = util.getElectronVersion();
const electronPath = path.join(root, '.build', 'electron');
const versionFile = path.join(electronPath, 'version');
const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`;

View file

@ -342,6 +342,10 @@
"name": "vs/workbench/services/userDataSync",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/views",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/timeline",
"project": "vscode-workbench"

View file

@ -130,6 +130,14 @@ const RULES = [
'lib.dom.d.ts' // no DOM
]
},
// Electron (sandbox)
{
target: '**/vs/**/electron-sandbox/**',
allowedTypes: CORE_TYPES,
disallowedDefinitions: [
'@types/node' // no node.js
]
},
// Electron (renderer): skip
{
target: '**/vs/**/electron-browser/**',

View file

@ -143,6 +143,15 @@ const RULES = [
]
},
// Electron (sandbox)
{
target: '**/vs/**/electron-sandbox/**',
allowedTypes: CORE_TYPES,
disallowedDefinitions: [
'@types/node' // no node.js
]
},
// Electron (renderer): skip
{
target: '**/vs/**/electron-browser/**',

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0;
exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0;
const es = require("event-stream");
const debounce = require("debounce");
const _filter = require("gulp-filter");
@ -14,6 +14,7 @@ const fs = require("fs");
const _rimraf = require("rimraf");
const git = require("./git");
const VinylFile = require("vinyl");
const root = path.dirname(path.dirname(__dirname));
const NoCancellationToken = { isCancellationRequested: () => false };
function incremental(streamProvider, initial, supportsCancellation) {
const input = es.through();
@ -255,3 +256,9 @@ function streamToPromise(stream) {
});
}
exports.streamToPromise = streamToPromise;
function getElectronVersion() {
const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
const target = /^target "(.*)"$/m.exec(yarnrc)[1];
return target;
}
exports.getElectronVersion = getElectronVersion;

View file

@ -18,6 +18,8 @@ import * as VinylFile from 'vinyl';
import { ThroughStream } from 'through';
import * as sm from 'source-map';
const root = path.dirname(path.dirname(__dirname));
export interface ICancellationToken {
isCancellationRequested(): boolean;
}
@ -318,3 +320,9 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise<void> {
stream.on('end', () => c());
});
}
export function getElectronVersion(): string {
const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
const target = /^target "(.*)"$/m.exec(yarnrc)![1];
return target;
}

View file

@ -33,9 +33,10 @@ function yarnInstall(location, opts) {
yarnInstall('extensions'); // node modules shared by all extensions
yarnInstall('remote'); // node modules used by vscode server
yarnInstall('remote/web'); // node modules used by vscode web
if (!(process.platform === 'win32' && process.env['npm_config_arch'] === 'arm64')) {
yarnInstall('remote'); // node modules used by vscode server
yarnInstall('remote/web'); // node modules used by vscode web
}
const allExtensionFolders = fs.readdirSync('extensions');
const extensions = allExtensionFolders.filter(e => {

View file

@ -33,6 +33,7 @@
"@typescript-eslint/parser": "^2.12.0",
"applicationinsights": "1.0.8",
"azure-storage": "^2.1.0",
"electron-osx-sign": "^0.4.16",
"github-releases": "^0.4.1",
"gulp-bom": "^1.0.0",
"gulp-sourcemaps": "^1.11.0",
@ -43,7 +44,7 @@
"minimist": "^1.2.3",
"request": "^2.85.0",
"terser": "4.3.8",
"typescript": "^3.9.1-rc",
"typescript": "^3.9.3",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.5.4",
"xml2js": "^0.4.17"

View file

@ -238,6 +238,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.confi
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.containerfile"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Containerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cpp"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles
@ -1011,7 +1018,7 @@ begin
#endif
#if "user" == InstallTarget
#if "ia32" == Arch
#if "ia32" == Arch || "arm64" == Arch
#define IncompatibleArchRootKey "HKLM32"
#else
#define IncompatibleArchRootKey "HKLM64"

View file

@ -557,6 +557,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-js@^1.2.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
@ -569,6 +574,11 @@ beeper@^1.0.0:
resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809"
integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=
bluebird@^3.5.0:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
@ -615,11 +625,29 @@ browserify-mime@~1.2.9:
resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f"
integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -717,6 +745,11 @@ commander@^2.8.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
compare-version@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -805,7 +838,7 @@ debug-fabulous@0.0.X:
lazy-debug-legacy "0.0.X"
object-assign "4.1.0"
debug@2.X:
debug@2.X, debug@^2.6.8:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@ -918,6 +951,18 @@ ecc-jsbn@~0.1.1:
dependencies:
jsbn "~0.1.0"
electron-osx-sign@^0.4.16:
version "0.4.16"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz#0be8e579b2d9fa4c12d2a21f063898294b3434aa"
integrity sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg==
dependencies:
bluebird "^3.5.0"
compare-version "^0.1.2"
debug "^2.6.8"
isbinaryfile "^3.0.2"
minimist "^1.2.0"
plist "^3.0.1"
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -1488,6 +1533,13 @@ isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isbinaryfile@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80"
integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==
dependencies:
buffer-alloc "^1.2.0"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -1969,6 +2021,15 @@ picomatch@^2.0.5:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
plist@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
dependencies:
base64-js "^1.2.3"
xmlbuilder "^9.0.7"
xmldom "0.1.x"
prettyjson@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
@ -2458,10 +2519,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^3.9.1-rc:
version "3.9.1-rc"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3"
integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw==
typescript@^3.9.3:
version "3.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a"
integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==
typical@^4.0.0:
version "4.0.0"
@ -2661,11 +2722,21 @@ xmlbuilder@0.4.3:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.3.tgz#c4614ba74e0ad196e609c9272cd9e1ddb28a8a58"
integrity sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=
xmlbuilder@^9.0.7:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
xmlbuilder@~9.0.1:
version "9.0.4"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f"
integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=
xmldom@0.1.x:
version "0.1.31"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"

View file

@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "0552e0d5de46ffa3b481d741f1db5c779e201565"
"commitHash": "8f502de1dc5b6df4218a900d0857de7a40301d98"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "7.2.4"
"version": "7.3.0"
},
{
"component": {

View file

@ -488,7 +488,7 @@
"t": "source.batchfile constant.character.escape.batchfile",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "constant.character: #569CD6"
@ -510,7 +510,7 @@
"t": "source.batchfile constant.character.escape.batchfile",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "constant.character: #569CD6"

View file

@ -1555,7 +1555,7 @@
"t": "source.coffee string.regexp.multiline.coffee keyword.control.anchor.regexp",
"r": {
"dark_plus": "keyword.control.anchor.regexp: #DCDCAA",
"light_plus": "keyword.control.anchor.regexp: #FF0000",
"light_plus": "keyword.control.anchor.regexp: #EE0000",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@ -1605,4 +1605,4 @@
"hc_black": "string.regexp: #D16969"
}
}
]
]

View file

@ -191,7 +191,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -257,7 +257,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -389,7 +389,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -433,7 +433,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -532,7 +532,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -587,7 +587,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -719,7 +719,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -763,7 +763,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -862,7 +862,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -906,7 +906,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -1027,7 +1027,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -1071,7 +1071,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@ -1187,4 +1187,4 @@
"hc_black": "meta.preprocessor: #569CD6"
}
}
]
]

View file

@ -2314,7 +2314,7 @@
"t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.character.escape.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #FF0000",
"light_plus": "constant.character.escape: #EE0000",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "constant.character: #569CD6"
@ -2430,4 +2430,4 @@
"hc_black": "default: #FFFFFF"
}
}
]
]

View file

@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext } from 'vscode';
import { LanguageClientOptions } from 'vscode-languageclient';
import { startClient, LanguageClientConstructor } from '../cssClient';
import { LanguageClient } from 'vscode-languageclient/browser';
declare const Worker: {
new(stringUrl: string): any;
};
declare const TextDecoder: {
new(encoding?: string): { decode(buffer: ArrayBuffer): string; };
};
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
const serverMain = context.asAbsolutePath('server/dist/browser/cssServerMain.js');
try {
const worker = new Worker(serverMain);
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, clientOptions, worker);
};
startClient(context, newLanguageClient, { TextDecoder });
} catch (e) {
console.log(e);
}
}

View file

@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface Options {
locale?: string;
cacheLanguageResolution?: boolean;
}
export interface LocalizeInfo {
key: string;
comment: string[];
}
export interface LocalizeFunc {
(info: LocalizeInfo, message: string, ...args: any[]): string;
(key: string, message: string, ...args: any[]): string;
}
export interface LoadFunc {
(file?: string): LocalizeFunc;
}
function format(message: string, args: any[]): string {
let result: string;
if (args.length === 0) {
result = message;
} else {
result = message.replace(/\{(\d+)\}/g, (match, rest) => {
let index = rest[0];
return typeof args[index] !== 'undefined' ? args[index] : match;
});
}
return result;
}
function localize(_key: string | LocalizeInfo, message: string, ...args: any[]): string {
return format(message, args);
}
export function loadMessageBundle(_file?: string): LocalizeFunc {
return localize;
}
export function config(_opt?: Options | string): LoadFunc {
return loadMessageBundle;
}

View file

@ -3,38 +3,31 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as path from 'path';
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode';
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, ProvideCompletionItemsSignature } from 'vscode-languageclient';
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode';
import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData';
import { getCustomDataSource } from './customData';
import { RequestService, serveFileSystemRequests } from './requests';
namespace CustomDataChangedNotification {
export const type: NotificationType<string[]> = new NotificationType('css/customDataChanged');
}
const localize = nls.loadMessageBundle();
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
let serverModule = context.asAbsolutePath(path.join('server', serverMain));
export interface Runtime {
TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } };
fs?: RequestService;
}
// The debug options for the server
let debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
let serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const customDataSource = getCustomDataSource(context.subscriptions);
let documentSelector = ['css', 'scss', 'less'];
let dataPaths = [
...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders),
...getCustomDataPathsFromAllExtensions()
];
// Options to control the language client
let clientOptions: LanguageClientOptions = {
documentSelector,
@ -42,7 +35,7 @@ export function activate(context: ExtensionContext) {
configurationSection: ['css', 'scss', 'less']
},
initializationOptions: {
dataPaths
handledSchemas: ['file']
},
middleware: {
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
@ -82,8 +75,17 @@ 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);
let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions);
client.registerProposedFeatures();
client.onReady().then(() => {
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
customDataSource.onDidChange(() => {
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
});
serveFileSystemRequests(client, runtime);
});
let disposable = client.start();
// Push the disposable to the context's subscriptions so that the
@ -118,7 +120,7 @@ export function activate(context: ExtensionContext) {
const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/;
return languages.registerCompletionItemProvider(documentSelector, {
provideCompletionItems(doc, pos) {
provideCompletionItems(doc: TextDocument, pos: Position) {
let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos));
let match = lineUntilPos.match(regionCompletionRegExpr);
if (match) {
@ -162,13 +164,3 @@ export function activate(context: ExtensionContext) {
}
}
}
function readJSONFile(location: string) {
try {
return JSON.parse(fs.readFileSync(location).toString());
} catch (e) {
console.log(`Problems reading ${location}: ${e}`);
return {};
}
}

View file

@ -3,42 +3,78 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { workspace, WorkspaceFolder, extensions } from 'vscode';
import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode';
import { resolvePath, joinPath } from './requests';
interface ExperimentalConfig {
customData?: string[];
experimental?: {
customData?: string[];
export function getCustomDataSource(toDispose: Disposable[]) {
let pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
let pathsInExtensions = getCustomDataPathsFromAllExtensions();
const onChange = new EventEmitter<void>();
toDispose.push(extensions.onDidChange(_ => {
const newPathsInExtensions = getCustomDataPathsFromAllExtensions();
if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) {
pathsInExtensions = newPathsInExtensions;
onChange.fire();
}
}));
toDispose.push(workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('css.customData')) {
pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
onChange.fire();
}
}));
return {
get uris() {
return pathsInWorkspace.concat(pathsInExtensions);
},
get onDidChange() {
return onChange.event;
}
};
}
export function getCustomDataPathsInAllWorkspaces(workspaceFolders: readonly WorkspaceFolder[] | undefined): string[] {
function getCustomDataPathsInAllWorkspaces(): string[] {
const workspaceFolders = workspace.workspaceFolders;
const dataPaths: string[] = [];
if (!workspaceFolders) {
return dataPaths;
}
workspaceFolders.forEach(wf => {
const allCssConfig = workspace.getConfiguration(undefined, wf.uri);
const wfCSSConfig = allCssConfig.inspect<ExperimentalConfig>('css');
if (wfCSSConfig && wfCSSConfig.workspaceFolderValue && wfCSSConfig.workspaceFolderValue.customData) {
const customData = wfCSSConfig.workspaceFolderValue.customData;
if (Array.isArray(customData)) {
customData.forEach(t => {
if (typeof t === 'string') {
dataPaths.push(path.resolve(wf.uri.fsPath, t));
}
});
const collect = (paths: string[] | undefined, rootFolder: Uri) => {
if (Array.isArray(paths)) {
for (const path of paths) {
if (typeof path === 'string') {
dataPaths.push(resolvePath(rootFolder, path).toString());
}
}
}
});
};
for (let i = 0; i < workspaceFolders.length; i++) {
const folderUri = workspaceFolders[i].uri;
const allCssConfig = workspace.getConfiguration('css', folderUri);
const customDataInspect = allCssConfig.inspect<string[]>('customData');
if (customDataInspect) {
collect(customDataInspect.workspaceFolderValue, folderUri);
if (i === 0) {
if (workspace.workspaceFile) {
collect(customDataInspect.workspaceValue, workspace.workspaceFile);
}
collect(customDataInspect.globalValue, folderUri);
}
}
}
return dataPaths;
}
export function getCustomDataPathsFromAllExtensions(): string[] {
function getCustomDataPathsFromAllExtensions(): string[] {
const dataPaths: string[] = [];
for (const extension of extensions.all) {
@ -47,7 +83,7 @@ export function getCustomDataPathsFromAllExtensions(): string[] {
if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) {
const relativePaths: string[] = contributes.css.customData;
relativePaths.forEach(rp => {
dataPaths.push(path.resolve(extension.extensionPath, rp));
dataPaths.push(joinPath(extension.extensionUri, rp).toString());
});
}
}

View file

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getNodeFSRequestService } from './nodeFs';
import { ExtensionContext, extensions } from 'vscode';
import { startClient, LanguageClientConstructor } from '../cssClient';
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
import { TextDecoder } from 'util';
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main;
const serverMain = clientMain?.replace('client', 'server').replace('cssClientMain', 'cssServerMain');
if (!serverMain) {
throw new Error('Unable to compute CSS server module path. Client: ' + clientMain);
}
const serverModule = context.asAbsolutePath(serverMain);
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, serverOptions, clientOptions);
};
startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder });
}

View file

@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import { Uri } from 'vscode';
import { getScheme, RequestService, FileType } from '../requests';
export function getNodeFSRequestService(): RequestService {
function ensureFileUri(location: string) {
if (getScheme(location) !== 'file') {
throw new Error('fileRequestService can only handle file URLs');
}
}
return {
getContent(location: string, encoding?: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.readFile(uri.fsPath, encoding, (err, buf) => {
if (err) {
return e(err);
}
c(buf.toString());
});
});
},
stat(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.stat(uri.fsPath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 });
} else {
return e(err);
}
}
let type = FileType.Unknown;
if (stats.isFile()) {
type = FileType.File;
} else if (stats.isDirectory()) {
type = FileType.Directory;
} else if (stats.isSymbolicLink()) {
type = FileType.SymbolicLink;
}
c({
type,
ctime: stats.ctime.getTime(),
mtime: stats.mtime.getTime(),
size: stats.size
});
});
});
},
readDirectory(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const path = Uri.parse(location).fsPath;
fs.readdir(path, { withFileTypes: true }, (err, children) => {
if (err) {
return e(err);
}
c(children.map(stat => {
if (stat.isSymbolicLink()) {
return [stat.name, FileType.SymbolicLink];
} else if (stat.isDirectory()) {
return [stat.name, FileType.Directory];
} else if (stat.isFile()) {
return [stat.name, FileType.File];
} else {
return [stat.name, FileType.Unknown];
}
}));
});
});
}
};
}

View file

@ -0,0 +1,148 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, workspace } from 'vscode';
import { RequestType, CommonLanguageClient } from 'vscode-languageclient';
import { Runtime } from './cssClient';
export namespace FsContentRequest {
export const type: RequestType<{ uri: string; encoding?: string; }, string, any, any> = new RequestType('fs/content');
}
export namespace FsStatRequest {
export const type: RequestType<string, FileStat, any, any> = new RequestType('fs/stat');
}
export namespace FsReadDirRequest {
export const type: RequestType<string, [string, FileType][], any, any> = new RequestType('fs/readDir');
}
export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) {
client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => {
const uri = Uri.parse(param.uri);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.getContent(param.uri);
}
return workspace.fs.readFile(uri).then(buffer => {
return new runtime.TextDecoder(param.encoding).decode(buffer);
});
});
client.onRequest(FsReadDirRequest.type, (uriString: string) => {
const uri = Uri.parse(uriString);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.readDirectory(uriString);
}
return workspace.fs.readDirectory(uri);
});
client.onRequest(FsStatRequest.type, (uriString: string) => {
const uri = Uri.parse(uriString);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.stat(uriString);
}
return workspace.fs.stat(uri);
});
}
export enum FileType {
/**
* The file type is unknown.
*/
Unknown = 0,
/**
* A regular file.
*/
File = 1,
/**
* A directory.
*/
Directory = 2,
/**
* A symbolic link to a file.
*/
SymbolicLink = 64
}
export interface FileStat {
/**
* The type of the file, e.g. is a regular file, a directory, or symbolic link
* to a file.
*/
type: FileType;
/**
* The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
ctime: number;
/**
* The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
mtime: number;
/**
* The size in bytes.
*/
size: number;
}
export interface RequestService {
getContent(uri: string, encoding?: string): Promise<string>;
stat(uri: string): Promise<FileStat>;
readDirectory(uri: string): Promise<[string, FileType][]>;
}
export function getScheme(uri: string) {
return uri.substr(0, uri.indexOf(':'));
}
export function dirname(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
}
export function basename(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return uri.substr(lastIndexOfSlash + 1);
}
const Slash = '/'.charCodeAt(0);
const Dot = '.'.charCodeAt(0);
export function isAbsolutePath(path: string) {
return path.charCodeAt(0) === Slash;
}
export function resolvePath(uri: Uri, path: string): Uri {
if (isAbsolutePath(path)) {
return uri.with({ path: normalizePath(path.split('/')) });
}
return joinPath(uri, path);
}
export function normalizePath(parts: string[]): string {
const newParts: string[] = [];
for (const part of parts) {
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
// ignore
} else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
newParts.pop();
} else {
newParts.push(part);
}
}
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
newParts.push('');
}
let res = newParts.join('/');
if (parts[0].length === 0) {
res = '/' + res;
}
return res;
}
export function joinPath(uri: Uri, ...paths: string[]): Uri {
const parts = uri.path.split('/');
for (let path of paths) {
parts.push(...path.split('/'));
}
return uri.with({ path: normalizePath(parts) });
}

View file

@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const webpack = require('webpack');
const vscodeNlsReplacement = new webpack.NormalModuleReplacementPlugin(
/vscode\-nls\/lib\/main\.js/,
path.join(__dirname, 'client/out/browser/vscodeNlsShim.js')
);
const clientConfig = withDefaults({
target: 'webworker',
context: path.join(__dirname, 'client'),
entry: {
extension: './src/browser/cssClientMain.ts'
},
output: {
filename: 'cssClientMain.js',
path: path.join(__dirname, 'client', 'dist', 'browser')
}
});
clientConfig.plugins[1] = vscodeNlsReplacement; // replace nls bundler
clientConfig.module.rules[0].use.shift(); // remove nls loader
const serverConfig = withDefaults({
target: 'webworker',
context: path.join(__dirname, 'server'),
entry: {
extension: './src/browser/cssServerMain.ts',
},
output: {
filename: 'cssServerMain.js',
path: path.join(__dirname, 'server', 'dist', 'browser'),
libraryTarget: 'var'
}
});
serverConfig.plugins[1] = vscodeNlsReplacement; // replace nls bundler
serverConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = [clientConfig, serverConfig];

View file

@ -13,10 +13,10 @@ const path = require('path');
module.exports = withDefaults({
context: path.join(__dirname, 'client'),
entry: {
extension: './src/cssMain.ts',
extension: './src/node/cssClientMain.ts',
},
output: {
filename: 'cssMain.js',
path: path.join(__dirname, 'client', 'dist')
filename: 'cssClientMain.js',
path: path.join(__dirname, 'client', 'dist', 'node')
}
});

View file

@ -15,7 +15,8 @@
"onLanguage:scss",
"onCommand:_css.applyCodeAction"
],
"main": "./client/out/cssMain",
"main": "./client/out/node/cssClientMain",
"browser": "./client/dist/browser/cssClientMain",
"enableProposedApi": true,
"scripts": {
"compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server",
@ -806,7 +807,7 @@
]
},
"dependencies": {
"vscode-languageclient": "^6.1.3",
"vscode-languageclient": "7.0.0-next.4",
"vscode-nls": "^4.1.2"
},
"devDependencies": {

View file

@ -13,10 +13,10 @@ const path = require('path');
module.exports = withDefaults({
context: path.join(__dirname),
entry: {
extension: './src/cssServerMain.ts',
extension: './src/node/cssServerMain.ts',
},
output: {
filename: 'cssServerMain.js',
path: path.join(__dirname, 'dist')
path: path.join(__dirname, 'dist', 'node'),
}
});

View file

@ -7,10 +7,12 @@
"engines": {
"node": "*"
},
"main": "./out/cssServerMain",
"main": "./out/node/cssServerMain",
"browser": "./dist/browser/cssServerMain",
"dependencies": {
"vscode-css-languageservice": "^4.1.2",
"vscode-languageserver": "^6.1.1"
"vscode-css-languageservice": "4.3.0-next.2",
"vscode-languageserver": "7.0.0-next.3",
"vscode-uri": "^2.1.2"
},
"devDependencies": {
"@types/mocha": "7.0.2",
@ -27,6 +29,6 @@
"install-service-local": "npm install ../../../../vscode-css-languageservice -f",
"install-server-next": "yarn add vscode-languageserver@next",
"install-server-local": "npm install ../../../../vscode-languageserver-node/server -f",
"test": "../../../node_modules/.bin/mocha"
"test": "node ./test/index.js"
}
}

View file

@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
import { startServer } from '../cssServer';
declare let self: any;
const messageReader = new BrowserMessageReader(self);
const messageWriter = new BrowserMessageWriter(self);
const connection = createConnection(messageReader, messageWriter);
startServer(connection, {});

View file

@ -0,0 +1,373 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType
} from 'vscode-languageserver/lib/common/api';
import { URI } from 'vscode-uri';
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { formatError, runSafeAsync } from './utils/runner';
import { getDocumentContext } from './utils/documentContext';
import { fetchDataProviders } from './customData';
import { RequestService, getRequestService } from './requests';
namespace CustomDataChangedNotification {
export const type: NotificationType<string[]> = new NotificationType('css/customDataChanged');
}
export interface Settings {
css: LanguageSettings;
less: LanguageSettings;
scss: LanguageSettings;
}
export interface RuntimeEnvironment {
file?: RequestService;
http?: RequestService
}
export function startServer(connection: Connection, runtime: RuntimeEnvironment) {
// Create a text document manager.
const documents = new TextDocuments(TextDocument);
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
const stylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => getLanguageService(document).parseStylesheet(document));
documents.onDidClose(e => {
stylesheets.onDocumentRemoved(e.document);
});
connection.onShutdown(() => {
stylesheets.dispose();
});
let scopedSettingsSupport = false;
let foldingRangeLimit = Number.MAX_VALUE;
let workspaceFolders: WorkspaceFolder[];
let dataProvidersReady: Promise<any> = Promise.resolve();
const languageServices: { [id: string]: LanguageService } = {};
const notReady = () => Promise.reject('Not Ready');
let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady };
// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
workspaceFolders = (<any>params).workspaceFolders;
if (!Array.isArray(workspaceFolders)) {
workspaceFolders = [];
if (params.rootPath) {
workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() });
}
}
requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime);
function getClientCapability<T>(name: string, def: T) {
const keys = name.split('.');
let c: any = params.capabilities;
for (let i = 0; c && i < keys.length; i++) {
if (!c.hasOwnProperty(keys[i])) {
return def;
}
c = c[keys[i]];
}
return c;
}
const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
scopedSettingsSupport = !!getClientCapability('workspace.configuration', false);
foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
const capabilities: ServerCapabilities = {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined,
hoverProvider: true,
documentSymbolProvider: true,
referencesProvider: true,
definitionProvider: true,
documentHighlightProvider: true,
documentLinkProvider: {
resolveProvider: false
},
codeActionProvider: true,
renameProvider: true,
colorProvider: {},
foldingRangeProvider: true,
selectionRangeProvider: true
};
return { capabilities };
});
function getLanguageService(document: TextDocument) {
let service = languageServices[document.languageId];
if (!service) {
connection.console.log('Document type is ' + document.languageId + ', using css instead.');
service = languageServices['css'];
}
return service;
}
let documentSettings: { [key: string]: Thenable<LanguageSettings | undefined> } = {};
// remove document settings on close
documents.onDidClose(e => {
delete documentSettings[e.document.uri];
});
function getDocumentSettings(textDocument: TextDocument): Thenable<LanguageSettings | undefined> {
if (scopedSettingsSupport) {
let promise = documentSettings[textDocument.uri];
if (!promise) {
const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] };
promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]);
documentSettings[textDocument.uri] = promise;
}
return promise;
}
return Promise.resolve(undefined);
}
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration(change => {
updateConfiguration(<Settings>change.settings);
});
function updateConfiguration(settings: Settings) {
for (const languageId in languageServices) {
languageServices[languageId].configure((settings as any)[languageId]);
}
// reset all document settings
documentSettings = {};
// Revalidate any open text documents
documents.all().forEach(triggerValidation);
}
const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
const validationDelayMs = 500;
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {
triggerValidation(change.document);
});
// a document has closed: clear all diagnostics
documents.onDidClose(event => {
cleanPendingValidation(event.document);
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
});
function cleanPendingValidation(textDocument: TextDocument): void {
const request = pendingValidationRequests[textDocument.uri];
if (request) {
clearTimeout(request);
delete pendingValidationRequests[textDocument.uri];
}
}
function triggerValidation(textDocument: TextDocument): void {
cleanPendingValidation(textDocument);
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
delete pendingValidationRequests[textDocument.uri];
validateTextDocument(textDocument);
}, validationDelayMs);
}
function validateTextDocument(textDocument: TextDocument): void {
const settingsPromise = getDocumentSettings(textDocument);
Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => {
const stylesheet = stylesheets.get(textDocument);
const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings);
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}, e => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
});
}
function updateDataProviders(dataPaths: string[]) {
dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => {
for (const lang in languageServices) {
languageServices[lang].setDataProviders(true, customDataProviders);
}
});
}
connection.onCompletion((textDocumentPosition, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (document) {
await dataProvidersReady;
const styleSheet = stylesheets.get(document);
const documentContext = getDocumentContext(document.uri, workspaceFolders);
return getLanguageService(document).doComplete2(document, textDocumentPosition.position, styleSheet, documentContext);
}
return null;
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onHover((textDocumentPosition, token) => {
return runSafeAsync(async () => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (document) {
await dataProvidersReady;
const styleSheet = stylesheets.get(document);
return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
}
return null;
}, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(documentSymbolParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentSymbols(document, stylesheet);
}
return [];
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDefinition((documentDefinitionParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(documentDefinitionParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet);
}
return null;
}, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token);
});
connection.onDocumentHighlight((documentHighlightParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(documentHighlightParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet);
}
return [];
}, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token);
});
connection.onDocumentLinks(async (documentLinkParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(documentLinkParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const documentContext = getDocumentContext(document.uri, workspaceFolders);
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext);
}
return [];
}, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token);
});
connection.onReferences((referenceParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(referenceParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
}
return [];
}, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token);
});
connection.onCodeAction((codeActionParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(codeActionParams.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
}
return [];
}, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token);
});
connection.onDocumentColor((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentColors(document, stylesheet);
}
return [];
}, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
});
connection.onColorPresentation((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onRenameRequest((renameParameters, token) => {
return runSafeAsync(async () => {
const document = documents.get(renameParameters.textDocument.uri);
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
}
return null;
}, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token);
});
connection.onFoldingRanges((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
if (document) {
await dataProvidersReady;
return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit });
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
connection.onSelectionRanges((params, token) => {
return runSafeAsync(async () => {
const document = documents.get(params.textDocument.uri);
const positions: Position[] = params.positions;
if (document) {
await dataProvidersReady;
const stylesheet = stylesheets.get(document);
return getLanguageService(document).getSelectionRanges(document, positions, stylesheet);
}
return [];
}, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
});
connection.onNotification(CustomDataChangedNotification.type, updateDataProviders);
// Listen on the connection
connection.listen();
}

View file

@ -1,390 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind
} from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { stat as fsStat } from 'fs';
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, FileSystemProvider, FileType, TextDocument, CompletionList, Position } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { getPathCompletionParticipant } from './pathCompletion';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
import { getDocumentContext } from './utils/documentContext';
import { getDataProviders } from './customData';
export interface Settings {
css: LanguageSettings;
less: LanguageSettings;
scss: LanguageSettings;
}
// Create a connection for the server.
const connection: IConnection = createConnection();
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
process.on('unhandledRejection', (e: any) => {
connection.console.error(formatError(`Unhandled exception`, e));
});
// Create a text document manager.
const documents = new TextDocuments(TextDocument);
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
const stylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => getLanguageService(document).parseStylesheet(document));
documents.onDidClose(e => {
stylesheets.onDocumentRemoved(e.document);
});
connection.onShutdown(() => {
stylesheets.dispose();
});
let scopedSettingsSupport = false;
let foldingRangeLimit = Number.MAX_VALUE;
let workspaceFolders: WorkspaceFolder[];
const languageServices: { [id: string]: LanguageService } = {};
const fileSystemProvider: FileSystemProvider = {
stat(documentUri: string) {
const filePath = URI.parse(documentUri).fsPath;
return new Promise((c, e) => {
fsStat(filePath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
return c({
type: FileType.Unknown,
ctime: -1,
mtime: -1,
size: -1
});
} else {
return e(err);
}
}
let type = FileType.Unknown;
if (stats.isFile()) {
type = FileType.File;
} else if (stats.isDirectory()) {
type = FileType.Directory;
} else if (stats.isSymbolicLink()) {
type = FileType.SymbolicLink;
}
c({
type,
ctime: stats.ctime.getTime(),
mtime: stats.mtime.getTime(),
size: stats.size
});
});
});
}
};
// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
workspaceFolders = (<any>params).workspaceFolders;
if (!Array.isArray(workspaceFolders)) {
workspaceFolders = [];
if (params.rootPath) {
workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() });
}
}
const dataPaths: string[] = params.initializationOptions.dataPaths || [];
const customDataProviders = getDataProviders(dataPaths);
function getClientCapability<T>(name: string, def: T) {
const keys = name.split('.');
let c: any = params.capabilities;
for (let i = 0; c && i < keys.length; i++) {
if (!c.hasOwnProperty(keys[i])) {
return def;
}
c = c[keys[i]];
}
return c;
}
const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
scopedSettingsSupport = !!getClientCapability('workspace.configuration', false);
foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
languageServices.css = getCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
languageServices.scss = getSCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
languageServices.less = getLESSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
const capabilities: ServerCapabilities = {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined,
hoverProvider: true,
documentSymbolProvider: true,
referencesProvider: true,
definitionProvider: true,
documentHighlightProvider: true,
documentLinkProvider: {
resolveProvider: false
},
codeActionProvider: true,
renameProvider: true,
colorProvider: {},
foldingRangeProvider: true,
selectionRangeProvider: true
};
return { capabilities };
});
function getLanguageService(document: TextDocument) {
let service = languageServices[document.languageId];
if (!service) {
connection.console.log('Document type is ' + document.languageId + ', using css instead.');
service = languageServices['css'];
}
return service;
}
let documentSettings: { [key: string]: Thenable<LanguageSettings | undefined> } = {};
// remove document settings on close
documents.onDidClose(e => {
delete documentSettings[e.document.uri];
});
function getDocumentSettings(textDocument: TextDocument): Thenable<LanguageSettings | undefined> {
if (scopedSettingsSupport) {
let promise = documentSettings[textDocument.uri];
if (!promise) {
const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] };
promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]);
documentSettings[textDocument.uri] = promise;
}
return promise;
}
return Promise.resolve(undefined);
}
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration(change => {
updateConfiguration(<Settings>change.settings);
});
function updateConfiguration(settings: Settings) {
for (const languageId in languageServices) {
languageServices[languageId].configure((settings as any)[languageId]);
}
// reset all document settings
documentSettings = {};
// Revalidate any open text documents
documents.all().forEach(triggerValidation);
}
const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
const validationDelayMs = 500;
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {
triggerValidation(change.document);
});
// a document has closed: clear all diagnostics
documents.onDidClose(event => {
cleanPendingValidation(event.document);
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
});
function cleanPendingValidation(textDocument: TextDocument): void {
const request = pendingValidationRequests[textDocument.uri];
if (request) {
clearTimeout(request);
delete pendingValidationRequests[textDocument.uri];
}
}
function triggerValidation(textDocument: TextDocument): void {
cleanPendingValidation(textDocument);
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
delete pendingValidationRequests[textDocument.uri];
validateTextDocument(textDocument);
}, validationDelayMs);
}
function validateTextDocument(textDocument: TextDocument): void {
const settingsPromise = getDocumentSettings(textDocument);
settingsPromise.then(settings => {
const stylesheet = stylesheets.get(textDocument);
const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings);
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}, e => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
});
}
connection.onCompletion((textDocumentPosition, token) => {
return runSafe(() => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (!document) {
return null;
}
const cssLS = getLanguageService(document);
const pathCompletionList: CompletionList = {
isIncomplete: false,
items: []
};
cssLS.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, pathCompletionList)]);
const result = cssLS.doComplete(document, textDocumentPosition.position, stylesheets.get(document));
return {
isIncomplete: pathCompletionList.isIncomplete,
items: [...pathCompletionList.items, ...result.items]
};
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onHover((textDocumentPosition, token) => {
return runSafe(() => {
const document = documents.get(textDocumentPosition.textDocument.uri);
if (document) {
const styleSheet = stylesheets.get(document);
return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
}
return null;
}, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafe(() => {
const document = documents.get(documentSymbolParams.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentSymbols(document, stylesheet);
}
return [];
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDefinition((documentDefinitionParams, token) => {
return runSafe(() => {
const document = documents.get(documentDefinitionParams.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet);
}
return null;
}, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token);
});
connection.onDocumentHighlight((documentHighlightParams, token) => {
return runSafe(() => {
const document = documents.get(documentHighlightParams.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet);
}
return [];
}, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token);
});
connection.onDocumentLinks(async (documentLinkParams, token) => {
return runSafeAsync(async () => {
const document = documents.get(documentLinkParams.textDocument.uri);
if (document) {
const documentContext = getDocumentContext(document.uri, workspaceFolders);
const stylesheet = stylesheets.get(document);
return await getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext);
}
return [];
}, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token);
});
connection.onReferences((referenceParams, token) => {
return runSafe(() => {
const document = documents.get(referenceParams.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
}
return [];
}, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token);
});
connection.onCodeAction((codeActionParams, token) => {
return runSafe(() => {
const document = documents.get(codeActionParams.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
}
return [];
}, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token);
});
connection.onDocumentColor((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentColors(document, stylesheet);
}
return [];
}, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
});
connection.onColorPresentation((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onRenameRequest((renameParameters, token) => {
return runSafe(() => {
const document = documents.get(renameParameters.textDocument.uri);
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
}
return null;
}, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token);
});
connection.onFoldingRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit });
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
connection.onSelectionRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
const positions: Position[] = params.positions;
if (document) {
const stylesheet = stylesheets.get(document);
return getLanguageService(document).getSelectionRanges(document, positions, stylesheet);
}
return [];
}, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
});
// Listen on the connection
connection.listen();

View file

@ -3,48 +3,36 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice';
import * as fs from 'fs';
import { ICSSDataProvider, newCSSDataProvider } from 'vscode-css-languageservice';
import { RequestService } from './requests';
export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] {
const providers = dataPaths.map(p => {
if (fs.existsSync(p)) {
const data = parseCSSData(fs.readFileSync(p, 'utf-8'));
return {
provideProperties: () => data.properties || [],
provideAtDirectives: () => data.atDirectives || [],
providePseudoClasses: () => data.pseudoClasses || [],
providePseudoElements: () => data.pseudoElements || []
};
} else {
return {
provideProperties: () => [],
provideAtDirectives: () => [],
providePseudoClasses: () => [],
providePseudoElements: () => []
};
export function fetchDataProviders(dataPaths: string[], requestService: RequestService): Promise<ICSSDataProvider[]> {
const providers = dataPaths.map(async p => {
try {
const content = await requestService.getContent(p);
return parseCSSData(content);
} catch (e) {
return newCSSDataProvider({ version: 1 });
}
});
return providers;
return Promise.all(providers);
}
function parseCSSData(source: string): CSSDataV1 {
function parseCSSData(source: string): ICSSDataProvider {
let rawData: any;
try {
rawData = JSON.parse(source);
} catch (err) {
return {
version: 1
};
return newCSSDataProvider({ version: 1 });
}
return {
return newCSSDataProvider({
version: 1,
properties: rawData.properties || [],
atDirectives: rawData.atDirectives || [],
pseudoClasses: rawData.pseudoClasses || [],
pseudoElements: rawData.pseudoElements || []
};
});
}

View file

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, Connection } from 'vscode-languageserver/node';
import { formatError } from '../utils/runner';
import { startServer } from '../cssServer';
import { getNodeFSRequestService } from './nodeFs';
// Create a connection for the server.
const connection: Connection = createConnection();
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
process.on('unhandledRejection', (e: any) => {
connection.console.error(formatError(`Unhandled exception`, e));
});
startServer(connection, { file: getNodeFSRequestService() });

View file

@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RequestService, getScheme } from '../requests';
import { URI as Uri } from 'vscode-uri';
import * as fs from 'fs';
import { FileType } from 'vscode-css-languageservice';
export function getNodeFSRequestService(): RequestService {
function ensureFileUri(location: string) {
if (getScheme(location) !== 'file') {
throw new Error('fileRequestService can only handle file URLs');
}
}
return {
getContent(location: string, encoding?: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.readFile(uri.fsPath, encoding, (err, buf) => {
if (err) {
return e(err);
}
c(buf.toString());
});
});
},
stat(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.stat(uri.fsPath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 });
} else {
return e(err);
}
}
let type = FileType.Unknown;
if (stats.isFile()) {
type = FileType.File;
} else if (stats.isDirectory()) {
type = FileType.Directory;
} else if (stats.isSymbolicLink()) {
type = FileType.SymbolicLink;
}
c({
type,
ctime: stats.ctime.getTime(),
mtime: stats.mtime.getTime(),
size: stats.size
});
});
});
},
readDirectory(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const path = Uri.parse(location).fsPath;
fs.readdir(path, { withFileTypes: true }, (err, children) => {
if (err) {
return e(err);
}
c(children.map(stat => {
if (stat.isSymbolicLink()) {
return [stat.name, FileType.SymbolicLink];
} else if (stat.isDirectory()) {
return [stat.name, FileType.Directory];
} else if (stat.isFile()) {
return [stat.name, FileType.File];
} else {
return [stat.name, FileType.Unknown];
}
}));
});
});
}
};
}

View file

@ -1,213 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as fs from 'fs';
import { URI } from 'vscode-uri';
import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types';
import { WorkspaceFolder } from 'vscode-languageserver';
import { ICompletionParticipant } from 'vscode-css-languageservice';
import { startsWith, endsWith } from './utils/strings';
export function getPathCompletionParticipant(
document: TextDocument,
workspaceFolders: WorkspaceFolder[],
result: CompletionList
): ICompletionParticipant {
return {
onCssURILiteralValue: ({ position, range, uriValue }) => {
const fullValue = stripQuotes(uriValue);
if (!shouldDoPathCompletion(uriValue, workspaceFolders)) {
if (fullValue === '.' || fullValue === '..') {
result.isIncomplete = true;
}
return;
}
let suggestions = providePathSuggestions(uriValue, position, range, document, workspaceFolders);
result.items = [...suggestions, ...result.items];
},
onCssImportPath: ({ position, range, pathValue }) => {
const fullValue = stripQuotes(pathValue);
if (!shouldDoPathCompletion(pathValue, workspaceFolders)) {
if (fullValue === '.' || fullValue === '..') {
result.isIncomplete = true;
}
return;
}
let suggestions = providePathSuggestions(pathValue, position, range, document, workspaceFolders);
if (document.languageId === 'scss') {
suggestions.forEach(s => {
if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) {
if (s.textEdit) {
s.textEdit.newText = s.label.slice(1, -5);
} else {
s.label = s.label.slice(1, -5);
}
}
});
}
result.items = [...suggestions, ...result.items];
}
};
}
function providePathSuggestions(pathValue: string, position: Position, range: Range, document: TextDocument, workspaceFolders: WorkspaceFolder[]) {
const fullValue = stripQuotes(pathValue);
const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`);
const valueBeforeCursor = isValueQuoted
? fullValue.slice(0, position.character - (range.start.character + 1))
: fullValue.slice(0, position.character - range.start.character);
const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
const currentDocFsPath = URI.parse(document.uri).fsPath;
const paths = providePaths(valueBeforeCursor, currentDocFsPath, workspaceRoot)
.filter(p => {
// Exclude current doc's path
return path.resolve(currentDocFsPath, '../', p) !== currentDocFsPath;
})
.filter(p => {
// Exclude paths that start with `.`
return p[0] !== '.';
});
const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range;
const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange);
const suggestions = paths.map(p => pathToSuggestion(p, replaceRange));
return suggestions;
}
function shouldDoPathCompletion(pathValue: string, workspaceFolders: WorkspaceFolder[]): boolean {
const fullValue = stripQuotes(pathValue);
if (fullValue === '.' || fullValue === '..') {
return false;
}
if (!workspaceFolders || workspaceFolders.length === 0) {
return false;
}
return true;
}
function stripQuotes(fullValue: string) {
if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) {
return fullValue.slice(1, -1);
} else {
return fullValue;
}
}
/**
* Get a list of path suggestions. Folder suggestions are suffixed with a slash.
*/
function providePaths(valueBeforeCursor: string, activeDocFsPath: string, root?: string): string[] {
const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
const valueBeforeLastSlash = valueBeforeCursor.slice(0, lastIndexOfSlash + 1);
const startsWithSlash = startsWith(valueBeforeCursor, '/');
let parentDir: string;
if (startsWithSlash) {
if (!root) {
return [];
}
parentDir = path.resolve(root, '.' + valueBeforeLastSlash);
} else {
parentDir = path.resolve(activeDocFsPath, '..', valueBeforeLastSlash);
}
try {
return fs.readdirSync(parentDir).map(f => {
return isDir(path.resolve(parentDir, f))
? f + '/'
: f;
});
} catch (e) {
return [];
}
}
const isDir = (p: string) => {
try {
return fs.statSync(p).isDirectory();
} catch (e) {
return false;
}
};
function pathToReplaceRange(valueBeforeCursor: string, fullValue: string, fullValueRange: Range) {
let replaceRange: Range;
const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
if (lastIndexOfSlash === -1) {
replaceRange = fullValueRange;
} else {
// For cases where cursor is in the middle of attribute value, like <script src="./s|rc/test.js">
// Find the last slash before cursor, and calculate the start of replace range from there
const valueAfterLastSlash = fullValue.slice(lastIndexOfSlash + 1);
const startPos = shiftPosition(fullValueRange.end, -valueAfterLastSlash.length);
// If whitespace exists, replace until it
const whitespaceIndex = valueAfterLastSlash.indexOf(' ');
let endPos;
if (whitespaceIndex !== -1) {
endPos = shiftPosition(startPos, whitespaceIndex);
} else {
endPos = fullValueRange.end;
}
replaceRange = Range.create(startPos, endPos);
}
return replaceRange;
}
function pathToSuggestion(p: string, replaceRange: Range): CompletionItem {
const isDir = p[p.length - 1] === '/';
if (isDir) {
return {
label: escapePath(p),
kind: CompletionItemKind.Folder,
textEdit: TextEdit.replace(replaceRange, escapePath(p)),
command: {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
}
};
} else {
return {
label: escapePath(p),
kind: CompletionItemKind.File,
textEdit: TextEdit.replace(replaceRange, escapePath(p))
};
}
}
// Escape https://www.w3.org/TR/CSS1/#url
function escapePath(p: string) {
return p.replace(/(\s|\(|\)|,|"|')/g, '\\$1');
}
function resolveWorkspaceRoot(activeDoc: TextDocument, workspaceFolders: WorkspaceFolder[]): string | undefined {
for (const folder of workspaceFolders) {
if (startsWith(activeDoc.uri, folder.uri)) {
return path.resolve(URI.parse(folder.uri).fsPath);
}
}
return undefined;
}
function shiftPosition(pos: Position, offset: number): Position {
return Position.create(pos.line, pos.character + offset);
}
function shiftRange(range: Range, startOffset: number, endOffset: number): Range {
const start = shiftPosition(range.start, startOffset);
const end = shiftPosition(range.end, endOffset);
return Range.create(start, end);
}

View file

@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vscode-uri';
import { RequestType, Connection } from 'vscode-languageserver';
import { RuntimeEnvironment } from './cssServer';
export namespace FsContentRequest {
export const type: RequestType<{ uri: string; encoding?: string; }, string, any, any> = new RequestType('fs/content');
}
export namespace FsStatRequest {
export const type: RequestType<string, FileStat, any, any> = new RequestType('fs/stat');
}
export namespace FsReadDirRequest {
export const type: RequestType<string, [string, FileType][], any, any> = new RequestType('fs/readDir');
}
export enum FileType {
/**
* The file type is unknown.
*/
Unknown = 0,
/**
* A regular file.
*/
File = 1,
/**
* A directory.
*/
Directory = 2,
/**
* A symbolic link to a file.
*/
SymbolicLink = 64
}
export interface FileStat {
/**
* The type of the file, e.g. is a regular file, a directory, or symbolic link
* to a file.
*/
type: FileType;
/**
* The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
ctime: number;
/**
* The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
mtime: number;
/**
* The size in bytes.
*/
size: number;
}
export interface RequestService {
getContent(uri: string, encoding?: string): Promise<string>;
stat(uri: string): Promise<FileStat>;
readDirectory(uri: string): Promise<[string, FileType][]>;
}
export function getRequestService(handledSchemas: string[], connection: Connection, runtime: RuntimeEnvironment): RequestService {
const builtInHandlers: { [protocol: string]: RequestService | undefined } = {};
for (let protocol of handledSchemas) {
if (protocol === 'file') {
builtInHandlers[protocol] = runtime.file;
} else if (protocol === 'http' || protocol === 'https') {
builtInHandlers[protocol] = runtime.http;
}
}
return {
async stat(uri: string): Promise<FileStat> {
const handler = builtInHandlers[getScheme(uri)];
if (handler) {
return handler.stat(uri);
}
const res = await connection.sendRequest(FsStatRequest.type, uri.toString());
return res;
},
readDirectory(uri: string): Promise<[string, FileType][]> {
const handler = builtInHandlers[getScheme(uri)];
if (handler) {
return handler.readDirectory(uri);
}
return connection.sendRequest(FsReadDirRequest.type, uri.toString());
},
getContent(uri: string, encoding?: string): Promise<string> {
const handler = builtInHandlers[getScheme(uri)];
if (handler) {
return handler.getContent(uri, encoding);
}
return connection.sendRequest(FsContentRequest.type, { uri: uri.toString(), encoding });
}
};
}
export function getScheme(uri: string) {
return uri.substr(0, uri.indexOf(':'));
}
export function dirname(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
}
export function basename(uri: string) {
const lastIndexOfSlash = uri.lastIndexOf('/');
return uri.substr(lastIndexOfSlash + 1);
}
const Slash = '/'.charCodeAt(0);
const Dot = '.'.charCodeAt(0);
export function isAbsolutePath(path: string) {
return path.charCodeAt(0) === Slash;
}
export function resolvePath(uriString: string, path: string): string {
if (isAbsolutePath(path)) {
const uri = URI.parse(uriString);
const parts = path.split('/');
return uri.with({ path: normalizePath(parts) }).toString();
}
return joinPath(uriString, path);
}
export function normalizePath(parts: string[]): string {
const newParts: string[] = [];
for (const part of parts) {
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
// ignore
} else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
newParts.pop();
} else {
newParts.push(part);
}
}
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
newParts.push('');
}
let res = newParts.join('/');
if (parts[0].length === 0) {
res = '/' + res;
}
return res;
}
export function joinPath(uriString: string, ...paths: string[]): string {
const uri = URI.parse(uriString);
const parts = uri.path.split('/');
for (let path of paths) {
parts.push(...path.split('/'));
}
return uri.with({ path: normalizePath(parts) }).toString();
}

View file

@ -6,10 +6,11 @@ import 'mocha';
import * as assert from 'assert';
import * as path from 'path';
import { URI } from 'vscode-uri';
import { TextDocument, CompletionList } from 'vscode-languageserver-types';
import { TextDocument, CompletionList, TextEdit } from 'vscode-languageserver-types';
import { WorkspaceFolder } from 'vscode-languageserver-protocol';
import { getPathCompletionParticipant } from '../pathCompletion';
import { getCSSLanguageService } from 'vscode-css-languageservice';
import { getCSSLanguageService, LanguageServiceOptions, getSCSSLanguageService } from 'vscode-css-languageservice';
import { getNodeFSRequestService } from '../node/nodeFs';
import { getDocumentContext } from '../utils/documentContext';
export interface ItemDescription {
label: string;
@ -17,7 +18,6 @@ export interface ItemDescription {
}
suite('Completions', () => {
const cssLanguageService = getCSSLanguageService();
let assertCompletion = function (completions: CompletionList, expected: ItemDescription, document: TextDocument, _offset: number) {
let matches = completions.items.filter(completion => {
@ -26,12 +26,12 @@ suite('Completions', () => {
assert.equal(matches.length, 1, `${expected.label} should only existing once: Actual: ${completions.items.map(c => c.label).join(', ')}`);
let match = matches[0];
if (expected.resultText && match.textEdit) {
if (expected.resultText && TextEdit.is(match.textEdit)) {
assert.equal(TextDocument.applyEdits(document, [match.textEdit]), expected.resultText);
}
};
function assertCompletions(value: string, expected: { count?: number, items?: ItemDescription[] }, testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): void {
async function assertCompletions(value: string, expected: { count?: number, items?: ItemDescription[] }, testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): Promise<any> {
const offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);
@ -42,12 +42,12 @@ suite('Completions', () => {
workspaceFolders = [{ name: 'x', uri: testUri.substr(0, testUri.lastIndexOf('/')) }];
}
let participantResult = CompletionList.create([]);
cssLanguageService.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, participantResult)]);
const lsOptions: LanguageServiceOptions = { fileSystemProvider: getNodeFSRequestService() };
const cssLanguageService = lang === 'scss' ? getSCSSLanguageService(lsOptions) : getCSSLanguageService(lsOptions);
const context = getDocumentContext(testUri, workspaceFolders);
const stylesheet = cssLanguageService.parseStylesheet(document);
let list = cssLanguageService.doComplete(document, position, stylesheet)!;
list.items = list.items.concat(participantResult.items);
let list = await cssLanguageService.doComplete2(document, position, stylesheet, context);
if (expected.count) {
assert.equal(list.items.length, expected.count);
@ -59,17 +59,17 @@ suite('Completions', () => {
}
}
test('CSS url() Path completion', function () {
test('CSS url() Path completion', async function () {
let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }];
assertCompletions('html { background-image: url("./|")', {
await assertCompletions('html { background-image: url("./|")', {
items: [
{ label: 'about.html', resultText: 'html { background-image: url("./about.html")' }
]
}, testUri, folders);
assertCompletions(`html { background-image: url('../|')`, {
await assertCompletions(`html { background-image: url('../|')`, {
items: [
{ label: 'about/', resultText: `html { background-image: url('../about/')` },
{ label: 'index.html', resultText: `html { background-image: url('../index.html')` },
@ -77,7 +77,7 @@ suite('Completions', () => {
]
}, testUri, folders);
assertCompletions(`html { background-image: url('../src/a|')`, {
await assertCompletions(`html { background-image: url('../src/a|')`, {
items: [
{ label: 'feature.js', resultText: `html { background-image: url('../src/feature.js')` },
{ label: 'data/', resultText: `html { background-image: url('../src/data/')` },
@ -85,25 +85,25 @@ suite('Completions', () => {
]
}, testUri, folders);
assertCompletions(`html { background-image: url('../src/data/f|.asar')`, {
await assertCompletions(`html { background-image: url('../src/data/f|.asar')`, {
items: [
{ label: 'foo.asar', resultText: `html { background-image: url('../src/data/foo.asar')` }
]
}, testUri, folders);
assertCompletions(`html { background-image: url('|')`, {
await assertCompletions(`html { background-image: url('|')`, {
items: [
{ label: 'about.html', resultText: `html { background-image: url('about.html')` },
]
}, testUri, folders);
assertCompletions(`html { background-image: url('/|')`, {
await assertCompletions(`html { background-image: url('/|')`, {
items: [
{ label: 'pathCompletionFixtures/', resultText: `html { background-image: url('/pathCompletionFixtures/')` }
]
}, testUri, folders);
assertCompletions(`html { background-image: url('/pathCompletionFixtures/|')`, {
await assertCompletions(`html { background-image: url('/pathCompletionFixtures/|')`, {
items: [
{ label: 'about/', resultText: `html { background-image: url('/pathCompletionFixtures/about/')` },
{ label: 'index.html', resultText: `html { background-image: url('/pathCompletionFixtures/index.html')` },
@ -111,53 +111,53 @@ suite('Completions', () => {
]
}, testUri, folders);
assertCompletions(`html { background-image: url("/|")`, {
await assertCompletions(`html { background-image: url("/|")`, {
items: [
{ label: 'pathCompletionFixtures/', resultText: `html { background-image: url("/pathCompletionFixtures/")` }
]
}, testUri, folders);
});
test('CSS url() Path Completion - Unquoted url', function () {
test('CSS url() Path Completion - Unquoted url', async function () {
let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }];
assertCompletions('html { background-image: url(./|)', {
await assertCompletions('html { background-image: url(./|)', {
items: [
{ label: 'about.html', resultText: 'html { background-image: url(./about.html)' }
]
}, testUri, folders);
assertCompletions('html { background-image: url(./a|)', {
await assertCompletions('html { background-image: url(./a|)', {
items: [
{ label: 'about.html', resultText: 'html { background-image: url(./about.html)' }
]
}, testUri, folders);
assertCompletions('html { background-image: url(../|src/)', {
await assertCompletions('html { background-image: url(../|src/)', {
items: [
{ label: 'about/', resultText: 'html { background-image: url(../about/)' }
]
}, testUri, folders);
assertCompletions('html { background-image: url(../s|rc/)', {
await assertCompletions('html { background-image: url(../s|rc/)', {
items: [
{ label: 'about/', resultText: 'html { background-image: url(../about/)' }
]
}, testUri, folders);
});
test('CSS @import Path completion', function () {
test('CSS @import Path completion', async function () {
let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }];
assertCompletions(`@import './|'`, {
await assertCompletions(`@import './|'`, {
items: [
{ label: 'about.html', resultText: `@import './about.html'` },
]
}, testUri, folders);
assertCompletions(`@import '../|'`, {
await assertCompletions(`@import '../|'`, {
items: [
{ label: 'about/', resultText: `@import '../about/'` },
{ label: 'scss/', resultText: `@import '../scss/'` },
@ -170,14 +170,14 @@ suite('Completions', () => {
/**
* For SCSS, `@import 'foo';` can be used for importing partial file `_foo.scss`
*/
test('SCSS @import Path completion', function () {
test('SCSS @import Path completion', async function () {
let testCSSUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }];
/**
* We are in a CSS file, so no special treatment for SCSS partial files
*/
assertCompletions(`@import '../scss/|'`, {
await assertCompletions(`@import '../scss/|'`, {
items: [
{ label: 'main.scss', resultText: `@import '../scss/main.scss'` },
{ label: '_foo.scss', resultText: `@import '../scss/_foo.scss'` }
@ -185,18 +185,18 @@ suite('Completions', () => {
}, testCSSUri, folders);
let testSCSSUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/scss/main.scss')).toString();
assertCompletions(`@import './|'`, {
await assertCompletions(`@import './|'`, {
items: [
{ label: '_foo.scss', resultText: `@import './foo'` }
]
}, testSCSSUri, folders, 'scss');
});
test('Completion should ignore files/folders starting with dot', function () {
test('Completion should ignore files/folders starting with dot', async function () {
let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }];
assertCompletions('html { background-image: url("../|")', {
await assertCompletions('html { background-image: url("../|")', {
count: 4
}, testUri, folders);

View file

@ -10,6 +10,7 @@ import { TextDocument, DocumentLink } from 'vscode-languageserver-types';
import { WorkspaceFolder } from 'vscode-languageserver-protocol';
import { getCSSLanguageService } from 'vscode-css-languageservice';
import { getDocumentContext } from '../utils/documentContext';
import { getNodeFSRequestService } from '../node/nodeFs';
export interface ItemDescription {
offset: number;
@ -18,7 +19,7 @@ export interface ItemDescription {
}
suite('Links', () => {
const cssLanguageService = getCSSLanguageService();
const cssLanguageService = getCSSLanguageService({ fileSystemProvider: getNodeFSRequestService() });
let assertLink = function (links: DocumentLink[], expected: ItemDescription, document: TextDocument) {
let matches = links.filter(link => {
@ -31,7 +32,7 @@ suite('Links', () => {
assert.equal(match.target, expected.target);
};
function assertLinks(value: string, expected: ItemDescription[], testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): void {
async function assertLinks(value: string, expected: ItemDescription[], testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): Promise<void> {
const offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);
@ -44,7 +45,7 @@ suite('Links', () => {
const context = getDocumentContext(testUri, workspaceFolders);
const stylesheet = cssLanguageService.parseStylesheet(document);
let links = cssLanguageService.findDocumentLinks(document, stylesheet, context)!;
let links = await cssLanguageService.findDocumentLinks2(document, stylesheet, context)!;
assert.equal(links.length, expected.length);
@ -57,32 +58,32 @@ suite('Links', () => {
return URI.file(resolve(__dirname, '../../test/linksTestFixtures', path)).toString();
}
test('url links', function () {
test('url links', async function () {
let testUri = getTestResource('about.css');
let folders = [{ name: 'x', uri: getTestResource('') }];
assertLinks('html { background-image: url("hello.html|")',
await assertLinks('html { background-image: url("hello.html|")',
[{ offset: 29, value: '"hello.html"', target: getTestResource('hello.html') }], testUri, folders
);
});
test('node module resolving', function () {
test('node module resolving', async function () {
let testUri = getTestResource('about.css');
let folders = [{ name: 'x', uri: getTestResource('') }];
assertLinks('html { background-image: url("~foo/hello.html|")',
await assertLinks('html { background-image: url("~foo/hello.html|")',
[{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders
);
});
test('node module subfolder resolving', function () {
test('node module subfolder resolving', async function () {
let testUri = getTestResource('subdir/about.css');
let folders = [{ name: 'x', uri: getTestResource('') }];
assertLinks('html { background-image: url("~foo/hello.html|")',
await assertLinks('html { background-image: url("~foo/hello.html|")',
[{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders
);
});

View file

@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
import * as assert from 'assert';
import { joinPath, normalizePath, resolvePath } from '../requests';
suite('requests', () => {
test('join', async function () {
assert.equal(joinPath('foo://a/foo/bar', 'x'), 'foo://a/foo/bar/x');
assert.equal(joinPath('foo://a/foo/bar/', 'x'), 'foo://a/foo/bar/x');
assert.equal(joinPath('foo://a/foo/bar/', '/x'), 'foo://a/foo/bar/x');
assert.equal(joinPath('foo://a/foo/bar/', 'x/'), 'foo://a/foo/bar/x/');
assert.equal(joinPath('foo://a/foo/bar/', 'x', 'y'), 'foo://a/foo/bar/x/y');
assert.equal(joinPath('foo://a/foo/bar/', 'x/', '/y'), 'foo://a/foo/bar/x/y');
assert.equal(joinPath('foo://a/foo/bar/', '.', '/y'), 'foo://a/foo/bar/y');
assert.equal(joinPath('foo://a/foo/bar/', 'x/y/z', '..'), 'foo://a/foo/bar/x/y');
});
test('resolve', async function () {
assert.equal(resolvePath('foo://a/foo/bar', 'x'), 'foo://a/foo/bar/x');
assert.equal(resolvePath('foo://a/foo/bar/', 'x'), 'foo://a/foo/bar/x');
assert.equal(resolvePath('foo://a/foo/bar/', '/x'), 'foo://a/x');
assert.equal(resolvePath('foo://a/foo/bar/', 'x/'), 'foo://a/foo/bar/x/');
});
test('normalize', async function () {
function assertNormalize(path: string, expected: string) {
assert.equal(normalizePath(path.split('/')), expected, path);
}
assertNormalize('a', 'a');
assertNormalize('/a', '/a');
assertNormalize('a/', 'a/');
assertNormalize('a/b', 'a/b');
assertNormalize('/a/foo/bar/x', '/a/foo/bar/x');
assertNormalize('/a/foo/bar//x', '/a/foo/bar/x');
assertNormalize('/a/foo/bar///x', '/a/foo/bar/x');
assertNormalize('/a/foo/bar/x/', '/a/foo/bar/x/');
assertNormalize('a/foo/bar/x/', 'a/foo/bar/x/');
assertNormalize('a/foo/bar/x//', 'a/foo/bar/x/');
assertNormalize('//a/foo/bar/x//', '/a/foo/bar/x/');
assertNormalize('a/.', 'a');
assertNormalize('a/./b', 'a/b');
assertNormalize('a/././b', 'a/b');
assertNormalize('a/n/../b', 'a/b');
assertNormalize('a/n/../', 'a/');
assertNormalize('a/n/../', 'a/');
assertNormalize('/a/n/../..', '/');
assertNormalize('..', '');
assertNormalize('/..', '/');
});
});

View file

@ -5,31 +5,8 @@
import { DocumentContext } from 'vscode-css-languageservice';
import { endsWith, startsWith } from '../utils/strings';
import * as url from 'url';
import { WorkspaceFolder } from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { join, dirname } from 'path';
import { existsSync } from 'fs';
function getModuleNameFromPath(path: string) {
// If a scoped module (starts with @) then get up until second instance of '/', otherwise get until first isntance of '/'
if (path[0] === '@') {
return path.substring(0, path.indexOf('/', path.indexOf('/') + 1));
}
return path.substring(0, path.indexOf('/'));
}
function resolvePathToModule(_moduleName: string, _relativeToFolder: string, _rootFolder: string | undefined): string | undefined {
// resolve the module relative to the document. We can't use `require` here as the code is webpacked.
const packPath = join(_relativeToFolder, 'node_modules', _moduleName, 'package.json');
if (existsSync(packPath)) {
return URI.file(packPath).toString();
} else if (_rootFolder && _relativeToFolder.startsWith(_rootFolder) && (_relativeToFolder.length !== _rootFolder.length)) {
return resolvePathToModule(_moduleName, dirname(_relativeToFolder), _rootFolder);
}
return undefined;
}
import { resolvePath } from '../requests';
export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext {
function getRootFolder(): string | undefined {
@ -46,38 +23,15 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp
}
return {
resolveReference: (ref, base = documentUri) => {
resolveReference: (ref: string, base = documentUri) => {
if (ref[0] === '/') { // resolve absolute path against the current workspace folder
if (startsWith(base, 'file://')) {
let folderUri = getRootFolder();
if (folderUri) {
return folderUri + ref.substr(1);
}
let folderUri = getRootFolder();
if (folderUri) {
return folderUri + ref.substr(1);
}
}
// Following [css-loader](https://github.com/webpack-contrib/css-loader#url)
// and [sass-loader's](https://github.com/webpack-contrib/sass-loader#imports)
// convention, if an import path starts with ~ then use node module resolution
// *unless* it starts with "~/" as this refers to the user's home directory.
if (ref[0] === '~' && ref[1] !== '/') {
ref = ref.substring(1);
if (startsWith(base, 'file://')) {
const moduleName = getModuleNameFromPath(ref);
const rootFolderUri = getRootFolder();
let rootFolder;
if (rootFolderUri) {
rootFolder = URI.parse(rootFolderUri).fsPath;
}
const documentFolder = dirname(URI.parse(base).fsPath);
const modulePath = resolvePathToModule(moduleName, documentFolder, rootFolder);
if (modulePath) {
const pathWithinModule = ref.substring(moduleName.length + 1);
return url.resolve(modulePath, pathWithinModule);
}
}
}
return url.resolve(base, ref);
base = base.substr(0, base.lastIndexOf('/') + 1);
return resolvePath(base, ref);
},
};
}

View file

@ -38,30 +38,6 @@ export function runSafeAsync<T>(func: () => Thenable<T>, errorVal: T, errorMessa
});
}
export function runSafe<T, E>(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable<T | ResponseError<E>> {
return new Promise<T | ResponseError<E>>((resolve) => {
setImmediate(() => {
if (token.isCancellationRequested) {
resolve(cancelValue());
} else {
try {
let result = func();
if (token.isCancellationRequested) {
resolve(cancelValue());
return;
} else {
resolve(result);
}
} catch (e) {
console.error(formatError(errorMessage, e));
resolve(errorVal);
}
}
});
});
}
function cancelValue<E>() {
return new ResponseError<E>(ErrorCodes.RequestCancelled, 'Request cancelled');
}

View file

@ -701,55 +701,55 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
vscode-css-languageservice@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.2.tgz#533bfeb79b38e8add07230dc67001cceb80253e8"
integrity sha512-clIjSS940NPBvtfubZokKT/YDNfE5ST9VDwsuwdCbQSkJAVZPAbmIgfmgrz/f/o8PawYQU/ooUBEuRIvIYq3ag==
vscode-css-languageservice@4.3.0-next.2:
version "4.3.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.0-next.2.tgz#a7a1289d8d68ddcdee55d4f18b12a455acaf5962"
integrity sha512-4h/s/N7wt6If/5EUNMtfAbwWwImH6EvveqZMf9SmQdMMMqekZkRLA68E98hGzuzI13rHEiLckwlAC+RNLq6FXg==
dependencies:
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.15.1"
vscode-languageserver-types "3.16.0-next.2"
vscode-nls "^4.1.2"
vscode-uri "^2.1.1"
vscode-uri "^2.1.2"
vscode-jsonrpc@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
vscode-jsonrpc@6.0.0-next.2:
version "6.0.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f"
integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw==
vscode-languageserver-protocol@^3.15.3:
version "3.15.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
vscode-languageserver-protocol@3.16.0-next.4:
version "3.16.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f"
integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ==
dependencies:
vscode-jsonrpc "^5.0.1"
vscode-languageserver-types "3.15.1"
vscode-jsonrpc "6.0.0-next.2"
vscode-languageserver-types "3.16.0-next.2"
vscode-languageserver-textdocument@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f"
integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==
vscode-languageserver-types@3.15.1, vscode-languageserver-types@^3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
vscode-languageserver-types@3.16.0-next.2:
version "3.16.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083"
integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==
vscode-languageserver@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762"
integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==
vscode-languageserver@7.0.0-next.3:
version "7.0.0-next.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0-next.3.tgz#3833bd09259a4a085baeba90783f1e4d06d81095"
integrity sha512-qSt8eb546iFuoFIN+9MPl4Avru6Iz2/JP0UmS/3djf40ICa31Np/yJ7anX2j0Az5rCzb0fak8oeKwDioGeVOYg==
dependencies:
vscode-languageserver-protocol "^3.15.3"
vscode-languageserver-protocol "3.16.0-next.4"
vscode-nls@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
vscode-uri@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90"
integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A==
vscode-uri@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
which-module@^2.0.0:
version "2.0.0"

View file

@ -635,31 +635,31 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
vscode-jsonrpc@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
vscode-jsonrpc@6.0.0-next.2:
version "6.0.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f"
integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw==
vscode-languageclient@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a"
integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==
vscode-languageclient@7.0.0-next.4:
version "7.0.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0-next.4.tgz#b4586b7800eba0a301484a6690c6c2d924976e1d"
integrity sha512-Iu12KwG+CloQJK1Dp7+nZrLpwhRA/ZacffVL0Tyf9ASTa9+Hb0cNn6FXaSSYwNsfeOYCM8SVYubEQZSXfF7E9g==
dependencies:
semver "^6.3.0"
vscode-languageserver-protocol "^3.15.3"
vscode-languageserver-protocol "3.16.0-next.4"
vscode-languageserver-protocol@^3.15.3:
version "3.15.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
vscode-languageserver-protocol@3.16.0-next.4:
version "3.16.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f"
integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ==
dependencies:
vscode-jsonrpc "^5.0.1"
vscode-languageserver-types "3.15.1"
vscode-jsonrpc "6.0.0-next.2"
vscode-languageserver-types "3.16.0-next.2"
vscode-languageserver-types@3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
vscode-languageserver-types@3.16.0-next.2:
version "3.16.0-next.2"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083"
integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==
vscode-nls@^4.1.2:
version "4.1.2"

View file

@ -12,9 +12,9 @@
"contributes": {
"languages": [{
"id": "dockerfile",
"extensions": [ ".dockerfile" ],
"filenames": [ "Dockerfile" ],
"aliases": [ "Dockerfile" ],
"extensions": [ ".dockerfile", ".containerfile" ],
"filenames": [ "Dockerfile", "Containerfile" ],
"aliases": [ "Dockerfile", "Containerfile" ],
"configuration": "./language-configuration.json"
}],
"grammars": [{

View file

@ -105,6 +105,12 @@
"category": "Git",
"icon": "$(add)"
},
{
"command": "git.stageAllMerge",
"title": "%command.stageAllMerge%",
"category": "Git",
"icon": "$(add)"
},
{
"command": "git.stageSelectedRanges",
"title": "%command.stageSelectedRanges%",
@ -490,6 +496,10 @@
"command": "git.stageAllUntracked",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageAllMerge",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageSelectedRanges",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
@ -897,12 +907,12 @@
],
"scm/resourceGroup/context": [
{
"command": "git.stageAll",
"command": "git.stageAllMerge",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "1_modification"
},
{
"command": "git.stageAll",
"command": "git.stageAllMerge",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "inline"
},
@ -1678,10 +1688,7 @@
"description": "%config.terminalAuthentication%"
},
"git.githubAuthentication": {
"type": "boolean",
"scope": "resource",
"default": true,
"description": "%config.githubAuthentication%"
"deprecationMessage": "This setting is now deprecated, please use `github.gitAuthentication` instead."
}
}
},

View file

@ -14,6 +14,7 @@
"command.stageAll": "Stage All Changes",
"command.stageAllTracked": "Stage All Tracked Changes",
"command.stageAllUntracked": "Stage All Untracked Changes",
"command.stageAllMerge": "Stage All Merge Changes",
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change",
@ -55,7 +56,7 @@
"command.pushToForce": "Push to... (Force)",
"command.pushFollowTags": "Push (Follow Tags)",
"command.pushFollowTagsForce": "Push (Follow Tags, Force)",
"command.addRemote": "Add Remote",
"command.addRemote": "Add Remote...",
"command.removeRemote": "Remove Remote",
"command.sync": "Sync",
"command.syncRebase": "Sync (Rebase)",
@ -146,7 +147,6 @@
"config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.",
"config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.",
"config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.",
"config.githubAuthentication": "Controls whether to enable automatic GitHub authentication for git commands within VS Code.",
"colors.added": "Color for added resources.",
"colors.modified": "Color for modified resources.",
"colors.deleted": "Color for deleted resources.",

View file

@ -209,6 +209,7 @@ export interface RemoteSourceProvider {
readonly icon?: string; // codicon name
readonly supportsQuery?: boolean;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
publishRepository?(repository: Repository): Promise<void>;
}
export interface Credentials {

View file

@ -19,6 +19,7 @@ import { grep, isDescendant, pathEquals } from './util';
import { Log, LogLevel } from './log';
import { GitTimelineItem } from './timelineProvider';
import { throttle, debounce } from './decorators';
import { ApiRepository } from './api/api1';
const localize = nls.loadMessageBundle();
@ -216,6 +217,11 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] {
return [...heads, ...tags, ...remoteHeads];
}
function sanitizeRemoteName(name: string) {
name = name.trim();
return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-');
}
class TagItem implements QuickPickItem {
get label(): string { return this.ref.name ?? ''; }
get description(): string { return this.ref.commit?.substr(0, 8) ?? ''; }
@ -287,6 +293,7 @@ class RemoteSourceProviderQuickPick {
}
} catch (err) {
this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }];
console.error(err);
} finally {
this.quickpick.busy = false;
}
@ -527,7 +534,7 @@ export class CommandCenter {
.map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('clonefrom', "Clone from {0}", provider.name), alwaysShow: true, provider }));
quickpick.placeholder = providers.length === 0
? localize('provide url', "Provide repository URL.")
? localize('provide url', "Provide repository URL")
: localize('provide url or pick', "Provide repository URL or pick a repository source.");
const updatePicks = (value?: string) => {
@ -993,37 +1000,14 @@ export class CommandCenter {
@command('git.stageAll', { repository: true })
async stageAll(repository: Repository): Promise<void> {
const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);
const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates];
const uris = resources.map(r => r.resourceUri);
try {
for (const deletionConflict of deletionConflicts) {
await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
}
} catch (err) {
if (/Cancelled/.test(err.message)) {
return;
}
throw err;
if (uris.length > 0) {
const config = workspace.getConfiguration('git', Uri.file(repository.root));
const untrackedChanges = config.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
await repository.add(uris, untrackedChanges === 'mixed' ? undefined : { update: true });
}
if (unresolved.length > 0) {
const message = unresolved.length > 1
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));
const yes = localize('yes', "Yes");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
}
const config = workspace.getConfiguration('git', Uri.file(repository.root));
const untrackedChanges = config.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
await repository.add([], untrackedChanges === 'mixed' ? undefined : { update: true });
}
private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> {
@ -1079,6 +1063,43 @@ export class CommandCenter {
await repository.add(uris);
}
@command('git.stageAllMerge', { repository: true })
async stageAllMerge(repository: Repository): Promise<void> {
const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);
try {
for (const deletionConflict of deletionConflicts) {
await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
}
} catch (err) {
if (/Cancelled/.test(err.message)) {
return;
}
throw err;
}
if (unresolved.length > 0) {
const message = unresolved.length > 1
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));
const yes = localize('yes', "Yes");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
}
const uris = resources.map(r => r.resourceUri);
if (uris.length > 0) {
await repository.add(uris);
}
}
@command('git.stageChange')
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
@ -1998,9 +2019,17 @@ export class CommandCenter {
const remotes = repository.remotes;
if (remotes.length === 0) {
if (!pushOptions.silent) {
window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to."));
if (pushOptions.silent) {
return;
}
const addRemote = localize('addremote', 'Add Remote');
const result = await window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to."), addRemote);
if (result === addRemote) {
await this.addRemote(repository);
}
return;
}
@ -2117,48 +2146,79 @@ export class CommandCenter {
@command('git.addRemote', { repository: true })
async addRemote(repository: Repository): Promise<string | undefined> {
const remotes = repository.remotes;
const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>();
quickpick.ignoreFocusOut = true;
const sanitize = (name: string) => {
name = name.trim();
return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-');
const providers = this.model.getRemoteProviders()
.map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('addfrom', "Add remote from {0}", provider.name), alwaysShow: true, provider }));
quickpick.placeholder = providers.length === 0
? localize('provide url', "Provide repository URL")
: localize('provide url or pick', "Provide repository URL or pick a repository source.");
const updatePicks = (value?: string) => {
if (value) {
quickpick.items = [{
label: localize('addFrom', "Add remote from URL"),
description: value,
alwaysShow: true,
url: value
},
...providers];
} else {
quickpick.items = providers;
}
};
quickpick.onDidChangeValue(updatePicks);
updatePicks();
const result = await getQuickPickResult(quickpick);
let url: string | undefined;
if (result) {
if (result.url) {
url = result.url;
} else if (result.provider) {
const quickpick = new RemoteSourceProviderQuickPick(result.provider);
const remote = await quickpick.pick();
if (remote) {
if (typeof remote.url === 'string') {
url = remote.url;
} else if (remote.url.length > 0) {
url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") });
}
}
}
}
if (!url) {
return;
}
const resultName = await window.showInputBox({
placeHolder: localize('remote name', "Remote name"),
prompt: localize('provide remote name', "Please provide a remote name"),
ignoreFocusOut: true,
validateInput: (name: string) => {
if (sanitize(name)) {
return null;
if (!sanitizeRemoteName(name)) {
return localize('remote name format invalid', "Remote name format invalid");
} else if (repository.remotes.find(r => r.name === name)) {
return localize('remote already exists', "Remote '{0}' already exists.", name);
}
return localize('remote name format invalid', "Remote name format invalid");
return null;
}
});
const name = sanitize(resultName || '');
const name = sanitizeRemoteName(resultName || '');
if (!name) {
return;
}
if (remotes.find(r => r.name === name)) {
window.showErrorMessage(localize('remote already exists', "Remote '{0}' already exists.", name));
return;
}
const url = await window.showInputBox({
placeHolder: localize('remote url', "Remote URL"),
prompt: localize('provide remote URL', "Enter URL for remote \"{0}\"", name),
ignoreFocusOut: true
});
if (!url) {
return;
}
await repository.addRemote(name, url);
return name;
}
@ -2268,15 +2328,38 @@ export class CommandCenter {
@command('git.publish', { repository: true })
async publish(repository: Repository): Promise<void> {
const branchName = repository.HEAD && repository.HEAD.name || '';
const remotes = repository.remotes;
if (remotes.length === 0) {
window.showWarningMessage(localize('no remotes to publish', "Your repository has no remotes configured to publish to."));
const providers = this.model.getRemoteProviders().filter(p => !!p.publishRepository);
if (providers.length === 0) {
window.showWarningMessage(localize('no remotes to publish', "Your repository has no remotes configured to publish to."));
return;
}
let provider: RemoteSourceProvider;
if (providers.length === 1) {
provider = providers[0];
} else {
const picks = providers
.map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('publish to', "Publish to {0}", provider.name), alwaysShow: true, provider }));
const placeHolder = localize('pick provider', "Pick a provider to publish the branch '{0}' to:", branchName);
const choice = await window.showQuickPick(picks, { placeHolder });
if (!choice) {
return;
}
provider = choice.provider;
}
await provider.publishRepository!(new ApiRepository(repository));
return;
}
const branchName = repository.HEAD && repository.HEAD.name || '';
if (remotes.length === 1) {
return await repository.pushTo(remotes[0].name, branchName, true);
}

View file

@ -1223,15 +1223,13 @@ export class Repository {
args.push('-A');
}
args.push('--');
if (paths && paths.length) {
args.push.apply(args, paths.map(sanitizePath));
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
}
} else {
args.push('.');
await this.run([...args, '--', '.']);
}
await this.run(args);
}
async rm(paths: string[]): Promise<void> {
@ -1440,10 +1438,11 @@ export class Repository {
const limiter = new Limiter(5);
const promises: Promise<any>[] = [];
const args = ['clean', '-f', '-q'];
for (const paths of groups) {
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
promises.push(limiter.queue(() => this.run(['clean', '-f', '-q', '--', ...chunk])));
promises.push(limiter.queue(() => this.run([...args, '--', ...chunk])));
}
}
@ -1475,19 +1474,19 @@ export class Repository {
// In case there are no branches, we must use rm --cached
if (!result.stdout) {
args = ['rm', '--cached', '-r', '--'];
args = ['rm', '--cached', '-r'];
} else {
args = ['reset', '-q', treeish, '--'];
}
if (paths && paths.length) {
args.push.apply(args, paths.map(sanitizePath));
} else {
args.push('.');
args = ['reset', '-q', treeish];
}
try {
await this.run(args);
if (paths && paths.length > 0) {
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
}
} else {
await this.run([...args, '--', '.']);
}
} catch (err) {
// In case there are merge conflicts to be resolved, git reset will output
// some "needs merge" data. We try to get around that.
@ -1975,10 +1974,10 @@ export class Repository {
}
async updateSubmodules(paths: string[]): Promise<void> {
const args = ['submodule', 'update', '--'];
const args = ['submodule', 'update'];
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
await this.run([...args, ...chunk]);
await this.run([...args, '--', ...chunk]);
}
}

View file

@ -1,67 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CredentialsProvider, Credentials } from './api/git';
import { IDisposable, filterEvent, EmptyDisposable } from './util';
import { workspace, Uri, AuthenticationSession, authentication } from 'vscode';
import { Askpass } from './askpass';
export class GitHubCredentialProvider implements CredentialsProvider {
async getCredentials(host: Uri): Promise<Credentials | undefined> {
if (!/github\.com/i.test(host.authority)) {
return;
}
const session = await this.getSession();
return { username: session.account.id, password: await session.getAccessToken() };
}
private async getSession(): Promise<AuthenticationSession> {
const authenticationSessions = await authentication.getSessions('github', ['repo']);
if (authenticationSessions.length) {
return await authenticationSessions[0];
} else {
return await authentication.login('github', ['repo']);
}
}
}
export class GithubCredentialProviderManager {
private providerDisposable: IDisposable = EmptyDisposable;
private readonly disposable: IDisposable;
private _enabled = false;
private set enabled(enabled: boolean) {
if (this._enabled === enabled) {
return;
}
this._enabled = enabled;
if (enabled) {
this.providerDisposable = this.askpass.registerCredentialsProvider(new GitHubCredentialProvider());
} else {
this.providerDisposable.dispose();
}
}
constructor(private readonly askpass: Askpass) {
this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git'))(this.refresh, this);
this.refresh();
}
private refresh(): void {
const config = workspace.getConfiguration('git', null);
this.enabled = config.get<boolean>('enabled', true) && config.get('githubAuthentication', true);
}
dispose(): void {
this.enabled = false;
this.disposable.dispose();
}
}

View file

@ -22,7 +22,6 @@ import * as path from 'path';
import * as fs from 'fs';
import { GitTimelineProvider } from './timelineProvider';
import { registerAPICommands } from './api/api1';
import { GithubCredentialProviderManager } from './github';
import { TerminalEnvironmentManager } from './terminal';
const deactivateTasks: { (): Promise<any>; }[] = [];
@ -44,9 +43,6 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
const terminalEnvironmentManager = new TerminalEnvironmentManager(context, env);
disposables.push(terminalEnvironmentManager);
const githubCredentialProviderManager = new GithubCredentialProviderManager(askpass);
context.subscriptions.push(githubCredentialProviderManager);
const git = new Git({ gitPath: info.path, version: info.version, env });
const model = new Model(git, askpass, context.globalState, outputChannel);
disposables.push(model);

View file

@ -14,6 +14,7 @@ import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
import { APIState as State, RemoteSourceProvider, CredentialsProvider } from './api/git';
import { Askpass } from './askpass';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
const localize = nls.loadMessageBundle();
@ -45,7 +46,7 @@ interface OpenRepository extends Disposable {
repository: Repository;
}
export class Model {
export class Model implements IRemoteSourceProviderRegistry {
private _onDidOpenRepository = new EventEmitter<Repository>();
readonly onDidOpenRepository: Event<Repository> = this._onDidOpenRepository.event;
@ -76,7 +77,13 @@ export class Model {
commands.executeCommand('setContext', 'git.state', state);
}
private remoteProviders = new Set<RemoteSourceProvider>();
private remoteSourceProviders = new Set<RemoteSourceProvider>();
private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidAddRemoteSourceProvider = this._onDidAddRemoteSourceProvider.event;
private _onDidRemoveRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event;
private disposables: Disposable[] = [];
@ -246,7 +253,7 @@ export class Model {
}
const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this.globalState, this.outputChannel);
this.open(repository);
await repository.status();
@ -445,8 +452,13 @@ export class Model {
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
this.remoteProviders.add(provider);
return toDisposable(() => this.remoteProviders.delete(provider));
this.remoteSourceProviders.add(provider);
this._onDidAddRemoteSourceProvider.fire(provider);
return toDisposable(() => {
this.remoteSourceProviders.delete(provider);
this._onDidRemoveRemoteSourceProvider.fire(provider);
});
}
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
@ -454,7 +466,7 @@ export class Model {
}
getRemoteProviders(): RemoteSourceProvider[] {
return [...this.remoteProviders.values()];
return [...this.remoteSourceProviders.values()];
}
dispose(): void {

View file

@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, Event } from 'vscode';
import { RemoteSourceProvider } from './api/git';
export interface IRemoteSourceProviderRegistry {
readonly onDidAddRemoteSourceProvider: Event<RemoteSourceProvider>;
readonly onDidRemoveRemoteSourceProvider: Event<RemoteSourceProvider>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
getRemoteProviders(): RemoteSourceProvider[];
}

View file

@ -16,6 +16,7 @@ import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util';
import { IFileWatcher, watch } from './watch';
import { Log, LogLevel } from './log';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
@ -251,11 +252,13 @@ export class Resource implements SourceControlResourceState {
}
get resourceDecoration(): Decoration {
const title = this.tooltip;
const letter = this.letter;
const color = this.color;
const priority = this.priority;
return { bubble: true, title, letter, color, priority };
return {
bubble: this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED,
title: this.tooltip,
letter: this.letter,
color: this.color,
priority: this.priority
};
}
constructor(
@ -679,6 +682,7 @@ export class Repository implements Disposable {
constructor(
private readonly repository: BaseRepository,
remoteSourceProviderRegistry: IRemoteSourceProviderRegistry,
globalState: Memento,
outputChannel: OutputChannel
) {
@ -774,7 +778,7 @@ export class Repository implements Disposable {
}
}, null, this.disposables);
const statusBar = new StatusBarCommands(this);
const statusBar = new StatusBarCommands(this, remoteSourceProviderRegistry);
this.disposables.push(statusBar);
statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables);
this._sourceControl.statusBarCommands = statusBar.commands;

View file

@ -7,7 +7,8 @@ import { Disposable, Command, EventEmitter, Event, workspace, Uri } from 'vscode
import { Repository, Operation } from './repository';
import { anyEvent, dispose, filterEvent } from './util';
import * as nls from 'vscode-nls';
import { Branch } from './api/git';
import { Branch, RemoteSourceProvider } from './api/git';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
const localize = nls.loadMessageBundle();
@ -39,41 +40,45 @@ class CheckoutStatusBar {
}
interface SyncStatusBarState {
enabled: boolean;
isSyncRunning: boolean;
hasRemotes: boolean;
HEAD: Branch | undefined;
readonly enabled: boolean;
readonly isSyncRunning: boolean;
readonly hasRemotes: boolean;
readonly HEAD: Branch | undefined;
readonly remoteSourceProviders: RemoteSourceProvider[];
}
class SyncStatusBar {
private static StartState: SyncStatusBarState = {
enabled: true,
isSyncRunning: false,
hasRemotes: false,
HEAD: undefined
};
private _onDidChange = new EventEmitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
private disposables: Disposable[] = [];
private _state: SyncStatusBarState = SyncStatusBar.StartState;
private _state: SyncStatusBarState;
private get state() { return this._state; }
private set state(state: SyncStatusBarState) {
this._state = state;
this._onDidChange.fire();
}
constructor(private repository: Repository) {
repository.onDidRunGitStatus(this.onModelChange, this, this.disposables);
repository.onDidChangeOperations(this.onOperationsChange, this, this.disposables);
constructor(private repository: Repository, private remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);
anyEvent(remoteSourceProviderRegistry.onDidAddRemoteSourceProvider, remoteSourceProviderRegistry.onDidRemoveRemoteSourceProvider)
(this.onDidChangeRemoteSourceProviders, this, this.disposables);
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.enableStatusBarSync'));
onEnablementChange(this.updateEnablement, this, this.disposables);
this.updateEnablement();
this._onDidChange.fire();
this._state = {
enabled: true,
isSyncRunning: false,
hasRemotes: false,
HEAD: undefined,
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
.filter(p => !!p.publishRepository)
};
}
private updateEnablement(): void {
@ -83,7 +88,7 @@ class SyncStatusBar {
this.state = { ... this.state, enabled };
}
private onOperationsChange(): void {
private onDidChangeOperations(): void {
const isSyncRunning = this.repository.operations.isRunning(Operation.Sync) ||
this.repository.operations.isRunning(Operation.Push) ||
this.repository.operations.isRunning(Operation.Pull);
@ -91,7 +96,7 @@ class SyncStatusBar {
this.state = { ...this.state, isSyncRunning };
}
private onModelChange(): void {
private onDidRunGitStatus(): void {
this.state = {
...this.state,
hasRemotes: this.repository.remotes.length > 0,
@ -99,9 +104,34 @@ class SyncStatusBar {
};
}
private onDidChangeRemoteSourceProviders(): void {
this.state = {
...this.state,
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
.filter(p => !!p.publishRepository)
};
}
get command(): Command | undefined {
if (!this.state.enabled || !this.state.hasRemotes) {
return undefined;
if (!this.state.enabled) {
return;
}
if (!this.state.hasRemotes) {
if (this.state.remoteSourceProviders.length === 0) {
return;
}
const tooltip = this.state.remoteSourceProviders.length === 1
? localize('publish to', "Publish to {0}", this.state.remoteSourceProviders[0].name)
: localize('publish to...', "Publish to...");
return {
command: 'git.publish',
title: `$(cloud-upload)`,
tooltip,
arguments: [this.repository.sourceControl]
};
}
const HEAD = this.state.HEAD;
@ -152,38 +182,21 @@ class SyncStatusBar {
export class StatusBarCommands {
readonly onDidChange: Event<void>;
private syncStatusBar: SyncStatusBar;
private checkoutStatusBar: CheckoutStatusBar;
private disposables: Disposable[] = [];
constructor(repository: Repository) {
this.syncStatusBar = new SyncStatusBar(repository);
constructor(repository: Repository, remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
this.syncStatusBar = new SyncStatusBar(repository, remoteSourceProviderRegistry);
this.checkoutStatusBar = new CheckoutStatusBar(repository);
}
get onDidChange(): Event<void> {
return anyEvent(
this.syncStatusBar.onDidChange,
this.checkoutStatusBar.onDidChange
);
this.onDidChange = anyEvent(this.syncStatusBar.onDidChange, this.checkoutStatusBar.onDidChange);
}
get commands(): Command[] {
const result: Command[] = [];
const checkout = this.checkoutStatusBar.command;
if (checkout) {
result.push(checkout);
}
const sync = this.syncStatusBar.command;
if (sync) {
result.push(sync);
}
return result;
return [this.checkoutStatusBar.command, this.syncStatusBar.command]
.filter((c): c is Command => !!c);
}
dispose(): void {

View file

@ -1,35 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const fs = require('fs');
const path = require('path');
const schemes = ['OSS', 'INSIDERS', 'STABLE', 'EXPLORATION', 'VSO', 'VSO_PPE', 'VSO_DEV'];
function main() {
let content = {};
for (const scheme of schemes) {
const id = process.env[`${scheme}_GITHUB_ID`];
const secret = process.env[`${scheme}_GITHUB_SECRET`];
if (id && secret) {
content[scheme] = { id, secret };
}
}
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppSecret = process.env.GITHUB_APP_SECRET;
if (githubAppId && githubAppSecret) {
content.GITHUB_APP = { id: githubAppId, secret: githubAppSecret }
}
if (Object.keys(content).length > 0) {
fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content));
}
}
main();

View file

@ -1,102 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, env } from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
export interface ClientDetails {
id?: string;
secret?: string;
}
export interface ClientConfig {
OSS: ClientDetails;
INSIDERS: ClientDetails;
STABLE: ClientDetails;
EXPLORATION: ClientDetails;
VSO: ClientDetails;
VSO_PPE: ClientDetails;
VSO_DEV: ClientDetails;
GITHUB_APP: ClientDetails;
}
export class Registrar {
private _config: ClientConfig;
constructor() {
try {
const fileContents = fs.readFileSync(path.join(env.appRoot, 'extensions/github-authentication/src/common/config.json')).toString();
this._config = JSON.parse(fileContents);
} catch (e) {
this._config = {
OSS: {},
INSIDERS: {},
STABLE: {},
EXPLORATION: {},
VSO: {},
VSO_PPE: {},
VSO_DEV: {},
GITHUB_APP: {}
};
}
}
getGitHubAppDetails(): ClientDetails {
if (!this._config.GITHUB_APP.id || !this._config.GITHUB_APP.secret) {
throw new Error(`No GitHub App client configuration available`);
}
return this._config.GITHUB_APP;
}
getClientDetails(callbackUri: Uri): ClientDetails {
let details: ClientDetails | undefined;
switch (callbackUri.scheme) {
case 'code-oss':
details = this._config.OSS;
break;
case 'vscode-insiders':
details = this._config.INSIDERS;
break;
case 'vscode':
details = this._config.STABLE;
break;
case 'vscode-exploration':
details = this._config.EXPLORATION;
break;
case 'https':
switch (callbackUri.authority) {
case 'online.visualstudio.com':
details = this._config.VSO;
break;
case 'online-ppe.core.vsengsaas.visualstudio.com':
details = this._config.VSO_PPE;
break;
case 'online.dev.core.vsengsaas.visualstudio.com':
details = this._config.VSO_DEV;
break;
}
default:
throw new Error(`Unrecognized callback ${callbackUri}`);
}
if (!details.id || !details.secret) {
throw new Error(`No client configuration available for ${callbackUri}`);
}
return details;
}
}
const ClientRegistrar = new Registrar();
export default ClientRegistrar;

View file

@ -21,20 +21,8 @@ interface SessionData {
accessToken: string;
}
// TODO remove
interface OldSessionData {
id: string;
accountName: string;
scopes: string[];
accessToken: string;
}
function isOldSessionData(x: any): x is OldSessionData {
return !!x.accountName;
}
export class GitHubAuthenticationProvider {
private _sessions: vscode.AuthenticationSession[] = [];
private _sessions: vscode.AuthenticationSession2[] = [];
private _githubServer = new GitHubServer();
public async initialize(): Promise<void> {
@ -44,14 +32,12 @@ export class GitHubAuthenticationProvider {
// Ignore, network request failed
}
// TODO revert Cannot validate tokens from auth server, no available clientId
// await this.validateSessions();
this.pollForChange();
}
private pollForChange() {
setTimeout(async () => {
let storedSessions: vscode.AuthenticationSession[];
let storedSessions: vscode.AuthenticationSession2[];
try {
storedSessions = await this.readSessions();
} catch (e) {
@ -94,13 +80,13 @@ export class GitHubAuthenticationProvider {
}, 1000 * 30);
}
private async readSessions(): Promise<vscode.AuthenticationSession[]> {
private async readSessions(): Promise<vscode.AuthenticationSession2[]> {
const storedSessions = await keychain.getToken();
if (storedSessions) {
try {
const sessionData: (SessionData | OldSessionData)[] = JSON.parse(storedSessions);
const sessionPromises = sessionData.map(async (session: SessionData | OldSessionData): Promise<vscode.AuthenticationSession> => {
const needsUserInfo = isOldSessionData(session) || !session.account;
const sessionData: SessionData[] = JSON.parse(storedSessions);
const sessionPromises = sessionData.map(async (session: SessionData): Promise<vscode.AuthenticationSession2> => {
const needsUserInfo = !session.account;
let userInfo: { id: string, accountName: string };
if (needsUserInfo) {
userInfo = await this._githubServer.getUserInfo(session.accessToken);
@ -109,15 +95,11 @@ export class GitHubAuthenticationProvider {
return {
id: session.id,
account: {
displayName: isOldSessionData(session)
? session.accountName
: session.account?.displayName ?? userInfo!.accountName,
id: isOldSessionData(session)
? userInfo!.id
: session.account?.id ?? userInfo!.id
displayName: session.account?.displayName ?? userInfo!.accountName,
id: session.account?.id ?? userInfo!.id
},
scopes: session.scopes,
getAccessToken: () => Promise.resolve(session.accessToken)
accessToken: session.accessToken
};
});
@ -136,57 +118,30 @@ export class GitHubAuthenticationProvider {
}
private async storeSessions(): Promise<void> {
const sessionData: SessionData[] = await Promise.all(this._sessions.map(async session => {
const resolvedAccessToken = await session.getAccessToken();
return {
id: session.id,
account: session.account,
scopes: session.scopes,
accessToken: resolvedAccessToken
};
}));
await keychain.setToken(JSON.stringify(sessionData));
await keychain.setToken(JSON.stringify(this._sessions));
}
get sessions(): vscode.AuthenticationSession[] {
get sessions(): vscode.AuthenticationSession2[] {
return this._sessions;
}
public async login(scopes: string): Promise<vscode.AuthenticationSession> {
const token = scopes === 'vso' ? await this.loginAndInstallApp(scopes) : await this._githubServer.login(scopes);
public async login(scopes: string): Promise<vscode.AuthenticationSession2> {
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
await this.setToken(session);
return session;
}
public async loginAndInstallApp(scopes: string): Promise<string> {
const token = await this._githubServer.login(scopes);
const hasUserInstallation = await this._githubServer.hasUserInstallation(token);
if (hasUserInstallation) {
return token;
} else {
return this._githubServer.installApp();
}
}
public async manuallyProvideToken(): Promise<void> {
this._githubServer.manuallyProvideToken();
}
private async tokenToSession(token: string, scopes: string[]): Promise<vscode.AuthenticationSession> {
private async tokenToSession(token: string, scopes: string[]): Promise<vscode.AuthenticationSession2> {
const userInfo = await this._githubServer.getUserInfo(token);
return {
id: uuid(),
getAccessToken: () => Promise.resolve(token),
account: {
displayName: userInfo.accountName,
id: userInfo.id
},
scopes: scopes
};
return new vscode.AuthenticationSession2(uuid(), token, { displayName: userInfo.accountName, id: userInfo.id }, scopes);
}
private async setToken(session: vscode.AuthenticationSession): Promise<void> {
private async setToken(session: vscode.AuthenticationSession2): Promise<void> {
const sessionIndex = this._sessions.findIndex(s => s.id === session.id);
if (sessionIndex > -1) {
this._sessions.splice(sessionIndex, 1, session);
@ -201,14 +156,6 @@ export class GitHubAuthenticationProvider {
const sessionIndex = this._sessions.findIndex(session => session.id === id);
if (sessionIndex > -1) {
this._sessions.splice(sessionIndex, 1);
// TODO revert
// Cannot revoke tokens from auth server, no clientId available
// const token = await session.getAccessToken();
// try {
// await this._githubServer.revokeToken(token);
// } catch (_) {
// // ignore, should still remove from keychain
// }
}
await this.storeSessions();

View file

@ -9,7 +9,6 @@ import * as vscode from 'vscode';
import * as uuid from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
import ClientRegistrar from './common/clientRegistrar';
const localize = nls.loadMessageBundle();
@ -24,8 +23,10 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
export const uriHandler = new UriEventHandler;
const exchangeCodeForToken: (state: string, host: string, getPath: (code: string) => string) => PromiseAdapter<vscode.Uri, string> =
(state, host, getPath) => async (uri, resolve, reject) => {
const onDidManuallyProvideToken = new vscode.EventEmitter<string>();
const exchangeCodeForToken: (state: string) => PromiseAdapter<vscode.Uri, string> =
(state) => async (uri, resolve, reject) => {
Logger.info('Exchanging code for token...');
const query = parseQuery(uri);
const code = query.code;
@ -36,8 +37,8 @@ const exchangeCodeForToken: (state: string, host: string, getPath: (code: string
}
const post = https.request({
host: host,
path: getPath(code),
host: AUTH_RELAY_SERVER,
path: `/token?code=${code}&state=${state}`,
method: 'POST',
headers: {
Accept: 'application/json'
@ -81,26 +82,16 @@ export class GitHubServer {
const state = uuid();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
let uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
if (scopes === 'vso') {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
uri = vscode.Uri.parse(`https://github.com/login/oauth/authorize?redirect_uri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&client_id=${clientDetails.id}`);
}
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code&authServer=https://github.com`);
vscode.env.openExternal(uri);
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state,
scopes === 'vso' ? 'github.com' : AUTH_RELAY_SERVER,
(code) => {
if (scopes === 'vso') {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
return `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`;
} else {
return `/token?code=${code}&state=${state}`;
}
})).finally(() => {
this.updateStatusBarItem(false);
});
return Promise.race([
promiseFromEvent(uriHandler.event, exchangeCodeForToken(state)),
promiseFromEvent<string, string>(onDidManuallyProvideToken.event)
]).finally(() => {
this.updateStatusBarItem(false);
});
}
private updateStatusBarItem(isStart?: boolean) {
@ -125,56 +116,12 @@ export class GitHubServer {
if (!uri.scheme || uri.scheme === 'file') { throw new Error; }
uriHandler.handleUri(uri);
} catch (e) {
Logger.error(e);
vscode.window.showErrorMessage(localize('unexpectedInput', "The input did not matched the expected format"));
// If it doesn't look like a URI, treat it as a token.
Logger.info('Treating input as token');
onDidManuallyProvideToken.fire(uriOrToken);
}
}
public async hasUserInstallation(token: string): Promise<boolean> {
return new Promise((resolve, reject) => {
Logger.info('Getting user installations...');
const post = https.request({
host: 'api.github.com',
path: `/user/installations`,
method: 'GET',
headers: {
Accept: 'application/vnd.github.machine-man-preview+json',
Authorization: `token ${token}`,
'User-Agent': 'Visual-Studio-Code'
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
const json = JSON.parse(Buffer.concat(buffer).toString());
Logger.info('Got installation info!');
const hasInstallation = json.installations.some((installation: { app_slug: string }) => installation.app_slug === 'microsoft-visual-studio-code');
resolve(hasInstallation);
} else {
reject(new Error(result.statusMessage));
}
});
});
post.end();
post.on('error', err => {
reject(err);
});
});
}
public async installApp(): Promise<string> {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
const state = uuid();
const uri = vscode.Uri.parse(`https://github.com/apps/microsoft-visual-studio-code/installations/new?state=${state}`);
vscode.env.openExternal(uri);
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state, 'github.com', (code) => `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`));
}
public async getUserInfo(token: string): Promise<{ id: string, accountName: string }> {
return new Promise((resolve, reject) => {
Logger.info('Getting account info...');
@ -210,91 +157,4 @@ export class GitHubServer {
});
});
}
public async validateToken(token: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
const payload = JSON.stringify({ access_token: token });
Logger.info('Validating token...');
const post = https.request({
host: 'api.github.com',
path: `/applications/${clientDetails.id}/token`,
method: 'POST',
headers: {
Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
'User-Agent': 'Visual-Studio-Code',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
Logger.info('Validated token!');
resolve();
} else {
Logger.info(`Validating token failed: ${result.statusMessage}`);
reject(new Error(result.statusMessage));
}
});
});
post.write(payload);
post.end();
post.on('error', err => {
Logger.error(err.message);
reject(new Error(NETWORK_ERROR));
});
});
}
public async revokeToken(token: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
const payload = JSON.stringify({ access_token: token });
Logger.info('Revoking token...');
const post = https.request({
host: 'api.github.com',
path: `/applications/${clientDetails.id}/token`,
method: 'DELETE',
headers: {
Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
'User-Agent': 'Visual-Studio-Code',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 204) {
Logger.info('Revoked token!');
resolve();
} else {
Logger.info(`Revoking token failed: ${result.statusMessage}`);
reject(new Error(result.statusMessage));
}
});
});
post.write(payload);
post.end();
post.on('error', err => {
reject(err);
});
});
}
}

View file

@ -0,0 +1,8 @@
src/**
!src/common/config.json
out/**
build/**
extension.webpack.config.js
tsconfig.json
yarn.lock
README.md

View file

@ -0,0 +1,7 @@
# GitHub for Visual Studio Code
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
## Features
This extension provides GitHub features for VS Code.

View file

@ -3,13 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
//@ts-check
export interface INativeOpenDialogOptions {
forceNewWindow?: boolean;
'use strict';
defaultPath?: string;
const withDefaults = require('../shared.webpack.config');
telemetryEventName?: string;
telemetryExtraData?: ITelemetryData;
}
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
});

View file

@ -0,0 +1,67 @@
{
"name": "github",
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
"version": "0.0.1",
"engines": {
"vscode": "^1.41.0"
},
"enableProposedApi": true,
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"extensionDependencies": [
"vscode.git"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "github.publish",
"title": "Publish to GitHub"
}
],
"configuration": [
{
"title": "GitHub",
"properties": {
"github.gitAuthentication": {
"type": "boolean",
"scope": "resource",
"default": true,
"description": "%config.gitAuthentication%"
}
}
}
],
"viewsWelcome": [
{
"view": "scm",
"contents": "%welcome.publishFolder%",
"when": "config.git.enabled && git.state == initialized && workbenchState == folder"
},
{
"view": "scm",
"contents": "%welcome.publishWorkspaceFolder%",
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "gulp compile-extension:github",
"watch": "gulp watch-extension:github"
},
"dependencies": {
"@octokit/rest": "^17.9.1",
"tunnel": "^0.0.6",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
"@types/node": "^10.12.21"
}
}

View file

@ -0,0 +1,7 @@
{
"displayName": "GitHub",
"description": "GitHub",
"config.gitAuthentication": "Controls whether to enable automatic GitHub authentication for git commands within VS Code.",
"welcome.publishFolder": "You can also directly publish this folder to a GitHub repository.\n[$(github) Publish to GitHub](command:github.publish)",
"welcome.publishWorkspaceFolder": "You can also directly publish a workspace folder to a GitHub repository.\n[$(github) Publish to GitHub](command:github.publish)"
}

View file

@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AuthenticationSession, authentication, window } from 'vscode';
import { Agent, globalAgent } from 'https';
import { Octokit } from '@octokit/rest';
import { httpsOverHttp } from 'tunnel';
import { URL } from 'url';
function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent {
if (!url) {
return globalAgent;
}
try {
const { hostname, port, username, password } = new URL(url);
const auth = username && password && `${username}:${password}`;
return httpsOverHttp({ proxy: { host: hostname, port, proxyAuth: auth } });
} catch (e) {
window.showErrorMessage(`HTTPS_PROXY environment variable ignored: ${e.message}`);
return globalAgent;
}
}
const scopes = ['repo'];
export async function getSession(): Promise<AuthenticationSession> {
const authenticationSessions = await authentication.getSessions('github', scopes);
if (authenticationSessions.length) {
return await authenticationSessions[0];
} else {
return await authentication.login('github', scopes);
}
}
let _octokit: Promise<Octokit> | undefined;
export function getOctokit(): Promise<Octokit> {
if (!_octokit) {
_octokit = getSession().then(async session => {
const token = await session.getAccessToken();
const agent = getAgent();
return new Octokit({
request: { agent },
userAgent: 'GitHub VSCode',
auth: `token ${token}`
});
}).then(null, async err => {
_octokit = undefined;
throw err;
});
}
return _octokit;
}

View file

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { API as GitAPI } from './typings/git';
import { publishRepository } from './publish';
export function registerCommands(gitAPI: GitAPI): vscode.Disposable[] {
const disposables = [];
disposables.push(vscode.commands.registerCommand('github.publish', async () => {
try {
publishRepository(gitAPI);
} catch (err) {
vscode.window.showErrorMessage(err.message);
}
}));
return disposables;
}

View file

@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CredentialsProvider, Credentials, API as GitAPI } from './typings/git';
import { workspace, Uri, Disposable } from 'vscode';
import { getSession } from './auth';
const EmptyDisposable: Disposable = { dispose() { } };
class GitHubCredentialProvider implements CredentialsProvider {
async getCredentials(host: Uri): Promise<Credentials | undefined> {
if (!/github\.com/i.test(host.authority)) {
return;
}
const session = await getSession();
return { username: session.account.id, password: await session.getAccessToken() };
}
}
export class GithubCredentialProviderManager {
private providerDisposable: Disposable = EmptyDisposable;
private readonly disposable: Disposable;
private _enabled = false;
private set enabled(enabled: boolean) {
if (this._enabled === enabled) {
return;
}
this._enabled = enabled;
if (enabled) {
this.providerDisposable = this.gitAPI.registerCredentialsProvider(new GitHubCredentialProvider());
} else {
this.providerDisposable.dispose();
}
}
constructor(private gitAPI: GitAPI) {
this.disposable = workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('github')) {
this.refresh();
}
});
this.refresh();
}
private refresh(): void {
const config = workspace.getConfiguration('github', null);
const enabled = config.get<boolean>('gitAuthentication', true);
this.enabled = !!enabled;
}
dispose(): void {
this.enabled = false;
this.disposable.dispose();
}
}

View file

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { GithubRemoteSourceProvider } from './remoteSourceProvider';
import { GitExtension } from './typings/git';
import { registerCommands } from './commands';
import { GithubCredentialProviderManager } from './credentialProvider';
export async function activate(context: vscode.ExtensionContext) {
const gitExtension = vscode.extensions.getExtension<GitExtension>('vscode.git')!.exports;
const gitAPI = gitExtension.getAPI(1);
context.subscriptions.push(...registerCommands(gitAPI));
context.subscriptions.push(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI)));
context.subscriptions.push(new GithubCredentialProviderManager(gitAPI));
}

View file

@ -0,0 +1,172 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as path from 'path';
import { promises as fs } from 'fs';
import { API as GitAPI, Repository } from './typings/git';
import { getOctokit } from './auth';
const localize = nls.loadMessageBundle();
function sanitizeRepositoryName(value: string): string {
return value.trim().replace(/[^a-z0-9_.]/ig, '-');
}
function getPick<T extends vscode.QuickPickItem>(quickpick: vscode.QuickPick<T>): Promise<T | undefined> {
return Promise.race<T | undefined>([
new Promise<T>(c => quickpick.onDidAccept(() => quickpick.selectedItems.length > 0 && c(quickpick.selectedItems[0]))),
new Promise<undefined>(c => quickpick.onDidHide(() => c(undefined)))
]);
}
export async function publishRepository(gitAPI: GitAPI, repository?: Repository): Promise<void> {
if (!vscode.workspace.workspaceFolders?.length) {
return;
}
let folder: vscode.WorkspaceFolder;
if (vscode.workspace.workspaceFolders.length === 1) {
folder = vscode.workspace.workspaceFolders[0];
} else {
const picks = vscode.workspace.workspaceFolders.map(folder => ({ label: folder.name, folder }));
const placeHolder = localize('pick folder', "Pick a folder to publish to GitHub");
const pick = await vscode.window.showQuickPick(picks, { placeHolder });
if (!pick) {
return;
}
folder = pick.folder;
}
let quickpick = vscode.window.createQuickPick<vscode.QuickPickItem & { repo?: string, auth?: 'https' | 'ssh' }>();
quickpick.ignoreFocusOut = true;
quickpick.placeholder = 'Repository Name';
quickpick.value = folder.name;
quickpick.show();
quickpick.busy = true;
const octokit = await getOctokit();
const user = await octokit.users.getAuthenticated({});
const owner = user.data.login;
quickpick.busy = false;
let repo: string | undefined;
const onDidChangeValue = async () => {
const sanitizedRepo = sanitizeRepositoryName(quickpick.value);
if (!sanitizedRepo) {
quickpick.items = [];
} else {
quickpick.items = [{ label: `$(repo) Publish to GitHub private repository`, description: `$(github) ${owner}/${sanitizedRepo}`, alwaysShow: true, repo: sanitizedRepo }];
}
};
onDidChangeValue();
while (true) {
const listener = quickpick.onDidChangeValue(onDidChangeValue);
const pick = await getPick(quickpick);
listener.dispose();
repo = pick?.repo;
if (repo) {
try {
quickpick.busy = true;
await octokit.repos.get({ owner, repo: repo });
quickpick.items = [{ label: `$(error) GitHub repository already exists`, description: `$(github) ${owner}/${repo}`, alwaysShow: true }];
} catch {
break;
} finally {
quickpick.busy = false;
}
}
}
quickpick.dispose();
if (!repo) {
return;
}
quickpick = vscode.window.createQuickPick();
quickpick.placeholder = localize('ignore', "Select which files should be included in the repository.");
quickpick.canSelectMany = true;
quickpick.show();
try {
quickpick.busy = true;
const repositoryPath = folder.uri.fsPath;
const currentPath = path.join(repositoryPath);
const children = await fs.readdir(currentPath);
quickpick.items = children.map(name => ({ label: name }));
quickpick.selectedItems = quickpick.items;
quickpick.busy = false;
const result = await Promise.race([
new Promise<readonly vscode.QuickPickItem[]>(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))),
new Promise<undefined>(c => quickpick.onDidHide(() => c(undefined)))
]);
if (!result) {
return;
}
const ignored = new Set(children);
result.forEach(c => ignored.delete(c.label));
const raw = [...ignored].map(i => `/${i}`).join('\n');
await fs.writeFile(path.join(repositoryPath, '.gitignore'), raw, 'utf8');
} finally {
quickpick.dispose();
}
const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => {
progress.report({ message: 'Publishing to GitHub private repository', increment: 25 });
const res = await octokit.repos.createForAuthenticatedUser({
name: repo!,
private: true
});
const createdGithubRepository = res.data;
progress.report({ message: 'Creating first commit', increment: 25 });
if (!repository) {
repository = await gitAPI.init(folder.uri) || undefined;
if (!repository) {
return;
}
await repository.commit('first commit', { all: true });
}
progress.report({ message: 'Uploading files', increment: 25 });
await repository.addRemote('origin', createdGithubRepository.clone_url);
await repository.push('origin', 'master', true);
return createdGithubRepository;
});
if (!githubRepository) {
return;
}
const openInGitHub = 'Open In GitHub';
const action = await vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub);
if (action === openInGitHub) {
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url));
}
}

View file

@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { API as GitAPI, RemoteSourceProvider, RemoteSource, Repository } from './typings/git';
import { getOctokit } from './auth';
import { Octokit } from '@octokit/rest';
import { publishRepository } from './publish';
function asRemoteSource(raw: any): RemoteSource {
return {
name: `$(github) ${raw.full_name}`,
description: raw.description || undefined,
url: raw.clone_url
};
}
export class GithubRemoteSourceProvider implements RemoteSourceProvider {
readonly name = 'GitHub';
readonly icon = 'github';
readonly supportsQuery = true;
private userReposCache: RemoteSource[] = [];
constructor(private gitAPI: GitAPI) { }
async getRemoteSources(query?: string): Promise<RemoteSource[]> {
const octokit = await getOctokit();
const [fromUser, fromQuery] = await Promise.all([
this.getUserRemoteSources(octokit, query),
this.getQueryRemoteSources(octokit, query)
]);
const userRepos = new Set(fromUser.map(r => r.name));
return [
...fromUser,
...fromQuery.filter(r => !userRepos.has(r.name))
];
}
private async getUserRemoteSources(octokit: Octokit, query?: string): Promise<RemoteSource[]> {
if (!query) {
const user = await octokit.users.getAuthenticated({});
const username = user.data.login;
const res = await octokit.repos.listForUser({ username, sort: 'updated', per_page: 100 });
this.userReposCache = res.data.map(asRemoteSource);
}
return this.userReposCache;
}
private async getQueryRemoteSources(octokit: Octokit, query?: string): Promise<RemoteSource[]> {
if (!query) {
return [];
}
const raw = await octokit.search.repos({ q: query, sort: 'updated' });
return raw.data.items.map(asRemoteSource);
}
publishRepository(repository: Repository): Promise<void> {
return publishRepository(this.gitAPI, repository);
}
}

295
extensions/github/src/typings/git.d.ts vendored Normal file
View file

@ -0,0 +1,295 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
export { ProviderResult } from 'vscode';
export interface Git {
readonly path: string;
}
export interface InputBox {
value: string;
}
export const enum RefType {
Head,
RemoteHead,
Tag
}
export interface Ref {
readonly type: RefType;
readonly name?: string;
readonly commit?: string;
readonly remote?: string;
}
export interface UpstreamRef {
readonly remote: string;
readonly name: string;
}
export interface Branch extends Ref {
readonly upstream?: UpstreamRef;
readonly ahead?: number;
readonly behind?: number;
}
export interface Commit {
readonly hash: string;
readonly message: string;
readonly parents: string[];
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
}
export interface Submodule {
readonly name: string;
readonly path: string;
readonly url: string;
}
export interface Remote {
readonly name: string;
readonly fetchUrl?: string;
readonly pushUrl?: string;
readonly isReadOnly: boolean;
}
export const enum Status {
INDEX_MODIFIED,
INDEX_ADDED,
INDEX_DELETED,
INDEX_RENAMED,
INDEX_COPIED,
MODIFIED,
DELETED,
UNTRACKED,
IGNORED,
INTENT_TO_ADD,
ADDED_BY_US,
ADDED_BY_THEM,
DELETED_BY_US,
DELETED_BY_THEM,
BOTH_ADDED,
BOTH_DELETED,
BOTH_MODIFIED
}
export interface Change {
/**
* Returns either `originalUri` or `renameUri`, depending
* on whether this change is a rename change. When
* in doubt always use `uri` over the other two alternatives.
*/
readonly uri: Uri;
readonly originalUri: Uri;
readonly renameUri: Uri | undefined;
readonly status: Status;
}
export interface RepositoryState {
readonly HEAD: Branch | undefined;
readonly refs: Ref[];
readonly remotes: Remote[];
readonly submodules: Submodule[];
readonly rebaseCommit: Commit | undefined;
readonly mergeChanges: Change[];
readonly indexChanges: Change[];
readonly workingTreeChanges: Change[];
readonly onDidChange: Event<void>;
}
export interface RepositoryUIState {
readonly selected: boolean;
readonly onDidChange: Event<void>;
}
/**
* Log options.
*/
export interface LogOptions {
/** Max number of log entries to retrieve. If not specified, the default is 32. */
readonly maxEntries?: number;
readonly path?: string;
}
export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
}
export interface BranchQuery {
readonly remote?: boolean;
readonly contains?: string;
}
export interface Repository {
readonly rootUri: Uri;
readonly inputBox: InputBox;
readonly state: RepositoryState;
readonly ui: RepositoryUIState;
getConfigs(): Promise<{ key: string; value: string; }[]>;
getConfig(key: string): Promise<string>;
setConfig(key: string, value: string): Promise<string>;
getGlobalConfig(key: string): Promise<string>;
getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>;
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>;
buffer(ref: string, path: string): Promise<Buffer>;
show(ref: string, path: string): Promise<string>;
getCommit(ref: string): Promise<Commit>;
clean(paths: string[]): Promise<void>;
apply(patch: string, reverse?: boolean): Promise<void>;
diff(cached?: boolean): Promise<string>;
diffWithHEAD(): Promise<Change[]>;
diffWithHEAD(path: string): Promise<string>;
diffWith(ref: string): Promise<Change[]>;
diffWith(ref: string, path: string): Promise<string>;
diffIndexWithHEAD(): Promise<Change[]>;
diffIndexWithHEAD(path: string): Promise<string>;
diffIndexWith(ref: string): Promise<Change[]>;
diffIndexWith(ref: string, path: string): Promise<string>;
diffBlobs(object1: string, object2: string): Promise<string>;
diffBetween(ref1: string, ref2: string): Promise<Change[]>;
diffBetween(ref1: string, ref2: string, path: string): Promise<string>;
hashObject(data: string): Promise<string>;
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
deleteBranch(name: string, force?: boolean): Promise<void>;
getBranch(name: string): Promise<Branch>;
getBranches(query: BranchQuery): Promise<Ref[]>;
setBranchUpstream(name: string, upstream: string): Promise<void>;
getMergeBase(ref1: string, ref2: string): Promise<string>;
status(): Promise<void>;
checkout(treeish: string): Promise<void>;
addRemote(name: string, url: string): Promise<void>;
removeRemote(name: string): Promise<void>;
renameRemote(name: string, newName: string): Promise<void>;
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
blame(path: string): Promise<string>;
log(options?: LogOptions): Promise<Commit[]>;
commit(message: string, opts?: CommitOptions): Promise<void>;
}
export interface RemoteSource {
readonly name: string;
readonly description?: string;
readonly url: string | string[];
}
export interface RemoteSourceProvider {
readonly name: string;
readonly icon?: string; // codicon name
readonly supportsQuery?: boolean;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
publishRepository?(repository: Repository): Promise<void>;
}
export interface Credentials {
readonly username: string;
readonly password: string;
}
export interface CredentialsProvider {
getCredentials(host: Uri): ProviderResult<Credentials>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>;
readonly onDidCloseRepository: Event<Repository>;
toGitUri(uri: Uri, ref: string): Uri;
getRepository(uri: Uri): Repository | null;
init(root: Uri): Promise<Repository | null>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
}
export interface GitExtension {
readonly enabled: boolean;
readonly onDidChangeEnablement: Event<boolean>;
/**
* Returns a specific API version.
*
* Throws error if git extension is disabled. You can listed to the
* [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event
* to know when the extension becomes enabled/disabled.
*
* @param version Version number.
* @returns API instance
*/
getAPI(version: 1): API;
}
export const enum GitErrorCodes {
BadConfigFile = 'BadConfigFile',
AuthenticationFailed = 'AuthenticationFailed',
NoUserNameConfigured = 'NoUserNameConfigured',
NoUserEmailConfigured = 'NoUserEmailConfigured',
NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified',
NotAGitRepository = 'NotAGitRepository',
NotAtRepositoryRoot = 'NotAtRepositoryRoot',
Conflict = 'Conflict',
StashConflict = 'StashConflict',
UnmergedChanges = 'UnmergedChanges',
PushRejected = 'PushRejected',
RemoteConnectionError = 'RemoteConnectionError',
DirtyWorkTree = 'DirtyWorkTree',
CantOpenResource = 'CantOpenResource',
GitNotFound = 'GitNotFound',
CantCreatePipe = 'CantCreatePipe',
CantAccessRemote = 'CantAccessRemote',
RepositoryNotFound = 'RepositoryNotFound',
RepositoryIsLocked = 'RepositoryIsLocked',
BranchNotFullyMerged = 'BranchNotFullyMerged',
NoRemoteReference = 'NoRemoteReference',
InvalidBranchName = 'InvalidBranchName',
BranchAlreadyExists = 'BranchAlreadyExists',
NoLocalChanges = 'NoLocalChanges',
NoStashFound = 'NoStashFound',
LocalChangesOverwritten = 'LocalChangesOverwritten',
NoUpstreamBranch = 'NoUpstreamBranch',
IsInSubmodule = 'IsInSubmodule',
WrongCase = 'WrongCase',
CantLockRef = 'CantLockRef',
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
PatchDoesNotApply = 'PatchDoesNotApply',
NoPathFound = 'NoPathFound',
UnknownPath = 'UnknownPath',
}

Some files were not shown because too many files have changed in this diff Show more