Merge branch 'master' into electron-2.0.x

This commit is contained in:
Benjamin Pasero 2018-07-13 07:00:04 +02:00
commit fd6c2340e2
143 changed files with 3064 additions and 1524 deletions

9
.vscode/launch.json vendored
View file

@ -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": [

View file

@ -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"
},
{

View file

@ -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',

View file

@ -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);

View file

@ -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

View file

@ -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

View 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/`

View file

@ -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.

View file

@ -0,0 +1,2 @@
src/**
tsconfig.json

View 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"
}
}

View 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"
}

View 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);
}
});
}

View 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);
}
});
}

View 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);
}
}
});
}

View 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}`));
}
});
});
}

View 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;
}

View 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'/>

View file

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./out",
"lib": [
"es2015"
],
"strict": true,
"noUnusedLocals": true,
"downlevelIteration": true
},
"include": [
"src/**/*"
]
}

View 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"

View 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.

View file

@ -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.

View 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/`

View file

@ -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.

View file

@ -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.

View 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.

View 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.

View 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.

View file

@ -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 @@
}
]
}
}
}

View file

@ -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();

View file

@ -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)) {

View file

@ -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(

View file

@ -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,

View file

@ -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);

View file

@ -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

View file

@ -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[]>();

View file

@ -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();

View file

@ -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

View file

@ -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) {

View file

@ -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() {

View file

@ -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(

View file

@ -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(

View file

@ -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();

View file

@ -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 {

View file

@ -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(

View file

@ -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

View file

@ -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';

View file

@ -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());
}

View file

@ -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> {

View file

@ -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';

View file

@ -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 = [

View file

@ -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 {
/**

View file

@ -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();

View file

@ -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';

View file

@ -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 {

View file

@ -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 {

View file

@ -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();

View file

@ -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) {

View file

@ -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();

View file

@ -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

View file

@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { workspace } from 'vscode';
import * as Proto from '../protocol';
import Logger from './logger';
enum Trace {
Off,
Messages,

View file

@ -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;
}

View file

@ -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();

View file

@ -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();

View file

@ -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 {

View file

@ -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;

View file

@ -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"

View file

@ -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": {

View file

@ -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 {

View file

@ -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');

View file

@ -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> {

View file

@ -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;

View file

@ -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) => {

View file

@ -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 = {

View file

@ -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> {

View file

@ -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);

View file

@ -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', '');

View file

@ -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 {

View file

@ -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', () => {

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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[]>;

View file

@ -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[]> {

View file

@ -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[]> {

View file

@ -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;

View file

@ -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);
}

View file

@ -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> {

View file

@ -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),

View file

@ -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
}

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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));
}

View file

@ -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;
}

View file

@ -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];

View file

@ -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> {

View file

@ -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(() => {

View file

@ -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;
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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