mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Merge branch 'master' into electron-2.0.x
This commit is contained in:
commit
fd6c2340e2
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
|
@ -236,6 +236,15 @@
|
|||
"VSCODE_DEV": "1",
|
||||
"VSCODE_CLI": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch Built-in Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/extensions/debug-auto-launch"
|
||||
]
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[
|
||||
{
|
||||
"name": "ms-vscode.node-debug",
|
||||
"version": "1.26.1",
|
||||
"version": "1.26.2",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -78,7 +78,6 @@ const vscodeResources = [
|
|||
'out-build/vs/workbench/parts/webview/electron-browser/webview-pre.js',
|
||||
'out-build/vs/**/markdown.css',
|
||||
'out-build/vs/workbench/parts/tasks/**/*.json',
|
||||
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',
|
||||
'out-build/vs/workbench/parts/welcome/walkThrough/**/*.md',
|
||||
'out-build/vs/workbench/services/files/**/*.exe',
|
||||
'out-build/vs/workbench/services/files/**/*.md',
|
||||
|
|
|
@ -43,7 +43,12 @@ function update(idOrPath) {
|
|||
let apiToken = process.env.TRANSIFEX_API_TOKEN;
|
||||
let languageId = localization.transifexId || localization.languageId;
|
||||
let translationDataFolder = path.join(locExtFolder, 'translations');
|
||||
|
||||
if (languageId === "zh-cn") {
|
||||
languageId = "zh-hans";
|
||||
}
|
||||
if (languageId === "zh-tw") {
|
||||
languageId = "zh-hant";
|
||||
}
|
||||
if (fs.existsSync(translationDataFolder) && fs.existsSync(path.join(translationDataFolder, 'main.i18n.json'))) {
|
||||
console.log('Clearing \'' + translationDataFolder + '\'...');
|
||||
rimraf.sync(translationDataFolder);
|
||||
|
|
|
@ -36,14 +36,88 @@ steps:
|
|||
|
||||
- script: |
|
||||
set -e
|
||||
npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb"
|
||||
npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm"
|
||||
#npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap"
|
||||
REPO="$(pwd)"
|
||||
ROOT="$REPO/.."
|
||||
ARCH="$(VSCODE_ARCH)"
|
||||
|
||||
# Publish tarball
|
||||
PLATFORM_LINUX="linux-$(VSCODE_ARCH)"
|
||||
[[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64"
|
||||
[[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64"
|
||||
BUILDNAME="VSCode-$PLATFORM_LINUX"
|
||||
BUILD="$ROOT/$BUILDNAME"
|
||||
BUILD_VERSION="$(date +%s)"
|
||||
[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz"
|
||||
TARBALL_PATH="$ROOT/$TARBALL_FILENAME"
|
||||
PACKAGEJSON="$BUILD/resources/app/package.json"
|
||||
VERSION=$(node -p "require(\"$PACKAGEJSON\").version")
|
||||
|
||||
rm -rf $ROOT/code-*.tar.*
|
||||
(cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME)
|
||||
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
|
||||
./build/tfs/linux/release.sh "$(VSCODE_ARCH)" "$(LINUX_REPO_PASSWORD)"
|
||||
node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH"
|
||||
|
||||
# publish hockeyapp symbols
|
||||
# Publish hockeyapp symbols
|
||||
node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)"
|
||||
|
||||
# Publish DEB
|
||||
npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb"
|
||||
PLATFORM_DEB="linux-deb-$ARCH"
|
||||
[[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64"
|
||||
DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)"
|
||||
DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME"
|
||||
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
|
||||
node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH"
|
||||
|
||||
# Publish RPM
|
||||
npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm"
|
||||
PLATFORM_RPM="linux-rpm-$ARCH"
|
||||
[[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64"
|
||||
RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)"
|
||||
RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME"
|
||||
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
|
||||
node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH"
|
||||
|
||||
# SNAP_FILENAME="$(ls $REPO/.build/linux/snap/$ARCH/ | grep .snap)"
|
||||
# SNAP_PATH="$REPO/.build/linux/snap/$ARCH/$SNAP_FILENAME"
|
||||
|
||||
# Publish to MS repo
|
||||
IS_FROZEN="$(node build/tfs/linux/frozen-check.js $VSCODE_QUALITY)"
|
||||
|
||||
if [ -z "$VSCODE_QUALITY" ]; then
|
||||
echo "VSCODE_QUALITY is not set, skipping repo package publish"
|
||||
elif [ "$IS_FROZEN" = "true" ]; then
|
||||
echo "$VSCODE_QUALITY is frozen, skipping repo package publish"
|
||||
else
|
||||
if [ "$BUILD_SOURCEBRANCH" = "master" ] || [ "$BUILD_SOURCEBRANCH" = "refs/heads/master" ]; then
|
||||
if [[ $BUILD_QUEUEDBY = *"Project Collection Service Accounts"* || $BUILD_QUEUEDBY = *"Microsoft.VisualStudio.Services.TFS"* ]]; then
|
||||
# Write config files needed by API, use eval to force environment variable expansion
|
||||
pushd build/tfs/linux
|
||||
# Submit to apt repo
|
||||
if [ "$DEB_ARCH" = "amd64" ]; then
|
||||
eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"username\": \"vscode\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > apt-config.json
|
||||
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
|
||||
LINUX_REPO_PASSWORD="$(LINUX_REPO_PASSWORD)" \
|
||||
./repoapi_client.sh -config apt-config.json -addfile $DEB_PATH
|
||||
fi
|
||||
# Submit to yum repo (disabled as it's manual until signing is automated)
|
||||
# eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4ae3542421134a1a48d1b\", \"username\": \"vscode\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > yum-config.json
|
||||
|
||||
# ./repoapi_client.sh -config yum-config.json -addfile $RPM_PATH
|
||||
popd
|
||||
echo "To check repo publish status run ./repoapi_client.sh -config config.json -check <id>"
|
||||
fi
|
||||
fi
|
||||
fi
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Arguments
|
||||
ARCH="$1"
|
||||
LINUX_REPO_PASSWORD="$2"
|
||||
|
||||
# Variables
|
||||
PLATFORM_LINUX="linux-$ARCH"
|
||||
PLATFORM_DEB="linux-deb-$ARCH"
|
||||
PLATFORM_RPM="linux-rpm-$ARCH"
|
||||
[[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64"
|
||||
[[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64"
|
||||
REPO="`pwd`"
|
||||
ROOT="$REPO/.."
|
||||
BUILDNAME="VSCode-$PLATFORM_LINUX"
|
||||
BUILD="$ROOT/$BUILDNAME"
|
||||
BUILD_VERSION="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/ | sed -e 's/code-[a-z]*_//g' -e 's/\.deb$//g')"
|
||||
[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz"
|
||||
TARBALL_PATH="$ROOT/$TARBALL_FILENAME"
|
||||
PACKAGEJSON="$BUILD/resources/app/package.json"
|
||||
VERSION=$(node -p "require(\"$PACKAGEJSON\").version")
|
||||
|
||||
rm -rf $ROOT/code-*.tar.*
|
||||
(cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME)
|
||||
|
||||
node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_LINUX archive-unsigned $TARBALL_FILENAME $VERSION true $TARBALL_PATH
|
||||
|
||||
DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)"
|
||||
DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME"
|
||||
|
||||
node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_DEB package $DEB_FILENAME $VERSION true $DEB_PATH
|
||||
|
||||
RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)"
|
||||
RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME"
|
||||
|
||||
node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_RPM package $RPM_FILENAME $VERSION true $RPM_PATH
|
||||
|
||||
# SNAP_FILENAME="$(ls $REPO/.build/linux/snap/$ARCH/ | grep .snap)"
|
||||
# SNAP_PATH="$REPO/.build/linux/snap/$ARCH/$SNAP_FILENAME"
|
||||
|
||||
IS_FROZEN="$(node build/tfs/linux/frozen-check.js $VSCODE_QUALITY)"
|
||||
|
||||
if [ -z "$VSCODE_QUALITY" ]; then
|
||||
echo "VSCODE_QUALITY is not set, skipping repo package publish"
|
||||
elif [ "$IS_FROZEN" = "true" ]; then
|
||||
echo "$VSCODE_QUALITY is frozen, skipping repo package publish"
|
||||
else
|
||||
if [ "$BUILD_SOURCEBRANCH" = "master" ] || [ "$BUILD_SOURCEBRANCH" = "refs/heads/master" ]; then
|
||||
if [[ $BUILD_QUEUEDBY = *"Project Collection Service Accounts"* || $BUILD_QUEUEDBY = *"Microsoft.VisualStudio.Services.TFS"* ]]; then
|
||||
# Write config files needed by API, use eval to force environment variable expansion
|
||||
pushd build/tfs/linux
|
||||
# Submit to apt repo
|
||||
if [ "$DEB_ARCH" = "amd64" ]; then
|
||||
eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"username\": \"vscode\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > apt-config.json
|
||||
|
||||
./repoapi_client.sh -config apt-config.json -addfile $DEB_PATH
|
||||
fi
|
||||
# Submit to yum repo (disabled as it's manual until signing is automated)
|
||||
# eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4ae3542421134a1a48d1b\", \"username\": \"vscode\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > yum-config.json
|
||||
|
||||
# ./repoapi_client.sh -config yum-config.json -addfile $RPM_PATH
|
||||
popd
|
||||
echo "To check repo publish status run ./repoapi_client.sh -config config.json -check <id>"
|
||||
fi
|
||||
fi
|
||||
fi
|
38
extensions/css-language-features/CONTRIBUTING.md
Normal file
38
extensions/css-language-features/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
|
||||
## Setup
|
||||
|
||||
- Clone [Microsoft/vscode](https://github.com/microsoft/vscode)
|
||||
- Run `yarn` at `/`, this will install
|
||||
- Dependencies for `/extension/css-language-features/`
|
||||
- Dependencies for `/extension/css-language-features/server/`
|
||||
- devDependencies such as `gulp`
|
||||
- Open `/extensions/css-language-features/` as the workspace in VS Code
|
||||
- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/css-language-features/.vscode/launch.json) debug target in the Debug View. This will:
|
||||
- Launch the `preLaunchTask` task to compile the extension
|
||||
- Launch a new VS Code instance with the `css-language-features` extension loaded
|
||||
- You should see a notification saying the development version of `css-language-features` overwrites the bundled version of `css-language-features`
|
||||
- Test the behavior of this extension by editing CSS/SCSS/Less files
|
||||
- Run `Reload Window` command in the launched instance to reload the extension
|
||||
|
||||
### Contribute to vscode-css-languageservice
|
||||
|
||||
[Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) contains the language smarts for CSS/SCSS/Less.
|
||||
This extension wraps the css language service into a Language Server for VS Code.
|
||||
If you want to fix CSS/SCSS/Less issues or make improvements, you should make changes at [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice).
|
||||
|
||||
However, within this extension, you can run a development version of `vscode-css-languageservice` to debug code or test language features interactively:
|
||||
|
||||
#### Linking `vscode-css-languageservice` in `css-language-features/server/`
|
||||
|
||||
- Clone [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice)
|
||||
- Run `yarn` in `vscode-css-languageservice`
|
||||
- Run `yarn link` in `vscode-css-languageservice`. This will compile and link `vscode-css-languageservice`
|
||||
- In `css-language-features/server/`, run `npm link vscode-css-languageservice`
|
||||
|
||||
#### Testing the development version of `vscode-css-languageservice`
|
||||
|
||||
- Open both `vscode-css-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature
|
||||
- Run `yarn watch` at `css-languagefeatures/server/` to recompile this extension with the linked version of `vscode-css-languageservice`
|
||||
- Make some changes in `vscode-css-languageservice`
|
||||
- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-css-languageservice`. You can interactively test the language features.
|
||||
- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-css-languageservice` and `css-language-features/server/`
|
|
@ -4,47 +4,6 @@
|
|||
|
||||
## Features
|
||||
|
||||
This extension provides rich language support to CSS/SCSS/LESS files.
|
||||
- auto-completion
|
||||
- linting
|
||||
- diagnostics
|
||||
- extra information on hover
|
||||
- navigation to definition and references
|
||||
See [CSS, SCSS and Less in VS Code](https://code.visualstudio.com/docs/languages/css) to learn about the features of this extension.
|
||||
|
||||
## Development
|
||||
|
||||
- Clone [Microsoft/vscode](https://github.com/microsoft/vscode)
|
||||
- Run `yarn` at `/`, this will install
|
||||
- Dependencies for `/extension/css-language-features/`
|
||||
- Dependencies for `/extension/css-language-features/server/`
|
||||
- devDependencies such as `gulp`
|
||||
- Open `/extensions/css-language-features/` as the workspace in VS Code
|
||||
- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/css-language-features/.vscode/launch.json) debug target in the Debug View. This will:
|
||||
- Launch the `preLaunchTask` task to compile the extension
|
||||
- Launch a new VS Code instance with the `css-language-features` extension loaded
|
||||
- You should see a notification saying the development version of `css-language-features` overwrites the bundled version of `css-language-features`
|
||||
- Test the behavior of this extension by editing CSS/SCSS/Less files
|
||||
- Run `Reload Window` command in the launched instance to reload the extension
|
||||
|
||||
### Contribute to vscode-css-languageservice
|
||||
|
||||
[Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) contains the language smarts for CSS/SCSS/Less.
|
||||
This extension wraps the css language service into a Language Server for VS Code.
|
||||
If you want to fix CSS/SCSS/Less issues or make improvements, you should make changes at [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice).
|
||||
|
||||
However, within this extension, you can run a development version of `vscode-css-languageservice` to debug code or test language features interactively:
|
||||
|
||||
#### Linking `vscode-css-languageservice` in `css-language-features/server/`
|
||||
|
||||
- Clone [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice)
|
||||
- Run `yarn` in `vscode-css-languageservice`
|
||||
- Run `yarn link` in `vscode-css-languageservice`. This will compile and link `vscode-css-languageservice`
|
||||
- In `css-language-features/server/`, run `npm link vscode-css-languageservice`
|
||||
|
||||
#### Testing the development version of `vscode-css-languageservice`
|
||||
|
||||
- Open both `vscode-css-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature
|
||||
- Run `yarn watch` at `css-languagefeatures/server/` to recompile this extension with the linked version of `vscode-css-languageservice`
|
||||
- Make some changes in `vscode-css-languageservice`
|
||||
- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-css-languageservice`. You can interactively test the language features.
|
||||
- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-css-languageservice` and `css-language-features/server/`
|
||||
Please read the [CONTRIBUTING.md](https://github.com/Microsoft/vscode/blob/master/extensions/css-language-features/CONTRIBUTING.md) file to learn how to contribute to this extension.
|
2
extensions/debug-auto-launch/.vscodeignore
Normal file
2
extensions/debug-auto-launch/.vscodeignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
src/**
|
||||
tsconfig.json
|
54
extensions/debug-auto-launch/package.json
Normal file
54
extensions/debug-auto-launch/package.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "debug-auto-launch",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "1.0.0",
|
||||
"publisher": "vscode",
|
||||
"engines": {
|
||||
"vscode": "^1.5.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:debug-auto-launch",
|
||||
"watch": "gulp watch-extension:debug-auto-launch"
|
||||
},
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"title": "Node debug",
|
||||
"properties": {
|
||||
"debug.node.autoAttach": {
|
||||
"scope": "window",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled",
|
||||
"on",
|
||||
"off"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%debug.node.autoAttach.disabled.description%",
|
||||
"%debug.node.autoAttach.on.description%",
|
||||
"%debug.node.autoAttach.off.description%"
|
||||
],
|
||||
"description": "%debug.node.autoAttach.description%",
|
||||
"default": "disabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "extension.node-debug.toggleAutoAttach",
|
||||
"title": "%toggle.auto.attach%",
|
||||
"category": "Debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "8.0.33"
|
||||
}
|
||||
}
|
11
extensions/debug-auto-launch/package.nls.json
Normal file
11
extensions/debug-auto-launch/package.nls.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"displayName": "Node Debug Auto-attach",
|
||||
"description": "Helper for auto-attach feature when node-debug extensions are not active.",
|
||||
|
||||
"debug.node.autoAttach.description": "Automatically attach node debugger when node.js was launched in debug mode from integrated terminal.",
|
||||
"debug.node.autoAttach.disabled.description": "Auto attach is disabled and not shown in status bar.",
|
||||
"debug.node.autoAttach.on.description": "Auto attach is active.",
|
||||
"debug.node.autoAttach.off.description": "Auto attach is inactive.",
|
||||
|
||||
"toggle.auto.attach": "Toggle Auto Attach"
|
||||
}
|
24
extensions/debug-auto-launch/src/autoAttach.ts
Normal file
24
extensions/debug-auto-launch/src/autoAttach.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { basename } from 'path';
|
||||
import { pollProcesses, attachToProcess } from './nodeProcessTree';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export function startAutoAttach(rootPid: number): vscode.Disposable {
|
||||
|
||||
return pollProcesses(rootPid, true, (pid, cmdPath, args) => {
|
||||
const cmdName = basename(cmdPath, '.exe');
|
||||
if (cmdName === 'node') {
|
||||
const name = localize('process.with.pid.label', "Process {0}", pid);
|
||||
attachToProcess(undefined, name, pid, args);
|
||||
}
|
||||
});
|
||||
}
|
132
extensions/debug-auto-launch/src/extension.ts
Normal file
132
extensions/debug-auto-launch/src/extension.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { basename } from 'path';
|
||||
import { pollProcesses, attachToProcess } from './nodeProcessTree';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const ON_TEXT = localize('status.text.auto.attach.on', "Auto Attach: On");
|
||||
const OFF_TEXT = localize('status.text.auto.attach.off', "Auto Attach: Off");
|
||||
|
||||
const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach';
|
||||
|
||||
let currentState: string;
|
||||
let autoAttacher: vscode.Disposable | undefined;
|
||||
let statusItem: vscode.StatusBarItem | undefined = undefined;
|
||||
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): void {
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttach));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('debug.node.autoAttach')) {
|
||||
updateAutoAttachInStatus(context);
|
||||
}
|
||||
}));
|
||||
|
||||
updateAutoAttachInStatus(context);
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
}
|
||||
|
||||
|
||||
function toggleAutoAttach(context: vscode.ExtensionContext) {
|
||||
|
||||
const conf = vscode.workspace.getConfiguration('debug.node');
|
||||
|
||||
let value = conf.get('autoAttach');
|
||||
if (value === 'on') {
|
||||
value = 'off';
|
||||
} else {
|
||||
value = 'on';
|
||||
}
|
||||
|
||||
const info = conf.inspect('autoAttach');
|
||||
let target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Global;
|
||||
if (info) {
|
||||
if (info.workspaceFolderValue) {
|
||||
target = vscode.ConfigurationTarget.WorkspaceFolder;
|
||||
} else if (info.workspaceValue) {
|
||||
target = vscode.ConfigurationTarget.Workspace;
|
||||
} else if (info.globalValue) {
|
||||
target = vscode.ConfigurationTarget.Global;
|
||||
} else if (info.defaultValue) {
|
||||
// setting not yet used: store setting in workspace
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
target = vscode.ConfigurationTarget.Workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
conf.update('autoAttach', value, target);
|
||||
|
||||
updateAutoAttachInStatus(context);
|
||||
}
|
||||
|
||||
function updateAutoAttachInStatus(context: vscode.ExtensionContext) {
|
||||
|
||||
const newState = <string>vscode.workspace.getConfiguration('debug.node').get('autoAttach');
|
||||
|
||||
if (newState !== currentState) {
|
||||
|
||||
currentState = newState;
|
||||
|
||||
if (newState === 'disabled') {
|
||||
|
||||
// turn everything off
|
||||
if (statusItem) {
|
||||
statusItem.hide();
|
||||
statusItem.text = OFF_TEXT;
|
||||
}
|
||||
if (autoAttacher) {
|
||||
autoAttacher.dispose();
|
||||
autoAttacher = undefined;
|
||||
}
|
||||
|
||||
} else { // 'on' or 'off'
|
||||
|
||||
// make sure status bar item exists and is visible
|
||||
if (!statusItem) {
|
||||
statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
|
||||
statusItem.command = TOGGLE_COMMAND;
|
||||
statusItem.text = OFF_TEXT;
|
||||
statusItem.tooltip = localize('status.tooltip.auto.attach', "Automatically attach to node.js processes in debug mode");
|
||||
statusItem.show();
|
||||
context.subscriptions.push(statusItem);
|
||||
} else {
|
||||
statusItem.show();
|
||||
}
|
||||
|
||||
if (newState === 'off') {
|
||||
statusItem.text = OFF_TEXT;
|
||||
if (autoAttacher) {
|
||||
autoAttacher.dispose();
|
||||
autoAttacher = undefined;
|
||||
}
|
||||
} else if (newState === 'on') {
|
||||
statusItem.text = ON_TEXT;
|
||||
const vscode_pid = process.env['VSCODE_PID'];
|
||||
const rootPid = vscode_pid ? parseInt(vscode_pid) : 0;
|
||||
autoAttacher = startAutoAttach(rootPid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startAutoAttach(rootPid: number): vscode.Disposable {
|
||||
|
||||
return pollProcesses(rootPid, true, (pid, cmdPath, args) => {
|
||||
const cmdName = basename(cmdPath, '.exe');
|
||||
if (cmdName === 'node') {
|
||||
const name = localize('process.with.pid.label', "Process {0}", pid);
|
||||
attachToProcess(undefined, name, pid, args);
|
||||
}
|
||||
});
|
||||
}
|
139
extensions/debug-auto-launch/src/nodeProcessTree.ts
Normal file
139
extensions/debug-auto-launch/src/nodeProcessTree.ts
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 vscode from 'vscode';
|
||||
import { getProcessTree, ProcessTreeNode } from './processTree';
|
||||
import { analyseArguments } from './protocolDetection';
|
||||
|
||||
const pids = new Set<number>();
|
||||
|
||||
const POLL_INTERVAL = 1000;
|
||||
|
||||
/**
|
||||
* Poll for all subprocesses of given root process.
|
||||
*/
|
||||
export function pollProcesses(rootPid: number, inTerminal: boolean, cb: (pid: number, cmd: string, args: string) => void): vscode.Disposable {
|
||||
|
||||
let stopped = false;
|
||||
|
||||
function poll() {
|
||||
//const start = Date.now();
|
||||
findChildProcesses(rootPid, inTerminal, cb).then(_ => {
|
||||
//console.log(`duration: ${Date.now() - start}`);
|
||||
setTimeout(_ => {
|
||||
if (!stopped) {
|
||||
poll();
|
||||
}
|
||||
}, POLL_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
poll();
|
||||
|
||||
return new vscode.Disposable(() => stopped = true);
|
||||
}
|
||||
|
||||
export function attachToProcess(folder: vscode.WorkspaceFolder | undefined, name: string, pid: number, args: string, baseConfig?: vscode.DebugConfiguration) {
|
||||
|
||||
if (pids.has(pid)) {
|
||||
return;
|
||||
}
|
||||
pids.add(pid);
|
||||
|
||||
const config: vscode.DebugConfiguration = {
|
||||
type: 'node',
|
||||
request: 'attach',
|
||||
name: name,
|
||||
stopOnEntry: false
|
||||
};
|
||||
|
||||
if (baseConfig) {
|
||||
// selectively copy attributes
|
||||
if (baseConfig.timeout) {
|
||||
config.timeout = baseConfig.timeout;
|
||||
}
|
||||
if (baseConfig.sourceMaps) {
|
||||
config.sourceMaps = baseConfig.sourceMaps;
|
||||
}
|
||||
if (baseConfig.outFiles) {
|
||||
config.outFiles = baseConfig.outFiles;
|
||||
}
|
||||
if (baseConfig.sourceMapPathOverrides) {
|
||||
config.sourceMapPathOverrides = baseConfig.sourceMapPathOverrides;
|
||||
}
|
||||
if (baseConfig.smartStep) {
|
||||
config.smartStep = baseConfig.smartStep;
|
||||
}
|
||||
if (baseConfig.skipFiles) {
|
||||
config.skipFiles = baseConfig.skipFiles;
|
||||
}
|
||||
if (baseConfig.showAsyncStacks) {
|
||||
config.sourceMaps = baseConfig.showAsyncStacks;
|
||||
}
|
||||
if (baseConfig.trace) {
|
||||
config.trace = baseConfig.trace;
|
||||
}
|
||||
}
|
||||
|
||||
let { usePort, protocol, port } = analyseArguments(args);
|
||||
if (usePort) {
|
||||
config.processId = `${protocol}${port}`;
|
||||
} else {
|
||||
if (protocol && port > 0) {
|
||||
config.processId = `${pid}${protocol}${port}`;
|
||||
} else {
|
||||
config.processId = pid.toString();
|
||||
}
|
||||
}
|
||||
|
||||
vscode.debug.startDebugging(folder, config);
|
||||
}
|
||||
|
||||
function findChildProcesses(rootPid: number, inTerminal: boolean, cb: (pid: number, cmd: string, args: string) => void): Promise<void> {
|
||||
|
||||
function walker(node: ProcessTreeNode, terminal: boolean, renderer: number) {
|
||||
|
||||
if (node.args.indexOf('--type=terminal') >= 0 && (renderer === 0 || node.ppid === renderer)) {
|
||||
terminal = true;
|
||||
}
|
||||
|
||||
let { protocol } = analyseArguments(node.args);
|
||||
if (terminal && protocol) {
|
||||
cb(node.pid, node.command, node.args);
|
||||
}
|
||||
|
||||
for (const child of node.children || []) {
|
||||
walker(child, terminal, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
function finder(node: ProcessTreeNode, pid: number): ProcessTreeNode | undefined {
|
||||
if (node.pid === pid) {
|
||||
return node;
|
||||
}
|
||||
for (const child of node.children || []) {
|
||||
const p = finder(child, pid);
|
||||
if (p) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getProcessTree(rootPid).then(tree => {
|
||||
if (tree) {
|
||||
|
||||
// find the pid of the renderer process
|
||||
const extensionHost = finder(tree, process.pid);
|
||||
let rendererPid = extensionHost ? extensionHost.ppid : 0;
|
||||
|
||||
for (const child of tree.children || []) {
|
||||
walker(child, !inTerminal, rendererPid);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
186
extensions/debug-auto-launch/src/processTree.ts
Normal file
186
extensions/debug-auto-launch/src/processTree.ts
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { spawn, ChildProcess } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
export class ProcessTreeNode {
|
||||
children?: ProcessTreeNode[];
|
||||
|
||||
constructor(public pid: number, public ppid: number, public command: string, public args: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProcessTree(rootPid: number): Promise<ProcessTreeNode | undefined> {
|
||||
|
||||
const map = new Map<number, ProcessTreeNode>();
|
||||
|
||||
map.set(0, new ProcessTreeNode(0, 0, '???', ''));
|
||||
|
||||
try {
|
||||
await getProcesses((pid: number, ppid: number, command: string, args: string) => {
|
||||
if (pid !== ppid) {
|
||||
map.set(pid, new ProcessTreeNode(pid, ppid, command, args));
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const values = map.values();
|
||||
for (const p of values) {
|
||||
const parent = map.get(p.ppid);
|
||||
if (parent && parent !== p) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
parent.children.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(rootPid) && rootPid > 0) {
|
||||
return map.get(rootPid);
|
||||
}
|
||||
return map.get(0);
|
||||
}
|
||||
|
||||
export function getProcesses(one: (pid: number, ppid: number, command: string, args: string, date?: number) => void): Promise<void> {
|
||||
|
||||
// returns a function that aggregates chunks of data until one or more complete lines are received and passes them to a callback.
|
||||
function lines(callback: (a: string) => void) {
|
||||
let unfinished = ''; // unfinished last line of chunk
|
||||
return (data: string | Buffer) => {
|
||||
const lines = data.toString().split(/\r?\n/);
|
||||
const finishedLines = lines.slice(0, lines.length - 1);
|
||||
finishedLines[0] = unfinished + finishedLines[0]; // complete previous unfinished line
|
||||
unfinished = lines[lines.length - 1]; // remember unfinished last line of this chunk for next round
|
||||
for (const s of finishedLines) {
|
||||
callback(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let proc: ChildProcess;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
// attributes columns are in alphabetic order!
|
||||
const CMD_PAT = /^(.*)\s+([0-9]+)\.[0-9]+[+-][0-9]+\s+([0-9]+)\s+([0-9]+)$/;
|
||||
|
||||
const wmic = join(process.env['WINDIR'] || 'C:\\Windows', 'System32', 'wbem', 'WMIC.exe');
|
||||
proc = spawn(wmic, ['process', 'get', 'CommandLine,CreationDate,ParentProcessId,ProcessId']);
|
||||
proc.stdout.setEncoding('utf8');
|
||||
proc.stdout.on('data', lines(line => {
|
||||
let matches = CMD_PAT.exec(line.trim());
|
||||
if (matches && matches.length === 5) {
|
||||
const pid = Number(matches[4]);
|
||||
const ppid = Number(matches[3]);
|
||||
const date = Number(matches[2]);
|
||||
let args = matches[1].trim();
|
||||
if (!isNaN(pid) && !isNaN(ppid) && args) {
|
||||
let command = args;
|
||||
if (args[0] === '"') {
|
||||
const end = args.indexOf('"', 1);
|
||||
if (end > 0) {
|
||||
command = args.substr(1, end - 1);
|
||||
args = args.substr(end + 2);
|
||||
}
|
||||
} else {
|
||||
const end = args.indexOf(' ');
|
||||
if (end > 0) {
|
||||
command = args.substr(0, end);
|
||||
args = args.substr(end + 1);
|
||||
} else {
|
||||
args = '';
|
||||
}
|
||||
}
|
||||
one(pid, ppid, command, args, date);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
} else if (process.platform === 'darwin') { // OS X
|
||||
|
||||
proc = spawn('/bin/ps', ['-x', '-o', `pid,ppid,comm=${'a'.repeat(256)},command`]);
|
||||
proc.stdout.setEncoding('utf8');
|
||||
proc.stdout.on('data', lines(line => {
|
||||
|
||||
const pid = Number(line.substr(0, 5));
|
||||
const ppid = Number(line.substr(6, 5));
|
||||
const command = line.substr(12, 256).trim();
|
||||
const args = line.substr(269 + command.length);
|
||||
|
||||
if (!isNaN(pid) && !isNaN(ppid)) {
|
||||
one(pid, ppid, command, args);
|
||||
}
|
||||
}));
|
||||
|
||||
} else { // linux
|
||||
|
||||
proc = spawn('/bin/ps', ['-ax', '-o', 'pid,ppid,comm:20,command']);
|
||||
proc.stdout.setEncoding('utf8');
|
||||
proc.stdout.on('data', lines(line => {
|
||||
|
||||
const pid = Number(line.substr(0, 5));
|
||||
const ppid = Number(line.substr(6, 5));
|
||||
let command = line.substr(12, 20).trim();
|
||||
let args = line.substr(33);
|
||||
|
||||
let pos = args.indexOf(command);
|
||||
if (pos >= 0) {
|
||||
pos = pos + command.length;
|
||||
while (pos < args.length) {
|
||||
if (args[pos] === ' ') {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
command = args.substr(0, pos);
|
||||
args = args.substr(pos + 1);
|
||||
}
|
||||
|
||||
if (!isNaN(pid) && !isNaN(ppid)) {
|
||||
one(pid, ppid, command, args);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
proc.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
proc.stderr.setEncoding('utf8');
|
||||
proc.stderr.on('data', data => {
|
||||
reject(new Error(data.toString()));
|
||||
});
|
||||
|
||||
proc.on('close', (code, signal) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else if (code > 0) {
|
||||
reject(new Error(`process terminated with exit code: ${code}`));
|
||||
}
|
||||
if (signal) {
|
||||
reject(new Error(`process terminated with signal: ${signal}`));
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
//resolve();
|
||||
} else if (code > 0) {
|
||||
reject(new Error(`process terminated with exit code: ${code}`));
|
||||
}
|
||||
if (signal) {
|
||||
reject(new Error(`process terminated with signal: ${signal}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
58
extensions/debug-auto-launch/src/protocolDetection.ts
Normal file
58
extensions/debug-auto-launch/src/protocolDetection.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export const INSPECTOR_PORT_DEFAULT = 9229;
|
||||
export const LEGACY_PORT_DEFAULT = 5858;
|
||||
|
||||
export interface DebugArguments {
|
||||
usePort: boolean; // if true debug by using the debug port
|
||||
protocol?: 'legacy' | 'inspector';
|
||||
address?: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
/*
|
||||
* analyse the given command line arguments and extract debug port and protocol from it.
|
||||
*/
|
||||
export function analyseArguments(args: string): DebugArguments {
|
||||
|
||||
const DEBUG_FLAGS_PATTERN = /--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/;
|
||||
const DEBUG_PORT_PATTERN = /--(inspect|debug)-port=(\d+)/;
|
||||
|
||||
const result: DebugArguments = {
|
||||
usePort: false,
|
||||
port: -1
|
||||
};
|
||||
|
||||
// match --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, --inspect=1234, --inspect-brk, --inspect-brk=1234
|
||||
let matches = DEBUG_FLAGS_PATTERN.exec(args);
|
||||
if (matches && matches.length >= 2) {
|
||||
// attach via port
|
||||
result.usePort = true;
|
||||
if (matches.length >= 6 && matches[5]) {
|
||||
result.address = matches[5];
|
||||
}
|
||||
if (matches.length >= 7 && matches[6]) {
|
||||
result.port = parseInt(matches[6]);
|
||||
}
|
||||
result.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector';
|
||||
}
|
||||
|
||||
// a debug-port=1234 or --inspect-port=1234 overrides the port
|
||||
matches = DEBUG_PORT_PATTERN.exec(args);
|
||||
if (matches && matches.length === 3) {
|
||||
// override port
|
||||
result.port = parseInt(matches[2]);
|
||||
result.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector';
|
||||
}
|
||||
|
||||
if (result.port < 0) {
|
||||
result.port = result.protocol === 'inspector' ? INSPECTOR_PORT_DEFAULT : LEGACY_PORT_DEFAULT;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
7
extensions/debug-auto-launch/src/typings/ref.d.ts
vendored
Normal file
7
extensions/debug-auto-launch/src/typings/ref.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
16
extensions/debug-auto-launch/tsconfig.json
Normal file
16
extensions/debug-auto-launch/tsconfig.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"outDir": "./out",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
11
extensions/debug-auto-launch/yarn.lock
Normal file
11
extensions/debug-auto-launch/yarn.lock
Normal file
|
@ -0,0 +1,11 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@8.0.33":
|
||||
version "8.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
|
||||
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
14
extensions/emmet/CONTRIBUTING.md
Normal file
14
extensions/emmet/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
## How to build and run from source?
|
||||
|
||||
Read the basics about extension authoring from [Extending Visual Studio Code](https://code.visualstudio.com/docs/extensions/overview)
|
||||
|
||||
- Read [Build and Run VS Code from Source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run-from-source) to get a local dev set up running for VS Code
|
||||
- Open the `extensions/emmet` folder in the vscode repo in VS Code
|
||||
- Press F5 to start debugging
|
||||
|
||||
## Running tests
|
||||
|
||||
Tests for Emmet extension are run as integration tests as part of VS Code.
|
||||
|
||||
- Read [Build and Run VS Code from Source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run-from-source) to get a local dev set up running for VS Code
|
||||
- Run `./scripts/test-integration.sh` to run all the integrations tests that include the Emmet tests.
|
|
@ -4,22 +4,9 @@
|
|||
|
||||
## Features
|
||||
|
||||
See [Emmet in Visual Studio Code](https://code.visualstudio.com/docs/editor/emmet)
|
||||
See [Emmet in Visual Studio Code](https://code.visualstudio.com/docs/editor/emmet) to learn about the features of this extension.
|
||||
|
||||
## How to build and run from source?
|
||||
|
||||
Read the basics about extension authoring from [Extending Visual Studio Code](https://code.visualstudio.com/docs/extensions/overview)
|
||||
|
||||
- Read [Build and Run VS Code from Source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run-from-source) to get a local dev set up running for VS Code
|
||||
- Open the `extensions/emmet` folder in the vscode repo in VS Code
|
||||
- Press F5 to start debugging
|
||||
|
||||
## Running tests
|
||||
|
||||
Tests for Emmet extension are run as integration tests as part of VS Code.
|
||||
|
||||
- Read [Build and Run VS Code from Source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run-from-source) to get a local dev set up running for VS Code
|
||||
- Run `./scripts/test-integration.sh` to run all the integrations tests that include the Emmet tests.
|
||||
Please read the [CONTRIBUTING.md](https://github.com/Microsoft/vscode/blob/master/extensions/emmet/CONTRIBUTING.md) file to learn how to contribute to this extension.
|
||||
|
||||
|
||||
|
||||
|
|
37
extensions/html-language-features/CONTRIBUTING.md
Normal file
37
extensions/html-language-features/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
## Setup
|
||||
|
||||
- Clone [Microsoft/vscode](https://github.com/microsoft/vscode)
|
||||
- Run `yarn` at `/`, this will install
|
||||
- Dependencies for `/extension/html-language-features/`
|
||||
- Dependencies for `/extension/html-language-features/server/`
|
||||
- devDependencies such as `gulp`
|
||||
- Open `/extensions/html-language-features/` as the workspace in VS Code
|
||||
- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/html-language-features/.vscode/launch.json) debug target in the Debug View. This will:
|
||||
- Launch the `preLaunchTask` task to compile the extension
|
||||
- Launch a new VS Code instance with the `html-language-features` extension loaded
|
||||
- You should see a notification saying the development version of `html-language-features` overwrites the bundled version of `html-language-features`
|
||||
- Test the behavior of this extension by editing html files
|
||||
- Run `Reload Window` command in the launched instance to reload the extension
|
||||
|
||||
### Contribute to vscode-html-languageservice
|
||||
|
||||
[Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice) contains the language smarts for html.
|
||||
This extension wraps the html language service into a Language Server for VS Code.
|
||||
If you want to fix html issues or make improvements, you should make changes at [Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice).
|
||||
|
||||
However, within this extension, you can run a development version of `vscode-html-languageservice` to debug code or test language features interactively:
|
||||
|
||||
#### Linking `vscode-html-languageservice` in `html-language-features/server/`
|
||||
|
||||
- Clone [Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice)
|
||||
- Run `yarn` in `vscode-html-languageservice`
|
||||
- Run `yarn link` in `vscode-html-languageservice`. This will compile and link `vscode-html-languageservice`
|
||||
- In `html-language-features/server/`, run `npm link vscode-html-languageservice`
|
||||
|
||||
#### Testing the development version of `vscode-html-languageservice`
|
||||
|
||||
- Open both `vscode-html-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature
|
||||
- Run `yarn watch` at `html-languagefeatures/server/` to recompile this extension with the linked version of `vscode-html-languageservice`
|
||||
- Make some changes in `vscode-html-languageservice`
|
||||
- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-html-languageservice`. You can interactively test the language features.
|
||||
- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-html-languageservice` and `html-language-features/server/`
|
|
@ -4,48 +4,6 @@
|
|||
|
||||
## Features
|
||||
|
||||
This extension provides rich language support to HTML files.
|
||||
- auto-completion
|
||||
- linting
|
||||
- diagnostics
|
||||
- extra information on hover
|
||||
- navigation to definition and references
|
||||
- formatting
|
||||
See [HTML in Visual Studio Code](https://code.visualstudio.com/docs/languages/html) to learn about the features of this extension.
|
||||
|
||||
## Development
|
||||
|
||||
- Clone [Microsoft/vscode](https://github.com/microsoft/vscode)
|
||||
- Run `yarn` at `/`, this will install
|
||||
- Dependencies for `/extension/html-language-features/`
|
||||
- Dependencies for `/extension/html-language-features/server/`
|
||||
- devDependencies such as `gulp`
|
||||
- Open `/extensions/html-language-features/` as the workspace in VS Code
|
||||
- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/html-language-features/.vscode/launch.json) debug target in the Debug View. This will:
|
||||
- Launch the `preLaunchTask` task to compile the extension
|
||||
- Launch a new VS Code instance with the `html-language-features` extension loaded
|
||||
- You should see a notification saying the development version of `html-language-features` overwrites the bundled version of `html-language-features`
|
||||
- Test the behavior of this extension by editing html files
|
||||
- Run `Reload Window` command in the launched instance to reload the extension
|
||||
|
||||
### Contribute to vscode-html-languageservice
|
||||
|
||||
[Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice) contains the language smarts for html.
|
||||
This extension wraps the html language service into a Language Server for VS Code.
|
||||
If you want to fix html issues or make improvements, you should make changes at [Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice).
|
||||
|
||||
However, within this extension, you can run a development version of `vscode-html-languageservice` to debug code or test language features interactively:
|
||||
|
||||
#### Linking `vscode-html-languageservice` in `html-language-features/server/`
|
||||
|
||||
- Clone [Microsoft/vscode-html-languageservice](https://github.com/Microsoft/vscode-html-languageservice)
|
||||
- Run `yarn` in `vscode-html-languageservice`
|
||||
- Run `yarn link` in `vscode-html-languageservice`. This will compile and link `vscode-html-languageservice`
|
||||
- In `html-language-features/server/`, run `npm link vscode-html-languageservice`
|
||||
|
||||
#### Testing the development version of `vscode-html-languageservice`
|
||||
|
||||
- Open both `vscode-html-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature
|
||||
- Run `yarn watch` at `html-languagefeatures/server/` to recompile this extension with the linked version of `vscode-html-languageservice`
|
||||
- Make some changes in `vscode-html-languageservice`
|
||||
- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-html-languageservice`. You can interactively test the language features.
|
||||
- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-html-languageservice` and `html-language-features/server/`
|
||||
Please read the [CONTRIBUTING.md](https://github.com/Microsoft/vscode/blob/master/extensions/html-language-features/CONTRIBUTING.md) file to learn how to contribute to this extension.
|
||||
|
|
|
@ -4,8 +4,4 @@
|
|||
|
||||
## Features
|
||||
|
||||
This extension provides language support to JSON files.
|
||||
- auto-completion
|
||||
- diagnostics
|
||||
- extra information on hover
|
||||
- formatting
|
||||
See [JSON in Visual Studio Code](https://code.visualstudio.com/docs/languages/json) to learn about the features of this extension.
|
7
extensions/markdown-language-features/README.md
Normal file
7
extensions/markdown-language-features/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Language Features for Markdown files.
|
||||
|
||||
**Notice** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
## Features
|
||||
|
||||
See [Markdown in Visual Studio Code](https://code.visualstudio.com/docs/languages/markdown) to learn about the features of this extension.
|
7
extensions/php-language-features/README.md
Normal file
7
extensions/php-language-features/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Language Features for PHP files.
|
||||
|
||||
**Notice** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
## Features
|
||||
|
||||
See [PHP in Visual Studio Code](https://code.visualstudio.com/docs/languages/php) to learn about the features of this extension.
|
7
extensions/typescript-language-features/README.md
Normal file
7
extensions/typescript-language-features/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Language Features for Typescript and Javascript files.
|
||||
|
||||
**Notice** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
## Features
|
||||
|
||||
See [Typescript in Visual Studio Code](https://code.visualstudio.com/docs/languages/typescript) and [Javascript in Visual Studio Code](https://code.visualstudio.com/docs/languages/javascript) to learn about the features of this extension.
|
|
@ -16,6 +16,7 @@
|
|||
"Programming Languages"
|
||||
],
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^2.0.1",
|
||||
"semver": "4.3.6",
|
||||
"vscode-extension-telemetry": "0.0.17",
|
||||
"vscode-nls": "^3.2.4"
|
||||
|
@ -40,7 +41,11 @@
|
|||
"onCommand:javascript.goToProjectConfig",
|
||||
"onCommand:typescript.goToProjectConfig",
|
||||
"onCommand:typescript.openTsServerLog",
|
||||
"onCommand:workbench.action.tasks.runTask"
|
||||
"onCommand:workbench.action.tasks.runTask",
|
||||
"workspaceContains:**/tsconfig.json",
|
||||
"workspaceContains:**/jsconfig.json",
|
||||
"workspaceContains:**/tsconfig.*.json",
|
||||
"workspaceContains:**/jsconfig.*.json"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"contributes": {
|
||||
|
@ -676,4 +681,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import TypeScriptServiceClientHost from './typeScriptServiceClientHost';
|
||||
import { Command } from './utils/commandManager';
|
||||
import { Lazy } from './utils/lazy';
|
||||
import { openOrCreateConfigFile, isImplicitProjectConfigFile } from './utils/tsconfig';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './utils/tsconfig';
|
||||
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ export function activate(
|
|||
context.subscriptions.push(new TypeScriptTaskProviderManager(lazyClientHost.map(x => x.serviceClient)));
|
||||
context.subscriptions.push(new LanguageConfigurationManager());
|
||||
|
||||
import('./features/tsconfig').then(module => {
|
||||
context.subscriptions.push(module.register());
|
||||
});
|
||||
|
||||
const supportedLanguage = [].concat.apply([], standardLanguageDescriptions.map(x => x.modeIds).concat(plugins.map(x => x.languages)));
|
||||
function didOpenTextDocument(textDocument: vscode.TextDocument): boolean {
|
||||
if (isSupportedDocument(supportedLanguage, textDocument)) {
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Position, Event, EventEmitter } from 'vscode';
|
||||
import { CancellationToken, CodeLens, CodeLensProvider, Event, EventEmitter, Position, Range, TextDocument, Uri } from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import { escapeRegExp } from '../utils/regexp';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
export class ReferencesCodeLens extends CodeLens {
|
||||
constructor(
|
||||
|
|
|
@ -11,8 +11,8 @@ import API from '../utils/api';
|
|||
import { Delayer } from '../utils/async';
|
||||
import { disposeAll } from '../utils/dispose';
|
||||
import * as languageModeIds from '../utils/languageModeIds';
|
||||
import { ResourceMap } from '../utils/resourceMap';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import { ResourceMap } from './resourceMap';
|
||||
|
||||
enum BufferKind {
|
||||
TypeScript = 1,
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import TypingsStatus from '../utils/typingsStatus';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as Proto from '../protocol';
|
||||
import * as PConst from '../protocol.const';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { applyCodeAction } from '../utils/codeAction';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import * as Previewer from '../utils/previewer';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { applyCodeAction } from '../utils/codeAction';
|
||||
import { CommandManager, Command } from '../utils/commandManager';
|
||||
import TypingsStatus from '../utils/typingsStatus';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
import API from '../utils/api';
|
||||
|
||||
|
||||
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
@ -273,7 +273,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
|
|||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
context: vscode.CompletionContext
|
||||
): Promise<vscode.CompletionItem[]> {
|
||||
): Promise<vscode.CompletionItem[] | null> {
|
||||
if (this.typingsStatus.isAcquiringTypings) {
|
||||
return Promise.reject<vscode.CompletionItem[]>({
|
||||
label: localize(
|
||||
|
@ -287,14 +287,14 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
|
|||
|
||||
const file = this.client.toPath(document.uri);
|
||||
if (!file) {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
const line = document.lineAt(position.line);
|
||||
const completionConfiguration = CompletionConfiguration.getConfigurationForResource(document.uri);
|
||||
|
||||
if (!this.shouldTrigger(context, completionConfiguration, line, position)) {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token);
|
||||
|
@ -309,23 +309,20 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
|
|||
let msg: ReadonlyArray<Proto.CompletionEntry> | undefined = undefined;
|
||||
try {
|
||||
if (this.client.apiVersion.gte(API.v300)) {
|
||||
const response = await this.client.execute('completionInfo', args, token);
|
||||
if (!response.body) {
|
||||
return [];
|
||||
const { body } = await this.client.execute('completionInfo', args, token);
|
||||
if (!body || body.isNewIdentifierLocation) {
|
||||
return null;
|
||||
}
|
||||
if (response.body.isNewIdentifierLocation) {
|
||||
return [];
|
||||
}
|
||||
msg = response.body.entries;
|
||||
msg = body.entries;
|
||||
} else {
|
||||
const response = await this.client.execute('completions', args, token);
|
||||
if (!response.body) {
|
||||
return [];
|
||||
const { body } = await this.client.execute('completions', args, token);
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
msg = response.body;
|
||||
msg = body;
|
||||
}
|
||||
} catch {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
const enableDotCompletions = this.shouldEnableDotCompletions(document, position);
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, Position, CancellationToken, Location } from 'vscode';
|
||||
|
||||
import { CancellationToken, Location, Position, TextDocument } from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
export default class TypeScriptDefinitionProviderBase {
|
||||
constructor(
|
||||
protected readonly client: ITypeScriptServiceClient
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from './resourceMap';
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from '../utils/resourceMap';
|
||||
|
||||
export class DiagnosticSet {
|
||||
private _map = new ResourceMap<vscode.Diagnostic[]>();
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import API from '../utils/api';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
|
||||
class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider {
|
||||
public constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
import * as PConst from '../protocol.const';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import API from '../utils/api';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
const getSymbolKind = (kind: string): vscode.SymbolKind => {
|
||||
switch (kind) {
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { workspace as Workspace, FormattingOptions, TextDocument, CancellationToken, window, Disposable, workspace, WorkspaceConfiguration } from 'vscode';
|
||||
|
||||
import { CancellationToken, Disposable, FormattingOptions, TextDocument, window, workspace as Workspace, workspace, WorkspaceConfiguration } from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { isTypeScriptDocument } from '../utils/languageModeIds';
|
||||
|
||||
|
||||
function objsAreEqual<T>(a: T, b: T): boolean {
|
||||
let keys = Object.keys(a);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
|
@ -89,12 +89,12 @@ export default class FileConfigurationManager {
|
|||
return;
|
||||
}
|
||||
|
||||
this.formatOptions[key] = currentOptions;
|
||||
const args: Proto.ConfigureRequestArguments = {
|
||||
file,
|
||||
...currentOptions,
|
||||
};
|
||||
await this.client.execute('configure', args, token);
|
||||
this.formatOptions[key] = currentOptions;
|
||||
}
|
||||
|
||||
public reset() {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import API from '../utils/api';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider {
|
||||
public constructor(
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { tagsMarkdownPreview } from '../utils/previewer';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
class TypeScriptHoverProvider implements vscode.HoverProvider {
|
||||
|
||||
public constructor(
|
||||
|
|
|
@ -7,11 +7,11 @@ import * as vscode from 'vscode';
|
|||
import * as nls from 'vscode-nls';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import * as typeconverts from '../utils/typeConverters';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import API from '../utils/api';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ import { ITypeScriptServiceClient } from '../typescriptService';
|
|||
import API from '../utils/api';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import FormattingOptionsManager from './fileConfigurationManager';
|
||||
import TelemetryReporter from '../utils/telemetry';
|
||||
|
||||
|
||||
class ApplyRefactoringCommand implements Command {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import API from '../utils/api';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
class TypeScriptReferenceSupport implements vscode.ReferenceProvider {
|
||||
public constructor(
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
|
||||
class TypeScriptRenameProvider implements vscode.RenameProvider {
|
||||
public constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider';
|
||||
import { isImplicitProjectConfigFile } from '../utils/tsconfig';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Lazy } from '../utils/lazy';
|
||||
import { isImplicitProjectConfigFile } from '../utils/tsconfig';
|
||||
import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider';
|
||||
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
type AutoDetect = 'on' | 'off' | 'build' | 'watch';
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as jsonc from 'jsonc-parser';
|
||||
import { dirname, join } from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
class TsconfigLinkProvider implements vscode.DocumentLinkProvider {
|
||||
|
||||
public provideDocumentLinks(
|
||||
document: vscode.TextDocument,
|
||||
_token: vscode.CancellationToken
|
||||
): vscode.ProviderResult<vscode.DocumentLink[]> {
|
||||
const root = jsonc.parseTree(document.getText());
|
||||
if (!root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getNodes(root).map(node =>
|
||||
new vscode.DocumentLink(
|
||||
this.getRange(document, node),
|
||||
this.getTarget(document, node)));
|
||||
}
|
||||
|
||||
private getNodes(root: jsonc.Node): ReadonlyArray<jsonc.Node> {
|
||||
const nodes: jsonc.Node[] = [];
|
||||
const extendsNode = jsonc.findNodeAtLocation(root, ['extends']);
|
||||
if (this.isPathValue(extendsNode)) {
|
||||
nodes.push(extendsNode);
|
||||
}
|
||||
|
||||
const referencesNode = jsonc.findNodeAtLocation(root, ['references']);
|
||||
if (referencesNode && referencesNode.type === 'array' && referencesNode.children) {
|
||||
for (const child of referencesNode.children) {
|
||||
const path = jsonc.findNodeAtLocation(child, ['path']);
|
||||
if (this.isPathValue(path)) {
|
||||
nodes.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private isPathValue(extendsNode: jsonc.Node | undefined): extendsNode is jsonc.Node {
|
||||
return extendsNode && extendsNode.type === 'string' && extendsNode.value;
|
||||
}
|
||||
|
||||
private getTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri {
|
||||
return vscode.Uri.file(join(dirname(document.uri.fsPath), node!.value));
|
||||
}
|
||||
|
||||
private getRange(document: vscode.TextDocument, node: jsonc.Node) {
|
||||
const offset = node!.offset;
|
||||
const start = document.positionAt(offset + 1);
|
||||
const end = document.positionAt(offset + (node!.length - 1));
|
||||
return new vscode.Range(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
export function register() {
|
||||
const patterns: vscode.GlobPattern[] = [
|
||||
'**/[jt]sconfig.json',
|
||||
'**/[jt]sconfig.*.json',
|
||||
];
|
||||
|
||||
const languages = ['json', 'jsonc'];
|
||||
|
||||
const selector: vscode.DocumentSelector = ([] as any[]).concat(
|
||||
...languages.map(language => patterns.map((pattern): vscode.DocumentFilter => ({ language, pattern }))));
|
||||
return vscode.languages.registerDocumentLinkProvider(selector, new TsconfigLinkProvider());
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import DefinitionProviderBase from './definitionProviderBase';
|
||||
import API from '../utils/api';
|
||||
|
||||
export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider {
|
||||
public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken | boolean): Promise<vscode.Definition | undefined> {
|
||||
|
|
|
@ -3,20 +3,20 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { basename } from 'path';
|
||||
|
||||
import TypeScriptServiceClient from './typescriptServiceClient';
|
||||
import TypingsStatus from './utils/typingsStatus';
|
||||
import FileConfigurationManager from './features/fileConfigurationManager';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { DiagnosticsManager, DiagnosticKind } from './features/diagnostics';
|
||||
import { LanguageDescription } from './utils/languageDescription';
|
||||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import * as vscode from 'vscode';
|
||||
import { CachedNavTreeResponse } from './features/baseCodeLensProvider';
|
||||
import { memoize } from './utils/memoize';
|
||||
import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics';
|
||||
import FileConfigurationManager from './features/fileConfigurationManager';
|
||||
import TypeScriptServiceClient from './typescriptServiceClient';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { disposeAll } from './utils/dispose';
|
||||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import { LanguageDescription } from './utils/languageDescription';
|
||||
import { memoize } from './utils/memoize';
|
||||
import TelemetryReporter from './utils/telemetry';
|
||||
import TypingsStatus from './utils/typingsStatus';
|
||||
|
||||
|
||||
const validateSetting = 'validate.enable';
|
||||
const suggestionSetting = 'suggestionActions.enabled';
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Disposable, Memento, Range, Uri, workspace } from 'vscode';
|
||||
import { DiagnosticKind } from './features/diagnostics';
|
||||
import FileConfigurationManager from './features/fileConfigurationManager';
|
||||
import { UpdateImportsOnFileRenameHandler } from './features/updatePathsOnRename';
|
||||
import LanguageProvider from './languageProvider';
|
||||
import * as Proto from './protocol';
|
||||
import * as PConst from './protocol.const';
|
||||
|
@ -24,7 +25,6 @@ import { TypeScriptServerPlugin } from './utils/plugins';
|
|||
import * as typeConverters from './utils/typeConverters';
|
||||
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
|
||||
import VersionStatus from './utils/versionStatus';
|
||||
import { UpdateImportsOnFileRenameHandler } from './features/updatePathsOnRename';
|
||||
|
||||
// Style check diagnostics that can be reported as warnings
|
||||
const styleCheckDiagnostics = [
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, Uri, Event } from 'vscode';
|
||||
import { CancellationToken, Event, Uri } from 'vscode';
|
||||
import BufferSyncSupport from './features/bufferSyncSupport';
|
||||
import * as Proto from './protocol';
|
||||
import API from './utils/api';
|
||||
import { TypeScriptServerPlugin } from './utils/plugins';
|
||||
import { TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import Logger from './utils/logger';
|
||||
import BufferSyncSupport from './features/bufferSyncSupport';
|
||||
import { TypeScriptServerPlugin } from './utils/plugins';
|
||||
|
||||
export interface ITypeScriptServiceClient {
|
||||
/**
|
||||
|
|
|
@ -4,34 +4,34 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as electron from './utils/electron';
|
||||
import { Reader, ICallback } from './utils/wireProtocol';
|
||||
|
||||
import { workspace, window, Uri, CancellationToken, Disposable, Memento, MessageItem, EventEmitter, commands, env } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { CancellationToken, commands, Disposable, env, EventEmitter, Memento, MessageItem, Uri, window, workspace } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import BufferSyncSupport from './features/bufferSyncSupport';
|
||||
import { DiagnosticKind } from './features/diagnostics';
|
||||
import * as Proto from './protocol';
|
||||
import { ITypeScriptServiceClient } from './typescriptService';
|
||||
import { TypeScriptServerPlugin } from './utils/plugins';
|
||||
import Logger from './utils/logger';
|
||||
|
||||
import API from './utils/api';
|
||||
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import { disposeAll } from './utils/dispose';
|
||||
import * as electron from './utils/electron';
|
||||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import * as is from './utils/is';
|
||||
import LogDirectoryProvider from './utils/logDirectoryProvider';
|
||||
import Logger from './utils/logger';
|
||||
import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
|
||||
import { TypeScriptServerPlugin } from './utils/plugins';
|
||||
import TelemetryReporter from './utils/telemetry';
|
||||
import Tracer from './utils/tracer';
|
||||
import API from './utils/api';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { TypeScriptServiceConfiguration, TsServerLogLevel } from './utils/configuration';
|
||||
import { TypeScriptVersionProvider, TypeScriptVersion } from './utils/versionProvider';
|
||||
import { TypeScriptVersionPicker } from './utils/versionPicker';
|
||||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import { inferredProjectConfig } from './utils/tsconfig';
|
||||
import LogDirectoryProvider from './utils/logDirectoryProvider';
|
||||
import { disposeAll } from './utils/dispose';
|
||||
import { DiagnosticKind } from './features/diagnostics';
|
||||
import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
|
||||
import BufferSyncSupport from './features/bufferSyncSupport';
|
||||
import { TypeScriptVersionPicker } from './utils/versionPicker';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider';
|
||||
import { ICallback, Reader } from './utils/wireProtocol';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { WorkspaceEdit, workspace } from 'vscode';
|
||||
import { workspace, WorkspaceEdit } from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import * as typeConverters from './typeConverters';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { WorkspaceConfiguration, workspace } from 'vscode';
|
||||
import { workspace, WorkspaceConfiguration } from 'vscode';
|
||||
import * as arrays from './arrays';
|
||||
|
||||
export enum TsServerLogLevel {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OutputChannel, window } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as is from './is';
|
||||
import { memoize } from './memoize';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export default class Logger {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'path';
|
||||
import { workspace } from 'vscode';
|
||||
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
import { RelativeWorkspacePathResolver } from './relativePathResolver';
|
||||
|
||||
|
||||
export class TypeScriptPluginPathsProvider {
|
||||
public readonly relativePathResolver: RelativeWorkspacePathResolver = new RelativeWorkspacePathResolver();
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
import { MarkdownString } from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
|
||||
function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
if (!tag.text) {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { loadMessageBundle } from 'vscode-nls';
|
||||
import { openOrCreateConfigFile, isImplicitProjectConfigFile } from './tsconfig';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import TelemetryReporter from './telemetry';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './tsconfig';
|
||||
|
||||
const localize = loadMessageBundle();
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import { Uri } from 'vscode';
|
||||
import { memoize } from '../utils/memoize';
|
||||
import { getTempFile } from '../utils/temp';
|
||||
import { memoize } from './memoize';
|
||||
import { getTempFile } from './temp';
|
||||
|
||||
/**
|
||||
* Maps of file resources
|
|
@ -4,11 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { workspace } from 'vscode';
|
||||
|
||||
import * as Proto from '../protocol';
|
||||
import Logger from './logger';
|
||||
|
||||
|
||||
|
||||
enum Trace {
|
||||
Off,
|
||||
Messages,
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as Proto from '../protocol';
|
||||
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
|
||||
|
||||
export function isImplicitProjectConfigFile(configFileName: string) {
|
||||
return configFileName.indexOf('/dev/null/') === 0;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MessageItem, workspace, Disposable, ProgressLocation, window } from 'vscode';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { Disposable, MessageItem, ProgressLocation, window, workspace } from 'vscode';
|
||||
import { loadMessageBundle } from 'vscode-nls';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
|
||||
const localize = loadMessageBundle();
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, Memento, QuickPickItem, Uri, window, workspace } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { TypeScriptVersionProvider, TypeScriptVersion } from './versionProvider';
|
||||
import { Memento, commands, Uri, window, QuickPickItem, workspace } from 'vscode';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from './versionProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { workspace, window } from 'vscode';
|
||||
|
||||
import * as path from 'path';
|
||||
import { window, workspace } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import API from './api';
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
import { RelativeWorkspacePathResolver } from './relativePathResolver';
|
||||
import API from './api';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export class TypeScriptVersion {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { TypeScriptVersion } from './versionProvider';
|
||||
import * as languageModeIds from './languageModeIds';
|
||||
import { TypeScriptVersion } from './versionProvider';
|
||||
|
||||
export default class VersionStatus {
|
||||
private readonly _onChangeEditorSub: vscode.Disposable;
|
||||
|
|
|
@ -835,6 +835,10 @@ json-stringify-safe@~5.0.1:
|
|||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
|
||||
jsonc-parser@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60"
|
||||
|
||||
jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"native-watchdog": "0.3.0",
|
||||
"node-pty": "0.7.6",
|
||||
"semver": "^5.5.0",
|
||||
"spdlog": "0.6.0",
|
||||
"spdlog": "0.7.1",
|
||||
"sudo-prompt": "8.2.0",
|
||||
"v8-inspect-profiler": "^0.0.8",
|
||||
"vscode-chokidar": "1.6.2",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"vscode-nsfw": "1.0.17",
|
||||
"vscode-ripgrep": "^1.0.1",
|
||||
"vscode-textmate": "^4.0.1",
|
||||
"vscode-xterm": "3.6.0-beta2",
|
||||
"vscode-xterm": "3.6.0-beta3",
|
||||
"yauzl": "^2.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Use custom CSS vars to expose padding into parent select for padding calculation */
|
||||
.monaco-select-box-dropdown-padding {
|
||||
--dropdown-padding-top: 1px;
|
||||
--dropdown-padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.hc-black .monaco-select-box-dropdown-padding {
|
||||
--dropdown-padding-top: 3px;
|
||||
--dropdown-padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.monaco-select-box-dropdown-container {
|
||||
display: none;
|
||||
}
|
||||
|
@ -18,8 +29,8 @@
|
|||
.monaco-select-box-dropdown-container > .select-box-dropdown-list-container {
|
||||
flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
padding-bottom: 1px;
|
||||
padding-top: 1px;
|
||||
padding-top: var(--dropdown-padding-top);
|
||||
padding-bottom: var(--dropdown-padding-bottom);
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
width: 100%;
|
||||
|
@ -32,8 +43,8 @@
|
|||
}
|
||||
|
||||
.hc-black .monaco-select-box-dropdown-container > .select-box-dropdown-list-container {
|
||||
padding-bottom: 4px;
|
||||
padding-top: 3px;
|
||||
padding-top: var(--dropdown-padding-top);
|
||||
padding-bottom: var(--dropdown-padding-bottom);
|
||||
}
|
||||
|
||||
.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-text {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes';
|
|||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IContextViewProvider, AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
|
@ -79,6 +79,8 @@ class SelectListRenderer implements IRenderer<ISelectOptionItem, ISelectListTemp
|
|||
export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISelectOptionItem> {
|
||||
|
||||
private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32;
|
||||
private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 42;
|
||||
private static readonly DEFAULT_MINIMUM_VISIBLE_OPTIONS = 3;
|
||||
|
||||
private _isVisible: boolean;
|
||||
private selectBoxOptions: ISelectBoxOptions;
|
||||
|
@ -97,6 +99,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
private selectDropDownListContainer: HTMLElement;
|
||||
private widthControlElement: HTMLElement;
|
||||
private _currentSelection: number;
|
||||
private _dropDownPosition: AnchorPosition;
|
||||
|
||||
constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) {
|
||||
|
||||
|
@ -111,7 +114,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
}
|
||||
|
||||
this.selectElement = document.createElement('select');
|
||||
this.selectElement.className = 'monaco-select-box';
|
||||
// Use custom CSS vars for padding calculation
|
||||
this.selectElement.className = 'monaco-select-box monaco-select-box-dropdown-padding';
|
||||
|
||||
this._onDidSelect = new Emitter<ISelectData>();
|
||||
this.styles = styles;
|
||||
|
@ -137,7 +141,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
// SetUp ContextView container to hold select Dropdown
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
this.selectDropDownContainer = dom.$('.monaco-select-box-dropdown-container');
|
||||
|
||||
// Use custom CSS vars for padding calculation (shared with parent select)
|
||||
dom.addClass(this.selectDropDownContainer, 'monaco-select-box-dropdown-padding');
|
||||
// Setup list for drop-down select
|
||||
this.createSelectList(this.selectDropDownContainer);
|
||||
|
||||
|
@ -148,6 +153,9 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
this.widthControlElement.className = 'option-text-width-control';
|
||||
dom.append(widthControlInnerDiv, this.widthControlElement);
|
||||
|
||||
// Always default to below position
|
||||
this._dropDownPosition = AnchorPosition.BELOW;
|
||||
|
||||
// Inline stylesheet for themes
|
||||
this.styleElement = dom.createStyleSheet(this.selectDropDownContainer);
|
||||
}
|
||||
|
@ -363,7 +371,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
return option;
|
||||
}
|
||||
|
||||
// Non-native select list handling
|
||||
// ContextView dropdown methods
|
||||
|
||||
private showSelectDropDown() {
|
||||
|
@ -371,8 +378,12 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
return;
|
||||
}
|
||||
|
||||
// Set drop-down position above/below from required height and margins
|
||||
this.layoutSelectDropDown(true);
|
||||
|
||||
this._isVisible = true;
|
||||
this.cloneElementFont(this.selectElement, this.selectDropDownContainer);
|
||||
|
||||
this.contextViewProvider.showContextView({
|
||||
getAnchor: () => this.selectElement,
|
||||
render: (container: HTMLElement) => this.renderSelectDropDown(container),
|
||||
|
@ -380,8 +391,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
onHide: () => {
|
||||
dom.toggleClass(this.selectDropDownContainer, 'visible', false);
|
||||
dom.toggleClass(this.selectElement, 'synthetic-focus', false);
|
||||
}
|
||||
},
|
||||
anchorPosition: this._dropDownPosition
|
||||
});
|
||||
|
||||
// Track initial selection the case user escape, blur
|
||||
this._currentSelection = this.selected;
|
||||
}
|
||||
|
||||
|
@ -407,36 +421,62 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
};
|
||||
}
|
||||
|
||||
private layoutSelectDropDown() {
|
||||
private layoutSelectDropDown(preLayoutPosition?: boolean) {
|
||||
|
||||
// Layout ContextView drop down select list and container
|
||||
// Have to manage our vertical overflow, sizing
|
||||
// Need to be visible to measure
|
||||
// Have to manage our vertical overflow, sizing, position below or above
|
||||
// Position has to be determined and set prior to contextView instantiation
|
||||
|
||||
dom.toggleClass(this.selectDropDownContainer, 'visible', true);
|
||||
|
||||
const selectWidth = dom.getTotalWidth(this.selectElement);
|
||||
const selectPosition = dom.getDomNodePagePosition(this.selectElement);
|
||||
|
||||
// Set container height to max from select bottom to margin (default/minBottomMargin)
|
||||
let maxSelectDropDownHeight = (window.innerHeight - selectPosition.top - selectPosition.height - this.selectBoxOptions.minBottomMargin);
|
||||
|
||||
if (maxSelectDropDownHeight < 0) {
|
||||
maxSelectDropDownHeight = 0;
|
||||
}
|
||||
|
||||
// SetUp list dimensions and layout - account for container padding
|
||||
if (this.selectList) {
|
||||
|
||||
const selectPosition = dom.getDomNodePagePosition(this.selectElement);
|
||||
const styles = getComputedStyle(this.selectElement);
|
||||
const verticalPadding = parseFloat(styles.getPropertyValue('--dropdown-padding-top')) + parseFloat(styles.getPropertyValue('--dropdown-padding-bottom'));
|
||||
let maxSelectDropDownHeight = 0;
|
||||
maxSelectDropDownHeight = (window.innerHeight - selectPosition.top - selectPosition.height - this.selectBoxOptions.minBottomMargin);
|
||||
|
||||
this.selectList.layout();
|
||||
let listHeight = this.selectList.contentHeight;
|
||||
const listContainerHeight = dom.getTotalHeight(this.selectDropDownListContainer);
|
||||
const totalVerticalListPadding = listContainerHeight - listHeight;
|
||||
|
||||
// Always show complete list items - never more than Max available vertical height
|
||||
if (listContainerHeight > maxSelectDropDownHeight) {
|
||||
listHeight = ((Math.floor((maxSelectDropDownHeight - totalVerticalListPadding) / this.getHeight())) * this.getHeight());
|
||||
// If we are only doing pre-layout check/adjust position only
|
||||
// Calculate vertical space available, flip up if insufficient
|
||||
// Use reflected padding on parent select, ContextView style properties not available before DOM attachment
|
||||
if (preLayoutPosition) {
|
||||
|
||||
// Always show complete list items - never more than Max available vertical height
|
||||
if (listHeight + verticalPadding > maxSelectDropDownHeight) {
|
||||
const maxVisibleOptions = ((Math.floor((maxSelectDropDownHeight - verticalPadding) / this.getHeight())));
|
||||
|
||||
// Check if we can at least show min items otherwise flip above
|
||||
if (maxVisibleOptions < SelectBoxList.DEFAULT_MINIMUM_VISIBLE_OPTIONS) {
|
||||
this._dropDownPosition = AnchorPosition.ABOVE;
|
||||
} else {
|
||||
this._dropDownPosition = AnchorPosition.BELOW;
|
||||
}
|
||||
}
|
||||
// Do full layout on showSelectDropDown only
|
||||
return;
|
||||
}
|
||||
|
||||
// Make visible to enable measurements
|
||||
dom.toggleClass(this.selectDropDownContainer, 'visible', true);
|
||||
|
||||
// SetUp list dimensions and layout - account for container padding
|
||||
// Use position to check above or below available space
|
||||
if (this._dropDownPosition === AnchorPosition.BELOW) {
|
||||
// Set container height to max from select bottom to margin (default/minBottomMargin)
|
||||
if (listHeight + verticalPadding > maxSelectDropDownHeight) {
|
||||
listHeight = ((Math.floor((maxSelectDropDownHeight - verticalPadding) / this.getHeight())) * this.getHeight());
|
||||
}
|
||||
} else {
|
||||
// Set container height to max from select top to margin (default/minTopMargin)
|
||||
maxSelectDropDownHeight = (selectPosition.top - SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN);
|
||||
if (listHeight + verticalPadding > maxSelectDropDownHeight) {
|
||||
listHeight = ((Math.floor((maxSelectDropDownHeight - SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN) / this.getHeight())) * this.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
// Set adjusted list height and relayout
|
||||
this.selectList.layout(listHeight);
|
||||
this.selectList.domFocus();
|
||||
|
||||
|
@ -447,13 +487,14 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
|
|||
}
|
||||
|
||||
// Set final container height after adjustments
|
||||
this.selectDropDownContainer.style.height = (listHeight + totalVerticalListPadding) + 'px';
|
||||
this.selectDropDownContainer.style.height = (listHeight + verticalPadding) + 'px';
|
||||
|
||||
// Determine optimal width - min(longest option), opt(parent select), max(ContextView controlled)
|
||||
const selectWidth = dom.getTotalWidth(this.selectElement);
|
||||
const selectMinWidth = this.setWidthControlElement(this.widthControlElement);
|
||||
const selectOptimalWidth = Math.max(selectMinWidth, Math.round(selectWidth)).toString() + 'px';
|
||||
|
||||
this.selectDropDownContainer.style.minWidth = selectOptimalWidth;
|
||||
this.selectDropDownContainer.style.width = selectOptimalWidth;
|
||||
|
||||
// Maintain focus outline on parent select as well as list container - tabindex for focus
|
||||
this.selectDropDownListContainer.setAttribute('tabindex', '0');
|
||||
|
|
|
@ -413,6 +413,7 @@ export interface IChainableEvent<T> {
|
|||
filter(fn: (e: T) => boolean): IChainableEvent<T>;
|
||||
latch(): IChainableEvent<T>;
|
||||
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
}
|
||||
|
||||
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
|
@ -454,6 +455,10 @@ class ChainableEvent<T> implements IChainableEvent<T> {
|
|||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return this._event(listener, thisArgs, disposables);
|
||||
}
|
||||
|
||||
once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return once(this._event)(listener, thisArgs, disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export function chain<T>(event: Event<T>): IChainableEvent<T> {
|
||||
|
|
|
@ -247,8 +247,8 @@ const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else
|
|||
|
||||
export type ParsedPattern = (path: string, basename?: string) => boolean;
|
||||
|
||||
// The ParsedExpression returns a Promise iff siblingsFn returns a Promise.
|
||||
export type ParsedExpression = (path: string, basename?: string, siblingsFn?: () => string[] | TPromise<string[]>) => string | TPromise<string> /* the matching pattern */;
|
||||
// The ParsedExpression returns a Promise iff hasSibling returns a Promise.
|
||||
export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>) => string | TPromise<string> /* the matching pattern */;
|
||||
|
||||
export interface IGlobOptions {
|
||||
/**
|
||||
|
@ -264,9 +264,8 @@ interface ParsedStringPattern {
|
|||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
}
|
||||
type SiblingsPattern = { siblings: string[], name: string };
|
||||
interface ParsedExpressionPattern {
|
||||
(path: string, basename: string, siblingsPatternFn: () => SiblingsPattern | TPromise<SiblingsPattern>): string | TPromise<string> /* the matching pattern */;
|
||||
(path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise<boolean>): string | TPromise<string> /* the matching pattern */;
|
||||
requiresSiblings?: boolean;
|
||||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
|
@ -436,13 +435,13 @@ function toRegExp(pattern: string): ParsedStringPattern {
|
|||
* - character ranges (using [...])
|
||||
*/
|
||||
export function match(pattern: string | IRelativePattern, path: string): boolean;
|
||||
export function match(expression: IExpression, path: string, siblingsFn?: () => string[]): string /* the matching pattern */;
|
||||
export function match(arg1: string | IExpression | IRelativePattern, path: string, siblingsFn?: () => string[]): any {
|
||||
export function match(expression: IExpression, path: string, hasSibling?: (name: string) => boolean): string /* the matching pattern */;
|
||||
export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): any {
|
||||
if (!arg1 || !path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse(<IExpression>arg1)(path, undefined, siblingsFn);
|
||||
return parse(<IExpression>arg1)(path, undefined, hasSibling);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -482,6 +481,44 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG
|
|||
return parsedExpression(<IExpression>arg1, options);
|
||||
}
|
||||
|
||||
export function hasSiblingPromiseFn(siblingsFn?: () => TPromise<string[]>) {
|
||||
if (!siblingsFn) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let siblings: TPromise<Record<string, true>>;
|
||||
return (name: string) => {
|
||||
if (!siblings) {
|
||||
siblings = (siblingsFn() || TPromise.as([]))
|
||||
.then(list => list ? listToMap(list) : {});
|
||||
}
|
||||
return siblings.then(map => !!map[name]);
|
||||
};
|
||||
}
|
||||
|
||||
export function hasSiblingFn(siblingsFn?: () => string[]) {
|
||||
if (!siblingsFn) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let siblings: Record<string, true>;
|
||||
return (name: string) => {
|
||||
if (!siblings) {
|
||||
const list = siblingsFn();
|
||||
siblings = list ? listToMap(list) : {};
|
||||
}
|
||||
return !!siblings[name];
|
||||
};
|
||||
}
|
||||
|
||||
function listToMap(list: string[]) {
|
||||
const map: Record<string, true> = {};
|
||||
for (const key of list) {
|
||||
map[key] = true;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
export function isRelativePattern(obj: any): obj is IRelativePattern {
|
||||
const rp = obj as IRelativePattern;
|
||||
|
||||
|
@ -493,8 +530,8 @@ export function isRelativePattern(obj: any): obj is IRelativePattern {
|
|||
*/
|
||||
export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression {
|
||||
const parsedExpression = parse(expression, options);
|
||||
return (path: string, basename?: string, siblingsFn?: () => string[] | TPromise<string[]>): string | TPromise<string> => {
|
||||
const result = parsedExpression(path, basename, siblingsFn);
|
||||
return (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>): string | TPromise<string> => {
|
||||
const result = parsedExpression(path, basename, hasSibling);
|
||||
return result instanceof TPromise ? result : TPromise.as(result);
|
||||
};
|
||||
}
|
||||
|
@ -522,7 +559,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
|||
return <ParsedStringPattern>parsedPatterns[0];
|
||||
}
|
||||
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string, siblingsFn?: () => string[]) {
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string) {
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
// Pattern matches path
|
||||
const result = (<ParsedStringPattern>parsedPatterns[i])(path, basename);
|
||||
|
@ -547,38 +584,21 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
|||
return resultExpression;
|
||||
}
|
||||
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string, siblingsFn?: () => string[] | TPromise<string[]>) {
|
||||
let siblingsPattern: SiblingsPattern | TPromise<SiblingsPattern>;
|
||||
let siblingsResolved = !siblingsFn;
|
||||
|
||||
function siblingsToSiblingsPattern(siblings: string[]) {
|
||||
if (siblings && siblings.length) {
|
||||
if (!basename) {
|
||||
basename = paths.basename(path);
|
||||
}
|
||||
const name = basename.substr(0, basename.length - paths.extname(path).length);
|
||||
return { siblings, name };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function siblingsPatternFn() {
|
||||
// Resolve siblings only once
|
||||
if (!siblingsResolved) {
|
||||
siblingsResolved = true;
|
||||
const siblings = siblingsFn();
|
||||
siblingsPattern = TPromise.is(siblings) ?
|
||||
siblings.then(siblingsToSiblingsPattern) :
|
||||
siblingsToSiblingsPattern(siblings);
|
||||
}
|
||||
|
||||
return siblingsPattern;
|
||||
}
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | TPromise<boolean>) {
|
||||
let name: string;
|
||||
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
// Pattern matches path
|
||||
const result = (<ParsedExpressionPattern>parsedPatterns[i])(path, basename, siblingsPatternFn);
|
||||
const parsedPattern = (<ParsedExpressionPattern>parsedPatterns[i]);
|
||||
if (parsedPattern.requiresSiblings && hasSibling) {
|
||||
if (!basename) {
|
||||
basename = paths.basename(path);
|
||||
}
|
||||
if (!name) {
|
||||
name = basename.substr(0, basename.length - paths.extname(path).length);
|
||||
}
|
||||
}
|
||||
const result = parsedPattern(path, basename, name, hasSibling);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
@ -619,28 +639,16 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio
|
|||
if (value) {
|
||||
const when = (<SiblingClause>value).when;
|
||||
if (typeof when === 'string') {
|
||||
const siblingsPatternToMatchingPattern = (siblingsPattern: SiblingsPattern): string => {
|
||||
let clausePattern = when.replace('$(basename)', siblingsPattern.name);
|
||||
if (siblingsPattern.siblings.indexOf(clausePattern) !== -1) {
|
||||
return pattern;
|
||||
} else {
|
||||
return null; // pattern does not match in the end because the when clause is not satisfied
|
||||
}
|
||||
};
|
||||
|
||||
const result: ParsedExpressionPattern = (path: string, basename: string, siblingsPatternFn: () => SiblingsPattern | TPromise<SiblingsPattern>) => {
|
||||
if (!parsedPattern(path, basename)) {
|
||||
const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise<boolean>) => {
|
||||
if (!hasSibling || !parsedPattern(path, basename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const siblingsPattern = siblingsPatternFn();
|
||||
if (!siblingsPattern) {
|
||||
return null; // pattern is malformed or we don't have siblings
|
||||
}
|
||||
|
||||
return TPromise.is(siblingsPattern) ?
|
||||
siblingsPattern.then(siblingsPatternToMatchingPattern) :
|
||||
siblingsPatternToMatchingPattern(siblingsPattern);
|
||||
const clausePattern = when.replace('$(basename)', name);
|
||||
const matched = hasSibling(clausePattern);
|
||||
return TPromise.is(matched) ?
|
||||
matched.then(m => m ? pattern : null) :
|
||||
matched ? pattern : null;
|
||||
};
|
||||
result.requiresSiblings = true;
|
||||
return result;
|
||||
|
|
|
@ -430,6 +430,7 @@ suite('Glob', () => {
|
|||
|
||||
test('expression support (single)', function () {
|
||||
let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js'];
|
||||
let hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
|
||||
// { "**/*.js": { "when": "$(basename).ts" } }
|
||||
let expression: glob.IExpression = {
|
||||
|
@ -438,9 +439,9 @@ suite('Glob', () => {
|
|||
}
|
||||
};
|
||||
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings));
|
||||
assert.strictEqual(glob.match(expression, 'test.js', () => []), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.js', () => ['te.ts']), null);
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', hasSibling));
|
||||
assert.strictEqual(glob.match(expression, 'test.js', () => false), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.js', name => name === 'te.ts'), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.js'), null);
|
||||
|
||||
expression = {
|
||||
|
@ -449,22 +450,23 @@ suite('Glob', () => {
|
|||
}
|
||||
};
|
||||
|
||||
assert.strictEqual(glob.match(expression, 'test.js', () => siblings), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.js', hasSibling), null);
|
||||
|
||||
expression = {
|
||||
'**/*.js': {
|
||||
}
|
||||
};
|
||||
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings));
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', hasSibling));
|
||||
|
||||
expression = {};
|
||||
|
||||
assert.strictEqual(glob.match(expression, 'test.js', () => siblings), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.js', hasSibling), null);
|
||||
});
|
||||
|
||||
test('expression support (multiple)', function () {
|
||||
let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js'];
|
||||
let hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
|
||||
// { "**/*.js": { "when": "$(basename).ts" } }
|
||||
let expression: glob.IExpression = {
|
||||
|
@ -474,11 +476,11 @@ suite('Glob', () => {
|
|||
'**/*.bananas': { bananas: true }
|
||||
};
|
||||
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings));
|
||||
assert.strictEqual('**/*.as', glob.match(expression, 'test.as', () => siblings));
|
||||
assert.strictEqual('**/*.bananas', glob.match(expression, 'test.bananas', () => siblings));
|
||||
assert.strictEqual('**/*.js', glob.match(expression, 'test.js', hasSibling));
|
||||
assert.strictEqual('**/*.as', glob.match(expression, 'test.as', hasSibling));
|
||||
assert.strictEqual('**/*.bananas', glob.match(expression, 'test.bananas', hasSibling));
|
||||
assert.strictEqual('**/*.bananas', glob.match(expression, 'test.bananas'));
|
||||
assert.strictEqual(glob.match(expression, 'test.foo', () => siblings), null);
|
||||
assert.strictEqual(glob.match(expression, 'test.foo', hasSibling), null);
|
||||
});
|
||||
|
||||
test('brackets', function () {
|
||||
|
@ -713,15 +715,16 @@ suite('Glob', () => {
|
|||
'**/*.js': { when: '$(basename).ts' }
|
||||
};
|
||||
|
||||
let sibilings = () => ['foo.ts', 'foo.js', 'foo', 'bar'];
|
||||
let siblings = ['foo.ts', 'foo.js', 'foo', 'bar'];
|
||||
let hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
|
||||
assert.strictEqual(glob.match(expr, 'bar', sibilings), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo', sibilings), null);
|
||||
assert.strictEqual(glob.match(expr, 'foo/bar', sibilings), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo\\bar', sibilings), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo/foo', sibilings), null);
|
||||
assert.strictEqual(glob.match(expr, 'foo.js', sibilings), '**/*.js');
|
||||
assert.strictEqual(glob.match(expr, 'bar.js', sibilings), null);
|
||||
assert.strictEqual(glob.match(expr, 'bar', hasSibling), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo', hasSibling), null);
|
||||
assert.strictEqual(glob.match(expr, 'foo/bar', hasSibling), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo\\bar', hasSibling), '**/bar');
|
||||
assert.strictEqual(glob.match(expr, 'foo/foo', hasSibling), null);
|
||||
assert.strictEqual(glob.match(expr, 'foo.js', hasSibling), '**/*.js');
|
||||
assert.strictEqual(glob.match(expr, 'bar.js', hasSibling), null);
|
||||
});
|
||||
|
||||
test('expression with multipe basename globs', function () {
|
||||
|
@ -766,10 +769,11 @@ suite('Glob', () => {
|
|||
assert.strictEqual(glob.parse('{**/baz,**/foo}')('baz/foo', 'foo'), true);
|
||||
|
||||
let expr = { '**/*.js': { when: '$(basename).ts' } };
|
||||
let sibilings = () => ['foo.ts', 'foo.js'];
|
||||
let siblings = ['foo.ts', 'foo.js'];
|
||||
let hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
|
||||
assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', sibilings), null);
|
||||
assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', sibilings), '**/*.js');
|
||||
assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', hasSibling), null);
|
||||
assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', hasSibling), '**/*.js');
|
||||
});
|
||||
|
||||
test('expression/pattern basename terms', function () {
|
||||
|
@ -809,7 +813,8 @@ suite('Glob', () => {
|
|||
['bar/nope', null]
|
||||
]);
|
||||
|
||||
const siblingsFn = () => ['baz', 'baz.zip', 'nope'];
|
||||
const siblings = ['baz', 'baz.zip', 'nope'];
|
||||
const hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
testOptimizationForBasenames({
|
||||
'**/foo/**': { when: '$(basename).zip' },
|
||||
'**/bar/**': true
|
||||
|
@ -820,12 +825,12 @@ suite('Glob', () => {
|
|||
['foo/bar', '**/bar/**'],
|
||||
], [
|
||||
null,
|
||||
siblingsFn,
|
||||
siblingsFn
|
||||
hasSibling,
|
||||
hasSibling
|
||||
]);
|
||||
});
|
||||
|
||||
function testOptimizationForBasenames(pattern: string | glob.IExpression, basenameTerms: string[], matches: [string, string | boolean][], siblingsFns: (() => string[])[] = []) {
|
||||
function testOptimizationForBasenames(pattern: string | glob.IExpression, basenameTerms: string[], matches: [string, string | boolean][], siblingsFns: ((name: string) => boolean)[] = []) {
|
||||
const parsed = glob.parse(<glob.IExpression>pattern, { trimForExclusions: true });
|
||||
assert.deepStrictEqual(glob.getBasenameTerms(parsed), basenameTerms);
|
||||
matches.forEach(([text, result], i) => {
|
||||
|
@ -914,7 +919,8 @@ suite('Glob', () => {
|
|||
[nativeSep('/foo/bar/nope'), null]
|
||||
]);
|
||||
|
||||
const siblingsFn = () => ['baz', 'baz.zip', 'nope'];
|
||||
const siblings = ['baz', 'baz.zip', 'nope'];
|
||||
let hasSibling = name => siblings.indexOf(name) !== -1;
|
||||
testOptimizationForPaths({
|
||||
'**/foo/123/**': { when: '$(basename).zip' },
|
||||
'**/bar/123/**': true
|
||||
|
@ -925,12 +931,12 @@ suite('Glob', () => {
|
|||
[nativeSep('foo/bar/123'), '**/bar/123/**'],
|
||||
], [
|
||||
null,
|
||||
siblingsFn,
|
||||
siblingsFn
|
||||
hasSibling,
|
||||
hasSibling
|
||||
]);
|
||||
});
|
||||
|
||||
function testOptimizationForPaths(pattern: string | glob.IExpression, pathTerms: string[], matches: [string, string | boolean][], siblingsFns: (() => string[])[] = []) {
|
||||
function testOptimizationForPaths(pattern: string | glob.IExpression, pathTerms: string[], matches: [string, string | boolean][], siblingsFns: ((name: string) => boolean)[] = []) {
|
||||
const parsed = glob.parse(<glob.IExpression>pattern, { trimForExclusions: true });
|
||||
assert.deepStrictEqual(glob.getPathTerms(parsed), pathTerms);
|
||||
matches.forEach(([text, result], i) => {
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isFalsyOrEmpty, mergeSort, flatten } from 'vs/base/common/arrays';
|
||||
import { flatten, isFalsyOrEmpty, mergeSort } from 'vs/base/common/arrays';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { illegalArgument, onUnexpectedExternalError, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionProviderRegistry, CodeActionContext, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
|
||||
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Selection, trigger?: CodeActionTrigger, token: CancellationToken = CancellationToken.None): Promise<CodeAction[]> {
|
||||
const codeActionContext: CodeActionContext = {
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { CodeAction } from 'vs/editor/common/modes';
|
||||
|
@ -18,14 +21,11 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
|
|||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { CodeActionModel, CodeActionsComputeEvent, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
|
||||
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger';
|
||||
import { CodeActionContextMenu } from './codeActionWidget';
|
||||
import { LightBulbWidget } from './lightBulbWidget';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { CancelablePromise } from 'vs/base/common/async';
|
||||
|
||||
function contextKeyForSupportedActions(kind: CodeActionKind) {
|
||||
return ContextKeyExpr.regex(
|
||||
|
@ -122,7 +122,9 @@ export class QuickFixController implements IEditorContribution {
|
|||
}
|
||||
|
||||
private _handleLightBulbSelect(coords: { x: number, y: number }): void {
|
||||
this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords);
|
||||
if (this._lightBulbWidget.model.actions) {
|
||||
this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords);
|
||||
}
|
||||
}
|
||||
|
||||
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Thenable<CodeAction[] | undefined> {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { SourceAction, QuickFixController, QuickFixAction, CodeActionCommand, RefactorAction, OrganizeImportsAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
|
||||
|
||||
registerEditorContribution(QuickFixController);
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event, debounceEvent } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -14,10 +15,9 @@ import { Selection } from 'vs/editor/common/core/selection';
|
|||
import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { getCodeActions } from './codeAction';
|
||||
import { CodeActionTrigger } from './codeActionTrigger';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async';
|
||||
|
||||
export const SUPPORTED_CODE_ACTIONS = new RawContextKey<string>('supportedCodeAction', '');
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ import * as dom from 'vs/base/browser/dom';
|
|||
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import 'vs/css!./lightBulbWidget';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { CodeActionsComputeEvent } from './codeActionModel';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
|
||||
import { CodeActionsComputeEvent } from './codeActionModel';
|
||||
|
||||
export class LightBulbWidget implements IDisposable, IContentWidget {
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { CodeActionProviderRegistry, LanguageIdentifier, CodeActionProvider, Command, WorkspaceEdit, ResourceTextEdit, CodeAction, CodeActionContext } from 'vs/editor/common/modes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { CodeAction, CodeActionContext, CodeActionProvider, CodeActionProviderRegistry, Command, LanguageIdentifier, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
|
||||
import { MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
|
||||
suite('CodeAction', () => {
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
|||
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { first, size } from 'vs/base/common/collections';
|
||||
import { first, size, forEach } from 'vs/base/common/collections';
|
||||
import { isFalsyOrEmpty, binarySearch, coalesce } from 'vs/base/common/arrays';
|
||||
import { commonPrefixLength } from 'vs/base/common/strings';
|
||||
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
|
@ -18,10 +18,13 @@ import { LRUCache } from 'vs/base/common/map';
|
|||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
export abstract class TreeElement {
|
||||
|
||||
abstract id: string;
|
||||
abstract children: { [id: string]: TreeElement };
|
||||
abstract parent: TreeElement | any;
|
||||
|
||||
abstract adopt(newParent: TreeElement): TreeElement;
|
||||
|
||||
static findId(candidate: DocumentSymbol | string, container: TreeElement): string {
|
||||
// complex id-computation which contains the origin/extension,
|
||||
// the parent path, and some dedupe logic when names collide
|
||||
|
@ -85,6 +88,12 @@ export class OutlineElement extends TreeElement {
|
|||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
adopt(parent: OutlineModel | OutlineGroup | OutlineElement): OutlineElement {
|
||||
let res = new OutlineElement(this.id, parent, this.symbol);
|
||||
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineGroup extends TreeElement {
|
||||
|
@ -100,6 +109,12 @@ export class OutlineGroup extends TreeElement {
|
|||
super();
|
||||
}
|
||||
|
||||
adopt(parent: OutlineModel): OutlineGroup {
|
||||
let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex);
|
||||
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
updateMatches(pattern: string, topMatch: OutlineElement): OutlineElement {
|
||||
for (const key in this.children) {
|
||||
topMatch = this._updateMatches(pattern, this.children[key], topMatch);
|
||||
|
@ -286,34 +301,7 @@ export class OutlineModel extends TreeElement {
|
|||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
|
||||
let count = 0;
|
||||
for (const key in result._groups) {
|
||||
let group = result._groups[key];
|
||||
if (first(group.children) === undefined) { // empty
|
||||
delete result._groups[key];
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (count !== 1) {
|
||||
//
|
||||
result.children = result._groups;
|
||||
|
||||
} else {
|
||||
// adopt all elements of the first group
|
||||
let group = first(result._groups);
|
||||
for (let key in group.children) {
|
||||
let child = group.children[key];
|
||||
child.parent = result;
|
||||
result.children[child.id] = child;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return Promise.all(promises).then(() => result._compact());
|
||||
}
|
||||
|
||||
private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void {
|
||||
|
@ -347,11 +335,38 @@ export class OutlineModel extends TreeElement {
|
|||
super();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
adopt(): OutlineModel {
|
||||
let res = new OutlineModel(this.textModel);
|
||||
forEach(this._groups, entry => res._groups[entry.key] = entry.value.adopt(res));
|
||||
return res._compact();
|
||||
}
|
||||
|
||||
adopt(other: OutlineModel): boolean {
|
||||
private _compact(): this {
|
||||
let count = 0;
|
||||
for (const key in this._groups) {
|
||||
let group = this._groups[key];
|
||||
if (first(group.children) === undefined) { // empty
|
||||
delete this._groups[key];
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if (count !== 1) {
|
||||
//
|
||||
this.children = this._groups;
|
||||
} else {
|
||||
// adopt all elements of the first group
|
||||
let group = first(this._groups);
|
||||
for (let key in group.children) {
|
||||
let child = group.children[key];
|
||||
child.parent = this;
|
||||
this.children[child.id] = child;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
merge(other: OutlineModel): boolean {
|
||||
if (this.textModel.uri.toString() !== other.textModel.uri.toString()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -236,10 +236,16 @@ interface SuggestController extends IEditorContribution {
|
|||
triggerSuggest(onlyFrom?: ISuggestSupport[]): void;
|
||||
}
|
||||
|
||||
let _suggestions: ISuggestion[];
|
||||
|
||||
let _provider = new class implements ISuggestSupport {
|
||||
|
||||
onlyOnceSuggestions: ISuggestion[] = [];
|
||||
|
||||
provideCompletionItems(): ISuggestResult {
|
||||
return _suggestions && { suggestions: _suggestions };
|
||||
let suggestions = this.onlyOnceSuggestions.slice(0);
|
||||
let result = { suggestions };
|
||||
this.onlyOnceSuggestions.length = 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -247,8 +253,7 @@ SuggestRegistry.register('*', _provider);
|
|||
|
||||
export function showSimpleSuggestions(editor: ICodeEditor, suggestions: ISuggestion[]) {
|
||||
setTimeout(() => {
|
||||
_suggestions = suggestions;
|
||||
_provider.onlyOnceSuggestions.push(...suggestions);
|
||||
editor.getContribution<SuggestController>('editor.contrib.suggestController').triggerSuggest([_provider]);
|
||||
_suggestions = undefined;
|
||||
}, 0);
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ export interface IExtensionManifest {
|
|||
categories?: string[];
|
||||
activationEvents?: string[];
|
||||
extensionDependencies?: string[];
|
||||
extensionPack?: string[];
|
||||
contributes?: IExtensionContributions;
|
||||
repository?: {
|
||||
url: string;
|
||||
|
@ -135,6 +136,7 @@ export interface IExtensionManifest {
|
|||
|
||||
export interface IGalleryExtensionProperties {
|
||||
dependencies?: string[];
|
||||
extensionPack?: string[];
|
||||
engine?: string;
|
||||
}
|
||||
|
||||
|
@ -205,6 +207,7 @@ export enum LocalExtensionType {
|
|||
export interface ILocalExtension {
|
||||
type: LocalExtensionType;
|
||||
identifier: IExtensionIdentifier;
|
||||
galleryIdentifier: IExtensionIdentifier;
|
||||
manifest: IExtensionManifest;
|
||||
metadata: IGalleryMetadata;
|
||||
location: URI;
|
||||
|
@ -303,10 +306,10 @@ export interface IExtensionManagementService {
|
|||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
install(zipPath: string): TPromise<ILocalExtension>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension>;
|
||||
install(zipPath: string): TPromise<void>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void>;
|
||||
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>;
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension>;
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void>;
|
||||
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>;
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]>;
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@ export interface IExtensionManagementChannel extends IChannel {
|
|||
listen(event: 'onDidInstallExtension'): Event<DidInstallExtensionEvent>;
|
||||
listen(event: 'onUninstallExtension'): Event<IExtensionIdentifier>;
|
||||
listen(event: 'onDidUninstallExtension'): Event<DidUninstallExtensionEvent>;
|
||||
call(command: 'install', args: [string]): TPromise<ILocalExtension>;
|
||||
call(command: 'installFromGallery', args: [IGalleryExtension]): TPromise<ILocalExtension>;
|
||||
call(command: 'install', args: [string]): TPromise<void>;
|
||||
call(command: 'installFromGallery', args: [IGalleryExtension]): TPromise<void>;
|
||||
call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>;
|
||||
call(command: 'reinstallFromGallery', args: [ILocalExtension]): TPromise<ILocalExtension>;
|
||||
call(command: 'reinstallFromGallery', args: [ILocalExtension]): TPromise<void>;
|
||||
call(command: 'getInstalled', args: [LocalExtensionType]): TPromise<ILocalExtension[]>;
|
||||
call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>;
|
||||
call(command: 'updateMetadata', args: [ILocalExtension, IGalleryMetadata]): TPromise<ILocalExtension>;
|
||||
|
@ -81,23 +81,20 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
|
|||
get onUninstallExtension(): Event<IExtensionIdentifier> { return this.channel.listen('onUninstallExtension'); }
|
||||
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this.channel.listen('onDidUninstallExtension'); }
|
||||
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
return this.channel.call('install', [zipPath])
|
||||
.then(extension => this._transform(extension));
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return this.channel.call('install', [zipPath]);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
return this.channel.call('installFromGallery', [extension])
|
||||
.then(extension => this._transform(extension));
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
return this.channel.call('installFromGallery', [extension]);
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force = false): TPromise<void> {
|
||||
return this.channel.call('uninstall', [extension, force]);
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
return this.channel.call('reinstallFromGallery', [extension])
|
||||
.then(extension => this._transform(extension));
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void> {
|
||||
return this.channel.call('reinstallFromGallery', [extension]);
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
|
||||
|
|
|
@ -41,7 +41,7 @@ export class MulitExtensionManagementService implements IExtensionManagementServ
|
|||
return this.getServer(extension).extensionManagementService.uninstall(extension, force);
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void> {
|
||||
return this.getServer(extension).extensionManagementService.reinstallFromGallery(extension);
|
||||
}
|
||||
|
||||
|
@ -49,12 +49,12 @@ export class MulitExtensionManagementService implements IExtensionManagementServ
|
|||
return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata);
|
||||
}
|
||||
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return this.servers[0].extensionManagementService.install(zipPath);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
return this.servers[0].extensionManagementService.installFromGallery(extension);
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
return TPromise.join(this.servers.map(server => server.extensionManagementService.installFromGallery(extension))).then(() => null);
|
||||
}
|
||||
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]> {
|
||||
|
|
|
@ -115,6 +115,7 @@ const AssetType = {
|
|||
|
||||
const PropertyType = {
|
||||
Dependency: 'Microsoft.VisualStudio.Code.ExtensionDependencies',
|
||||
ExtensionPack: 'Microsoft.VisualStudio.Code.ExtensionPack',
|
||||
Engine: 'Microsoft.VisualStudio.Code.Engine'
|
||||
};
|
||||
|
||||
|
@ -262,8 +263,8 @@ function getVersionAsset(version: IRawGalleryExtensionVersion, type: string): IG
|
|||
};
|
||||
}
|
||||
|
||||
function getDependencies(version: IRawGalleryExtensionVersion): string[] {
|
||||
const values = version.properties ? version.properties.filter(p => p.key === PropertyType.Dependency) : [];
|
||||
function getExtensions(version: IRawGalleryExtensionVersion, property: string): string[] {
|
||||
const values = version.properties ? version.properties.filter(p => p.key === property) : [];
|
||||
const value = values.length > 0 && values[0].value;
|
||||
return value ? value.split(',').map(v => adoptToGalleryExtensionId(v)) : [];
|
||||
}
|
||||
|
@ -308,7 +309,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
|
|||
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
|
||||
assets,
|
||||
properties: {
|
||||
dependencies: getDependencies(version),
|
||||
dependencies: getExtensions(version, PropertyType.Dependency),
|
||||
extensionPack: getExtensions(version, PropertyType.ExtensionPack),
|
||||
engine: getEngine(version)
|
||||
},
|
||||
/* __GDPR__FRAGMENT__
|
||||
|
@ -568,7 +570,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions)
|
||||
.then(rawVersion => {
|
||||
if (rawVersion) {
|
||||
extension.properties.dependencies = getDependencies(rawVersion);
|
||||
extension.properties.dependencies = getExtensions(rawVersion, PropertyType.Dependency);
|
||||
extension.properties.engine = getEngine(rawVersion);
|
||||
extension.assets.download = getVersionAsset(rawVersion, AssetType.VSIX);
|
||||
extension.version = rawVersion.version;
|
||||
|
|
|
@ -11,9 +11,9 @@ import * as pfs from 'vs/base/node/pfs';
|
|||
import * as errors from 'vs/base/common/errors';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { flatten, distinct } from 'vs/base/common/arrays';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { extract, buffer, ExtractError } from 'vs/base/node/zip';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base';
|
||||
import {
|
||||
IExtensionManagementService, IExtensionGalleryService, ILocalExtension,
|
||||
IGalleryExtension, IExtensionManifest, IGalleryMetadata,
|
||||
|
@ -53,6 +53,7 @@ const INSTALL_ERROR_LOCAL = 'local';
|
|||
const INSTALL_ERROR_EXTRACTING = 'extracting';
|
||||
const INSTALL_ERROR_RENAMING = 'renaming';
|
||||
const INSTALL_ERROR_DELETING = 'deleting';
|
||||
const INSTALL_ERROR_MALICIOUS = 'malicious';
|
||||
const ERROR_UNKNOWN = 'unknown';
|
||||
|
||||
export class ExtensionManagementError extends Error {
|
||||
|
@ -112,8 +113,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
private uninstalledFileLimiter: Limiter<void>;
|
||||
private reportedExtensions: TPromise<IReportedExtension[]> | undefined;
|
||||
private lastReportTimestamp = 0;
|
||||
private readonly installationStartTime: Map<string, number> = new Map<string, number>();
|
||||
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
|
||||
private readonly installingExtensions: Map<string, TPromise<void>> = new Map<string, TPromise<void>>();
|
||||
private readonly uninstallingExtensions: Map<string, TPromise<void>> = new Map<string, TPromise<void>>();
|
||||
private readonly manifestCache: ExtensionsManifestCache;
|
||||
private readonly extensionLifecycle: ExtensionsLifecycle;
|
||||
|
@ -152,14 +152,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
}));
|
||||
}
|
||||
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
install(zipPath: string): TPromise<void> {
|
||||
zipPath = path.resolve(zipPath);
|
||||
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(manifest => {
|
||||
const identifier = { id: getLocalExtensionIdFromManifest(manifest) };
|
||||
if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) {
|
||||
return TPromise.wrapError<ILocalExtension>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version)));
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version)));
|
||||
}
|
||||
return this.removeIfExists(identifier.id)
|
||||
.then(
|
||||
|
@ -173,7 +173,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
metadata => this.installFromZipPath(identifier, zipPath, metadata, manifest),
|
||||
error => this.installFromZipPath(identifier, zipPath, null, manifest))
|
||||
.then(
|
||||
local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; },
|
||||
() => { this.logService.info('Successfully installed the extension:', identifier.id); },
|
||||
e => {
|
||||
this.logService.error('Failed to install the extension:', identifier.id, e.message);
|
||||
return TPromise.wrapError(e);
|
||||
|
@ -219,20 +219,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
.then(installed => {
|
||||
const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed);
|
||||
return this.installExtension({ zipPath, id: identifier.id, metadata })
|
||||
.then(local => {
|
||||
if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) {
|
||||
return this.getDependenciesToInstall(local.manifest.extensionDependencies)
|
||||
.then(dependenciesToInstall => {
|
||||
dependenciesToInstall = metadata ? dependenciesToInstall.filter(d => d.identifier.uuid !== metadata.id) : dependenciesToInstall;
|
||||
return this.downloadAndInstallExtensions(dependenciesToInstall, dependenciesToInstall.map(d => this.getOperation(d.identifier, installed)));
|
||||
})
|
||||
.then(() => local, error => {
|
||||
this.setUninstalled(local);
|
||||
return TPromise.wrapError(new Error(nls.localize('errorInstallingDependencies', "Error while installing dependencies. {0}", error instanceof Error ? error.message : error)));
|
||||
});
|
||||
}
|
||||
return local;
|
||||
})
|
||||
.then(local => this.installDependenciesAndPackExtensions(local, null).then(() => local, error => this.uninstall(local, true).then(() => TPromise.wrapError(error), () => TPromise.wrapError(error))))
|
||||
.then(
|
||||
local => { this._onDidInstallExtension.fire({ identifier, zipPath, local, operation }); return local; },
|
||||
error => { this._onDidInstallExtension.fire({ identifier, zipPath, operation, error }); return TPromise.wrapError(error); }
|
||||
|
@ -240,26 +227,60 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
}));
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
this.onInstallExtensions([extension]);
|
||||
return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => this.collectExtensionsToInstall(extension)
|
||||
.then(
|
||||
extensionsToInstall => {
|
||||
if (extensionsToInstall.length > 1) {
|
||||
this.onInstallExtensions(extensionsToInstall.slice(1));
|
||||
}
|
||||
const operataions: InstallOperation[] = extensionsToInstall.map(e => this.getOperation(e.identifier, installed));
|
||||
return this.downloadAndInstallExtensions(extensionsToInstall, operataions)
|
||||
.then(
|
||||
locals => this.onDidInstallExtensions(extensionsToInstall, locals, operataions, [])
|
||||
.then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier))[0]),
|
||||
errors => this.onDidInstallExtensions(extensionsToInstall, [], operataions, errors));
|
||||
},
|
||||
error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension.identifier, installed)], [error]))));
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
let installingExtension = this.installingExtensions.get(extension.identifier.id);
|
||||
if (!installingExtension) {
|
||||
|
||||
let successCallback: ValueCallback<void>, errorCallback: ErrorCallback;
|
||||
installingExtension = new TPromise((c, e) => { successCallback = c; errorCallback = e; });
|
||||
this.installingExtensions.set(extension.identifier.id, installingExtension);
|
||||
|
||||
try {
|
||||
const startTime = new Date().getTime();
|
||||
const identifier = { id: getLocalExtensionIdFromGallery(extension, extension.version), uuid: extension.identifier.uuid };
|
||||
const telemetryData = getGalleryExtensionTelemetryData(extension);
|
||||
let operation: InstallOperation = InstallOperation.Install;
|
||||
|
||||
this.logService.info('Installing extension:', extension.name);
|
||||
this._onInstallExtension.fire({ identifier, gallery: extension });
|
||||
|
||||
this.checkMalicious(extension)
|
||||
.then(() => this.getInstalled(LocalExtensionType.User))
|
||||
.then(installed => {
|
||||
const existingExtension = installed.filter(i => areSameExtensions(i.galleryIdentifier, extension.identifier))[0];
|
||||
operation = existingExtension ? InstallOperation.Update : InstallOperation.Install;
|
||||
return this.downloadInstallableExtension(extension, operation)
|
||||
.then(installableExtension => this.installExtension(installableExtension))
|
||||
.then(local => this.installDependenciesAndPackExtensions(local, existingExtension)
|
||||
.then(() => local, error => this.uninstall(local, true).then(() => TPromise.wrapError(error), () => TPromise.wrapError(error))));
|
||||
})
|
||||
.then(
|
||||
local => {
|
||||
this.installingExtensions.delete(extension.identifier.id);
|
||||
this.logService.info(`Extensions installed successfully:`, extension.identifier.id);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery: extension, local, operation });
|
||||
this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, void 0);
|
||||
successCallback(null);
|
||||
},
|
||||
error => {
|
||||
this.installingExtensions.delete(extension.identifier.id);
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
|
||||
this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery: extension, operation, error: errorCode });
|
||||
this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, error);
|
||||
errorCallback(error);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
this.installingExtensions.delete(extension.identifier.id);
|
||||
errorCallback(error);
|
||||
}
|
||||
|
||||
}
|
||||
return installingExtension;
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
return TPromise.wrapError(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
|
||||
}
|
||||
|
@ -280,46 +301,19 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
return installed.some(i => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i), uuid: i.identifier.uuid }, extensionToInstall)) ? InstallOperation.Update : InstallOperation.Install;
|
||||
}
|
||||
|
||||
private collectExtensionsToInstall(extension: IGalleryExtension): TPromise<IGalleryExtension[]> {
|
||||
return this.galleryService.loadCompatibleVersion(extension)
|
||||
.then(compatible => {
|
||||
if (!compatible) {
|
||||
return TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(nls.localize('notFoundCompatible', "Unable to install '{0}'; there is no available version compatible with VS Code '{1}'.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
|
||||
private getTelemetryEvent(operation: InstallOperation): string {
|
||||
return operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install';
|
||||
}
|
||||
|
||||
private checkMalicious(extension: IGalleryExtension): TPromise<void> {
|
||||
return this.getExtensionsReport()
|
||||
.then(report => {
|
||||
if (getMaliciousExtensionsSet(report).has(extension.identifier.id)) {
|
||||
throw new ExtensionManagementError(INSTALL_ERROR_MALICIOUS, nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return this.getDependenciesToInstall(compatible.properties.dependencies)
|
||||
.then(
|
||||
dependenciesToInstall => ([compatible, ...dependenciesToInstall.filter(d => d.identifier.uuid !== compatible.identifier.uuid)]),
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
},
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private downloadAndInstallExtensions(extensions: IGalleryExtension[], operations: InstallOperation[]): TPromise<ILocalExtension[]> {
|
||||
return TPromise.join(extensions.map((extensionToInstall, index) => this.downloadAndInstallExtension(extensionToInstall, operations[index])))
|
||||
.then(null, errors => this.rollback(extensions).then(() => TPromise.wrapError(errors), () => TPromise.wrapError(errors)));
|
||||
}
|
||||
|
||||
private downloadAndInstallExtension(extension: IGalleryExtension, operation: InstallOperation): TPromise<ILocalExtension> {
|
||||
let installingExtension = this.installingExtensions.get(extension.identifier.id);
|
||||
if (!installingExtension) {
|
||||
installingExtension = this.getExtensionsReport()
|
||||
.then(report => {
|
||||
if (getMaliciousExtensionsSet(report).has(extension.identifier.id)) {
|
||||
throw new Error(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."));
|
||||
} else {
|
||||
return extension;
|
||||
}
|
||||
})
|
||||
.then(extension => this.downloadInstallableExtension(extension, operation))
|
||||
.then(installableExtension => this.installExtension(installableExtension))
|
||||
.then(
|
||||
local => { this.installingExtensions.delete(extension.identifier.id); return local; },
|
||||
e => { this.installingExtensions.delete(extension.identifier.id); return TPromise.wrapError(e); }
|
||||
);
|
||||
|
||||
this.installingExtensions.set(extension.identifier.id, installingExtension);
|
||||
}
|
||||
return installingExtension;
|
||||
});
|
||||
}
|
||||
|
||||
private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): TPromise<InstallableExtension> {
|
||||
|
@ -352,54 +346,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private onInstallExtensions(extensions: IGalleryExtension[]): void {
|
||||
for (const extension of extensions) {
|
||||
this.logService.info('Installing extension:', extension.name);
|
||||
this.installationStartTime.set(extension.identifier.id, new Date().getTime());
|
||||
const id = getLocalExtensionIdFromGallery(extension, extension.version);
|
||||
this._onInstallExtension.fire({ identifier: { id, uuid: extension.identifier.uuid }, gallery: extension });
|
||||
}
|
||||
}
|
||||
|
||||
private onDidInstallExtensions(extensions: IGalleryExtension[], locals: ILocalExtension[], operations: InstallOperation[], errors: Error[]): TPromise<any> {
|
||||
extensions.forEach((gallery, index) => {
|
||||
const identifier = { id: getLocalExtensionIdFromGallery(gallery, gallery.version), uuid: gallery.identifier.uuid };
|
||||
const local = locals[index];
|
||||
const error = errors[index];
|
||||
const operation = operations[index];
|
||||
if (local) {
|
||||
this.logService.info(`Extensions installed successfully:`, gallery.identifier.id);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, local, operation });
|
||||
} else {
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
|
||||
this.logService.error(`Failed to install extension:`, gallery.identifier.id, error ? error.message : errorCode);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, operation, error: errorCode });
|
||||
}
|
||||
const startTime = this.installationStartTime.get(gallery.identifier.id);
|
||||
this.reportTelemetry(operations[index] === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(gallery), startTime ? new Date().getTime() - startTime : void 0, error);
|
||||
this.installationStartTime.delete(gallery.identifier.id);
|
||||
});
|
||||
return errors.length ? TPromise.wrapError(this.joinErrors(errors)) : TPromise.as(null);
|
||||
}
|
||||
|
||||
private getDependenciesToInstall(dependencies: string[]): TPromise<IGalleryExtension[]> {
|
||||
if (dependencies.length) {
|
||||
return this.getInstalled()
|
||||
.then(installed => {
|
||||
const uninstalledDeps = dependencies.filter(d => installed.every(i => getGalleryExtensionId(i.manifest.publisher, i.manifest.name) !== d));
|
||||
if (uninstalledDeps.length) {
|
||||
return this.galleryService.loadAllDependencies(uninstalledDeps.map(id => (<IExtensionIdentifier>{ id })))
|
||||
.then(allDependencies => allDependencies.filter(d => {
|
||||
const extensionId = getLocalExtensionIdFromGallery(d, d.version);
|
||||
return installed.every(({ identifier }) => identifier.id !== extensionId);
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
private installExtension(installableExtension: InstallableExtension): TPromise<ILocalExtension> {
|
||||
return this.unsetUninstalledAndGetLocal(installableExtension.id)
|
||||
.then(
|
||||
|
@ -486,11 +432,44 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
});
|
||||
}
|
||||
|
||||
private installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension): TPromise<void> {
|
||||
if (this.galleryService.isEnabled()) {
|
||||
const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || [];
|
||||
if (installed.manifest.extensionPack) {
|
||||
for (const extension of installed.manifest.extensionPack) {
|
||||
// add only those extensions which are new in currently installed extension
|
||||
if (!(existing && existing.manifest.extensionPack && existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension })))) {
|
||||
if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) {
|
||||
dependenciesAndPackExtensions.push(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dependenciesAndPackExtensions.length) {
|
||||
return this.getInstalled()
|
||||
.then(installed => {
|
||||
// filter out installing and installed extensions
|
||||
const names = dependenciesAndPackExtensions.filter(id => !this.installingExtensions.has(adoptToGalleryExtensionId(id)) && installed.every(({ galleryIdentifier }) => !areSameExtensions(galleryIdentifier, { id })));
|
||||
if (names.length) {
|
||||
return this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length })
|
||||
.then(galleryResult => {
|
||||
const extensionsToInstall = galleryResult.firstPage;
|
||||
return TPromise.join(extensionsToInstall.map(e => this.installFromGallery(e)))
|
||||
.then(() => null, errors => this.rollback(extensionsToInstall).then(() => TPromise.wrapError(errors), () => TPromise.wrapError(errors)));
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
private rollback(extensions: IGalleryExtension[]): TPromise<void> {
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed =>
|
||||
TPromise.join(installed.filter(local => extensions.some(galleryExtension => local.identifier.id === getLocalExtensionIdFromGallery(galleryExtension, galleryExtension.version))) // Only check id (pub.name-version) because we want to rollback the exact version
|
||||
.map(local => this.setUninstalled(local))))
|
||||
.map(local => this.uninstall(local, true))))
|
||||
.then(() => null, () => null);
|
||||
}
|
||||
|
||||
|
@ -558,7 +537,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
|
||||
private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise<void> {
|
||||
return this.preUninstallExtension(extension)
|
||||
.then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.uninstallWithDependencies(extension, [], installed))
|
||||
.then(() => {
|
||||
if (force) {
|
||||
return this.uninstallExtensionAsPack(extension, installed);
|
||||
}
|
||||
const hasInstalledExtensionPack = extension.manifest.extensionPack && extension.manifest.extensionPack.length && installed.some(i => extension.manifest.extensionPack.some(dep => areSameExtensions({ id: dep }, i.galleryIdentifier)));
|
||||
const hasDependencies = extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length > 0;
|
||||
return hasInstalledExtensionPack || hasDependencies ? this.promptForPackAndUninstall(extension, installed) : this.uninstallExtensions(extension, [], installed);
|
||||
})
|
||||
.then(() => this.postUninstallExtension(extension),
|
||||
error => {
|
||||
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
|
||||
|
@ -566,47 +552,45 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
});
|
||||
}
|
||||
|
||||
private hasDependencies(extension: ILocalExtension, installed: ILocalExtension[]): boolean {
|
||||
if (extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length) {
|
||||
return installed.some(i => extension.manifest.extensionDependencies.indexOf(getGalleryExtensionIdFromLocal(i)) !== -1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private promptForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise<void> {
|
||||
if (force) {
|
||||
const dependencies = distinct(this.getDependenciesToUninstallRecursively(extension, installed, [])).filter(e => e !== extension);
|
||||
return this.uninstallWithDependencies(extension, dependencies, installed);
|
||||
}
|
||||
|
||||
const message = nls.localize('uninstallDependeciesConfirmation', "Would you like to uninstall '{0}' only or its dependencies also?", extension.manifest.displayName || extension.manifest.name);
|
||||
private promptForPackAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): TPromise<void> {
|
||||
const message = nls.localize('uninstallExtensionPackConfirmation', "Would you like to uninstall '{0}' only or as a pack?", extension.manifest.displayName || extension.manifest.name);
|
||||
const buttons = [
|
||||
nls.localize('uninstallOnly', "Extension Only"),
|
||||
nls.localize('uninstallAll', "Uninstall All"),
|
||||
nls.localize('uninstallPack', "Uninstall Extension Pack"),
|
||||
nls.localize('uninstallOnly', "Uninstall Extension Only"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
this.logService.info('Requesting for confirmation to uninstall extension with dependencies', extension.identifier.id);
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 2 })
|
||||
.then<void>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstallWithDependencies(extension, [], installed);
|
||||
return this.uninstallExtensionAsPack(extension, installed);
|
||||
}
|
||||
if (value === 1) {
|
||||
const dependencies = distinct(this.getDependenciesToUninstallRecursively(extension, installed, [])).filter(e => e !== extension);
|
||||
return this.uninstallWithDependencies(extension, dependencies, installed);
|
||||
return this.uninstallExtensions(extension, [], installed);
|
||||
}
|
||||
this.logService.info('Cancelled uninstalling extension:', extension.identifier.id);
|
||||
return TPromise.wrapError(errors.canceled());
|
||||
}, error => TPromise.wrapError(errors.canceled()));
|
||||
}
|
||||
|
||||
private uninstallWithDependencies(extension: ILocalExtension, dependencies: ILocalExtension[], installed: ILocalExtension[]): TPromise<void> {
|
||||
const dependenciesToUninstall = this.filterDependents(extension, dependencies, installed);
|
||||
let dependents = this.getDependents(extension, installed).filter(dependent => extension !== dependent && dependenciesToUninstall.indexOf(dependent) === -1);
|
||||
if (dependents.length) {
|
||||
return TPromise.wrapError<void>(new Error(this.getDependentsErrorMessage(extension, dependents)));
|
||||
private uninstallExtensionAsPack(extension: ILocalExtension, installed: ILocalExtension[]): TPromise<void> {
|
||||
const extensionsToUninstall = this.getDependenciesToUninstall(extension, installed);
|
||||
for (const packExtensionToUninstall of this.getAllPackExtensionsToUninstall(extension, installed)) {
|
||||
if (extensionsToUninstall.indexOf(packExtensionToUninstall) === -1) {
|
||||
extensionsToUninstall.push(packExtensionToUninstall);
|
||||
}
|
||||
}
|
||||
return TPromise.join([this.uninstallExtension(extension), ...dependenciesToUninstall.map(d => this.doUninstall(d))]).then(() => null);
|
||||
return this.uninstallExtensions(extension, extensionsToUninstall, installed);
|
||||
}
|
||||
|
||||
private uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): TPromise<void> {
|
||||
const dependents = this.getDependents(extension, installed);
|
||||
if (dependents.length) {
|
||||
const remainingDependents = dependents.filter(dependent => extension !== dependent && otherExtensionsToUninstall.indexOf(dependent) === -1);
|
||||
if (remainingDependents.length) {
|
||||
return TPromise.wrapError<void>(new Error(this.getDependentsErrorMessage(extension, remainingDependents)));
|
||||
}
|
||||
}
|
||||
return TPromise.join([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]).then(() => null);
|
||||
}
|
||||
|
||||
private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string {
|
||||
|
@ -622,7 +606,23 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
|
||||
}
|
||||
|
||||
private getDependenciesToUninstallRecursively(extension: ILocalExtension, installed: ILocalExtension[], checked: ILocalExtension[]): ILocalExtension[] {
|
||||
private getDependenciesToUninstall(extension: ILocalExtension, installed: ILocalExtension[]): ILocalExtension[] {
|
||||
const dependencies = this.getAllDependenciesToUninstall(extension, installed).filter(e => e !== extension);
|
||||
|
||||
const dependenciesToUninstall = dependencies.slice(0);
|
||||
for (let index = 0; index < dependencies.length; index++) {
|
||||
const dep = dependencies[index];
|
||||
const dependents = this.getDependents(dep, installed);
|
||||
// Remove the dependency from the uninstall list if there is a dependent which will not be uninstalled.
|
||||
if (dependents.some(e => e !== extension && dependencies.indexOf(e) === -1)) {
|
||||
dependenciesToUninstall.splice(index - (dependencies.length - dependenciesToUninstall.length), 1);
|
||||
}
|
||||
}
|
||||
|
||||
return dependenciesToUninstall;
|
||||
}
|
||||
|
||||
private getAllDependenciesToUninstall(extension: ILocalExtension, installed: ILocalExtension[], checked: ILocalExtension[] = []): ILocalExtension[] {
|
||||
if (checked.indexOf(extension) !== -1) {
|
||||
return [];
|
||||
}
|
||||
|
@ -630,29 +630,32 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
if (!extension.manifest.extensionDependencies || extension.manifest.extensionDependencies.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const dependenciesToUninstall = installed.filter(i => extension.manifest.extensionDependencies.indexOf(getGalleryExtensionIdFromLocal(i)) !== -1);
|
||||
const dependenciesToUninstall = installed.filter(i => extension.manifest.extensionDependencies.some(id => areSameExtensions({ id }, i.galleryIdentifier)));
|
||||
const depsOfDeps = [];
|
||||
for (const dep of dependenciesToUninstall) {
|
||||
depsOfDeps.push(...this.getDependenciesToUninstallRecursively(dep, installed, checked));
|
||||
depsOfDeps.push(...this.getAllDependenciesToUninstall(dep, installed, checked));
|
||||
}
|
||||
return [...dependenciesToUninstall, ...depsOfDeps];
|
||||
}
|
||||
|
||||
private filterDependents(extension: ILocalExtension, dependencies: ILocalExtension[], installed: ILocalExtension[]): ILocalExtension[] {
|
||||
installed = installed.filter(i => i !== extension && i.manifest.extensionDependencies && i.manifest.extensionDependencies.length > 0);
|
||||
let result = dependencies.slice(0);
|
||||
for (let i = 0; i < dependencies.length; i++) {
|
||||
const dep = dependencies[i];
|
||||
const dependents = this.getDependents(dep, installed).filter(e => dependencies.indexOf(e) === -1);
|
||||
if (dependents.length) {
|
||||
result.splice(i - (dependencies.length - result.length), 1);
|
||||
}
|
||||
private getAllPackExtensionsToUninstall(extension: ILocalExtension, installed: ILocalExtension[], checked: ILocalExtension[] = []): ILocalExtension[] {
|
||||
if (checked.indexOf(extension) !== -1) {
|
||||
return [];
|
||||
}
|
||||
return result;
|
||||
checked.push(extension);
|
||||
if (!extension.manifest.extensionPack || extension.manifest.extensionPack.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const packedExtensions = installed.filter(i => extension.manifest.extensionPack.some(id => areSameExtensions({ id }, i.galleryIdentifier)));
|
||||
const packOfPackedExtensions = [];
|
||||
for (const packedExtension of packedExtensions) {
|
||||
packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked));
|
||||
}
|
||||
return [...packedExtensions, ...packOfPackedExtensions];
|
||||
}
|
||||
|
||||
private getDependents(extension: ILocalExtension, installed: ILocalExtension[]): ILocalExtension[] {
|
||||
return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.indexOf(getGalleryExtensionIdFromLocal(extension)) !== -1);
|
||||
return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.galleryIdentifier)));
|
||||
}
|
||||
|
||||
private doUninstall(extension: ILocalExtension): TPromise<void> {
|
||||
|
@ -761,8 +764,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
if (manifest.extensionDependencies) {
|
||||
manifest.extensionDependencies = manifest.extensionDependencies.map(id => adoptToGalleryExtensionId(id));
|
||||
}
|
||||
if (manifest.extensionPack) {
|
||||
manifest.extensionPack = manifest.extensionPack.map(id => adoptToGalleryExtensionId(id));
|
||||
}
|
||||
const identifier = { id: type === LocalExtensionType.System ? folderName : getLocalExtensionIdFromManifest(manifest), uuid: metadata ? metadata.id : null };
|
||||
return { type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
|
||||
const galleryIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: identifier.uuid };
|
||||
return { type, identifier, galleryIdentifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
|
||||
}))
|
||||
.then(null, () => null);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export const NullAppender: ITelemetryAppender = { log: () => null, dispose: () =
|
|||
|
||||
export class LogAppender implements ITelemetryAppender {
|
||||
|
||||
private commonPropertiesRegex = /^sessionID$|^version$|^timestamp$|^common\./;
|
||||
private commonPropertiesRegex = /^sessionID$|^version$|^timestamp$|^commitHash$|^common\./;
|
||||
constructor(@ILogService private readonly _logService: ILogService) { }
|
||||
|
||||
dispose(): TPromise<any> {
|
||||
|
|
|
@ -48,8 +48,13 @@ export enum StateType {
|
|||
Ready = 'ready',
|
||||
}
|
||||
|
||||
export enum UpdateType {
|
||||
Setup,
|
||||
Archive
|
||||
}
|
||||
|
||||
export type Uninitialized = { type: StateType.Uninitialized };
|
||||
export type Idle = { type: StateType.Idle };
|
||||
export type Idle = { type: StateType.Idle, updateType: UpdateType };
|
||||
export type CheckingForUpdates = { type: StateType.CheckingForUpdates, context: any };
|
||||
export type AvailableForDownload = { type: StateType.AvailableForDownload, update: IUpdate };
|
||||
export type Downloading = { type: StateType.Downloading, update: IUpdate };
|
||||
|
@ -61,7 +66,7 @@ export type State = Uninitialized | Idle | CheckingForUpdates | AvailableForDown
|
|||
|
||||
export const State = {
|
||||
Uninitialized: { type: StateType.Uninitialized } as Uninitialized,
|
||||
Idle: { type: StateType.Idle } as Idle,
|
||||
Idle: (updateType: UpdateType) => ({ type: StateType.Idle, updateType }) as Idle,
|
||||
CheckingForUpdates: (context: any) => ({ type: StateType.CheckingForUpdates, context } as CheckingForUpdates),
|
||||
AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload),
|
||||
Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading),
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IUpdateService, State, StateType, AvailableForDownload } from 'vs/platform/update/common/update';
|
||||
import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
|
@ -72,7 +72,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
|||
return;
|
||||
}
|
||||
|
||||
this.setState({ type: StateType.Idle });
|
||||
this.setState(State.Idle(this.getUpdateType()));
|
||||
|
||||
// Start checking for updates after 30 seconds
|
||||
this.scheduleCheckForUpdates(30 * 1000)
|
||||
|
@ -173,6 +173,10 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
|||
});
|
||||
}
|
||||
|
||||
protected getUpdateType(): UpdateType {
|
||||
return UpdateType.Archive;
|
||||
}
|
||||
|
||||
protected doQuitAndInstall(): void {
|
||||
// noop
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Event, fromNodeEventEmitter } from 'vs/base/common/event';
|
|||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { State, IUpdate, StateType } from 'vs/platform/update/common/update';
|
||||
import { State, IUpdate, StateType, UpdateType } from 'vs/platform/update/common/update';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
@ -46,7 +46,7 @@ export class DarwinUpdateService extends AbstractUpdateService {
|
|||
|
||||
private onError(err: string): void {
|
||||
this.logService.error('UpdateService error: ', err);
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(UpdateType.Archive));
|
||||
}
|
||||
|
||||
protected buildUpdateFeedUrl(quality: string): string | undefined {
|
||||
|
@ -101,7 +101,7 @@ export class DarwinUpdateService extends AbstractUpdateService {
|
|||
*/
|
||||
this.telemetryService.publicLog('update:notAvailable', { explicit: !!this.state.context });
|
||||
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(UpdateType.Archive));
|
||||
}
|
||||
|
||||
protected doQuitAndInstall(): void {
|
||||
|
|
|
@ -9,7 +9,7 @@ import product from 'vs/platform/node/product';
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { State, IUpdate, AvailableForDownload } from 'vs/platform/update/common/update';
|
||||
import { State, IUpdate, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
@ -55,7 +55,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
|
|||
*/
|
||||
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
|
||||
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(UpdateType.Archive));
|
||||
} else {
|
||||
this.setState(State.AvailableForDownload(update));
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
|
|||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(UpdateType.Archive));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
|
|||
shell.openExternal(state.update.url);
|
||||
}
|
||||
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(UpdateType.Archive));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycle
|
|||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { TPromise, Promise } from 'vs/base/common/winjs.base';
|
||||
import { State, IUpdate, StateType, AvailableForDownload } from 'vs/platform/update/common/update';
|
||||
import { State, IUpdate, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
@ -44,17 +44,12 @@ interface IAvailableUpdate {
|
|||
updateFilePath?: string;
|
||||
}
|
||||
|
||||
enum UpdateType {
|
||||
Automatic,
|
||||
Manual
|
||||
}
|
||||
|
||||
let _updateType: UpdateType | undefined = undefined;
|
||||
function getUpdateType(): UpdateType {
|
||||
if (typeof _updateType === 'undefined') {
|
||||
_updateType = fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))
|
||||
? UpdateType.Automatic
|
||||
: UpdateType.Manual;
|
||||
? UpdateType.Setup
|
||||
: UpdateType.Archive;
|
||||
}
|
||||
|
||||
return _updateType;
|
||||
|
@ -81,6 +76,15 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
@ILogService logService: ILogService
|
||||
) {
|
||||
super(lifecycleService, configurationService, environmentService, requestService, logService);
|
||||
|
||||
if (getUpdateType() === UpdateType.Setup) {
|
||||
/* __GDPR__
|
||||
"update:win32SetupTarget" : {
|
||||
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('update:win32SetupTarget', { target: product.target });
|
||||
}
|
||||
}
|
||||
|
||||
protected buildUpdateFeedUrl(quality: string): string | undefined {
|
||||
|
@ -90,7 +94,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
platform += '-x64';
|
||||
}
|
||||
|
||||
if (getUpdateType() === UpdateType.Manual) {
|
||||
if (getUpdateType() === UpdateType.Archive) {
|
||||
platform += '-archive';
|
||||
} else if (product.target === 'user') {
|
||||
platform += '-user';
|
||||
|
@ -109,6 +113,8 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
this.requestService.request({ url: this.url })
|
||||
.then<IUpdate>(asJson)
|
||||
.then(update => {
|
||||
const updateType = getUpdateType();
|
||||
|
||||
if (!update || !update.url || !update.version || !update.productVersion) {
|
||||
/* __GDPR__
|
||||
"update:notAvailable" : {
|
||||
|
@ -117,11 +123,11 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
*/
|
||||
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
|
||||
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(updateType));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
if (getUpdateType() === UpdateType.Manual) {
|
||||
if (updateType === UpdateType.Archive) {
|
||||
this.setState(State.AvailableForDownload(update));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
@ -170,13 +176,13 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(getUpdateType()));
|
||||
});
|
||||
}
|
||||
|
||||
protected doDownloadUpdate(state: AvailableForDownload): TPromise<void> {
|
||||
shell.openExternal(state.update.url);
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(getUpdateType()));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
|
@ -220,7 +226,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
|
||||
child.once('exit', () => {
|
||||
this.availableUpdate = undefined;
|
||||
this.setState(State.Idle);
|
||||
this.setState(State.Idle(getUpdateType()));
|
||||
});
|
||||
|
||||
const readyMutexName = `${product.win32MutexName}-ready`;
|
||||
|
@ -249,4 +255,8 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected getUpdateType(): UpdateType {
|
||||
return getUpdateType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||
};
|
||||
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.cols, request.rows);
|
||||
request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
|
||||
request.proxy.onResize((cols, rows) => this._proxy.$acceptProcessResize(request.proxy.terminalId, cols, rows));
|
||||
request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows));
|
||||
request.proxy.onShutdown(() => this._proxy.$acceptProcessShutdown(request.proxy.terminalId));
|
||||
}
|
||||
|
||||
|
|
|
@ -315,12 +315,12 @@ class QueryGlobTester {
|
|||
/**
|
||||
* Guaranteed sync - siblingsFn should not return a promise.
|
||||
*/
|
||||
public includedInQuerySync(testPath: string, basename?: string, siblingsFn?: () => string[]): boolean {
|
||||
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, siblingsFn)) {
|
||||
public includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
|
||||
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, siblingsFn)) {
|
||||
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -330,9 +330,9 @@ class QueryGlobTester {
|
|||
/**
|
||||
* Guaranteed async.
|
||||
*/
|
||||
public includedInQuery(testPath: string, basename?: string, siblingsFn?: () => string[] | TPromise<string[]>): TPromise<boolean> {
|
||||
public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>): TPromise<boolean> {
|
||||
const excludeP = this._parsedExcludeExpression ?
|
||||
TPromise.as(this._parsedExcludeExpression(testPath, basename, siblingsFn)).then(result => !!result) :
|
||||
TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
TPromise.wrap(false);
|
||||
|
||||
return excludeP.then(excluded => {
|
||||
|
@ -341,7 +341,7 @@ class QueryGlobTester {
|
|||
}
|
||||
|
||||
return this._parsedIncludeExpression ?
|
||||
TPromise.as(this._parsedIncludeExpression(testPath, basename, siblingsFn)).then(result => !!result) :
|
||||
TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
TPromise.wrap(true);
|
||||
}).then(included => {
|
||||
return included;
|
||||
|
@ -427,13 +427,13 @@ class TextSearchEngine {
|
|||
const testingPs = [];
|
||||
const progress = {
|
||||
report: (result: vscode.TextSearchResult) => {
|
||||
const siblingFn = folderQuery.folder.scheme === 'file' && (() => {
|
||||
const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => {
|
||||
return this.readdir(path.dirname(result.uri.fsPath));
|
||||
});
|
||||
|
||||
const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath);
|
||||
testingPs.push(
|
||||
queryTester.includedInQuery(relativePath, path.basename(relativePath), siblingFn)
|
||||
queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling)
|
||||
.then(included => {
|
||||
if (included) {
|
||||
onResult(result);
|
||||
|
@ -692,6 +692,7 @@ class FileSearchEngine {
|
|||
const self = this;
|
||||
const filePattern = this.filePattern;
|
||||
function matchDirectory(entries: IDirectoryEntry[]) {
|
||||
const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename));
|
||||
for (let i = 0, n = entries.length; i < n; i++) {
|
||||
const entry = entries[i];
|
||||
const { relativePath, basename } = entry;
|
||||
|
@ -700,7 +701,7 @@ class FileSearchEngine {
|
|||
// If the user searches for the exact file name, we adjust the glob matching
|
||||
// to ignore filtering by siblings because the user seems to know what she
|
||||
// is searching for and we want to include the result in that case anyway
|
||||
if (!queryTester.includedInQuerySync(relativePath, basename, () => filePattern !== basename ? entries.map(entry => entry.basename) : [])) {
|
||||
if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,17 +5,15 @@
|
|||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as cp from 'child_process';
|
||||
import * as os from 'os';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
|
||||
import Uri from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
|
||||
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess';
|
||||
|
||||
const RENDERER_NO_PROCESS_ID = -1;
|
||||
|
||||
|
@ -226,7 +224,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
private _proxy: MainThreadTerminalServiceShape;
|
||||
private _activeTerminal: ExtHostTerminal;
|
||||
private _terminals: ExtHostTerminal[] = [];
|
||||
private _terminalProcesses: { [id: number]: cp.ChildProcess } = {};
|
||||
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
|
||||
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
|
||||
|
||||
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
|
||||
|
@ -359,7 +357,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
|
||||
const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated');
|
||||
|
||||
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
|
||||
if (!shellLaunchConfig.executable) {
|
||||
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged
|
||||
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
|
||||
|
@ -383,61 +380,48 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
|
||||
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
|
||||
// shellLaunchConfig.env = envFromShell;
|
||||
|
||||
// Merge process env with the env from config
|
||||
const parentEnv = { ...process.env };
|
||||
// terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
|
||||
const env = { ...process.env };
|
||||
// terminalEnvironment.mergeEnvironments(env, envFromConfig);
|
||||
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
|
||||
|
||||
// Continue env initialization, merging in the env from the launch
|
||||
// config and adding keys that are needed to create the process
|
||||
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, initialCwd, locale, cols, rows);
|
||||
const cwd = Uri.parse(require.toUrl('../../parts/terminal/node')).fsPath;
|
||||
const options = { env, cwd, execArgv: [] };
|
||||
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env, locale);
|
||||
|
||||
// Fork the process and listen for messages
|
||||
this._logService.debug(`Terminal process launching on ext host`, options);
|
||||
this._terminalProcesses[id] = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
|
||||
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
|
||||
switch (message.type) {
|
||||
case 'pid': this._proxy.$sendProcessPid(id, <number>message.content); break;
|
||||
case 'title': this._proxy.$sendProcessTitle(id, <string>message.content); break;
|
||||
case 'data': this._proxy.$sendProcessData(id, <string>message.content); break;
|
||||
}
|
||||
});
|
||||
this._terminalProcesses[id].on('exit', (exitCode) => this._onProcessExit(id, exitCode));
|
||||
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
|
||||
this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
|
||||
this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data));
|
||||
this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode));
|
||||
}
|
||||
|
||||
public $acceptProcessInput(id: number, data: string): void {
|
||||
if (this._terminalProcesses[id].connected) {
|
||||
this._terminalProcesses[id].send({ event: 'input', data });
|
||||
}
|
||||
this._terminalProcesses[id].input(data);
|
||||
}
|
||||
|
||||
public $acceptProcessResize(id: number, cols: number, rows: number): void {
|
||||
if (this._terminalProcesses[id].connected) {
|
||||
try {
|
||||
this._terminalProcesses[id].send({ event: 'resize', cols, rows });
|
||||
} catch (error) {
|
||||
// We tried to write to a closed pipe / channel.
|
||||
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
|
||||
throw (error);
|
||||
}
|
||||
try {
|
||||
this._terminalProcesses[id].resize(cols, rows);
|
||||
} catch (error) {
|
||||
// We tried to write to a closed pipe / channel.
|
||||
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
|
||||
throw (error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public $acceptProcessShutdown(id: number): void {
|
||||
if (this._terminalProcesses[id].connected) {
|
||||
this._terminalProcesses[id].send({ event: 'shutdown' });
|
||||
}
|
||||
this._terminalProcesses[id].shutdown();
|
||||
}
|
||||
|
||||
private _onProcessExit(id: number, exitCode: number): void {
|
||||
// Remove listeners
|
||||
const process = this._terminalProcesses[id];
|
||||
process.removeAllListeners('message');
|
||||
process.removeAllListeners('exit');
|
||||
this._terminalProcesses[id].dispose();
|
||||
|
||||
// Remove process reference
|
||||
delete this._terminalProcesses[id];
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom';
|
|||
import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { compareFileNames } from 'vs/base/common/comparers';
|
||||
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources';
|
||||
|
@ -20,7 +20,7 @@ import 'vs/css!./media/breadcrumbscontrol';
|
|||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
@ -31,19 +31,20 @@ import { IConstructorSignature2, IInstantiationService } from 'vs/platform/insta
|
|||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
|
||||
import { attachBreadcrumbsStyler, attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { breadcrumbsActiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { symbolKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
|
||||
|
@ -230,20 +231,10 @@ export class BreadcrumbsControl {
|
|||
return event.node;
|
||||
},
|
||||
render: (parent: HTMLElement) => {
|
||||
const container = document.createElement('div');
|
||||
parent.appendChild(container);
|
||||
const theme = this._themeService.getTheme();
|
||||
const color = theme.getColor(editorBackground).darken(theme.type === 'dark' ? .2 : .1);
|
||||
container.style.borderColor = color.toString();
|
||||
container.style.boxShadow = `2px 2px 3px ${color.toString()}`;
|
||||
container.style.position = 'absolute';
|
||||
container.style.zIndex = '1000';
|
||||
dom.addClasses(container, 'monaco-breadcrumbs-picker', 'show-file-icons');
|
||||
|
||||
let { element } = event.item as Item;
|
||||
let ctor: IConstructorSignature2<HTMLElement, BreadcrumbElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
|
||||
let res = this._instantiationService.createInstance(ctor, container, element);
|
||||
res.layout({ width: 330, height: 220 });
|
||||
let res = this._instantiationService.createInstance(ctor, parent, element);
|
||||
res.layout({ width: 220, height: 330 });
|
||||
let listener = res.onDidPickElement(data => {
|
||||
this._contextViewService.hideContextView();
|
||||
this._widget.setSelection(undefined);
|
||||
|
@ -290,15 +281,16 @@ export class BreadcrumbsControl {
|
|||
|
||||
export abstract class BreadcrumbsPicker {
|
||||
|
||||
readonly focus: dom.IFocusTracker;
|
||||
|
||||
protected readonly _onDidPickElement = new Emitter<any>();
|
||||
readonly onDidPickElement: Event<any> = this._onDidPickElement.event;
|
||||
|
||||
protected readonly _disposables = new Array<IDisposable>();
|
||||
protected readonly _domNode: HTMLDivElement;
|
||||
protected readonly _focus: dom.IFocusTracker;
|
||||
protected readonly _input: InputBox;
|
||||
protected readonly _tree: WorkbenchTree;
|
||||
|
||||
protected readonly _onDidPickElement = new Emitter<any>();
|
||||
|
||||
readonly onDidPickElement: Event<any> = this._onDidPickElement.event;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
input: BreadcrumbElement,
|
||||
|
@ -306,38 +298,122 @@ export abstract class BreadcrumbsPicker {
|
|||
@IThemeService protected readonly _themeService: IThemeService,
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.style.background = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString();
|
||||
this._domNode.className = 'monaco-breadcrumbs-picker';
|
||||
const color = this._themeService.getTheme().getColor(breadcrumbsActiveSelectionBackground);
|
||||
this._domNode.style.background = color.toString();
|
||||
this._domNode.style.boxShadow = `2px 2px 3px ${color.darken(.1)}`;
|
||||
this._domNode.style.position = 'absolute';
|
||||
this._domNode.style.zIndex = '1000';
|
||||
container.appendChild(this._domNode);
|
||||
|
||||
this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {});
|
||||
debounceEvent(this._tree.onDidChangeSelection, (_last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables);
|
||||
this._focus = dom.trackFocus(this._domNode);
|
||||
this._focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables);
|
||||
|
||||
const inputContainer = document.createElement('div');
|
||||
inputContainer.className = 'breadcrumbs-picker-input';
|
||||
this._domNode.appendChild(inputContainer);
|
||||
this._input = new InputBox(inputContainer, undefined, { placeholder: localize('placeholder', "Find") });
|
||||
this._input.setEnabled(false);
|
||||
this._disposables.push(attachListStyler(this._input, this._themeService));
|
||||
|
||||
const treeContainer = document.createElement('div');
|
||||
treeContainer.className = 'breadcrumbs-picker-tree';
|
||||
this._domNode.appendChild(treeContainer);
|
||||
const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined });
|
||||
this._tree = this._instantiationService.createInstance(WorkbenchTree, treeContainer, treeConifg, {});
|
||||
this._disposables.push(this._tree.onDidChangeSelection(e => {
|
||||
if (e.payload !== this) {
|
||||
setTimeout(_ => this._onDidChangeSelection(e)); // need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
|
||||
}
|
||||
}));
|
||||
|
||||
this._tree.setInput(this._getInput(input)).then(async _ => {
|
||||
|
||||
let selection = this._getInitialSelection(this._tree, input);
|
||||
if (selection) {
|
||||
await this._tree.reveal(selection);
|
||||
this._tree.setSelection([selection], this);
|
||||
this._tree.setFocus(selection);
|
||||
}
|
||||
|
||||
// input - interact with tree
|
||||
this._disposables.push(dom.addStandardDisposableListener(this._input.inputElement, 'keyup', event => {
|
||||
if (event.keyCode === KeyCode.DownArrow) {
|
||||
this._tree.focusNext();
|
||||
this._tree.domFocus();
|
||||
} else if (event.keyCode === KeyCode.UpArrow) {
|
||||
this._tree.focusPrevious();
|
||||
this._tree.domFocus();
|
||||
} else if (event.keyCode === KeyCode.Enter) {
|
||||
this._onDidChangeSelection({ selection: this._tree.getSelection() });
|
||||
} else if (event.keyCode === KeyCode.Escape) {
|
||||
this._input.value = '';
|
||||
this._tree.domFocus();
|
||||
}
|
||||
}));
|
||||
|
||||
// input - type to find
|
||||
this._disposables.push(this._input.onDidChange(async value => {
|
||||
let nav = this._tree.getNavigator(undefined, false);
|
||||
let topScore: FuzzyScore;
|
||||
let topElement: any;
|
||||
while (nav.next()) {
|
||||
let element = nav.current();
|
||||
let score = treeConifg.renderer.updateHighlights(this._tree, element, value);
|
||||
if (!topScore || score && topScore[0] < score[0]) {
|
||||
topScore = score;
|
||||
topElement = element;
|
||||
}
|
||||
this._tree.refresh(element).then(undefined, onUnexpectedError);
|
||||
}
|
||||
if (topElement) {
|
||||
await this._tree.reveal(topElement);
|
||||
this._tree.setFocus(topElement);
|
||||
this._tree.setSelection([topElement], this);
|
||||
}
|
||||
}));
|
||||
|
||||
this._input.setEnabled(true);
|
||||
|
||||
this.focus = dom.trackFocus(this._domNode);
|
||||
this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables);
|
||||
this._tree.setInput(this._getInput(input)).then(_ => {
|
||||
this._tree.focusFirst();
|
||||
this._tree.domFocus();
|
||||
}, onUnexpectedError);
|
||||
|
||||
// this._input.focus();
|
||||
this._tree.domFocus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
this._onDidPickElement.dispose();
|
||||
this._input.dispose();
|
||||
this._tree.dispose();
|
||||
this.focus.dispose();
|
||||
this._focus.dispose();
|
||||
}
|
||||
|
||||
layout(dim: dom.Dimension) {
|
||||
this._domNode.style.width = `${dim.width}px`;
|
||||
this._domNode.style.height = `${dim.height}px`;
|
||||
this._tree.layout(dim.height, dim.width);
|
||||
|
||||
this._input.layout();
|
||||
this._tree.layout(dim.height - this._input.height, dim.width);
|
||||
}
|
||||
|
||||
protected abstract _getInput(input: BreadcrumbElement): any;
|
||||
protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration;
|
||||
protected abstract _onDidChangeSelection(e: any): void;
|
||||
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
|
||||
protected abstract _completeTreeConfiguration(config: ITreeConfiguration2): ITreeConfiguration2;
|
||||
protected abstract _onDidChangeSelection(e: ISelectionEvent): void;
|
||||
}
|
||||
|
||||
interface IHighlightingRenderer {
|
||||
updateHighlights(tree: ITree, element: any, pattern: string): FuzzyScore;
|
||||
}
|
||||
|
||||
interface ITreeConfiguration2 extends ITreeConfiguration {
|
||||
renderer: IHighlightingRenderer & IRenderer;
|
||||
}
|
||||
|
||||
|
||||
//#region - Files
|
||||
|
||||
export class FileDataSource implements IDataSource {
|
||||
|
||||
private readonly _parents = new WeakMap<IFileStat, IFileStat>();
|
||||
|
@ -370,10 +446,12 @@ export class FileDataSource implements IDataSource {
|
|||
}
|
||||
}
|
||||
|
||||
export class FileRenderer implements IRenderer {
|
||||
export class FileRenderer implements IRenderer, IHighlightingRenderer {
|
||||
|
||||
private readonly _scores = new WeakMap<IFileStat, FuzzyScore>();
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) { }
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
|
@ -385,20 +463,27 @@ export class FileRenderer implements IRenderer {
|
|||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
|
||||
return this._instantiationService.createInstance(FileLabel, container, {});
|
||||
return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true });
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void {
|
||||
templateData.setFile(element.resource, {
|
||||
hidePath: true,
|
||||
fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE,
|
||||
fileDecorations: { colors: true, badges: true }
|
||||
fileDecorations: { colors: true, badges: true },
|
||||
matches: createMatches((this._scores.get(element) || [, []])[1])
|
||||
});
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
|
||||
updateHighlights(tree: ITree, element: any, pattern: string): FuzzyScore {
|
||||
let score = fuzzyScore(pattern, (element as IFileStat).name, undefined, true);
|
||||
this._scores.set(element, score);
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileSorter implements ISorter {
|
||||
|
@ -416,13 +501,23 @@ export class FileSorter implements ISorter {
|
|||
|
||||
export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
let { uri } = (input as FileElement);
|
||||
return dirname(uri);
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration {
|
||||
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
|
||||
let { uri } = (input as FileElement);
|
||||
let nav = tree.getNavigator();
|
||||
while (nav.next()) {
|
||||
if (isEqual(uri, (nav.current() as IFileStat).resource)) {
|
||||
return nav.current();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: ITreeConfiguration2): ITreeConfiguration2 {
|
||||
// todo@joh reuse explorer implementations?
|
||||
config.dataSource = this._instantiationService.createInstance(FileDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(FileRenderer);
|
||||
|
@ -438,17 +533,36 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
|||
}
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region - Symbols
|
||||
|
||||
class HighlightingOutlineRenderer extends OutlineRenderer implements IHighlightingRenderer {
|
||||
|
||||
updateHighlights(tree: ITree, element: any, pattern: string): FuzzyScore {
|
||||
if (element instanceof OutlineElement) {
|
||||
return element.score = fuzzyScore(pattern, element.symbol.name, undefined, true) || [-1, []];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
return (input as TreeElement).parent;
|
||||
let element = input as TreeElement;
|
||||
OutlineModel.get(element).updateMatches('');
|
||||
return (element).parent;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration {
|
||||
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
|
||||
return input;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: ITreeConfiguration2): ITreeConfiguration2 {
|
||||
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
|
||||
config.controller = this._instantiationService.createInstance(OutlineController, {});
|
||||
config.renderer = this._instantiationService.createInstance(HighlightingOutlineRenderer);
|
||||
config.sorter = new OutlineItemComparator();
|
||||
return config;
|
||||
}
|
||||
|
@ -464,6 +578,8 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
|||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region config
|
||||
|
||||
export abstract class BreadcrumbsConfig<T> {
|
||||
|
|
|
@ -114,6 +114,10 @@ export class EditorBreadcrumbsModel {
|
|||
});
|
||||
|
||||
OutlineModel.create(buffer, source.token).then(model => {
|
||||
|
||||
// copy the model
|
||||
model = model.adopt();
|
||||
|
||||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
|
||||
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => {
|
||||
timeout.cancelAndSet(() => {
|
||||
|
|
|
@ -26,7 +26,22 @@
|
|||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.monaco-breadcrumbs-picker {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
.monaco-workbench .monaco-breadcrumbs-picker {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .breadcrumbs-picker-input {
|
||||
padding: 5px 9px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .breadcrumbs-picker-tree {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .monaco-highlighted-label .highlight{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,16 @@
|
|||
line-height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2) {
|
||||
padding-left: 2px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
|
||||
|
|
|
@ -260,6 +260,7 @@
|
|||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control {
|
||||
flex: 1 100%;
|
||||
height: 25px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ResourceLabel } from 'vs/workbench/browser/labels';
|
|||
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
|
||||
import { IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
|
||||
|
@ -90,6 +90,10 @@ export class NoTabsTitleControl extends TitleControl {
|
|||
}
|
||||
}
|
||||
|
||||
getPreferredHeight(): number {
|
||||
return EDITOR_TITLE_HEIGHT;
|
||||
}
|
||||
|
||||
openEditor(editor: IEditorInput): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue