Merge branch 'master' into joao/compressed-trees

This commit is contained in:
Joao Moreno 2019-09-10 15:36:01 +02:00
commit 4cad4dff1f
94 changed files with 1353 additions and 775 deletions

View file

@ -119,7 +119,7 @@ const tasks = compilations.map(function (tsconfigFile) {
const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false);
const input = pipeline.tsProjectSrc();
const watchInput = watcher(src, srcOpts);
const watchInput = watcher(src, { ...srcOpts, ...{ readDelay: 200 } });
return watchInput
.pipe(util.incremental(pipeline, input))

View file

@ -93,6 +93,7 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series(
resources: vscodeResources,
loaderConfig: common.loaderConfig(nodeModules),
out: 'out-vscode',
inlineAmdImages: true,
bundleInfo: undefined
})
));

View file

@ -238,7 +238,8 @@ function prepareSnapPackage(arch) {
function buildSnapPackage(arch) {
const snapBuildPath = getSnapBuildPath(arch);
return shell.task(`cd ${snapBuildPath} && snapcraft build`);
// Default target for snapcraft runs: pull, build, stage and prime, and finally assembles the snap.
return shell.task(`cd ${snapBuildPath} && snapcraft`);
}
const BUILD_TARGETS = [

View file

@ -34,7 +34,8 @@ const nodeModules = Object.keys(product.dependencies || {})
const vscodeWebResources = [
// Workbench
'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,html}',
'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png}',
'out-build/vs/code/browser/workbench/workbench.html',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
'out-build/vs/**/markdown.css',

View file

@ -36,8 +36,8 @@ function getTypeScriptCompilerOptions(src) {
function createCompile(src, build, emitError) {
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) });
const ts = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
return function (token) {
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
function pipeline(token) {
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path)));
@ -48,7 +48,7 @@ function createCompile(src, build, emitError) {
.pipe(utf8Filter.restore)
.pipe(tsFilter)
.pipe(util.loadSourcemaps())
.pipe(ts(token))
.pipe(compilation(token))
.pipe(noDeclarationsFilter)
.pipe(build ? nls() : es.through())
.pipe(noDeclarationsFilter.restore)
@ -60,7 +60,11 @@ function createCompile(src, build, emitError) {
.pipe(tsFilter.restore)
.pipe(reporter.end(!!emitError));
return es.duplex(input, output);
}
pipeline.tsProjectSrc = () => {
return compilation.src({ base: src });
};
return pipeline;
}
function compileTask(src, out, build) {
return function () {
@ -81,7 +85,7 @@ function watchTask(out, build) {
return function () {
const compile = createCompile('src', build);
const src = gulp.src('src/**', { base: 'src' });
const watchSrc = watch('src/**', { base: 'src' });
const watchSrc = watch('src/**', { base: 'src', readDelay: 200 });
let generator = new MonacoGenerator(true);
generator.execute();
return watchSrc

View file

@ -39,13 +39,13 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions {
return options;
}
function createCompile(src: string, build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream {
function createCompile(src: string, build: boolean, emitError?: boolean) {
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) };
const ts = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
return function (token?: util.ICancellationToken) {
function pipeline(token?: util.ICancellationToken) {
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
@ -58,7 +58,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean): (token
.pipe(utf8Filter.restore)
.pipe(tsFilter)
.pipe(util.loadSourcemaps())
.pipe(ts(token))
.pipe(compilation(token))
.pipe(noDeclarationsFilter)
.pipe(build ? nls() : es.through())
.pipe(noDeclarationsFilter.restore)
@ -71,16 +71,18 @@ function createCompile(src: string, build: boolean, emitError?: boolean): (token
.pipe(reporter.end(!!emitError));
return es.duplex(input, output);
}
pipeline.tsProjectSrc = () => {
return compilation.src({ base: src });
};
return pipeline;
}
export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream {
return function () {
const compile = createCompile(src, build, true);
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
let generator = new MonacoGenerator(false);
if (src === 'src') {
generator.execute();
@ -99,7 +101,7 @@ export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteSt
const compile = createCompile('src', build);
const src = gulp.src('src/**', { base: 'src' });
const watchSrc = watch('src/**', { base: 'src' });
const watchSrc = watch('src/**', { base: 'src', readDelay: 200 });
let generator = new MonacoGenerator(true);
generator.execute();

View file

@ -5,6 +5,7 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const es = require("event-stream");
const fs = require("fs");
const gulp = require("gulp");
const concat = require("gulp-concat");
const minifyCSS = require("gulp-cssnano");
@ -132,6 +133,14 @@ function optimizeTask(opts) {
if (err || !result) {
return bundlesStream.emit('error', JSON.stringify(err));
}
if (opts.inlineAmdImages) {
try {
result = inlineAmdImages(src, result);
}
catch (err) {
return bundlesStream.emit('error', JSON.stringify(err));
}
}
toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream);
// Remove css inlined resources
const filteredResources = resources.slice();
@ -167,6 +176,39 @@ function optimizeTask(opts) {
};
}
exports.optimizeTask = optimizeTask;
function inlineAmdImages(src, result) {
for (const outputFile of result.files) {
for (const sourceFile of outputFile.sources) {
if (sourceFile.path && /\.js$/.test(sourceFile.path)) {
sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => {
let imagePath = m0;
// remove `` or ''
if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`')
|| (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) {
imagePath = imagePath.substr(1, imagePath.length - 2);
}
if (!/\.(png|svg)$/.test(imagePath)) {
console.log(`original: ${_}`);
return _;
}
const repoLocation = path.join(src, imagePath);
const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation);
if (!fs.existsSync(absoluteLocation)) {
const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`;
console.log(message);
throw new Error(message);
}
const fileContents = fs.readFileSync(absoluteLocation);
const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png';
// Mark the file as inlined so we don't ship it by itself
result.cssInlinedResources.push(repoLocation);
return `("data:${mime};base64,${fileContents.toString('base64')}")`;
});
}
}
}
return result;
}
/**
* Wrap around uglify and allow the preserveComments function
* to have a file "context" to include our copyright only once per file.
@ -202,9 +244,6 @@ function uglifyWithCopyrights() {
const output = input
.pipe(flatmap((stream, f) => {
return stream.pipe(minify({
compress: {
hoist_funs: true // required due to https://github.com/microsoft/vscode/issues/80202
},
output: {
comments: preserveComments(f),
max_line_len: 1024

View file

@ -6,6 +6,7 @@
'use strict';
import * as es from 'event-stream';
import * as fs from 'fs';
import * as gulp from 'gulp';
import * as concat from 'gulp-concat';
import * as minifyCSS from 'gulp-cssnano';
@ -159,6 +160,10 @@ export interface IOptimizeTaskOpts {
* (emit bundleInfo.json file)
*/
bundleInfo: boolean;
/**
* replace calls to `registerAndGetAmdImageURL` with data uris
*/
inlineAmdImages: boolean;
/**
* (out folder name)
*/
@ -192,6 +197,14 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
bundle.bundle(entryPoints, loaderConfig, function (err, result) {
if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); }
if (opts.inlineAmdImages) {
try {
result = inlineAmdImages(src, result);
} catch (err) {
return bundlesStream.emit('error', JSON.stringify(err));
}
}
toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream);
// Remove css inlined resources
@ -236,6 +249,42 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
};
}
function inlineAmdImages(src: string, result: bundle.IBundleResult): bundle.IBundleResult {
for (const outputFile of result.files) {
for (const sourceFile of outputFile.sources) {
if (sourceFile.path && /\.js$/.test(sourceFile.path)) {
sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => {
let imagePath = m0;
// remove `` or ''
if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`')
|| (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) {
imagePath = imagePath.substr(1, imagePath.length - 2);
}
if (!/\.(png|svg)$/.test(imagePath)) {
console.log(`original: ${_}`);
return _;
}
const repoLocation = path.join(src, imagePath);
const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation);
if (!fs.existsSync(absoluteLocation)) {
const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`;
console.log(message);
throw new Error(message);
}
const fileContents = fs.readFileSync(absoluteLocation);
const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png';
// Mark the file as inlined so we don't ship it by itself
result.cssInlinedResources.push(repoLocation);
return `("data:${mime};base64,${fileContents.toString('base64')}")`;
});
}
}
}
return result;
}
declare class FileWithCopyright extends VinylFile {
public __hasOurCopyright: boolean;
}
@ -278,9 +327,6 @@ function uglifyWithCopyrights(): NodeJS.ReadWriteStream {
const output = input
.pipe(flatmap((stream, f) => {
return stream.pipe(minify({
compress: {
hoist_funs: true // required due to https://github.com/microsoft/vscode/issues/80202
},
output: {
comments: preserveComments(<FileWithCopyright>f),
max_line_len: 1024

View file

@ -8,7 +8,10 @@ declare module "gulp-tsb" {
export interface IncrementalCompiler {
(token?: ICancellationToken): NodeJS.ReadWriteStream;
src(): NodeJS.ReadStream;
src(opts?: {
cwd?: string;
base?: string;
}): NodeJS.ReadStream;
}
export function create(projectPath: string, existingOptions: any, verbose?: boolean, onError?: (message: any) => void): IncrementalCompiler;

View file

@ -121,7 +121,7 @@ function loadSourcemaps() {
return;
}
if (!f.contents) {
cb(new Error('empty file'));
cb(undefined, f);
return;
}
const contents = f.contents.toString('utf8');

View file

@ -165,7 +165,7 @@ export function loadSourcemaps(): NodeJS.ReadWriteStream {
}
if (!f.contents) {
cb(new Error('empty file'));
cb(undefined, f);
return;
}

View file

@ -5,17 +5,6 @@
const es = require('event-stream');
/** Ugly hack for gulp-tsb */
function handleDeletions() {
return es.mapSync(f => {
if (/\.ts$/.test(f.relative) && !f.contents) {
f.contents = Buffer.from('');
f.stat = { mtime: new Date() };
}
return f;
});
}
let watch = undefined;
@ -24,6 +13,5 @@ if (!watch) {
}
module.exports = function () {
return watch.apply(null, arguments)
.pipe(handleDeletions());
return watch.apply(null, arguments);
};

View file

@ -40,7 +40,7 @@
"mime": "^1.3.4",
"minimist": "^1.2.0",
"request": "^2.85.0",
"terser": "^4.2.1",
"terser": "4.3.1",
"tslint": "^5.9.1",
"typescript": "3.6.2",
"vsce": "1.48.0",

View file

@ -2169,7 +2169,7 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
terser@*, terser@^4.2.1:
terser@*:
version "4.2.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.2.1.tgz#1052cfe17576c66e7bc70fcc7119f22b155bdac1"
integrity sha512-cGbc5utAcX4a9+2GGVX4DsenG6v0x3glnDi5hx8816X1McEAwPlPgRtXPJzSBsbpILxZ8MQMT0KvArLuE0HP5A==
@ -2178,6 +2178,15 @@ terser@*, terser@^4.2.1:
source-map "~0.6.1"
source-map-support "~0.5.12"
terser@4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.1.tgz#09820bcb3398299c4b48d9a86aefc65127d0ed65"
integrity sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
source-map-support "~0.5.12"
through2@2.X, through2@^2.0.0, through2@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"

View file

@ -71,6 +71,7 @@ class SyncStatusBar {
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.enableStatusBarSync'));
onEnablementChange(this.updateEnablement, this, this.disposables);
this.updateEnablement();
this._onDidChange.fire();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,7 @@
{
"extends": "../../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./dist/",
"module": "commonjs",
"target": "es6",
"jsx": "react",
"sourceMap": true,
"strict": true,
"strictBindCallApply": true,
"noImplicitAny": true,
"noUnusedLocals": true
"jsx": "react"
}
}
}

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.39.0",
"distro": "1d1e1c9985f210e8df9e24f68780e304eefa5f65",
"distro": "8516b3c6e37976c7f4a183ec1196a939f0ff11bf",
"author": {
"name": "Microsoft Corporation"
},
@ -52,7 +52,7 @@
"vscode-ripgrep": "^1.5.6",
"vscode-sqlite3": "4.0.8",
"vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta107",
"xterm": "3.15.0-beta108",
"xterm-addon-search": "0.2.0-beta5",
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",
@ -97,7 +97,7 @@
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.4",
"gulp-shell": "^0.6.5",
"gulp-tsb": "4.0.1",
"gulp-tsb": "4.0.2",
"gulp-tslint": "^8.1.3",
"gulp-untar": "^0.0.7",
"gulp-vinyl-zip": "^2.1.2",

View file

@ -21,7 +21,7 @@
"vscode-proxy-agent": "0.4.0",
"vscode-ripgrep": "^1.5.6",
"vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta107",
"xterm": "3.15.0-beta108",
"xterm-addon-search": "0.2.0-beta5",
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",

View file

@ -6,7 +6,7 @@
"onigasm-umd": "^2.2.2",
"semver-umd": "^5.5.3",
"vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta107",
"xterm": "3.15.0-beta108",
"xterm-addon-search": "0.2.0-beta5",
"xterm-addon-web-links": "0.1.0-beta10"
}

View file

@ -109,7 +109,7 @@ xterm-addon-web-links@0.1.0-beta10:
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23"
integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg==
xterm@3.15.0-beta107:
version "3.15.0-beta107"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta107.tgz#d1fea6434e66350c227b3993aabd8c5eb3afa427"
integrity sha512-a1bGMbh25pQzBT5lBXJaXD+pM9PX69iXfYrKL4PCQ5jgbUIu45SRCI2MnpS5X1WC+hCcOyn09PZuAdqTcpYeFA==
xterm@3.15.0-beta108:
version "3.15.0-beta108"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta108.tgz#d113f6d1e4d4b7645ab3ff002c81a4dba8a78a28"
integrity sha512-btZXgI9BeawFzitw3EZvFPsH3kfBgJS55LOdz9DjEany3y9FfvmnVhq7wn4x1PmTU/djHobGGhMNemxptk+ryg==

View file

@ -1173,10 +1173,10 @@ xterm-addon-web-links@0.1.0-beta10:
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23"
integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg==
xterm@3.15.0-beta107:
version "3.15.0-beta107"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta107.tgz#d1fea6434e66350c227b3993aabd8c5eb3afa427"
integrity sha512-a1bGMbh25pQzBT5lBXJaXD+pM9PX69iXfYrKL4PCQ5jgbUIu45SRCI2MnpS5X1WC+hCcOyn09PZuAdqTcpYeFA==
xterm@3.15.0-beta108:
version "3.15.0-beta108"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta108.tgz#d113f6d1e4d4b7645ab3ff002c81a4dba8a78a28"
integrity sha512-btZXgI9BeawFzitw3EZvFPsH3kfBgJS55LOdz9DjEany3y9FfvmnVhq7wn4x1PmTU/djHobGGhMNemxptk+ryg==
yauzl@^2.9.2:
version "2.10.0"

View file

@ -4,7 +4,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# test that VSCode wasn't installed inside WSL
if grep -qi Microsoft /proc/version; then
if grep -qi Microsoft /proc/version && [ -z "$DONT_PROMPT_WSL_INSTALL" ]; then
echo "To use VS Code with the Windows Subsystem for Linux, please install VS Code in Windows and uninstall the Linux version in WSL. You can then use the '@@PRODNAME@@' command in a WSL terminal just as you would in a normal command prompt." 1>&2
read -e -p "Do you want to continue anyways ? [y/N] " YN

View file

@ -14,6 +14,7 @@ import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { escape } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
codeBlockRenderer?: (modeId: string, value: string) => Promise<string>;
@ -172,9 +173,9 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
renderer
};
const allowedSchemes = ['http', 'https', 'mailto', 'data'];
const allowedSchemes = [Schemas.http, Schemas.https, Schemas.mailto, Schemas.data, Schemas.file, Schemas.vscodeRemote];
if (markdown.isTrusted) {
allowedSchemes.push('command');
allowedSchemes.push(Schemas.command);
}
const renderedMarkdown = marked.parse(markdown.value, markedOptions);

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { getOrDefault } from 'vs/base/common/objects';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import * as DOM from 'vs/base/browser/dom';
import { Event, Emitter } from 'vs/base/common/event';
@ -188,7 +188,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private currentDragFeedbackDisposable: IDisposable = Disposable.None;
private onDragLeaveTimeout: IDisposable = Disposable.None;
private disposables: IDisposable[];
private readonly disposables: DisposableStore = new DisposableStore();
private _onDidChangeContentHeight = new Emitter<number>();
readonly onDidChangeContentHeight: Event<number> = Event.latch(this._onDidChangeContentHeight.event);
@ -214,7 +214,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.renderers.set(renderer.templateId, renderer);
}
this.cache = new RowCache(this.renderers);
this.cache = this.disposables.add(new RowCache(this.renderers));
this.lastRenderTop = 0;
this.lastRenderHeight = 0;
@ -238,18 +238,16 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.rowsContainer.className = 'monaco-list-rows';
Gesture.addTarget(this.rowsContainer);
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
alwaysConsumeMouseWheel: true,
horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden,
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
});
}));
this.domNode.appendChild(this.scrollableElement.getDomNode());
container.appendChild(this.domNode);
this.disposables = [this.scrollableElement, this.cache];
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables);
@ -1178,6 +1176,6 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.domNode.parentNode.removeChild(this.domNode);
}
this.disposables = dispose(this.disposables);
dispose(this.disposables);
}
}

View file

@ -66,19 +66,32 @@ interface IDataTreeListTemplateData<T> {
templateData: T;
}
class AsyncDataTreeNodeWrapper<TInput, T, TFilterData> implements ITreeNode<TInput | T, TFilterData> {
class NodeMapper<TInput, T, TFilterData> {
get element(): T { return this.node.element!.element as T; }
get children(): ITreeNode<T, TFilterData>[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); }
get depth(): number { return this.node.depth; }
get visibleChildrenCount(): number { return this.node.visibleChildrenCount; }
get visibleChildIndex(): number { return this.node.visibleChildIndex; }
get collapsible(): boolean { return this.node.collapsible; }
get collapsed(): boolean { return this.node.collapsed; }
get visible(): boolean { return this.node.visible; }
get filterData(): TFilterData | undefined { return this.node.filterData; }
private map = new WeakMap<ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>, ITreeNode<TInput | T, TFilterData>>();
constructor(private node: ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>) { }
mapNode(node: ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>): ITreeNode<TInput | T, TFilterData> {
const that = this;
let result = this.map.get(node);
if (!result) {
result = new Proxy(node, {
get(obj, prop) {
if (prop === 'element') {
return node.element!.element;
} else if (prop === 'children') {
return node.children.map(child => that.mapNode(child));
}
return (obj as any)[prop];
}
}) as unknown as ITreeNode<TInput | T, TFilterData>;
this.map.set(node, result);
}
return result;
}
}
class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRenderer<IAsyncDataTreeNode<TInput, T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
@ -89,6 +102,7 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
constructor(
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
private nodeMapper: NodeMapper<TInput, T, TFilterData>,
readonly onDidChangeTwistieState: Event<IAsyncDataTreeNode<TInput, T>>
) {
this.templateId = renderer.templateId;
@ -100,7 +114,7 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
}
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
this.renderer.renderElement(this.nodeMapper.mapNode(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
@ -110,7 +124,7 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (this.renderer.disposeElement) {
this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
this.renderer.disposeElement(this.nodeMapper.mapNode(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
}
@ -305,6 +319,8 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
private readonly _onDidRender = new Emitter<void>();
private readonly _onDidChangeNodeSlowState = new Emitter<IAsyncDataTreeNode<TInput, T>>();
private readonly nodeMapper = new NodeMapper<TInput, T, TFilterData>();
protected readonly disposables: IDisposable[] = [];
get onDidScroll(): Event<ScrollEvent> { return this.tree.onDidScroll; }
@ -351,7 +367,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
this.collapseByDefault = options.collapseByDefault;
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event));
const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this.nodeMapper, this._onDidChangeNodeSlowState.event));
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
this.tree = new ObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
@ -509,7 +525,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
getNode(element: TInput | T = this.root.element): ITreeNode<TInput | T, TFilterData> {
const dataNode = this.getDataNode(element);
const node = this.tree.getNode(dataNode === this.root ? null : dataNode);
return new AsyncDataTreeNodeWrapper<TInput, T, TFilterData>(node);
return this.nodeMapper.mapNode(node);
}
collapse(element: T, recursive: boolean = false): boolean {

View file

@ -270,7 +270,7 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
private _setListNodeCollapseState(node: IIndexTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, update: CollapseStateUpdate): boolean {
const result = this._setNodeCollapseState(node, update, false);
if (!revealed || !node.visible) {
if (!revealed || !node.visible || !result) {
return result;
}

View file

@ -8,3 +8,11 @@ import { URI } from 'vs/base/common/uri';
export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string {
return URI.parse(requirefn.toUrl(relativePath)).fsPath;
}
/**
* Reference a resource that might be inlined.
* Do not rename this method unless you adopt the build scripts.
*/
export function registerAndGetAmdImageURL(absolutePath: string): string {
return require.toUrl(absolutePath);
}

View file

@ -5,11 +5,6 @@
import { CharCode } from 'vs/base/common/charCode';
/**
* The empty string.
*/
export const empty = '';
export function isFalsyOrWhitespace(str: string | undefined): boolean {
if (!str || typeof str !== 'string') {
return true;
@ -608,7 +603,7 @@ export function lcut(text: string, n: number) {
re.lastIndex += 1;
}
return text.substring(i).replace(/^\s/, empty);
return text.substring(i).replace(/^\s/, '');
}
// Escape codes
@ -636,7 +631,7 @@ export const removeAccents: (str: string) => string = (function () {
// see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463
const regex = /[\u0300-\u036f]/g;
return function (str: string) {
return (str as any).normalize('NFD').replace(regex, empty);
return (str as any).normalize('NFD').replace(regex, '');
};
}
})();

View file

@ -0,0 +1,210 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchConstructionOptions, create } from 'vs/workbench/workbench.web.api';
import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import { Event, Emitter } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { CancellationToken } from 'vs/base/common/cancellation';
import { streamToBuffer } from 'vs/base/common/buffer';
import { Disposable } from 'vs/base/common/lifecycle';
import { request } from 'vs/base/parts/request/browser/request';
import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService';
export function main(): void {
const options: IWorkbenchConstructionOptions = JSON.parse(document.getElementById('vscode-workbench-web-configuration')!.getAttribute('data-settings')!);
options.urlCallbackProvider = new PollingURLCallbackProvider();
options.credentialsProvider = new LocalStorageCredentialsProvider();
create(document.body, options);
}
interface ICredential {
service: string;
account: string;
password: string;
}
class LocalStorageCredentialsProvider implements ICredentialsProvider {
static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider';
private _credentials: ICredential[];
private get credentials(): ICredential[] {
if (!this._credentials) {
try {
const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY);
if (serializedCredentials) {
this._credentials = JSON.parse(serializedCredentials);
}
} catch (error) {
// ignore
}
if (!Array.isArray(this._credentials)) {
this._credentials = [];
}
}
return this._credentials;
}
private save(): void {
window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY, JSON.stringify(this.credentials));
}
async getPassword(service: string, account: string): Promise<string | null> {
return this.doGetPassword(service, account);
}
private async doGetPassword(service: string, account?: string): Promise<string | null> {
for (const credential of this.credentials) {
if (credential.service === service) {
if (typeof account !== 'string' || account === credential.account) {
return credential.password;
}
}
}
return null;
}
async setPassword(service: string, account: string, password: string): Promise<void> {
this.deletePassword(service, account);
this.credentials.push({ service, account, password });
this.save();
}
async deletePassword(service: string, account: string): Promise<boolean> {
let found = false;
this._credentials = this.credentials.filter(credential => {
if (credential.service === service && credential.account === account) {
found = true;
return false;
}
return true;
});
if (found) {
this.save();
}
return found;
}
async findPassword(service: string): Promise<string | null> {
return this.doGetPassword(service);
}
async findCredentials(service: string): Promise<Array<{ account: string, password: string }>> {
return this.credentials
.filter(credential => credential.service === service)
.map(({ account, password }) => ({ account, password }));
}
}
class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider {
static FETCH_INTERVAL = 500; // fetch every 500ms
static FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min
static QUERY_KEYS = {
REQUEST_ID: 'vscode-requestId',
SCHEME: 'vscode-scheme',
AUTHORITY: 'vscode-authority',
PATH: 'vscode-path',
QUERY: 'vscode-query',
FRAGMENT: 'vscode-fragment'
};
private readonly _onCallback: Emitter<URI> = this._register(new Emitter<URI>());
readonly onCallback: Event<URI> = this._onCallback.event;
create(options?: Partial<UriComponents>): URI {
const queryValues: Map<string, string> = new Map();
const requestId = generateUuid();
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);
const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined };
if (scheme) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.SCHEME, scheme);
}
if (authority) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority);
}
if (path) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.PATH, path);
}
if (query) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.QUERY, query);
}
if (fragment) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment);
}
// Start to poll on the callback being fired
this.periodicFetchCallback(requestId, Date.now());
return this.doCreateUri('/callback', queryValues);
}
private async periodicFetchCallback(requestId: string, startTime: number): Promise<void> {
// Ask server for callback results
const queryValues: Map<string, string> = new Map();
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);
const result = await request({
url: this.doCreateUri('/fetch-callback', queryValues).toString(true)
}, CancellationToken.None);
// Check for callback results
const content = await streamToBuffer(result.stream);
if (content.byteLength > 0) {
try {
this._onCallback.fire(URI.revive(JSON.parse(content.toString())));
} catch (error) {
console.error(error);
}
return; // done
}
// Continue fetching unless we hit the timeout
if (Date.now() - startTime < PollingURLCallbackProvider.FETCH_TIMEOUT) {
setTimeout(() => this.periodicFetchCallback(requestId, startTime), PollingURLCallbackProvider.FETCH_INTERVAL);
}
}
private doCreateUri(path: string, queryValues: Map<string, string>): URI {
let query: string | undefined = undefined;
if (queryValues) {
let index = 0;
queryValues.forEach((value, key) => {
if (!query) {
query = '';
}
const prefix = (index++ === 0) ? '' : '&';
query += `${prefix}${key}=${encodeURIComponent(value)}`;
});
}
return URI.parse(window.location.href).with({ path, query });
}
}

View file

@ -0,0 +1,65 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!-- Disable pinch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- Content Security Policy -->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
img-src 'self' https: data: blob:;
media-src 'none';
script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-4DqvCTjCHj2KW4QxC/Yt6uBwMRyYiEg7kOoykSEkonQ=' 'sha256-g94DXzh59qc37AZjL7sV1lYN7OX4K1fgKl4ts5tAQDw=';
child-src 'self';
frame-src 'self' {{WEBVIEW_ENDPOINT}} https://*.vscode-webview-test.com;
worker-src 'self';
style-src 'self' 'unsafe-inline';
connect-src 'self' ws: wss: https:;
font-src 'self' blob:;
manifest-src 'self';
">
<!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONGIGURATION}}">
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="/manifest.json">
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static/out/vs/workbench/workbench.web.api.css">
</head>
<body aria-label="">
</body>
<!-- Startup (do not modify order of script tags!) -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
self.require = {
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
'@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`,
}
};
</script>
<script src="./static/out/vs/loader.js"></script>
<script>
// NOTE: Changes to inline scripts require update of content security policy
require(['vs/code/browser/workbench/web.main'], function (web) {
web.main();
});
</script>
</html>

View file

@ -10,18 +10,19 @@
<!-- Content Security Policy -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none';
content="
default-src 'self';
img-src 'self' https: data: blob:;
media-src 'none';
script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https:;
script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-4DqvCTjCHj2KW4QxC/Yt6uBwMRyYiEg7kOoykSEkonQ=' 'sha256-kXwJWoOluR7vyWhuqykdzYEHvOuOu2ZZhnBm0EBbYvU=';
child-src 'self';
frame-src 'self' {{WEBVIEW_ENDPOINT}} https://*.vscode-webview-test.com;
worker-src 'self';
style-src 'self' 'unsafe-inline';
connect-src 'self' ws: wss: https:;
font-src 'self' blob:;
manifest-src 'self';"
>
manifest-src 'self';
">
<!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONGIGURATION}}">
@ -33,13 +34,40 @@
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="/manifest.json">
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static/out/vs/workbench/workbench.web.api.css">
<!-- Prefetch to avoid waterfall -->
<link rel="prefetch" href="./static/node_modules/semver-umd/lib/semver-umd.js">
<link rel="prefetch" href="./static/node_modules/onigasm-umd/release/main.js"> <!-- TODO@ben TODO@alex: should be lazy -->
<link rel="prefetch" href="./static/out/vs/code/browser/workbench/web.main.js"> <!--TODO@ben: Why is it not built -->
<link rel="prefetch" href="./static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js">
</head>
<body aria-label="">
</body>
<!-- Startup (do not modify order of script tags!) -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
self.require = {
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
'@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`,
}
};
</script>
<script src="./static/out/vs/loader.js"></script>
<script src="./static/out/vs/workbench/workbench.web.api.nls.js"></script>
<script src="./static/out/vs/code/browser/workbench/workbench.js"></script>
<script src="./static/out/vs/workbench/workbench.web.api.js"></script>
<!-- <script src="./static/out/vs/code/browser/workbench/web.main.js"></script> TODO@ben: Why is it not built-->
<script>
require(['vs/code/browser/workbench/web.main'], function (web) {
web.main();
});
</script>
</html>

View file

@ -1,31 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
(function () {
/** @type any */
const amdLoader = require;
amdLoader.config({
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
'@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`,
}
});
amdLoader(['vs/workbench/workbench.web.api'], function (api) {
const options = JSON.parse(document.getElementById('vscode-workbench-web-configuration').getAttribute('data-settings'));
api.create(document.body, options);
});
})();

View file

@ -26,7 +26,7 @@ import { IStateService } from 'vs/platform/state/common/state';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IURLService } from 'vs/platform/url/common/url';
import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/node/urlIpc';
import { URLHandlerChannelClient, URLServiceChannel, URLHandlerRouter } from 'vs/platform/url/common/urlIpc';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
@ -579,7 +579,8 @@ export class CodeApplication extends Disposable {
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', activeWindowRouter);
const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter);
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter);
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,

View file

@ -11,7 +11,7 @@ import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, R
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import product from 'vs/platform/product/node/product';
import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
@ -574,7 +574,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
windowConfiguration.partsSplashPath = path.join(this.environmentService.userDataPath, 'rapid_render.json');
// Config (combination of process.argv and window configuration)
const environment = parseArgs(process.argv);
const environment = parseArgs(process.argv, OPTIONS);
const config = objects.assign(environment, windowConfiguration);
for (const key in config) {
const configValue = (config as any)[key];

View file

@ -5,7 +5,7 @@
import { spawn, ChildProcess, SpawnOptions } from 'child_process';
import { assign } from 'vs/base/common/objects';
import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile } from 'vs/platform/environment/node/argv';
import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile, OPTIONS } from 'vs/platform/environment/node/argv';
import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import product from 'vs/platform/product/node/product';
@ -47,7 +47,7 @@ export async function main(argv: string[]): Promise<any> {
// Help
if (args.help) {
const executable = `${product.applicationName}${os.platform() === 'win32' ? '.exe' : ''}`;
console.log(buildHelpMessage(product.nameLong, executable, pkg.version));
console.log(buildHelpMessage(product.nameLong, executable, pkg.version, OPTIONS));
}
// Version Info

View file

@ -4,29 +4,28 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { formatOptions, Option, addArg } from 'vs/platform/environment/node/argv';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
suite('formatOptions', () => {
function o(id: keyof ParsedArgs, description: string): Option {
function o(description: string): Option<any> {
return {
id, description, type: 'string'
description, type: 'string'
};
}
test('Text should display small columns correctly', () => {
assert.deepEqual(
formatOptions([
o('add', 'bar')
], 80),
formatOptions({
'add': o('bar')
}, 80),
[' --add bar']
);
assert.deepEqual(
formatOptions([
o('add', 'bar'),
o('wait', 'ba'),
o('trace', 'b')
], 80),
formatOptions({
'add': o('bar'),
'wait': o('ba'),
'trace': o('b')
}, 80),
[
' --add bar',
' --wait ba',
@ -36,9 +35,9 @@ suite('formatOptions', () => {
test('Text should wrap', () => {
assert.deepEqual(
formatOptions([
o('add', (<any>'bar ').repeat(9))
], 40),
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 40),
[
' --add bar bar bar bar bar bar bar bar',
' bar'
@ -47,9 +46,9 @@ suite('formatOptions', () => {
test('Text should revert to the condensed view when the terminal is too narrow', () => {
assert.deepEqual(
formatOptions([
o('add', (<any>'bar ').repeat(9))
], 30),
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 30),
[
' --add',
' bar bar bar bar bar bar bar bar bar '

View file

@ -343,12 +343,8 @@ export namespace CoreNavigationCommands {
const validatedPosition = context.model.validatePosition(args.position);
const validatedViewPosition = context.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition);
let fromViewLineNumber = prevColumnSelectData.fromViewLineNumber;
let fromViewVisualColumn = prevColumnSelectData.fromViewVisualColumn;
if (!prevColumnSelectData.isReal && args.setAnchorIfNotSet) {
fromViewLineNumber = validatedViewPosition.lineNumber;
fromViewVisualColumn = args.mouseColumn - 1;
}
let fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber;
let fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1;
return ColumnSelection.columnSelect(context.config, context.viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1);
}
});

View file

@ -133,11 +133,7 @@ export class ViewController {
public dispatchMouse(data: IMouseDispatchData): void {
if (data.middleButton) {
if (data.inSelectionMode) {
this._columnSelect(data.position, data.mouseColumn, true);
} else {
this.moveTo(data.position);
}
this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode);
} else if (data.startedOnLineNumbers) {
// If the dragging started on the gutter, then have operations work on the entire line
if (this._hasMulticursorModifier(data)) {
@ -183,7 +179,7 @@ export class ViewController {
if (this._hasMulticursorModifier(data)) {
if (!this._hasNonMulticursorModifier(data)) {
if (data.shiftKey) {
this._columnSelect(data.position, data.mouseColumn, false);
this._columnSelect(data.position, data.mouseColumn, true);
} else {
// Do multi-cursor operations only when purely alt is pressed
if (data.inSelectionMode) {
@ -223,13 +219,13 @@ export class ViewController {
this._execMouseCommand(CoreNavigationCommands.MoveToSelect, this._usualArgs(viewPosition));
}
private _columnSelect(viewPosition: Position, mouseColumn: number, setAnchorIfNotSet: boolean): void {
private _columnSelect(viewPosition: Position, mouseColumn: number, doColumnSelect: boolean): void {
viewPosition = this._validateViewColumn(viewPosition);
this._execMouseCommand(CoreNavigationCommands.ColumnSelect, {
position: this._convertViewToModelPosition(viewPosition),
viewPosition: viewPosition,
mouseColumn: mouseColumn,
setAnchorIfNotSet: setAnchorIfNotSet
doColumnSelect: doColumnSelect
});
}

View file

@ -258,7 +258,6 @@ export interface IEditorOptions {
* Defaults to '.'.
*/
wordWrapBreakObtrusiveCharacters?: string;
/**
* Performance guard: Stop rendering a line after x characters.
* Defaults to 10000.
@ -303,6 +302,11 @@ export interface IEditorOptions {
* Defaults to true
*/
multiCursorMergeOverlapping?: boolean;
/**
* Configure the behaviour when pasting a text with the line count equal to the cursor count.
* Defaults to 'spread'.
*/
multiCursorPaste?: 'spread' | 'full';
/**
* Configure the editor's accessibility support.
* Defaults to 'auto'. It is best to leave this to 'auto'.
@ -863,7 +867,7 @@ class EditorEnumOption<K1 extends EditorOption, T extends string, V> extends Bas
if (typeof schema !== 'undefined') {
schema.type = 'string';
schema.enum = allowedValues;
schema.default = defaultValue;
schema.default = defaultStringValue;
}
super(id, name, defaultValue, schema);
this._allowedValues = allowedValues;
@ -1950,11 +1954,18 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
}
if (typeof _input === 'object') {
const input = _input as IQuickSuggestionsOptions;
return {
const opts = {
other: EditorBooleanOption.boolean(input.other, this.defaultValue.other),
comments: EditorBooleanOption.boolean(input.comments, this.defaultValue.comments),
strings: EditorBooleanOption.boolean(input.strings, this.defaultValue.strings),
};
if (opts.other && opts.comments && opts.strings) {
return true; // all on
} else if (!opts.other && !opts.comments && !opts.strings) {
return false; // all off
} else {
return opts;
}
}
return this.defaultValue;
}
@ -2671,6 +2682,7 @@ export const enum EditorOption {
mouseWheelZoom,
multiCursorMergeOverlapping,
multiCursorModifier,
multiCursorPaste,
occurrencesHighlight,
overviewRulerBorder,
overviewRulerLanes,
@ -2985,6 +2997,18 @@ export const EditorOptions = {
}, "The modifier to be used to add multiple cursors with the mouse. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier).")
}
)),
multiCursorPaste: register(new EditorStringEnumOption(
EditorOption.multiCursorPaste, 'multiCursorPaste',
'spread' as 'spread' | 'full',
['spread', 'full'] as const,
{
markdownEnumDescriptions: [
nls.localize('multiCursorPaste.spread', "Each cursor pastes a single line of the text."),
nls.localize('multiCursorPaste.full', "Each cursor pastes the full text.")
],
markdownDescription: nls.localize('multiCursorPaste', "Controls pasting when the line count of the pasted text matches the cursor count.")
}
)),
occurrencesHighlight: register(new EditorBooleanOption(
EditorOption.occurrencesHighlight, 'occurrencesHighlight', true,
{ description: nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences.") }

View file

@ -400,7 +400,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._columnSelectData;
}
const primaryCursor = this._cursors.getPrimaryCursor();
const primaryPos = primaryCursor.viewState.position;
const primaryPos = primaryCursor.viewState.selectionStart.getStartPosition();
const viewLineNumber = primaryPos.lineNumber;
const viewVisualColumn = CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos);
return {

View file

@ -97,6 +97,7 @@ export class CursorConfiguration {
public readonly emptySelectionClipboard: boolean;
public readonly copyWithSyntaxHighlighting: boolean;
public readonly multiCursorMergeOverlapping: boolean;
public readonly multiCursorPaste: 'spread' | 'full';
public readonly autoClosingBrackets: EditorAutoClosingStrategy;
public readonly autoClosingQuotes: EditorAutoClosingStrategy;
public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy;
@ -116,6 +117,7 @@ export class CursorConfiguration {
|| e.hasChanged(EditorOption.wordSeparators)
|| e.hasChanged(EditorOption.emptySelectionClipboard)
|| e.hasChanged(EditorOption.multiCursorMergeOverlapping)
|| e.hasChanged(EditorOption.multiCursorPaste)
|| e.hasChanged(EditorOption.autoClosingBrackets)
|| e.hasChanged(EditorOption.autoClosingQuotes)
|| e.hasChanged(EditorOption.autoClosingOvertype)
@ -147,6 +149,7 @@ export class CursorConfiguration {
this.emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard);
this.copyWithSyntaxHighlighting = options.get(EditorOption.copyWithSyntaxHighlighting);
this.multiCursorMergeOverlapping = options.get(EditorOption.multiCursorMergeOverlapping);
this.multiCursorPaste = options.get(EditorOption.multiCursorPaste);
this.autoClosingBrackets = options.get(EditorOption.autoClosingBrackets);
this.autoClosingQuotes = options.get(EditorOption.autoClosingQuotes);
this.autoClosingOvertype = options.get(EditorOption.autoClosingOvertype);

View file

@ -105,7 +105,7 @@ export class TypeOperations {
});
}
private static _distributePasteToCursors(selections: Selection[], text: string, pasteOnNewLine: boolean, multicursorText: string[]): string[] | null {
private static _distributePasteToCursors(config: CursorConfiguration, selections: Selection[], text: string, pasteOnNewLine: boolean, multicursorText: string[]): string[] | null {
if (pasteOnNewLine) {
return null;
}
@ -118,20 +118,27 @@ export class TypeOperations {
return multicursorText;
}
// Remove trailing \n if present
if (text.charCodeAt(text.length - 1) === CharCode.LineFeed) {
text = text.substr(0, text.length - 1);
}
let lines = text.split(/\r\n|\r|\n/);
if (lines.length === selections.length) {
return lines;
if (config.multiCursorPaste === 'spread') {
// Try to spread the pasted text in case the line count matches the cursor count
// Remove trailing \n if present
if (text.charCodeAt(text.length - 1) === CharCode.LineFeed) {
text = text.substr(0, text.length - 1);
}
// Remove trailing \r if present
if (text.charCodeAt(text.length - 1) === CharCode.CarriageReturn) {
text = text.substr(0, text.length - 1);
}
let lines = text.split(/\r\n|\r|\n/);
if (lines.length === selections.length) {
return lines;
}
}
return null;
}
public static paste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string, pasteOnNewLine: boolean, multicursorText: string[]): EditOperationResult {
const distributedPaste = this._distributePasteToCursors(selections, text, pasteOnNewLine, multicursorText);
const distributedPaste = this._distributePasteToCursors(config, selections, text, pasteOnNewLine, multicursorText);
if (distributedPaste) {
selections = selections.sort(Range.compareRangesUsingStarts);

View file

@ -2621,8 +2621,8 @@ class DecorationOptions implements model.IDecorationOptions {
readonly darkColor: string | ThemeColor;
constructor(options: model.IDecorationOptions) {
this.color = options.color || strings.empty;
this.darkColor = options.darkColor || strings.empty;
this.color = options.color || '';
this.darkColor = options.darkColor || '';
}
}
@ -2658,7 +2658,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
}
let c = color ? theme.getColor(color.id) : null;
if (!c) {
return strings.empty;
return '';
}
return c.toString();
}

View file

@ -74,9 +74,9 @@ export class FilePreview implements IDisposable {
const beforeRange = new Range(startLineNumber, word.startColumn, startLineNumber, startColumn);
const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Number.MAX_VALUE);
const before = model.getValueInRange(beforeRange).replace(/^\s+/, strings.empty);
const before = model.getValueInRange(beforeRange).replace(/^\s+/, '');
const inside = model.getValueInRange(range);
const after = model.getValueInRange(afterRange).replace(/\s+$/, strings.empty);
const after = model.getValueInRange(afterRange).replace(/\s+$/, '');
return {
value: before + inside + after,

View file

@ -711,7 +711,7 @@ suite('Editor Controller - Cursor', () => {
position: new Position(4, 4),
viewPosition: new Position(4, 4),
mouseColumn: 15,
setAnchorIfNotSet: false
doColumnSelect: true
});
let expectedSelections = [
@ -747,7 +747,7 @@ suite('Editor Controller - Cursor', () => {
position: new Position(4, 1),
viewPosition: new Position(4, 1),
mouseColumn: 1,
setAnchorIfNotSet: false
doColumnSelect: true
});
assertCursor(cursor, [
@ -787,7 +787,7 @@ suite('Editor Controller - Cursor', () => {
position: new Position(1, 1),
viewPosition: new Position(1, 1),
mouseColumn: 1,
setAnchorIfNotSet: false
doColumnSelect: true
});
assertCursor(cursor, [
new Selection(10, 10, 10, 1),
@ -806,7 +806,7 @@ suite('Editor Controller - Cursor', () => {
position: new Position(1, 1),
viewPosition: new Position(1, 1),
mouseColumn: 1,
setAnchorIfNotSet: false
doColumnSelect: true
});
assertCursor(cursor, [
new Selection(10, 10, 10, 1),
@ -1566,6 +1566,37 @@ suite('Editor Controller - Regression tests', () => {
});
});
test('issue #43722: Multiline paste doesn\'t work anymore', () => {
usingCursor({
text: [
'test',
'test',
'test',
'test'
],
}, (model, cursor) => {
cursor.setSelections('test', [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
new Selection(4, 1, 4, 5),
]);
cursorCommand(cursor, H.Paste, {
text: 'aaa\r\nbbb\r\nccc\r\nddd\r\n',
pasteOnNewLine: false,
multicursorText: null
});
assert.equal(model.getValue(), [
'aaa',
'bbb',
'ccc',
'ddd',
].join('\n'));
});
});
test('issue #46440: (1) Pasting a multi-line selection pastes entire selection into every insertion point', () => {
usingCursor({
text: [

5
src/vs/monaco.d.ts vendored
View file

@ -2704,6 +2704,11 @@ declare namespace monaco.editor {
* Defaults to true
*/
multiCursorMergeOverlapping?: boolean;
/**
* Configure the behaviour when pasting a text with the line count equal to the cursor count.
* Defaults to 'spread'.
*/
multiCursorPaste?: 'spread' | 'full';
/**
* Configure the editor's accessibility support.
* Defaults to 'auto'. It is best to leave this to 'auto'.

View file

@ -11,7 +11,7 @@ import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { URI as Uri, URI } from 'vs/base/common/uri';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupWorkspacesFormat, ISerializedWorkspace, IWorkspaceBackupInfo } from 'vs/platform/backup/common/backup';
import { HotExitConfiguration } from 'vs/platform/files/common/files';
@ -32,7 +32,7 @@ suite('BackupMainService', () => {
const backupHome = path.join(parentDir, 'Backups');
const backupWorkspacesPath = path.join(backupHome, 'workspaces.json');
const environmentService = new EnvironmentService(parseArgs(process.argv), process.execPath);
const environmentService = new EnvironmentService(parseArgs(process.argv, OPTIONS), process.execPath);
class TestBackupMainService extends BackupMainService {

View file

@ -25,7 +25,7 @@ export interface ParsedArgs {
'reuse-window'?: boolean;
locale?: string;
'user-data-dir'?: string;
'prof-startup'?: string;
'prof-startup'?: boolean;
'prof-startup-prefix'?: string;
'prof-append-timers'?: string;
verbose?: boolean;
@ -61,8 +61,8 @@ export interface ParsedArgs {
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
'install-source'?: string;
'disable-updates'?: string;
'disable-crash-reporter'?: string;
'disable-updates'?: boolean;
'disable-crash-reporter'?: boolean;
'skip-add-to-recently-opened'?: boolean;
'max-memory'?: string;
'file-write'?: boolean;
@ -76,7 +76,7 @@ export interface ParsedArgs {
'force'?: boolean;
'gitCredential'?: string;
// node flags
'js-flags'?: boolean;
'js-flags'?: string;
'disable-gpu'?: boolean;
'nolazy'?: boolean;

View file

@ -20,84 +20,96 @@ const helpCategories = {
t: localize('troubleshooting', "Troubleshooting")
};
export interface Option {
id: keyof ParsedArgs;
type: 'boolean' | 'string' | 'string[]';
export interface Option<OptionType> {
type: OptionType;
alias?: string;
deprecates?: string; // old deprecated id
args?: string | string[];
description?: string;
cat?: keyof typeof helpCategories;
}
//_urls
export const options: Option[] = [
{ id: 'diff', type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
{ id: 'add', type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
{ id: 'goto', type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") },
{ id: 'new-window', type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },
{ id: 'reuse-window', type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") },
{ id: 'wait', type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") },
{ id: 'locale', type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
{ id: 'user-data-dir', type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") },
{ id: 'version', type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") },
{ id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
{ id: 'telemetry', type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
{ id: 'folder-uri', type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
{ id: 'file-uri', type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
{ id: 'extensions-dir', type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
{ id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
{ id: 'show-versions', type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
{ id: 'category', type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
{ id: 'install-extension', type: 'string[]', cat: 'e', args: 'extension-id | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") },
{ id: 'uninstall-extension', type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") },
{ id: 'enable-proposed-api', type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") },
export type OptionDescriptions<T> = {
[P in keyof T]: Option<OptionTypeName<T[P]>>;
};
{ id: 'verbose', type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") },
{ id: 'log', type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") },
{ id: 'status', type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
{ id: 'prof-startup', type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
{ id: 'disable-extensions', type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
{ id: 'disable-extension', type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },
type OptionTypeName<T> =
T extends boolean ? 'boolean' :
T extends string ? 'string' :
T extends string[] ? 'string[]' :
T extends undefined ? 'undefined' :
'unknown';
{ id: 'inspect-extensions', type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
{ id: 'inspect-brk-extensions', type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },
{ id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
{ id: 'max-memory', type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
export const OPTIONS: OptionDescriptions<ParsedArgs> = {
'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") },
'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },
'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") },
'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") },
'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") },
'version': { type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") },
'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
{ id: 'remote', type: 'string' },
{ id: 'locate-extension', type: 'string[]' },
{ id: 'extensionDevelopmentPath', type: 'string[]' },
{ id: 'extensionTestsPath', type: 'string' },
{ id: 'extension-development-confirm-save', type: 'boolean' },
{ id: 'debugId', type: 'string' },
{ id: 'inspect-search', type: 'string', deprecates: 'debugSearch' },
{ id: 'inspect-brk-search', type: 'string', deprecates: 'debugBrkSearch' },
{ id: 'export-default-configuration', type: 'string' },
{ id: 'install-source', type: 'string' },
{ id: 'driver', type: 'string' },
{ id: 'logExtensionHostCommunication', type: 'boolean' },
{ id: 'skip-getting-started', type: 'boolean' },
{ id: 'skip-release-notes', type: 'boolean' },
{ id: 'sticky-quickopen', type: 'boolean' },
{ id: 'disable-restore-windows', type: 'boolean' },
{ id: 'disable-telemetry', type: 'boolean' },
{ id: 'disable-updates', type: 'boolean' },
{ id: 'disable-crash-reporter', type: 'boolean' },
{ id: 'skip-add-to-recently-opened', type: 'boolean' },
{ id: 'unity-launch', type: 'boolean' },
{ id: 'open-url', type: 'boolean' },
{ id: 'file-write', type: 'boolean' },
{ id: 'file-chmod', type: 'boolean' },
{ id: 'driver-verbose', type: 'boolean' },
{ id: 'force', type: 'boolean' },
{ id: 'trace-category-filter', type: 'string' },
{ id: 'trace-options', type: 'string' },
{ id: 'disable-inspect', type: 'boolean' },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") },
'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") },
'enable-proposed-api': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") },
{ id: 'js-flags', type: 'string' }, // chrome js flags
{ id: 'nolazy', type: 'boolean' }, // node inspect
];
'verbose': { type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") },
'log': { type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") },
'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
'disable-extensions': { type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
'disable-extension': { type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },
'inspect-extensions': { type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
'inspect-brk-extensions': { type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },
'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
'remote': { type: 'string' },
'locate-extension': { type: 'string[]' },
'extensionDevelopmentPath': { type: 'string[]' },
'extensionTestsPath': { type: 'string' },
'extension-development-confirm-save': { type: 'boolean' },
'debugId': { type: 'string' },
'inspect-search': { type: 'string', deprecates: 'debugSearch' },
'inspect-brk-search': { type: 'string', deprecates: 'debugBrkSearch' },
'export-default-configuration': { type: 'string' },
'install-source': { type: 'string' },
'driver': { type: 'string' },
'logExtensionHostCommunication': { type: 'boolean' },
'skip-getting-started': { type: 'boolean' },
'skip-release-notes': { type: 'boolean' },
'sticky-quickopen': { type: 'boolean' },
'disable-restore-windows': { type: 'boolean' },
'disable-telemetry': { type: 'boolean' },
'disable-updates': { type: 'boolean' },
'disable-crash-reporter': { type: 'boolean' },
'skip-add-to-recently-opened': { type: 'boolean' },
'unity-launch': { type: 'boolean' },
'open-url': { type: 'boolean' },
'file-write': { type: 'boolean' },
'file-chmod': { type: 'boolean' },
'driver-verbose': { type: 'boolean' },
'force': { type: 'boolean' },
'trace-category-filter': { type: 'string' },
'trace-options': { type: 'string' },
'disable-inspect': { type: 'boolean' },
'js-flags': { type: 'string' }, // chrome js flags
'nolazy': { type: 'boolean' }, // node inspect
_: { type: 'string[]' } // main arguments
};
export interface ErrorReporter {
onUnknownOption(id: string): void;
@ -109,24 +121,23 @@ const ignoringReporter: ErrorReporter = {
onMultipleValues: () => { }
};
export function parseArgs(args: string[], isOptionSupported = (_: Option) => true, errorReporter: ErrorReporter = ignoringReporter): ParsedArgs {
export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, errorReporter: ErrorReporter = ignoringReporter): T {
const alias: { [key: string]: string } = {};
const string: string[] = [];
const boolean: string[] = [];
for (let o of options) {
if (isOptionSupported(o)) {
if (o.alias) {
alias[o.id] = o.alias;
}
for (let optionId in options) {
const o = options[optionId];
if (o.alias) {
alias[optionId] = o.alias;
}
if (o.type === 'string' || o.type === 'string[]') {
string.push(o.id);
string.push(optionId);
if (o.deprecates) {
string.push(o.deprecates);
}
} else if (o.type === 'boolean') {
boolean.push(o.id);
boolean.push(optionId);
if (o.deprecates) {
boolean.push(o.deprecates);
}
@ -137,12 +148,17 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru
const cleanedArgs: any = {};
for (const o of options) {
// https://github.com/microsoft/vscode/issues/58177
cleanedArgs._ = parsedArgs._.filter(arg => arg.length > 0);
delete parsedArgs._;
for (let optionId in options) {
const o = options[optionId];
if (o.alias) {
delete parsedArgs[o.alias];
}
let val = parsedArgs[o.id];
let val = parsedArgs[optionId];
if (o.deprecates && parsedArgs.hasOwnProperty(o.deprecates)) {
if (!val) {
val = parsedArgs[o.deprecates];
@ -151,29 +167,21 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru
}
if (val) {
if (isOptionSupported(o)) {
if (o.type === 'string[]') {
if (val && !Array.isArray(val)) {
val = [val];
}
} else if (o.type === 'string') {
if (Array.isArray(val)) {
val = val.pop(); // take the last
errorReporter.onMultipleValues(o.id, val);
}
if (o.type === 'string[]') {
if (val && !Array.isArray(val)) {
val = [val];
}
} else if (o.type === 'string') {
if (Array.isArray(val)) {
val = val.pop(); // take the last
errorReporter.onMultipleValues(optionId, val);
}
cleanedArgs[o.id] = val;
} else {
errorReporter.onUnknownOption(o.id);
}
cleanedArgs[optionId] = val;
}
delete parsedArgs[o.id];
delete parsedArgs[optionId];
}
// https://github.com/microsoft/vscode/issues/58177
cleanedArgs._ = parsedArgs._.filter(arg => arg.length > 0);
delete parsedArgs._;
for (let key in parsedArgs) {
errorReporter.onUnknownOption(key);
}
@ -181,7 +189,7 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru
return cleanedArgs;
}
function formatUsage(option: Option) {
function formatUsage(optionId: string, option: Option<any>) {
let args = '';
if (option.args) {
if (Array.isArray(option.args)) {
@ -191,30 +199,37 @@ function formatUsage(option: Option) {
}
}
if (option.alias) {
return `-${option.alias} --${option.id}${args}`;
return `-${option.alias} --${optionId}${args}`;
}
return `--${option.id}${args}`;
return `--${optionId}${args}`;
}
// exported only for testing
export function formatOptions(docOptions: Option[], columns: number): string[] {
let usageTexts = docOptions.map(formatUsage);
let argLength = Math.max.apply(null, usageTexts.map(k => k.length)) + 2/*left padding*/ + 1/*right padding*/;
export function formatOptions(options: OptionDescriptions<any>, columns: number): string[] {
let maxLength = 0;
let usageTexts: [string, string][] = [];
for (const optionId in options) {
const o = options[optionId];
const usageText = formatUsage(optionId, o);
maxLength = Math.max(maxLength, usageText.length);
usageTexts.push([usageText, o.description!]);
}
let argLength = maxLength + 2/*left padding*/ + 1/*right padding*/;
if (columns - argLength < 25) {
// Use a condensed version on narrow terminals
return docOptions.reduce<string[]>((r, o, i) => r.concat([` ${usageTexts[i]}`, ` ${o.description}`]), []);
return usageTexts.reduce<string[]>((r, ut) => r.concat([` ${ut[0]}`, ` ${ut[1]}`]), []);
}
let descriptionColumns = columns - argLength - 1;
let result: string[] = [];
docOptions.forEach((o, i) => {
let usage = usageTexts[i];
let wrappedDescription = wrapText(o.description!, descriptionColumns);
for (const ut of usageTexts) {
let usage = ut[0];
let wrappedDescription = wrapText(ut[1], descriptionColumns);
let keyPadding = indent(argLength - usage.length - 2/*left padding*/);
result.push(' ' + usage + keyPadding + wrappedDescription[0]);
for (let i = 1; i < wrappedDescription.length; i++) {
result.push(indent(argLength) + wrappedDescription[i]);
}
});
}
return result;
}
@ -233,7 +248,7 @@ function wrapText(text: string, columns: number): string[] {
return lines;
}
export function buildHelpMessage(productName: string, executableName: string, version: string, isOptionSupported = (_: Option) => true, isPipeSupported = true): string {
export function buildHelpMessage(productName: string, executableName: string, version: string, options: OptionDescriptions<any>, isPipeSupported = true): string {
const columns = (process.stdout).isTTY && (process.stdout).columns || 80;
let help = [`${productName} ${version}`];
@ -248,11 +263,23 @@ export function buildHelpMessage(productName: string, executableName: string, ve
}
help.push('');
}
for (let helpCategoryKey in helpCategories) {
const optionsByCategory: { [P in keyof typeof helpCategories]?: OptionDescriptions<any> } = {};
for (const optionId in options) {
const o = options[optionId];
if (o.description && o.cat) {
let optionsByCat = optionsByCategory[o.cat];
if (!optionsByCat) {
optionsByCategory[o.cat] = optionsByCat = {};
}
optionsByCat[optionId] = o;
}
}
for (let helpCategoryKey in optionsByCategory) {
const key = <keyof typeof helpCategories>helpCategoryKey;
let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o));
if (categoryOptions.length) {
let categoryOptions = optionsByCategory[key];
if (categoryOptions) {
help.push(helpCategories[key]);
help.push(...formatOptions(categoryOptions, columns));
help.push('');

View file

@ -8,7 +8,7 @@ import { firstIndex } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
import { ParsedArgs } from '../common/environment';
import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files';
import { parseArgs, ErrorReporter } from 'vs/platform/environment/node/argv';
import { parseArgs, ErrorReporter, OPTIONS } from 'vs/platform/environment/node/argv';
function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): ParsedArgs {
const errorReporter: ErrorReporter = {
@ -20,7 +20,7 @@ function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): Parse
}
};
const args = parseArgs(cmdLineArgs, undefined, reportWarnings ? errorReporter : undefined);
const args = parseArgs(cmdLineArgs, OPTIONS, reportWarnings ? errorReporter : undefined);
if (args.goto) {
args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`.")));
}

View file

@ -5,13 +5,13 @@
import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { parseExtensionHostPort, parseUserDataDir } from 'vs/platform/environment/node/environmentService';
suite('EnvironmentService', () => {
test('parseExtensionHostPort when built', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a), true);
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), true);
assert.deepEqual(parse([]), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: null, break: false, debugId: undefined });
@ -28,7 +28,7 @@ suite('EnvironmentService', () => {
});
test('parseExtensionHostPort when unbuilt', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a), false);
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), false);
assert.deepEqual(parse([]), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: 5870, break: false, debugId: undefined });
@ -45,7 +45,7 @@ suite('EnvironmentService', () => {
});
test('userDataPath', () => {
const parse = (a: string[], b: { cwd: () => string, env: { [key: string]: string } }) => parseUserDataDir(parseArgs(a), <any>b);
const parse = (a: string[], b: { cwd: () => string, env: { [key: string]: string } }) => parseUserDataDir(parseArgs(a, OPTIONS), <any>b);
assert.equal(parse(['--user-data-dir', './dir'], { cwd: () => '/foo', env: {} }), path.resolve('/foo/dir'),
'should use cwd when --user-data-dir is specified');
@ -55,11 +55,11 @@ suite('EnvironmentService', () => {
// https://github.com/microsoft/vscode/issues/78440
test('careful with boolean file names', function () {
let actual = parseArgs(['-r', 'arg.txt']);
let actual = parseArgs(['-r', 'arg.txt'], OPTIONS);
assert(actual['reuse-window']);
assert.deepEqual(actual._, ['arg.txt']);
actual = parseArgs(['-r', 'true.txt']);
actual = parseArgs(['-r', 'true.txt'], OPTIONS);
assert(actual['reuse-window']);
assert.deepEqual(actual._, ['true.txt']);
});

View file

@ -6,7 +6,7 @@
import * as assert from 'assert';
import * as os from 'os';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { join } from 'vs/base/common/path';
import { mkdirp, RimRafMode, rimraf } from 'vs/base/node/pfs';
@ -51,7 +51,7 @@ suite('Extension Gallery Service', () => {
test('marketplace machine id', () => {
const args = ['--user-data-dir', marketplaceHome];
const environmentService = new EnvironmentService(parseArgs(args), process.execPath);
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers => {
assert.ok(isUUID(headers['X-Market-User-Id']));
@ -61,4 +61,4 @@ suite('Extension Gallery Service', () => {
});
});
});
});
});

View file

@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { BrowserWindow, ipcMain, screen, Event, dialog } from 'electron';
import { ILaunchService } from 'vs/platform/launch/electron-main/launchService';
@ -372,7 +372,7 @@ export class IssueService implements IIssueService {
}
function toLauchUrl<T>(pathToHtml: string, windowConfiguration: T): string {
const environment = parseArgs(process.argv);
const environment = parseArgs(process.argv, OPTIONS);
const config = objects.assign(environment, windowConfiguration);
for (const keyValue of Object.keys(config)) {
const key = keyValue as keyof typeof config;

View file

@ -12,7 +12,7 @@ import { tmpdir } from 'os';
import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs';
import { NullLogService } from 'vs/platform/log/common/log';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage';
suite('StorageService', () => {
@ -86,7 +86,7 @@ suite('StorageService', () => {
class StorageTestEnvironmentService extends EnvironmentService {
constructor(private workspaceStorageFolderPath: string, private _extensionsPath: string) {
super(parseArgs(process.argv), process.execPath);
super(parseArgs(process.argv, OPTIONS), process.execPath);
}
get workspaceStorageHome(): string {
@ -117,4 +117,4 @@ suite('StorageService', () => {
await storage.close();
await rimraf(storageDir, RimRafMode.MOVE);
});
});
});

View file

@ -3,11 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IChannel, IServerChannel, IClientRouter, IConnectionHub, Client } from 'vs/base/parts/ipc/common/ipc';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { CancellationToken } from 'vs/base/common/cancellation';
import { first } from 'vs/base/common/arrays';
export class URLServiceChannel implements IServerChannel {
@ -70,3 +72,37 @@ export class URLHandlerChannelClient implements IURLHandler {
return this.channel.call('handleURL', uri.toJSON());
}
}
export class URLHandlerRouter implements IClientRouter<string> {
constructor(private next: IClientRouter<string>) { }
async routeCall(hub: IConnectionHub<string>, command: string, arg?: any, cancellationToken?: CancellationToken): Promise<Client<string>> {
if (command !== 'handleURL') {
throw new Error(`Call not found: ${command}`);
}
if (arg) {
const uri = URI.revive(arg);
if (uri && uri.query) {
const match = /\bwindowId=([^&]+)/.exec(uri.query);
if (match) {
const windowId = match[1];
const connection = first(hub.connections, c => c.ctx === windowId);
if (connection) {
return connection;
}
}
}
}
return this.next.routeCall(hub, command, arg, cancellationToken);
}
routeEvent(_: IConnectionHub<string>, event: string): Promise<Client<string>> {
throw new Error(`Event not found: ${event}`);
}
}

View file

@ -9,7 +9,7 @@ import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces';
import { NullLogService } from 'vs/platform/log/common/log';
@ -47,7 +47,7 @@ suite('WorkspacesMainService', () => {
return service.createUntitledWorkspaceSync(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData)));
}
const environmentService = new TestEnvironmentService(parseArgs(process.argv), process.execPath);
const environmentService = new TestEnvironmentService(parseArgs(process.argv, OPTIONS), process.execPath);
const logService = new NullLogService();
let service: TestWorkspacesMainService;

View file

@ -52,6 +52,7 @@ import { toLocalResource } from 'vs/base/common/resources';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
@ -412,12 +413,12 @@ editorCommands.setup();
// Touch Bar
if (isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } },
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconLocation: { dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/back-tb.png')) } },
group: 'navigation'
});
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } },
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconLocation: { dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/forward-tb.png')) } },
group: 'navigation'
});
}
@ -447,7 +448,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
interface IEditorToolItem { id: string; title: string; iconDark: string; iconLight: string; }
interface IEditorToolItem { id: string; title: string; iconDark: URI; iconLight: URI; }
function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | undefined, order: number, alternative?: IEditorToolItem): void {
const item: IMenuItem = {
@ -455,8 +456,8 @@ function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | u
id: primary.id,
title: primary.title,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconDark}`)),
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconLight}`))
dark: primary.iconDark,
light: primary.iconLight
}
},
group: 'navigation',
@ -469,8 +470,8 @@ function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | u
id: alternative.id,
title: alternative.title,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconDark}`)),
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconLight}`))
dark: alternative.iconDark,
light: alternative.iconLight
}
};
}
@ -483,16 +484,16 @@ appendEditorToolItem(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorRight', "Split Editor Right"),
iconDark: 'split-editor-horizontal-dark.svg',
iconLight: 'split-editor-horizontal-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg'))
},
ContextKeyExpr.not('splitEditorsVertically'),
100000, // towards the end
{
id: editorCommands.SPLIT_EDITOR_DOWN,
title: nls.localize('splitEditorDown', "Split Editor Down"),
iconDark: 'split-editor-vertical-dark.svg',
iconLight: 'split-editor-vertical-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg'))
}
);
@ -500,16 +501,16 @@ appendEditorToolItem(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorDown', "Split Editor Down"),
iconDark: 'split-editor-vertical-dark.svg',
iconLight: 'split-editor-vertical-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg'))
},
ContextKeyExpr.has('splitEditorsVertically'),
100000, // towards the end
{
id: editorCommands.SPLIT_EDITOR_RIGHT,
title: nls.localize('splitEditorRight', "Split Editor Right"),
iconDark: 'split-editor-horizontal-dark.svg',
iconLight: 'split-editor-horizontal-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg'))
}
);
@ -518,16 +519,16 @@ appendEditorToolItem(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
iconDark: 'close-dark-alt.svg',
iconLight: 'close-light-alt.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dark-alt.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-light-alt.svg'))
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
title: nls.localize('closeAll', "Close All"),
iconDark: 'close-all-dark.svg',
iconLight: 'close-all-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-light.svg'))
}
);
@ -535,16 +536,16 @@ appendEditorToolItem(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
iconDark: 'close-dirty-dark-alt.svg',
iconLight: 'close-dirty-light-alt.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg'))
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
title: nls.localize('closeAll', "Close All"),
iconDark: 'close-all-dark.svg',
iconLight: 'close-all-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-light.svg'))
}
);
@ -553,8 +554,8 @@ appendEditorToolItem(
{
id: editorCommands.GOTO_PREVIOUS_CHANGE,
title: nls.localize('navigate.prev.label', "Previous Change"),
iconDark: 'previous-diff-dark.svg',
iconLight: 'previous-diff-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/previous-diff-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/previous-diff-light.svg'))
},
TextCompareEditorActiveContext,
10
@ -565,8 +566,8 @@ appendEditorToolItem(
{
id: editorCommands.GOTO_NEXT_CHANGE,
title: nls.localize('navigate.next.label', "Next Change"),
iconDark: 'next-diff-dark.svg',
iconLight: 'next-diff-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/next-diff-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/next-diff-light.svg'))
},
TextCompareEditorActiveContext,
11
@ -577,8 +578,8 @@ appendEditorToolItem(
{
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Trim Whitespace"),
iconDark: 'paragraph-dark.svg',
iconLight: 'paragraph-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-light.svg'))
},
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)),
20
@ -589,8 +590,8 @@ appendEditorToolItem(
{
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
title: nls.localize('showTrimWhitespace.label', "Show Trim Whitespace"),
iconDark: 'paragraph-disabled-dark.svg',
iconLight: 'paragraph-disabled-light.svg'
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg')),
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg'))
},
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)),
20

View file

@ -44,6 +44,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
const $ = dom.$;
@ -51,8 +52,8 @@ type Writeable<T> = { -readonly [P in keyof T]: T[P] };
const backButton = {
iconPath: {
dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/arrow-left-dark.svg')),
light: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/arrow-left-light.svg'))
dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/quickinput/media/arrow-left-dark.svg')),
light: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/quickinput/media/arrow-left-light.svg'))
},
tooltip: localize('quickInput.back', "Back"),
handle: -1 // TODO

View file

@ -20,5 +20,6 @@ function createModuleDescription(name, exclude) {
exports.collectModules = function () {
return [
createModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']),
createModuleDescription('vs/code/browser/workbench/web.main', ['vs/workbench/workbench.web.api']),
];
};

View file

@ -19,6 +19,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { DefaultSettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
const transientWordWrapState = 'transientWordWrapState';
const isWordWrapMinifiedKey = 'isWordWrapMinified';
@ -183,7 +184,7 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution
let currentlyApplyingEditorConfig = false;
this._register(editor.onDidChangeConfiguration((e) => {
if (!e.hasChanged(EditorOption.wrappingInfo) || !e.hasChanged(EditorOption.inDiffEditor)) {
if (!e.hasChanged(EditorOption.wrappingInfo) && !e.hasChanged(EditorOption.inDiffEditor)) {
return;
}
const options = this.editor.getOptions();
@ -276,8 +277,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: TOGGLE_WORD_WRAP_ID,
title: nls.localize('unwrapMinified', "Disable wrapping for this file"),
iconLocation: {
dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')),
light: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg'))
dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')),
light: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg'))
}
},
group: 'navigation',
@ -293,8 +294,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: TOGGLE_WORD_WRAP_ID,
title: nls.localize('wrapMinified', "Enable wrapping for this file"),
iconLocation: {
dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')),
light: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg'))
dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')),
light: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg'))
}
},
group: 'navigation',

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { IExpression, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IExpression, IDebugService, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, Variable, ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
@ -50,7 +50,7 @@ export function replaceWhitespace(value: string): string {
return value.replace(/[\n\r\t]/g, char => map[char]);
}
export function renderExpressionValue(expressionOrValue: IExpression | string, container: HTMLElement, options: IRenderValueOptions): void {
export function renderExpressionValue(expressionOrValue: IExpressionContainer | string, container: HTMLElement, options: IRenderValueOptions): void {
let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value;
// remove stale classes
@ -221,4 +221,4 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
disposeTemplate(templateData: IExpressionTemplateData): void {
dispose(templateData.toDispose);
}
}
}

View file

@ -48,6 +48,7 @@ import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchEx
import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView';
import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl';
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
class OpenDebugViewletAction extends ShowViewletAction {
public static readonly ID = VIEWLET_ID;
@ -271,7 +272,7 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
// Debug toolbar
const registerDebugToolBarItem = (id: string, title: string, icon: string, order: number, when?: ContextKeyExpr, precondition?: ContextKeyExpr) => {
const registerDebugToolBarItem = (id: string, title: string, iconLightUri: URI, iconDarkUri: URI, order: number, when?: ContextKeyExpr, precondition?: ContextKeyExpr) => {
MenuRegistry.appendMenuItem(MenuId.DebugToolBar, {
group: 'navigation',
when,
@ -280,24 +281,24 @@ const registerDebugToolBarItem = (id: string, title: string, icon: string, order
id,
title,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/debug/browser/media/${icon}-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/debug/browser/media/${icon}-dark.svg`))
light: iconLightUri,
dark: iconDarkUri
},
precondition
}
});
};
registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 'continue', 10, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 'pause', 10, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'));
registerDebugToolBarItem(STOP_ID, STOP_LABEL, 'stop', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 'disconnect', 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 'step-over', 20, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 'step-into', 30, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 'step-out', 40, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 'restart', 60);
registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 'step-back', 50, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 'reverse-continue', 60, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/continue-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/continue-dark.svg')), 10, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/pause-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/pause-dark.svg')), 10, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'));
registerDebugToolBarItem(STOP_ID, STOP_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stop-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stop-dark.svg')), 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/disconnect-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/disconnect-dark.svg')), 70, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-over-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-over-dark.svg')), 20, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-into-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-into-dark.svg')), 30, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-out-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-out-dark.svg')), 40, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/restart-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/restart-dark.svg')), 60);
registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-back-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/step-back-dark.svg')), 50, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/reverse-continue-light.svg')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/reverse-continue-dark.svg')), 60, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
// Debug callstack context menu
const registerDebugCallstackItem = (id: string, title: string, order: number, when?: ContextKeyExpr, precondition?: ContextKeyExpr, group = 'navigation') => {
@ -537,12 +538,12 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
// Touch Bar
if (isMacintosh) {
const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpr | undefined, icon: string) => {
const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpr | undefined, iconUri: URI) => {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: {
id,
title,
iconLocation: { dark: URI.parse(require.toUrl(`vs/workbench/contrib/debug/browser/media/${icon}`)) }
iconLocation: { dark: iconUri }
},
when,
group: '9_debug',
@ -550,13 +551,13 @@ if (isMacintosh) {
});
};
registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-tb.png');
registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-without-debugging-tb.png');
registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png');
registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png');
registerTouchBarEntry(STEP_OVER_ID, STEP_OUT_LABEL, 2, CONTEXT_IN_DEBUG_MODE, 'stepover-tb.png');
registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, 'stepinto-tb.png');
registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, 'stepout-tb.png');
registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/continue-tb.png')));
registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png')));
registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/continue-tb.png')));
registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/pause-tb.png')));
registerTouchBarEntry(STEP_OVER_ID, STEP_OUT_LABEL, 2, CONTEXT_IN_DEBUG_MODE, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stepover-tb.png')));
registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stepinto-tb.png')));
registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stepout-tb.png')));
registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/restart-tb.png')));
registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/debug/browser/media/stop-tb.png')));
}

View file

@ -44,6 +44,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel } from 'vs/workbench/contrib/debug/common/debug';
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
@ -492,7 +493,16 @@ export class DebugService implements IDebugService {
}
private registerSessionListeners(session: IDebugSession): void {
const sessionRunningScheduler = new RunOnceScheduler(() => {
// Do not immediatly defocus the stack frame if the session is running
if (session.state === State.Running && this.viewModel.focusedSession === session) {
this.viewModel.setFocus(undefined, this.viewModel.focusedThread, session, false);
}
}, 200);
this.toDispose.push(session.onDidChangeState(() => {
if (session.state === State.Running && this.viewModel.focusedSession === session) {
sessionRunningScheduler.schedule();
}
if (session === this.viewModel.focusedSession) {
this.onStateChange();
}

View file

@ -921,7 +921,9 @@ export class DebugSession implements IDebugSession {
}
async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
await this.repl.addReplExpression(stackFrame, name);
const expressionEvaluated = this.repl.addReplExpression(stackFrame, name);
this._onDidChangeREPLElements.fire();
await expressionEvaluated;
this._onDidChangeREPLElements.fire();
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
variableSetEmitter.fire();

View file

@ -48,11 +48,13 @@
cursor: text;
}
.repl .repl-tree .output.expression > .value {
.repl .repl-tree .output.expression > .value,
.repl .repl-tree .evaluation-result.expression > .value {
margin-left: 0px;
}
.repl .repl-tree .output.expression > .annotation {
.repl .repl-tree .output.expression > .annotation,
.repl .repl-tree .evaluation-result.expression > .annotation {
font-size: inherit;
padding-left: 6px;
}
@ -67,7 +69,7 @@
}
/* Only show 'stale expansion' info when the element gets expanded. */
.repl .repl-tree .input-output-pair > .output > .annotation::before {
.repl .repl-tree .evaluation-result > .annotation::before {
content: '';
}
@ -134,7 +136,8 @@
.monaco-workbench .repl .repl-tree .output.expression .code-underline { text-decoration: underline; }
/* Links */
.monaco-workbench .repl .repl-tree .output.expression a {
.monaco-workbench .repl .repl-tree .output.expression a,
.monaco-workbench .repl .repl-tree .evaluation-result.expression a {
text-decoration: underline;
cursor: pointer;
}

View file

@ -44,7 +44,8 @@ import { CompletionContext, CompletionList, CompletionProviderRegistry } from 'v
import { first } from 'vs/base/common/arrays';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { Variable, Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel';
import { Variable } from 'vs/workbench/contrib/debug/common/debugModel';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@ -412,7 +413,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
[
this.instantiationService.createInstance(VariablesRenderer),
this.instantiationService.createInstance(ReplSimpleElementsRenderer),
new ReplExpressionsRenderer(),
new ReplEvaluationInputsRenderer(),
new ReplEvaluationResultsRenderer(),
new ReplRawObjectsRenderer()
],
// https://github.com/microsoft/TypeScript/issues/32526
@ -568,12 +570,13 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
// Repl tree
interface IExpressionTemplateData {
input: HTMLElement;
output: HTMLElement;
interface IReplEvaluationInputTemplateData {
label: HighlightedLabel;
}
interface IReplEvaluationResultTemplateData {
value: HTMLElement;
annotation: HTMLElement;
label: HighlightedLabel;
}
interface ISimpleReplElementTemplateData {
@ -593,27 +596,46 @@ interface IRawObjectReplTemplateData {
label: HighlightedLabel;
}
class ReplExpressionsRenderer implements ITreeRenderer<Expression, FuzzyScore, IExpressionTemplateData> {
static readonly ID = 'expressionRepl';
class ReplEvaluationInputsRenderer implements ITreeRenderer<ReplEvaluationInput, FuzzyScore, IReplEvaluationInputTemplateData> {
static readonly ID = 'replEvaluationInput';
get templateId(): string {
return ReplExpressionsRenderer.ID;
return ReplEvaluationInputsRenderer.ID;
}
renderTemplate(container: HTMLElement): IExpressionTemplateData {
dom.addClass(container, 'input-output-pair');
const input = dom.append(container, $('.input.expression'));
renderTemplate(container: HTMLElement): IReplEvaluationInputTemplateData {
const input = dom.append(container, $('.expression'));
const label = new HighlightedLabel(input, false);
const output = dom.append(container, $('.output.expression'));
return { label };
}
renderElement(element: ITreeNode<ReplEvaluationInput, FuzzyScore>, index: number, templateData: IReplEvaluationInputTemplateData): void {
const evaluation = element.element;
templateData.label.set(evaluation.value, createMatches(element.filterData));
}
disposeTemplate(templateData: IReplEvaluationInputTemplateData): void {
// noop
}
}
class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluationResult, FuzzyScore, IReplEvaluationResultTemplateData> {
static readonly ID = 'replEvaluationResult';
get templateId(): string {
return ReplEvaluationResultsRenderer.ID;
}
renderTemplate(container: HTMLElement): IReplEvaluationResultTemplateData {
const output = dom.append(container, $('.evaluation-result.expression'));
const value = dom.append(output, $('span.value'));
const annotation = dom.append(output, $('span'));
return { input, label, output, value, annotation };
return { value, annotation };
}
renderElement(element: ITreeNode<Expression, FuzzyScore>, index: number, templateData: IExpressionTemplateData): void {
renderElement(element: ITreeNode<ReplEvaluationResult, FuzzyScore>, index: number, templateData: IReplEvaluationResultTemplateData): void {
const expression = element.element;
templateData.label.set(expression.name, createMatches(element.filterData));
renderExpressionValue(expression, templateData.value, {
preserveWhitespace: !expression.hasChildren,
showHover: false,
@ -625,7 +647,7 @@ class ReplExpressionsRenderer implements ITreeRenderer<Expression, FuzzyScore, I
}
}
disposeTemplate(templateData: IExpressionTemplateData): void {
disposeTemplate(templateData: IReplEvaluationResultTemplateData): void {
// noop
}
}
@ -757,24 +779,23 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
const rowHeight = Math.ceil(1.4 * fontSize);
const wordWrap = config.console.wordWrap;
if (!wordWrap) {
return element instanceof Expression ? 2 * rowHeight : rowHeight;
return rowHeight;
}
// In order to keep scroll position we need to give a good approximation to the tree
// For every 150 characters increase the number of lines needed
if (element instanceof Expression) {
let { name, value } = element;
let nameRows = countNumberOfLines(name) + Math.floor(name.length / 150);
if (element instanceof ReplEvaluationResult) {
let value = element.value;
if (element.hasChildren) {
return (nameRows + 1) * rowHeight;
return rowHeight;
}
let valueRows = value ? (countNumberOfLines(value) + Math.floor(value.length / 150)) : 0;
return rowHeight * (nameRows + valueRows);
return rowHeight * valueRows;
}
if (element instanceof SimpleReplElement) {
if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput) {
let value = element.value;
let valueRows = countNumberOfLines(value) + Math.floor(value.length / 150);
@ -788,8 +809,11 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
if (element instanceof Variable && element.name) {
return VariablesRenderer.ID;
}
if (element instanceof Expression) {
return ReplExpressionsRenderer.ID;
if (element instanceof ReplEvaluationResult) {
return ReplEvaluationResultsRenderer.ID;
}
if (element instanceof ReplEvaluationInput) {
return ReplEvaluationInputsRenderer.ID;
}
if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) {
// Variable with no name is a top level variable which should be rendered like a repl element #17404
@ -836,10 +860,7 @@ class ReplAccessibilityProvider implements IAccessibilityProvider<IReplElement>
if (element instanceof Variable) {
return nls.localize('replVariableAriaLabel', "Variable {0} has value {1}, read eval print loop, debug", element.name, element.value);
}
if (element instanceof Expression) {
return nls.localize('replExpressionAriaLabel', "Expression {0} has value {1}, read eval print loop, debug", element.name, element.value);
}
if (element instanceof SimpleReplElement) {
if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput || element instanceof ReplEvaluationResult) {
return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", element.value);
}
if (element instanceof RawObjectReplElement) {

View file

@ -104,13 +104,13 @@ export interface IExpressionContainer extends ITreeElement {
readonly hasChildren: boolean;
getChildren(): Promise<IExpression[]>;
readonly reference?: number;
readonly value: string;
readonly type?: string;
valueChanged?: boolean;
}
export interface IExpression extends IReplElement, IExpressionContainer {
export interface IExpression extends IExpressionContainer {
name: string;
readonly value: string;
valueChanged?: boolean;
readonly type?: string;
}
export interface IDebugger {

View file

@ -10,13 +10,12 @@ import * as lifecycle from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
import { RunOnceScheduler } from 'vs/base/common/async';
import severity from 'vs/base/common/severity';
import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types';
import { isString, isUndefinedOrNull } from 'vs/base/common/types';
import { distinct, lastIndex } from 'vs/base/common/arrays';
import { Range, IRange } from 'vs/editor/common/core/range';
import {
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource,
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel,
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint
} from 'vs/workbench/contrib/debug/common/debug';
import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource';
import { commonSuffixLength } from 'vs/base/common/strings';
@ -25,75 +24,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextEditor } from 'vs/workbench/common/editor';
export class SimpleReplElement implements IReplElement {
constructor(
private id: string,
public value: string,
public severity: severity,
public sourceData?: IReplElementSource,
) { }
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class RawObjectReplElement implements IExpression {
private static readonly MAX_CHILDREN = 1000; // upper bound of children per value
constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { }
getId(): string {
return this.id;
}
get value(): string {
if (this.valueObj === null) {
return 'null';
} else if (Array.isArray(this.valueObj)) {
return `Array[${this.valueObj.length}]`;
} else if (isObject(this.valueObj)) {
return 'Object';
} else if (isString(this.valueObj)) {
return `"${this.valueObj}"`;
}
return String(this.valueObj) || '';
}
get hasChildren(): boolean {
return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0);
}
getChildren(): Promise<IExpression[]> {
let result: IExpression[] = [];
if (Array.isArray(this.valueObj)) {
result = (<any[]>this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v));
} else if (isObject(this.valueObj)) {
result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key]));
}
return Promise.resolve(result);
}
toString(): string {
return `${this.name}\n${this.value}`;
}
}
export class ExpressionContainer implements IExpressionContainer {
public static allValues = new Map<string, string>();
// Use chunks to support variable paging #9537
private static readonly BASE_CHUNK_SIZE = 100;
public type: string | undefined;
public valueChanged = false;
private _value: string = '';
protected children?: Promise<IExpression[]>;
@ -195,13 +132,43 @@ export class ExpressionContainer implements IExpressionContainer {
toString(): string {
return this.value;
}
async evaluateExpression(
expression: string,
session: IDebugSession | undefined,
stackFrame: IStackFrame | undefined,
context: string): Promise<boolean> {
if (!session || (!stackFrame && context !== 'repl')) {
this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE;
this.reference = 0;
return false;
}
this.session = session;
try {
const response = await session.evaluate(expression, stackFrame ? stackFrame.frameId : undefined, context);
if (response && response.body) {
this.value = response.body.result || '';
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type || this.type;
return true;
}
return false;
} catch (e) {
this.value = e.message || '';
this.reference = 0;
return false;
}
}
}
export class Expression extends ExpressionContainer implements IExpression {
static DEFAULT_VALUE = nls.localize('notAvailable', "not available");
public available: boolean;
public type: string | undefined;
constructor(public name: string, id = generateUuid()) {
super(undefined, 0, id);
@ -214,30 +181,7 @@ export class Expression extends ExpressionContainer implements IExpression {
}
async evaluate(session: IDebugSession | undefined, stackFrame: IStackFrame | undefined, context: string): Promise<void> {
if (!session || (!stackFrame && context !== 'repl')) {
this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE;
this.available = false;
this.reference = 0;
return Promise.resolve(undefined);
}
this.session = session;
try {
const response = await session.evaluate(this.name, stackFrame ? stackFrame.frameId : undefined, context);
this.available = !!(response && response.body);
if (response && response.body) {
this.value = response.body.result || '';
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type || this.type;
}
} catch (e) {
this.value = e.message || '';
this.available = false;
this.reference = 0;
}
this.available = await this.evaluateExpression(this.name, session, stackFrame, context);
}
toString(): string {

View file

@ -6,15 +6,105 @@
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel';
import { isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel';
import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { endsWith } from 'vs/base/common/strings';
import { generateUuid } from 'vs/base/common/uuid';
const MAX_REPL_LENGTH = 10000;
let topReplElementCounter = 0;
export class SimpleReplElement implements IReplElement {
constructor(
private id: string,
public value: string,
public severity: severity,
public sourceData?: IReplElementSource,
) { }
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class RawObjectReplElement implements IExpression {
private static readonly MAX_CHILDREN = 1000; // upper bound of children per value
constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { }
getId(): string {
return this.id;
}
get value(): string {
if (this.valueObj === null) {
return 'null';
} else if (Array.isArray(this.valueObj)) {
return `Array[${this.valueObj.length}]`;
} else if (isObject(this.valueObj)) {
return 'Object';
} else if (isString(this.valueObj)) {
return `"${this.valueObj}"`;
}
return String(this.valueObj) || '';
}
get hasChildren(): boolean {
return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0);
}
getChildren(): Promise<IExpression[]> {
let result: IExpression[] = [];
if (Array.isArray(this.valueObj)) {
result = (<any[]>this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v));
} else if (isObject(this.valueObj)) {
result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key]));
}
return Promise.resolve(result);
}
toString(): string {
return `${this.name}\n${this.value}`;
}
}
export class ReplEvaluationInput implements IReplElement {
private id: string;
constructor(public value: string) {
this.id = generateUuid();
}
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class ReplEvaluationResult extends ExpressionContainer implements IReplElement {
constructor() {
super(undefined, 0, generateUuid());
}
toString(): string {
return `${this.value}`;
}
}
export class ReplModel {
private replElements: IReplElement[] = [];
@ -24,10 +114,11 @@ export class ReplModel {
return this.replElements;
}
addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
const expression = new Expression(name);
this.addReplElement(expression);
return expression.evaluate(this.session, stackFrame, 'repl');
async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
this.addReplElement(new ReplEvaluationInput(name));
const result = new ReplEvaluationResult();
await result.evaluateExpression(name, this.session, stackFrame, 'repl');
this.addReplElement(result);
}
appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void {

View file

@ -6,12 +6,12 @@
import * as assert from 'assert';
import { URI as uri } from 'vs/base/common/uri';
import severity from 'vs/base/common/severity';
import { SimpleReplElement, DebugModel, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
import { DebugModel, Expression, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
import * as sinon from 'sinon';
import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
import { NullOpenerService } from 'vs/platform/opener/common/opener';
@ -348,9 +348,7 @@ suite('Debug - Model', () => {
assert.equal(replModel.getReplElements().length, 3);
replModel.getReplElements().forEach(re => {
assert.equal((<Expression>re).available, false);
assert.equal((<Expression>re).name, 'myVariable');
assert.equal((<Expression>re).reference, 0);
assert.equal((<ReplEvaluationInput>re).value, 'myVariable');
});
replModel.removeReplExpressions();

View file

@ -21,6 +21,7 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron
import { URI } from 'vs/base/common/uri';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
// Singletons
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true);
@ -85,8 +86,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: DebugExtensionHostAction.ID,
title: DebugExtensionHostAction.LABEL,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/start-dark.svg`)),
light: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/start-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/start-dark.svg`)),
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/start-light.svg`)),
}
},
group: 'navigation',
@ -98,8 +99,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: StartExtensionHostProfileAction.ID,
title: StartExtensionHostProfileAction.LABEL,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/profile-start-dark.svg`)),
light: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/profile-start-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/profile-start-dark.svg`)),
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/profile-start-light.svg`)),
}
},
group: 'navigation',
@ -111,8 +112,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: StopExtensionHostProfileAction.ID,
title: StopExtensionHostProfileAction.LABEL,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/profile-stop-dark.svg`)),
light: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/profile-stop-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/profile-stop-dark.svg`)),
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/profile-stop-light.svg`)),
}
},
group: 'navigation',
@ -124,8 +125,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: SaveExtensionHostProfileAction.ID,
title: SaveExtensionHostProfileAction.LABEL,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/save-dark.svg`)),
light: URI.parse(require.toUrl(`vs/workbench/contrib/extensions/browser/media/save-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/save-dark.svg`)),
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/extensions/browser/media/save-light.svg`)),
},
precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED
},

View file

@ -26,6 +26,7 @@ import { Schemas } from 'vs/base/common/network';
import { SupportsWorkspacesContext, IsWebContext, RemoteFileDialogContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { OpenFileFolderAction, OpenLocalFileFolderCommand, OpenFileAction, OpenFolderAction, OpenLocalFileCommand, OpenLocalFolderCommand, OpenWorkspaceAction, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
// Contribute Global Actions
const category = { value: nls.localize('filesCategory', "File"), original: 'File' };
@ -223,12 +224,12 @@ function appendEditorTitleContextMenuItem(id: string, title: string, when: Conte
// Editor Title Menu for Conflict Resolution
appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), {
light: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check-dark.svg`))
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-dark.svg`))
}, -10, acceptLocalChangesCommand);
appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), {
light: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/undo-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/undo-dark.svg`))
light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-light.svg`)),
dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-dark.svg`))
}, -9, revertLocalChangesCommand);
function appendSaveConflictEditorTitleAction(id: string, title: string, iconLocation: { dark: URI; light?: URI; }, order: number, command: ICommandHandler): void {

View file

@ -39,6 +39,7 @@ import { ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/contrib
import { ILabelService } from 'vs/platform/label/common/label';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
@ -366,6 +367,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
});
const PREFERENCES_EDITOR_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`));
const PREFERENCES_EDITOR_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`));
class PreferencesActionsContribution extends Disposable implements IWorkbenchContribution {
constructor(
@ -381,8 +384,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
id: OpenGlobalKeybindingsAction.ID,
title: OpenGlobalKeybindingsAction.LABEL,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`))
light: PREFERENCES_EDITOR_LIGHT_ICON_URI,
dark: PREFERENCES_EDITOR_DARK_ICON_URI
}
},
when: ResourceContextKey.Resource.isEqualTo(environmentService.keybindingsResource.toString()),
@ -397,8 +400,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
id: commandId,
title: OpenSettings2Action.LABEL,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`))
light: PREFERENCES_EDITOR_LIGHT_ICON_URI,
dark: PREFERENCES_EDITOR_DARK_ICON_URI
}
},
when: ResourceContextKey.Resource.isEqualTo(environmentService.settingsResource.toString()),
@ -438,8 +441,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
id: commandId,
title: OpenSettings2Action.LABEL,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`))
light: PREFERENCES_EDITOR_LIGHT_ICON_URI,
dark: PREFERENCES_EDITOR_DARK_ICON_URI
}
},
when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')),
@ -466,8 +469,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
id: commandId,
title: OpenSettings2Action.LABEL,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`))
light: PREFERENCES_EDITOR_LIGHT_ICON_URI,
dark: PREFERENCES_EDITOR_DARK_ICON_URI
}
},
when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())),
@ -533,8 +536,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
id: OpenGlobalKeybindingsFileAction.ID,
title: OpenGlobalKeybindingsFileAction.LABEL,
iconLocation: {
light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)),
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`))
light: PREFERENCES_EDITOR_LIGHT_ICON_URI,
dark: PREFERENCES_EDITOR_DARK_ICON_URI
}
},
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR),

View file

@ -97,9 +97,11 @@ class OutlineModel extends QuickOpenModel {
}
});
this.entries.sort(SymbolEntry.compareByRank);
// select comparator based on the presence of the colon-prefix
this.entries.sort(searchValuePos === 0
? SymbolEntry.compareByRank
: SymbolEntry.compareByKindAndRank
);
// Mark all type groups
const visibleResults = <SymbolEntry[]>this.getEntries(true);
@ -297,7 +299,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup {
const kindB = NLS_SYMBOL_KIND_CACHE[b.getKind()] || FALLBACK_NLS_SYMBOL_KIND;
let r = kindA.localeCompare(kindB);
if (r === 0) {
r = this.compareByRank(a, b);
r = SymbolEntry.compareByRank(a, b);
}
return r;
}

View file

@ -278,6 +278,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown())));
this._onDidStateChange = this._register(new Emitter());
this.registerCommands();
this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise<string | undefined> => {
let tasks = await this.getTasksForGroup(TaskGroup.Build);
if (tasks.length > 0) {
let { defaults, users } = this.splitPerGroupType(tasks);
if (defaults.length === 1) {
return defaults[0]._label;
} else if (defaults.length + users.length > 0) {
tasks = defaults.concat(users);
}
}
let entry: TaskQuickPickEntry | null | undefined;
if (tasks && tasks.length > 0) {
entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task'));
}
let task: Task | undefined | null = entry ? entry.task : undefined;
if (!task) {
return undefined;
}
return task._label;
});
}
public get onDidStateChange(): Event<TaskEvent> {

View file

@ -21,7 +21,7 @@ import { FileService } from 'vs/platform/files/common/fileService';
import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService } from 'vs/platform/files/common/files';
import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
@ -49,7 +49,7 @@ const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(u
class TestBackupEnvironmentService extends WorkbenchEnvironmentService {
constructor(backupPath: string) {
super({ ...parseArgs(process.argv), ...{ backupPath, 'user-data-dir': userdataDir } } as IWindowConfiguration, process.execPath);
super({ ...parseArgs(process.argv, OPTIONS), ...{ backupPath, 'user-data-dir': userdataDir } } as IWindowConfiguration, process.execPath);
}
}

View file

@ -11,7 +11,7 @@ import * as fs from 'fs';
import * as json from 'vs/base/common/json';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import * as uuid from 'vs/base/common/uuid';
@ -46,7 +46,7 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file
class TestEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
super(parseArgs(process.argv, OPTIONS) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }

View file

@ -11,7 +11,7 @@ import * as os from 'os';
import { URI } from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import * as pfs from 'vs/base/node/pfs';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
@ -51,7 +51,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
class TestEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
super(parseArgs(process.argv, OPTIONS) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }

View file

@ -86,12 +86,12 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
}, envVariables);
}
public resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<any> {
// resolve any non-interactive variables
config = this.resolveAny(folder, config);
public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<any> {
// resolve any non-interactive variables and any contributed variables
config = await this.resolveAny(folder, config);
// resolve input variables in the order in which they are encountered
return this.resolveWithInteraction(folder, config, section, variables).then(mapping => {
return this.resolveWithInteraction(folder, config, section, variables, true).then(mapping => {
// finally substitute evaluated command variables (if there are any)
if (!mapping) {
return null;
@ -103,9 +103,9 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
});
}
public resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<Map<string, string> | undefined> {
// resolve any non-interactive variables
const resolved = this.resolveAnyMap(folder, config);
public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, skipContributed: boolean = false): Promise<Map<string, string> | undefined> {
// resolve any non-interactive variables and any contributed variables
const resolved = await this.resolveAnyMap(folder, config);
config = resolved.newConfig;
const allVariableMapping: Map<string, string> = resolved.resolvedVariables;

View file

@ -20,7 +20,7 @@ export interface IConfigurationResolverService {
* Recursively resolves all variables in the given config and returns a copy of it with substituted values.
* Command variables are only substituted if a "commandValueMapping" dictionary is given and if it contains an entry for the command.
*/
resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): any;
resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): Promise<any>;
/**
* Recursively resolves all variables (including commands and user input) in the given config and returns a copy of it with substituted values.
@ -36,6 +36,12 @@ export interface IConfigurationResolverService {
* Keys in the map will be of the format input:variableName or command:variableName.
*/
resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<Map<string, string> | undefined>;
/**
* Contributes a variable that can be resolved later. Consumers that use resolveAny, resolveWithInteraction,
* and resolveWithInteractionReplace will have contributed variables resolved.
*/
contributeVariable(variable: string, resolution: () => Promise<string | undefined>): void;
}
export interface PromptStringInputInfo {

View file

@ -28,9 +28,12 @@ export interface IVariableResolveContext {
export class AbstractVariableResolverService implements IConfigurationResolverService {
static VARIABLE_REGEXP = /\$\{(.*?)\}/g;
static VARIABLE_REGEXP_SINGLE = /\$\{(.*?)\}/;
_serviceBrand: undefined;
private _contributedVariables: Map<string, () => Promise<string | undefined>> = new Map();
constructor(
private _context: IVariableResolveContext,
private _envVariables: IProcessEnvironment
@ -50,8 +53,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
return this.recursiveResolve(root ? root.uri : undefined, value);
}
public resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
private async resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): Promise<any> {
const result = objects.deepClone(config) as any;
// hoist platform specific attributes to top level
@ -69,16 +71,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
delete result.linux;
// substitute all variables recursively in string values
return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables);
return this.recursiveResolvePromise(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables);
}
public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): any {
public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): Promise<any> {
return this.resolveAnyBase(workspaceFolder, config, commandValueMapping);
}
public resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): { newConfig: any, resolvedVariables: Map<string, string> } {
protected async resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): Promise<{ newConfig: any, resolvedVariables: Map<string, string> }> {
const resolvedVariables = new Map<string, string>();
const newConfig = this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables);
const newConfig = await this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables);
return { newConfig, resolvedVariables };
}
@ -90,6 +92,14 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
throw new Error('resolveWithInteraction not implemented.');
}
public contributeVariable(variable: string, resolution: () => Promise<string | undefined>): void {
if (this._contributedVariables.has(variable)) {
throw new Error('Variable ' + variable + ' is contributed twice.');
} else {
this._contributedVariables.set(variable, resolution);
}
}
private recursiveResolve(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
if (types.isString(value)) {
return this.resolveString(folderUri, value, commandValueMapping, resolvedVariables);
@ -106,6 +116,23 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
return value;
}
private async recursiveResolvePromise(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): Promise<any> {
if (types.isString(value)) {
return this.resolveStringPromise(folderUri, value, commandValueMapping, resolvedVariables);
} else if (types.isArray(value)) {
return Promise.all(value.map(s => this.recursiveResolvePromise(folderUri, s, commandValueMapping, resolvedVariables)));
} else if (types.isObject(value)) {
let result: IStringDictionary<string | IStringDictionary<string> | string[]> = Object.create(null);
const keys = Object.keys(value);
for (let key of keys) {
const replaced = await this.resolveStringPromise(folderUri, key, commandValueMapping, resolvedVariables);
result[replaced] = await this.recursiveResolvePromise(folderUri, value[key], commandValueMapping, resolvedVariables);
}
return result;
}
return value;
}
private resolveString(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary<string> | undefined, resolvedVariables?: Map<string, string>): string {
// loop through all variables occurrences in 'value'
@ -123,6 +150,37 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
return replaced;
}
private async resolveStringPromise(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary<string> | undefined, resolvedVariables?: Map<string, string>): Promise<string> {
// loop through all variables occurrences in 'value'
const matches = value.match(AbstractVariableResolverService.VARIABLE_REGEXP);
const replaces: Map<string, string> = new Map();
if (!matches) {
return value;
}
for (const match of matches) {
const evaluate = await this.evaluateSingleContributedVariable(match, match.match(AbstractVariableResolverService.VARIABLE_REGEXP_SINGLE)![1]);
if (evaluate !== match) {
replaces.set(match, evaluate);
}
}
const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => {
let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping);
if ((resolvedValue === match) && (replaces.has(match))) {
resolvedValue = replaces.get(match)!;
}
if (resolvedVariables) {
resolvedVariables.set(variable, resolvedValue);
}
return resolvedValue;
});
return replaced;
}
private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary<string> | undefined): string {
// try to separate variable arguments from variable name
@ -271,6 +329,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
}
}
private async evaluateSingleContributedVariable(match: string, variable: string): Promise<string> {
if (this._contributedVariables.has(variable)) {
const contributedValue: string | undefined = await this._contributedVariables.get(variable)!();
if (contributedValue !== undefined) {
return contributedValue;
}
}
return match;
}
private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary<string> | undefined, prefix: string): string {
if (argument && commandValueMapping) {
const v = commandValueMapping[prefix + ':' + argument];

View file

@ -487,6 +487,19 @@ suite('Configuration Resolver Service', () => {
assert.equal(2, mockCommandService.callCount);
});
});
test('contributed variable', () => {
const buildTask = 'npm: compile';
const variable = 'defaultBuildTask';
const configuration = {
'name': '${' + variable + '}',
};
configurationResolverService!.contributeVariable(variable, async () => { return buildTask; });
return configurationResolverService!.resolveAny(workspace, configuration).then(result => {
assert.deepEqual(result, {
'name': `${buildTask}`
});
});
});
});

View file

@ -25,7 +25,7 @@ export class BrowserCredentialsService implements ICredentialsService {
if (environmentService.options && environmentService.options.credentialsProvider) {
this.credentialsProvider = environmentService.options.credentialsProvider;
} else {
this.credentialsProvider = new LocalStorageCredentialsProvider();
this.credentialsProvider = new InMemoryCredentialsProvider();
}
}
@ -56,80 +56,44 @@ interface ICredential {
password: string;
}
class LocalStorageCredentialsProvider implements ICredentialsProvider {
class InMemoryCredentialsProvider implements ICredentialsProvider {
static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider';
private _credentials: ICredential[] | undefined;
private get credentials(): ICredential[] {
if (!this._credentials) {
try {
const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY);
if (serializedCredentials) {
this._credentials = JSON.parse(serializedCredentials);
}
} catch (error) {
// ignore
}
if (!Array.isArray(this._credentials)) {
this._credentials = [];
}
}
return this._credentials;
}
private save(): void {
window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY, JSON.stringify(this.credentials));
}
private credentials: ICredential[] = [];
async getPassword(service: string, account: string): Promise<string | null> {
return this.doGetPassword(service, account);
}
const credential = this.doFindPassword(service, account);
private async doGetPassword(service: string, account?: string): Promise<string | null> {
for (const credential of this.credentials) {
if (credential.service === service) {
if (typeof account !== 'string' || account === credential.account) {
return credential.password;
}
}
}
return null;
return credential ? credential.password : null;
}
async setPassword(service: string, account: string, password: string): Promise<void> {
this.deletePassword(service, account);
this.credentials.push({ service, account, password });
this.save();
}
async deletePassword(service: string, account: string): Promise<boolean> {
let found = false;
this._credentials = this.credentials.filter(credential => {
if (credential.service === service && credential.account === account) {
found = true;
return false;
}
return true;
});
if (found) {
this.save();
const credential = this.doFindPassword(service, account);
if (credential) {
this.credentials = this.credentials.splice(this.credentials.indexOf(credential), 1);
}
return found;
return !!credential;
}
async findPassword(service: string): Promise<string | null> {
return this.doGetPassword(service);
const credential = this.doFindPassword(service);
return credential ? credential.password : null;
}
private doFindPassword(service: string, account?: string): ICredential | null {
for (const credential of this.credentials) {
if (credential.service === service && (typeof account !== 'string' || credential.account === account)) {
return credential;
}
}
return null;
}
async findCredentials(service: string): Promise<Array<{ account: string, password: string }>> {

View file

@ -46,14 +46,14 @@ import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { URI } from 'vs/base/common/uri';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
class TestEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
super(parseArgs(process.argv, OPTIONS) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }

View file

@ -1043,6 +1043,13 @@ export function createValidator(prop: IConfigurationPropertySchema): (value: any
const stringArrayValue = value as string[];
if (prop.uniqueItems) {
if (new Set(stringArrayValue).size < stringArrayValue.length) {
message += nls.localize('validations.stringArrayUniqueItems', 'Array has duplicate items');
message += '\n';
}
}
if (prop.minItems && stringArrayValue.length < prop.minItems) {
message += nls.localize('validations.stringArrayMinItem', 'Array must have at least {0} items', prop.minItems);
message += '\n';

View file

@ -328,4 +328,10 @@ suite('Preferences Model test', () => {
arr.rejects(['a']).withMessage(`err: must be friendly`);
});
test('uniqueItems', () => {
const arr = new ArrayTester({ type: 'array', items: { type: 'string' }, uniqueItems: true });
arr.rejects(['a', 'a']).withMessage(`Array has duplicate items`);
});
});

View file

@ -158,9 +158,8 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
// since a backup did not happen, we have to confirm for the dirty files now
return this.confirmBeforeShutdown();
}, errors => {
const firstError = errors[0];
this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", firstError.message));
}, error => {
this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", error.message));
return true; // veto, the backups failed
});

View file

@ -5,17 +5,10 @@
import { IURLService } from 'vs/platform/url/common/url';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractURLService } from 'vs/platform/url/common/urlService';
import { Event, Emitter } from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Disposable } from 'vs/base/common/lifecycle';
import { IRequestService } from 'vs/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { streamToBuffer } from 'vs/base/common/buffer';
import { ILogService } from 'vs/platform/log/common/log';
import { generateUuid } from 'vs/base/common/uuid';
export interface IURLCallbackProvider {
@ -47,130 +40,30 @@ export class BrowserURLService extends AbstractURLService {
_serviceBrand: undefined;
private provider: IURLCallbackProvider;
private provider: IURLCallbackProvider | undefined;
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IInstantiationService instantiationService: IInstantiationService
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
super();
this.provider = environmentService.options && environmentService.options.urlCallbackProvider ? environmentService.options.urlCallbackProvider : instantiationService.createInstance(SelfhostURLCallbackProvider);
this.provider = environmentService.options!.urlCallbackProvider;
this.registerListeners();
}
private registerListeners(): void {
this._register(this.provider.onCallback(uri => this.open(uri)));
if (this.provider) {
this._register(this.provider.onCallback(uri => this.open(uri)));
}
}
create(options?: Partial<UriComponents>): URI {
return this.provider.create(options);
}
}
class SelfhostURLCallbackProvider extends Disposable implements IURLCallbackProvider {
static FETCH_INTERVAL = 500; // fetch every 500ms
static FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min
static QUERY_KEYS = {
REQUEST_ID: 'vscode-requestId',
SCHEME: 'vscode-scheme',
AUTHORITY: 'vscode-authority',
PATH: 'vscode-path',
QUERY: 'vscode-query',
FRAGMENT: 'vscode-fragment'
};
private readonly _onCallback: Emitter<URI> = this._register(new Emitter<URI>());
readonly onCallback: Event<URI> = this._onCallback.event;
constructor(
@IRequestService private readonly requestService: IRequestService,
@ILogService private readonly logService: ILogService
) {
super();
}
create(options?: Partial<UriComponents>): URI {
const queryValues: Map<string, string> = new Map();
const requestId = generateUuid();
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);
const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined };
if (scheme) {
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.SCHEME, scheme);
if (this.provider) {
return this.provider.create(options);
}
if (authority) {
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority);
}
if (path) {
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.PATH, path);
}
if (query) {
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.QUERY, query);
}
if (fragment) {
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment);
}
// Start to poll on the callback being fired
this.periodicFetchCallback(requestId, Date.now());
return this.doCreateUri('/callback', queryValues);
}
private async periodicFetchCallback(requestId: string, startTime: number): Promise<void> {
// Ask server for callback results
const queryValues: Map<string, string> = new Map();
queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);
const result = await this.requestService.request({
url: this.doCreateUri('/fetch-callback', queryValues).toString(true)
}, CancellationToken.None);
// Check for callback results
const content = await streamToBuffer(result.stream);
if (content.byteLength > 0) {
try {
this._onCallback.fire(URI.revive(JSON.parse(content.toString())));
} catch (error) {
this.logService.error(error);
}
return; // done
}
// Continue fetching unless we hit the timeout
if (Date.now() - startTime < SelfhostURLCallbackProvider.FETCH_TIMEOUT) {
setTimeout(() => this.periodicFetchCallback(requestId, startTime), SelfhostURLCallbackProvider.FETCH_INTERVAL);
}
}
private doCreateUri(path: string, queryValues: Map<string, string>): URI {
let query: string | undefined = undefined;
if (queryValues) {
let index = 0;
queryValues.forEach((value, key) => {
if (!query) {
query = '';
}
const prefix = (index++ === 0) ? '' : '&';
query += `${prefix}${key}=${encodeURIComponent(value)}`;
});
}
return URI.parse(window.location.href).with({ path, query });
return URI.parse('unsupported://');
}
}

View file

@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/node/urlIpc';
import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/node/urlService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import product from 'vs/platform/product/node/product';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWindowService } from 'vs/platform/windows/common/windows';
export class RelayURLService extends URLService implements IURLHandler {
@ -18,7 +19,8 @@ export class RelayURLService extends URLService implements IURLHandler {
constructor(
@IMainProcessService mainProcessService: IMainProcessService,
@IOpenerService openerService: IOpenerService
@IOpenerService openerService: IOpenerService,
@IWindowService private windowService: IWindowService
) {
super();
@ -28,6 +30,19 @@ export class RelayURLService extends URLService implements IURLHandler {
openerService.registerOpener(this);
}
create(options?: Partial<UriComponents>): URI {
const uri = super.create(options);
let query = uri.query;
if (!query) {
query = `windowId=${encodeURIComponent(this.windowService.windowId)}`;
} else {
query += `&windowId=${encodeURIComponent(this.windowService.windowId)}`;
}
return uri.with({ query });
}
async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise<boolean> {
if (resource.scheme !== product.urlProtocol) {
return false;

View file

@ -30,7 +30,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { ITextFileStreamContent, ITextFileService, IResourceEncoding, IReadTextFileOptions } from 'vs/workbench/services/textfile/common/textfiles';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@ -91,7 +91,7 @@ export function createFileInput(instantiationService: IInstantiationService, res
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
}
export const TestEnvironmentService = new WorkbenchEnvironmentService(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
export const TestEnvironmentService = new WorkbenchEnvironmentService(parseArgs(process.argv, OPTIONS) as IWindowConfiguration, process.execPath);
export class TestContextService implements IWorkspaceContextService {
public _serviceBrand: undefined;

View file

@ -28,7 +28,7 @@ const STACK_FRAME = `${VIEWLET} .monaco-list-row .stack-frame`;
const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title*="${filename}"]`;
const VARIABLE = `${VIEWLET} .debug-variables .monaco-list-row .expression`;
const CONSOLE_OUTPUT = `.repl .output.expression .value`;
const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`;
const CONSOLE_EVALUATION_RESULT = `.repl .evaluation-result.expression .value`;
const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea';
@ -132,8 +132,8 @@ export class Debug extends Viewlet {
// Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed
await this.editor.waitForEditorContents('debug:replinput', s => s.indexOf(text) >= 0);
await this.code.dispatchKeybinding('enter');
await this.code.waitForElement(CONSOLE_INPUT_OUTPUT);
await this.waitForOutput(output => accept(output[output.length - 1] || ''));
await this.code.waitForElements(CONSOLE_EVALUATION_RESULT, false,
elements => !!elements.length && accept(elements[elements.length - 1].textContent));
}
// Different node versions give different number of variables. As a workaround be more relaxed when checking for variable count

View file

@ -3697,10 +3697,10 @@ gulp-symdest@^1.1.1:
queue "^3.1.0"
vinyl-fs "^2.4.3"
gulp-tsb@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.1.tgz#be2d8900d9227abf0e728a33139891e49b9e85d3"
integrity sha512-HHR5qMjj/NyFlYdY6AIql7bWosFAknNfeJumwdkPkNaw6GtHVoaK+hPmWgmyK9Otf9nqcETtI5KI2vIla6Vhdw==
gulp-tsb@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.2.tgz#8717a18f1ce032147e010028f0a59863bd552b96"
integrity sha512-xF88h0vFH8JkunSnmVrYfrR3LGTMAY+KTkHHF/S9BAOCsdaC83/hv4EmpcLxev7B+2yd3+xcitlsDFMBSo/gSw==
dependencies:
ansi-colors "^1.0.1"
fancy-log "^1.3.2"
@ -9158,10 +9158,10 @@ xterm-addon-web-links@0.1.0-beta10:
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23"
integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg==
xterm@3.15.0-beta107:
version "3.15.0-beta107"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta107.tgz#d1fea6434e66350c227b3993aabd8c5eb3afa427"
integrity sha512-a1bGMbh25pQzBT5lBXJaXD+pM9PX69iXfYrKL4PCQ5jgbUIu45SRCI2MnpS5X1WC+hCcOyn09PZuAdqTcpYeFA==
xterm@3.15.0-beta108:
version "3.15.0-beta108"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta108.tgz#d113f6d1e4d4b7645ab3ff002c81a4dba8a78a28"
integrity sha512-btZXgI9BeawFzitw3EZvFPsH3kfBgJS55LOdz9DjEany3y9FfvmnVhq7wn4x1PmTU/djHobGGhMNemxptk+ryg==
y18n@^3.2.1:
version "3.2.1"