Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 (#6516)

* Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3

* fix tests
This commit is contained in:
Anthony Dresser
2019-07-28 15:15:24 -07:00
committed by GitHub
parent aacf1e7f1c
commit 1d56a17f32
292 changed files with 19784 additions and 1873 deletions
+3 -2
View File
@@ -59,5 +59,6 @@
"git.ignoreLimitWarning": true,
"remote.extensionKind": {
"msjsdiag.debugger-for-chrome": "workspace"
}
}
},
"files.insertFinalNewline": true
}
+1 -1
View File
@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "4.2.5"
target "4.2.7"
runtime "electron"
+3 -3
View File
@@ -31,12 +31,12 @@ steps:
git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git"
git fetch distro
# Push master branch into master and oss/master
git push distro origin/master:refs/heads/master origin/master:refs/heads/oss/master
# Push master branch into oss/master
git push distro origin/master:refs/heads/oss/master
# Push every release branch into oss/release
git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro
git merge $(node -p "require('./package.json').distro")
displayName: Sync & Merge Distro
displayName: Sync & Merge Distro
+1 -1
View File
@@ -17,7 +17,7 @@ const gunzip = require('gulp-gunzip');
const untar = require('gulp-untar');
const File = require('vinyl');
const fs = require('fs');
const remote = require('gulp-remote-src');
const remote = require('gulp-remote-retry-src');
const rename = require('gulp-rename');
const filter = require('gulp-filter');
const cp = require('child_process');
+1 -1
View File
@@ -13,7 +13,7 @@ const File = require("vinyl");
const vsce = require("vsce");
const stats_1 = require("./stats");
const util2 = require("./util");
const remote = require("gulp-remote-src");
const remote = require("gulp-remote-retry-src");
const vzip = require('gulp-vinyl-zip');
const filter = require("gulp-filter");
const rename = require("gulp-rename");
+1 -1
View File
@@ -13,7 +13,7 @@ import * as File from 'vinyl';
import * as vsce from 'vsce';
import { createStatsStream } from './stats';
import * as util2 from './util';
import remote = require('gulp-remote-src');
import remote = require('gulp-remote-retry-src');
const vzip = require('gulp-vinyl-zip');
import filter = require('gulp-filter');
import rename = require('gulp-rename');
+2 -2
View File
@@ -1,4 +1,4 @@
declare module 'gulp-remote-src' {
declare module 'gulp-remote-retry-src' {
import stream = require("stream");
@@ -20,4 +20,4 @@ declare module 'gulp-remote-src' {
}
export = remote;
}
}
+2
View File
@@ -5,6 +5,8 @@
declare namespace monaco {
// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.
export type Thenable<T> = PromiseLike<T>;
export interface IDisposable {
+1 -1
View File
@@ -30,7 +30,7 @@ import * as editorAPI from './vs/editor/editor.api';
a = (<ServiceIdentifier<any>>b).type;
a = (<IHighlight>b).start;
a = (<IHighlight>b).end;
a = (<SimpleWorkerClient<any>>b).getProxyObject; // IWorkerClient
a = (<SimpleWorkerClient<any, any>>b).getProxyObject; // IWorkerClient
a = create1;
a = create2;
a = (<DocumentRangeFormattingEditProvider>b).extensionId;
+3 -3
View File
@@ -20,7 +20,7 @@
"@types/minimatch": "^3.0.3",
"@types/minimist": "^1.2.0",
"@types/mocha": "2.2.39",
"@types/node": "8.0.33",
"@types/node": "^10.14.8",
"@types/pump": "^1.0.1",
"@types/request": "^2.47.0",
"@types/rimraf": "^2.0.2",
@@ -29,7 +29,7 @@
"@types/uglify-es": "^3.0.0",
"@types/underscore": "^1.8.9",
"@types/xml2js": "0.0.33",
"applicationinsights": "1.0.6",
"applicationinsights": "1.0.8",
"azure-storage": "^2.1.0",
"del": "^3.0.0",
"documentdb": "1.13.0",
@@ -44,7 +44,7 @@
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"typescript": "3.5.2",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "1.4.3",
"vscode-telemetry-extractor": "^1.5.1",
"xml2js": "^0.4.17"
},
"scripts": {
+22 -17
View File
@@ -187,10 +187,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==
"@types/node@8.0.33":
version "8.0.33"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==
"@types/node@^10.14.8":
version "10.14.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.13.tgz#ac786d623860adf39a3f51d629480aacd6a6eec7"
integrity sha512-yN/FNNW1UYsRR1wwAoyOwqvDuLDtVXnaJTZ898XIw/Q5cCaeVAlVwvsmXLX5PuiScBYwZsZU4JYSHB3TvfdwvQ==
"@types/pump@^1.0.1":
version "1.0.1"
@@ -374,10 +374,10 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
applicationinsights@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb"
integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw==
applicationinsights@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
dependencies:
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
@@ -1847,7 +1847,12 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
@@ -3388,7 +3393,7 @@ tough-cookie@~2.3.3:
dependencies:
punycode "^1.4.1"
ts-morph@^3.1.2:
ts-morph@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-3.1.3.tgz#bbfa1d14481ee23bdd1c030340ccf4a243cfc844"
integrity sha512-CwjgyJTtd3f8vBi7Vr0IOgdOY6Wi/Tq0MhieXOE2B5ns5WWRD7BwMNHtv+ZufKI/S2U/lMrh+Q3bOauE4tsv2g==
@@ -3613,19 +3618,19 @@ vsce@1.48.0:
yauzl "^2.3.1"
yazl "^2.2.2"
vscode-ripgrep@^1.4.0:
vscode-ripgrep@^1.5.5:
version "1.5.5"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961"
integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg==
vscode-telemetry-extractor@1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.4.3.tgz#e4af380beb4e2a63d6e4fa819b25ba7ef6dc4a10"
integrity sha512-OFklPErZnUBjrKte3hg+irQXue5rzgz4qnvE8kdaOnW1E/wynHUEXW9t5vF5q0hu2lodpGMkybwLkSjONZVzvg==
vscode-telemetry-extractor@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.5.1.tgz#67249e4ca9c65a21800ca53880732f8cef98d0fa"
integrity sha512-B5SnEdRiDrI4o6NMG9iHmengoaW1rxUQmS/sCaripgnchm+P79JURmKxhfXr5eRo4Mr1QSenFT/SDNaEop7aoQ==
dependencies:
command-line-args "^5.1.1"
ts-morph "^3.1.2"
vscode-ripgrep "^1.4.0"
ts-morph "^3.1.3"
vscode-ripgrep "^1.5.5"
vso-node-api@6.1.2-preview:
version "6.1.2-preview"
+2 -2
View File
@@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "5d67ec3da5376a5058990e8a9557bc9124ad59a8"
"commitHash": "36ea114ac0616e469e75ae94e6d53af48925e036"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "4.2.5"
"version": "4.2.7"
},
{
"component": {
+9 -1
View File
@@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode';
import { mapEvent } from '../util';
@@ -214,6 +214,14 @@ export class ApiImpl implements API {
readonly git = new ApiGit(this._model);
get state(): APIState {
return this._model.state;
}
get onDidChangeState(): Event<APIState> {
return this._model.onDidChangeState;
}
get onDidOpenRepository(): Event<Repository> {
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
}
+4
View File
@@ -176,7 +176,11 @@ export interface Repository {
log(options?: LogOptions): Promise<Commit[]>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>;
+3 -3
View File
@@ -1115,7 +1115,7 @@ export class CommandCenter {
if (scmResources.length === 1) {
if (untrackedCount > 0) {
message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(scmResources[0].resourceUri.fsPath));
message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(scmResources[0].resourceUri.fsPath));
yes = localize('delete file', "Delete file");
} else {
if (scmResources[0].type === Status.DELETED) {
@@ -1134,7 +1134,7 @@ export class CommandCenter {
}
if (untrackedCount > 0) {
message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!", untrackedCount)}`;
message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST.", untrackedCount)}`;
}
}
@@ -1175,7 +1175,7 @@ export class CommandCenter {
await repository.clean(resources.map(r => r.resourceUri));
return;
} else if (resources.length === 1) {
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(resources[0].resourceUri.fsPath));
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(resources[0].resourceUri.fsPath));
const yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
+28 -12
View File
@@ -12,7 +12,7 @@ import * as path from 'path';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
import { GitErrorCodes } from './api/git';
import { GitErrorCodes, APIState as State } from './api/git';
const localize = nls.loadMessageBundle();
@@ -63,15 +63,22 @@ export class Model {
private possibleGitRepositoryPaths = new Set<string>();
private _onDidChangeState = new EventEmitter<State>();
readonly onDidChangeState = this._onDidChangeState.event;
private _state: State = 'uninitialized';
get state(): State { return this._state; }
setState(state: State): void {
this._state = state;
this._onDidChangeState.fire(state);
}
private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] });
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
this.onDidChangeVisibleTextEditors(window.visibleTextEditors);
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
const fsWatcher = workspace.createFileSystemWatcher('**');
@@ -82,7 +89,15 @@ export class Model {
const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));
onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);
this.scanWorkspaceFolders();
this.doInitialScan().finally(() => this.setState('initialized'));
}
private async doInitialScan(): Promise<void> {
await Promise.all([
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
this.onDidChangeVisibleTextEditors(window.visibleTextEditors),
this.scanWorkspaceFolders()
]);
}
/**
@@ -157,8 +172,8 @@ export class Model {
.filter(r => !activeRepositories.has(r!.repository))
.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];
possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));
openRepositoriesToDispose.forEach(r => r.dispose());
await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));
}
private onDidChangeConfiguration(): void {
@@ -175,7 +190,7 @@ export class Model {
openRepositoriesToDispose.forEach(r => r.dispose());
}
private onDidChangeVisibleTextEditors(editors: TextEditor[]): void {
private async onDidChangeVisibleTextEditors(editors: TextEditor[]): Promise<void> {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
@@ -183,7 +198,7 @@ export class Model {
return;
}
editors.forEach(editor => {
await Promise.all(editors.map(async editor => {
const uri = editor.document.uri;
if (uri.scheme !== 'file') {
@@ -196,8 +211,8 @@ export class Model {
return;
}
this.openRepository(path.dirname(uri.fsPath));
});
await this.openRepository(path.dirname(uri.fsPath));
}));
}
@sequentialize
@@ -236,6 +251,7 @@ export class Model {
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
this.open(repository);
await repository.status();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
return;
@@ -421,4 +437,4 @@ export class Model {
this.possibleGitRepositoryPaths.clear();
this.disposables = dispose(this.disposables);
}
}
}
+18 -8
View File
@@ -519,8 +519,8 @@ class DotGitWatcher implements IFileWatcher {
this.transientDisposables.push(upstreamWatcher);
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) {
if (env.logLevel <= LogLevel.Info) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`);
if (env.logLevel <= LogLevel.Error) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}', is most likely packed.\n${err.stack || err}`);
}
}
}
@@ -651,19 +651,30 @@ export class Repository implements Disposable {
const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
this.disposables.push(dotGitFileWatcher);
let onDotGitFileChange: Event<Uri>;
try {
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
onDotGitFileChange = dotGitFileWatcher.event;
this.disposables.push(dotGitFileWatcher);
} catch (err) {
if (env.logLevel <= LogLevel.Error) {
outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`);
}
onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path));
}
// FS changes should trigger `git status`:
// - any change inside the repository working tree
// - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event);
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, onDotGitFileChange);
onFileChange(this.onFileChange, this, this.disposables);
// Relevate repository changes should trigger virtual document change events
dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
onDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel));
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -713,7 +724,6 @@ export class Repository implements Disposable {
this.disposables.push(progressManager);
this.updateCommitTemplate();
this.status();
}
validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined {
@@ -17,7 +17,7 @@
"vscode-json-languageservice": "^3.3.0",
"vscode-languageserver": "^5.3.0-next.8",
"vscode-nls": "^4.1.1",
"vscode-uri": "^2.0.1"
"vscode-uri": "^2.0.3"
},
"devDependencies": {
"@types/mocha": "2.2.33",
@@ -154,7 +154,8 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
documentSymbolProvider: true,
documentRangeFormattingProvider: false,
colorProvider: {},
foldingRangeProvider: true
foldingRangeProvider: true,
selectionRangeProvider: true
};
return { capabilities };
@@ -134,3 +134,8 @@ vscode-uri@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.1.tgz#5448e4f77d21d93ffa34b96f84c6c5e09e3f5a9b"
integrity sha512-s/k0zsYr6y+tsocFyxT/+G5aq8mEdpDZuph3LZ+UmCs7LNhx/xomiCy5kyP+jOAKC7RMCUvb6JbPD1/TgAvq0g==
vscode-uri@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543"
integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw==
@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as vscode from 'vscode';
suite('workspace-namespace', () => {
suite('Tasks', () => {
test('CustomExecution2 task should start and shutdown successfully', (done) => {
interface CustomTestingTaskDefinition extends vscode.TaskDefinition {
/**
* One of the task properties. This can be used to customize the task in the tasks.json
*/
customProp1: string;
}
const taskType: string = 'customTesting';
const taskName = 'First custom task';
const reg1 = vscode.window.onDidOpenTerminal(term => {
reg1.dispose();
const reg2 = term.onDidWriteData(e => {
reg2.dispose();
assert.equal(e, 'testing\r\n');
term.dispose();
});
});
const taskProvider = vscode.tasks.registerTaskProvider(taskType, {
provideTasks: () => {
const result: vscode.Task[] = [];
const kind: CustomTestingTaskDefinition = {
type: taskType,
customProp1: 'testing task one'
};
const writeEmitter = new vscode.EventEmitter<string>();
const execution = new vscode.CustomExecution2((): Thenable<vscode.Pseudoterminal> => {
const pty: vscode.Pseudoterminal = {
onDidWrite: writeEmitter.event,
open: () => {
writeEmitter.fire('testing\r\n');
},
close: () => {
taskProvider.dispose();
done();
}
};
return Promise.resolve(pty);
});
const task = new vscode.Task2(kind, vscode.TaskScope.Workspace, taskName, taskType, execution);
result.push(task);
return result;
},
resolveTask(_task: vscode.Task): vscode.Task | undefined {
assert.fail('resolveTask should not trigger during the test');
return undefined;
}
});
vscode.commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`);
});
});
});
@@ -0,0 +1,8 @@
{
"search.exclude": {
"**/search-exclude/**": true
},
"files.exclude": {
"**/files-exclude/**": true
}
}
@@ -0,0 +1 @@
file
@@ -0,0 +1 @@
file
+3 -3
View File
@@ -53,17 +53,17 @@
"statusBar/windowIndicator": [
{
"command": "vscode-testresolver.newWindow",
"when": "!remoteAuthority",
"when": "!remoteName",
"group": "9_local_testresolver@2"
},
{
"command": "vscode-testresolver.showLog",
"when": "remoteAuthority =~ /^test\\+.*$/",
"when": "remoteName == test",
"group": "1_remote_testresolver_open@3"
},
{
"command": "vscode-testresolver.newWindow",
"when": "remoteAuthority =~ /^test\\+.*$/",
"when": "remoteName == test",
"group": "1_remote_testresolver_open@1"
}
]
+25 -22
View File
@@ -1,31 +1,34 @@
{
"comments": {
"lineComment": "",
"blockComment": ["<!--", "-->"]
"blockComment": [ "<!--", "-->" ]
},
"brackets": [
["<", ">"]
["<!--", "-->"],
["<", ">"],
["{", "}"],
["(", ")"]
],
"autoClosingPairs": [
["<", ">"],
["'", "'"],
["\"", "\""]
{ "open": "{", "close": "}"},
{ "open": "[", "close": "]"},
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]},
{ "open": "<![CDATA[", "close": "]]>", "notIn": [ "comment", "string" ]}
],
"surroundingPairs": [
["<", ">"],
["'", "'"],
["\"", "\""]
]
// enhancedBrackets: [{
// tokenType: 'tag.tag-$1.xml',
// openTrigger: '>',
// open: /<(\w[\w\d]*)([^\/>]*(?!\/)>)[^<>]*$/i,
// closeComplete: '</$1>',
// closeTrigger: '>',
// close: /<\/(\w[\w\d]*)\s*>$/i
// }],
// autoClosingPairs: [['\'', '\''], ['"', '"'] ]
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "{", "close": "}"},
{ "open": "[", "close": "]"},
{ "open": "(", "close": ")" },
{ "open": "<", "close": ">" }
],
"folding": {
"markers": {
"start": "^\\s*<!--\\s*#region\\b.*-->",
"end": "^\\s*<!--\\s*#endregion\\b.*-->"
}
}
}
+4 -4
View File
@@ -76,8 +76,8 @@
"vscode-ripgrep": "^1.5.5",
"vscode-sqlite3": "4.0.8",
"vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta71",
"xterm-addon-search": "0.2.0-beta2",
"xterm": "3.15.0-beta89",
"xterm-addon-search": "0.2.0-beta3",
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",
"yazl": "^2.4.3",
@@ -124,7 +124,7 @@
"gulp-gunzip": "^1.0.0",
"gulp-json-editor": "^2.5.0",
"gulp-plumber": "^1.2.0",
"gulp-remote-src": "^0.4.4",
"gulp-remote-retry-src": "^0.6.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.4",
"gulp-shell": "^0.6.5",
@@ -192,4 +192,4 @@
"windows-mutex": "0.3.0",
"windows-process-tree": "0.2.4"
}
}
}
+2 -2
View File
@@ -20,8 +20,8 @@
"vscode-proxy-agent": "0.4.0",
"vscode-ripgrep": "^1.5.5",
"vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta71",
"xterm-addon-search": "0.2.0-beta2",
"xterm": "3.15.0-beta89",
"xterm-addon-search": "0.2.0-beta3",
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
+8 -8
View File
@@ -1149,20 +1149,20 @@ vscode-windows-registry@1.0.1:
dependencies:
nan "^2.12.1"
xterm-addon-search@0.2.0-beta2:
version "0.2.0-beta2"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta2.tgz#c3173f0a6f207ee9f1848849174ee5d6b6ce8262"
integrity sha512-XEcwi2TeFGk2MuIFjiI/OpVXSNO5dGQBvHH3o+9KzqG3ooVqhhDqzwxs092QGNcNCGh8hGn/PWZiczaBBnKm/g==
xterm-addon-search@0.2.0-beta3:
version "0.2.0-beta3"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b"
integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow==
xterm-addon-web-links@0.1.0-beta10:
version "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-beta71:
version "3.15.0-beta71"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta71.tgz#2728c9800ca3b08423e835e9504bd1f4b5de6253"
integrity sha512-8M/cLaxZ+iDopRxLPdPfKuDGaNNyYTdCeytdxjMSH0N7dZzbx6fbaEygQdCrV5pO9cGnT92MefSjVPGRXRiBLA==
xterm@3.15.0-beta89:
version "3.15.0-beta89"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta89.tgz#255962e2595deefb42b8c0043001256526163a3f"
integrity sha512-rNaoUamacPRg+ejbKDGRDNqR3SZ3Uf/pUW0mO+FF25/lIgdLq8x7RgZVBgFweCZ/dijPjxoyMcgfNDTH9h8LOg==
yauzl@^2.9.2:
version "2.10.0"
+1
View File
@@ -17,6 +17,7 @@ arguments=(
'(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]'
'--extensions-dir[set the root path for extensions]:root path:_directories'
'--list-extensions[list the installed extensions]'
'--category[filters instaled extension list by category, when using --list-extension]'
'--show-versions[show versions of installed extensions, when using --list-extension]'
'--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"'
'--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"'
+2 -1
View File
@@ -34,7 +34,8 @@ if grep -qi Microsoft /proc/version; then
if [ -n "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh
"$WSL_CODE" "$COMMIT" "$QUALITY" "$ELECTRON" "$APP_NAME" "$DATAFOLDER" "$@"
WIN_CODE_CMD=$(wslpath -w "$VSCODE_PATH/bin/$APP_NAME.cmd")
"$WSL_CODE" "$COMMIT" "$QUALITY" "$WIN_CODE_CMD" "$APP_NAME" "$DATAFOLDER" "$@"
exit $?
fi
else
@@ -31,7 +31,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
title: localize('disconnect', "Disconnect")
},
when: ContextKeyExpr.and(NodeContextKey.IsConnected,
new ContextKeyNotEqualsExpr('nodeType', NodeType.Folder))
ContextKeyNotEqualsExpr.create('nodeType', NodeType.Folder))
});
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
@@ -214,7 +214,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
},
when: ContextKeyExpr.and(MssqlNodeContext.NodeProvider.isEqualTo(mssqlProviderName),
MssqlNodeContext.IsWindows,
new ContextKeyRegexExpr('nodeType', /^(Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/))
ContextKeyRegexExpr.create('nodeType', /^(Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/))
});
//////////////// Scripting Actions /////////////////
@@ -283,4 +283,4 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
title: localize('editData', "Edit Data")
},
when: MssqlNodeContext.CanEditData
});
});
@@ -80,7 +80,7 @@ if (isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: RunQueryKeyboardAction.ID, title: RunQueryKeyboardAction.LABEL },
group: 'query',
when: new ContextKeyEqualsExpr('activeEditor', 'workbench.editor.queryEditor')
when: ContextKeyEqualsExpr.create('activeEditor', 'workbench.editor.queryEditor')
});
}
+38 -35
View File
@@ -1,4 +1,4 @@
// Type definitions for Electron 4.2.5
// Type definitions for Electron 4.2.7
// Project: http://electronjs.org/
// Definitions by: The Electron Team <https://github.com/electron/electron>
// Definitions: https://github.com/electron/electron-typescript-definitions
@@ -3479,14 +3479,14 @@ declare namespace Electron {
* Creates a new NativeImage instance from the NSImage that maps to the given image
* name. See NSImageName for a list of possible values. The hslShift is applied to
* the image with the following rules This means that [-1, 0, 1] will make the
* image completely white and [-1, 1, 0] will make the image completely black. In
* some cases, the NSImageName doesn't match its string representation; one example
* of this is NSFolderImageName, whose string representation would actually be
* NSFolder. Therefore, you'll need to determine the correct string representation
* for your image before passing it in. This can be done with the following: echo
* -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' |
* clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME
* should be replaced with any value from this list.
* image completely white and [-1, 1, 0] will make the image completely black. In
* some cases, the NSImageName doesn't match its string representation; one example
* of this is NSFolderImageName, whose string representation would actually be
* NSFolder. Therefore, you'll need to determine the correct string representation
* for your image before passing it in. This can be done with the following: echo
* -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' |
* clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME
* should be replaced with any value from this list.
*/
static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage;
/**
@@ -3737,6 +3737,17 @@ declare namespace Electron {
once(event: 'unlock-screen', listener: Function): this;
addListener(event: 'unlock-screen', listener: Function): this;
removeListener(event: 'unlock-screen', listener: Function): this;
/**
* Calculate the system idle state. idleThreshold is the amount of time (in
* seconds) before considered idle. callback will be called synchronously on some
* systems and with an idleState argument that describes the system's state. locked
* is available on supported systems only.
*/
querySystemIdleState(idleThreshold: number, callback: (idleState: 'active' | 'idle' | 'locked' | 'unknown') => void): void;
/**
* Calculate system idle time in seconds.
*/
querySystemIdleTime(callback: (idleTime: number) => void): void;
}
interface PowerSaveBlocker extends EventEmitter {
@@ -4418,30 +4429,6 @@ declare namespace Electron {
* The new RGBA color the user assigned to be their system accent color.
*/
newColor: string) => void): this;
/**
* NOTE: This event is only emitted after you have called
* startAppLevelAppearanceTrackingOS
*/
on(event: 'appearance-changed', listener: (
/**
* Can be `dark` or `light`
*/
newAppearance: ('dark' | 'light')) => void): this;
once(event: 'appearance-changed', listener: (
/**
* Can be `dark` or `light`
*/
newAppearance: ('dark' | 'light')) => void): this;
addListener(event: 'appearance-changed', listener: (
/**
* Can be `dark` or `light`
*/
newAppearance: ('dark' | 'light')) => void): this;
removeListener(event: 'appearance-changed', listener: (
/**
* Can be `dark` or `light`
*/
newAppearance: ('dark' | 'light')) => void): this;
on(event: 'color-changed', listener: (event: Event) => void): this;
once(event: 'color-changed', listener: (event: Event) => void): this;
addListener(event: 'color-changed', listener: (event: Event) => void): this;
@@ -8789,17 +8776,33 @@ declare namespace Electron {
* The type of media access being requested, can be video, audio or unknown
*/
mediaType: ('video' | 'audio' | 'unknown');
/**
* The last URL the requesting frame loaded
*/
requestingUrl: string;
/**
* Whether the frame making the request is the main frame
*/
isMainFrame: boolean;
}
interface PermissionRequestHandlerDetails {
/**
* The url of the openExternal request.
*/
externalURL: string;
externalURL?: string;
/**
* The types of media access being requested, elements can be video or audio
*/
mediaTypes: Array<'video' | 'audio'>;
mediaTypes?: Array<'video' | 'audio'>;
/**
* The last URL the requesting frame loaded
*/
requestingUrl: string;
/**
* Whether the frame making the request is the main frame
*/
isMainFrame: boolean;
}
interface PluginCrashedEvent extends Event {
+42 -11
View File
@@ -15,6 +15,11 @@ declare module 'xterm' {
*/
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
/**
* A string representing log level.
*/
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
/**
* A string representing a renderer type.
*/
@@ -107,6 +112,18 @@ declare module 'xterm' {
*/
lineHeight?: number;
/**
* What log level to use, this will log for all levels below and including
* what is set:
*
* 1. debug
* 2. info (default)
* 3. warn
* 4. error
* 5. off
*/
logLevel?: LogLevel;
/**
* Whether to treat option as the meta key.
*/
@@ -177,6 +194,12 @@ declare module 'xterm' {
* not whitespace.
*/
windowsMode?: boolean;
/**
* A string containing all characters that are considered word separated by the
* double click to select work logic.
*/
wordSeparator?: string;
}
/**
@@ -191,7 +214,7 @@ declare module 'xterm' {
cursor?: string,
/** The accent color of the cursor (fg color for a block cursor) */
cursorAccent?: string,
/** The selection color (can be transparent) */
/** The selection background color (can be transparent) */
selection?: string,
/** ANSI black (eg. `\x1b[30m`) */
black?: string,
@@ -483,12 +506,14 @@ declare module 'xterm' {
* final character (e.g "m" for SGR) of the CSI sequence.
* @param callback The function to handle the escape sequence. The callback
* is called with the numerical params, as well as the special characters
* (e.g. "$" for DECSCPP). Return true if the sequence was handled; false if
* (e.g. "$" for DECSCPP). If the sequence has subparams the array will
* contain subarrays with their numercial values.
* Return true if the sequence was handled; false if
* we should try a previous handler (set by addCsiHandler or setCsiHandler).
* The most recently-added handler is tried first.
* @return An IDisposable you can call to remove this handler.
*/
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable;
addCsiHandler(flag: string, callback: (params: (number | number[])[], collect: string) => boolean): IDisposable;
/**
* (EXPERIMENTAL) Adds a handler for OSC escape sequences.
@@ -668,12 +693,12 @@ declare module 'xterm' {
* Retrieves an option's value from the terminal.
* @param key The option key.
*/
getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'rendererType' | 'termName'): string;
getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string;
/**
* Retrieves an option's value from the terminal.
* @param key The option key.
*/
getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean;
getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean;
/**
* Retrieves an option's value from the terminal.
* @param key The option key.
@@ -700,13 +725,19 @@ declare module 'xterm' {
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'fontFamily' | 'termName' | 'bellSound', value: string): void;
setOption(key: 'fontFamily' | 'termName' | 'bellSound' | 'wordSeparator', value: string): void;
/**
* Sets an option on the terminal.
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void;
/**
* Sets an option on the terminal.
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'logLevel', value: LogLevel): void;
/**
* Sets an option on the terminal.
* @param key The option key.
@@ -724,7 +755,7 @@ declare module 'xterm' {
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void;
setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void;
/**
* Sets an option on the terminal.
* @param key The option key.
@@ -928,16 +959,16 @@ declare module 'xterm' {
// Modifications to official .d.ts below
declare module 'xterm' {
interface TerminalCore {
debug: boolean;
handler(text: string): void;
_onScroll: IEventEmitter<number>;
_onKey: IEventEmitter<{ key: string }>;
_charSizeService: {
width: number;
height: number;
};
_coreService: {
triggerDataEvent(data: string, wasUserInput?: boolean): void;
}
_renderService: {
+1 -1
View File
@@ -122,7 +122,7 @@ export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0);
export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0);
export const isStandalone = (window.matchMedia('(display-mode: standalone)').matches);
export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);
export function hasClipboardSupport() {
if (isIE) {
+1 -1
View File
@@ -1200,7 +1200,7 @@ export function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return _location.with({ path: '/vscode-resources/fetch', query: JSON.stringify({ u: uri.toJSON(), i: 1 }) });
return _location.with({ path: '/vscode-resources/fetch', query: `u=${JSON.stringify(uri)}` });
}
return uri;
}
+5 -1
View File
@@ -160,6 +160,8 @@ export class StandardWheelEvent {
this.deltaY = e1.wheelDeltaY / 120;
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3;
} else {
this.deltaY = -e.deltaY / 40;
}
// horizontal delta scroll
@@ -171,6 +173,8 @@ export class StandardWheelEvent {
}
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3;
} else {
this.deltaX = -e.deltaX / 40;
}
// Assume a vertical scroll if nothing else worked
@@ -195,4 +199,4 @@ export class StandardWheelEvent {
}
}
}
}
}
@@ -6,7 +6,7 @@
import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview';
import { $ } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
import { IView, IViewSize } from 'vs/base/browser/ui/grid/gridview';
import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
+85 -24
View File
@@ -7,10 +7,11 @@ import 'vs/css!./gridview';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { Disposable } from 'vs/base/common/lifecycle';
import { tail2 as tail, equals } from 'vs/base/common/arrays';
import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview';
import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview';
import { Event } from 'vs/base/common/event';
import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview';
export { Orientation, Sizing as GridViewSizing } from './gridview';
export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview';
export const enum Direction {
Up,
@@ -28,9 +29,15 @@ function oppositeDirection(direction: Direction): Direction {
}
}
export interface IView extends IGridViewView {
readonly preferredHeight?: number;
readonly preferredWidth?: number;
}
export interface GridLeafNode<T extends IView> {
readonly view: T;
readonly box: Box;
readonly cachedVisibleSize: number | undefined;
}
export interface GridBranchNode<T extends IView> {
@@ -173,16 +180,23 @@ function getGridLocation(element: HTMLElement): number[] {
return [...getGridLocation(ancestor), index];
}
export const enum Sizing {
Distribute = 'distribute',
Split = 'split'
export type DistributeSizing = { type: 'distribute' };
export type SplitSizing = { type: 'split' };
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
export namespace Sizing {
export const Distribute: DistributeSizing = { type: 'distribute' };
export const Split: SplitSizing = { type: 'split' };
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
}
export interface IGridStyles extends IGridViewStyles { }
export interface IGridOptions {
styles?: IGridStyles;
proportionalLayout?: boolean;
readonly styles?: IGridStyles;
readonly proportionalLayout?: boolean;
readonly firstViewVisibleCachedSize?: number;
}
export class Grid<T extends IView = IView> extends Disposable {
@@ -208,9 +222,13 @@ export class Grid<T extends IView = IView> extends Disposable {
this.gridview = new GridView(options);
this._register(this.gridview);
this._register(this.gridview.onDidSashReset(this.doResetViewSize, this));
this._register(this.gridview.onDidSashReset(this.onDidSashReset, this));
this._addView(view, 0, [0]);
const size: number | GridViewSizing = typeof options.firstViewVisibleCachedSize === 'number'
? GridViewSizing.Invisible(options.firstViewVisibleCachedSize)
: 0;
this._addView(view, size, [0]);
}
style(styles: IGridStyles): void {
@@ -241,10 +259,12 @@ export class Grid<T extends IView = IView> extends Disposable {
let viewSize: number | GridViewSizing;
if (size === Sizing.Split) {
if (typeof size === 'number') {
viewSize = size;
} else if (size.type === 'split') {
const [, index] = tail(referenceLocation);
viewSize = GridViewSizing.Split(index);
} else if (size === Sizing.Distribute) {
} else if (size.type === 'distribute') {
viewSize = GridViewSizing.Distribute;
} else {
viewSize = size;
@@ -264,7 +284,7 @@ export class Grid<T extends IView = IView> extends Disposable {
}
const location = this.getViewLocation(view);
this.gridview.removeView(location, sizing === Sizing.Distribute ? GridViewSizing.Distribute : undefined);
this.gridview.removeView(location, (sizing && sizing.type === 'distribute') ? GridViewSizing.Distribute : undefined);
this.views.delete(view);
}
@@ -320,7 +340,7 @@ export class Grid<T extends IView = IView> extends Disposable {
}
getViews(): GridBranchNode<T> {
return this.gridview.getViews() as GridBranchNode<T>;
return this.gridview.getView() as GridBranchNode<T>;
}
getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] {
@@ -355,8 +375,36 @@ export class Grid<T extends IView = IView> extends Disposable {
return getGridLocation(element);
}
private doResetViewSize(location: number[]): void {
const [parentLocation,] = tail(location);
private onDidSashReset(location: number[]): void {
const resizeToPreferredSize = (location: number[]): boolean => {
const node = this.gridview.getView(location) as GridNode<T>;
if (isGridBranchNode(node)) {
return false;
}
const direction = getLocationOrientation(this.orientation, location);
const size = direction === Orientation.HORIZONTAL ? node.view.preferredWidth : node.view.preferredHeight;
if (typeof size !== 'number') {
return false;
}
const viewSize = direction === Orientation.HORIZONTAL ? { width: Math.round(size) } : { height: Math.round(size) };
this.gridview.resizeView(location, viewSize);
return true;
};
if (resizeToPreferredSize(location)) {
return;
}
const [parentLocation, index] = tail(location);
if (resizeToPreferredSize([...parentLocation, index + 1])) {
return;
}
this.gridview.distributeViewSizes(parentLocation);
}
}
@@ -379,6 +427,7 @@ export interface ISerializedLeafNode {
type: 'leaf';
data: object | null;
size: number;
visible?: boolean;
}
export interface ISerializedBranchNode {
@@ -402,6 +451,10 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
if (!isGridBranchNode(node)) {
if (typeof node.cachedVisibleSize === 'number') {
return { type: 'leaf', data: node.view.toJSON(), size: node.cachedVisibleSize, visible: false };
}
return { type: 'leaf', data: node.view.toJSON(), size };
}
@@ -426,25 +479,26 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
throw new Error('Invalid JSON: \'size\' property of node must be a number.');
}
const childSize = child.type === 'leaf' && child.visible === false ? 0 : child.size;
const childBox: Box = orientation === Orientation.HORIZONTAL
? { top: box.top, left: box.left + offset, width: child.size, height: box.height }
: { top: box.top + offset, left: box.left, width: box.width, height: child.size };
? { top: box.top, left: box.left + offset, width: childSize, height: box.height }
: { top: box.top + offset, left: box.left, width: box.width, height: childSize };
children.push(SerializableGrid.deserializeNode(child, orthogonal(orientation), childBox, deserializer));
offset += child.size;
offset += childSize;
}
return { children, box };
} else if (json.type === 'leaf') {
const view: T = deserializer.fromJSON(json.data);
return { view, box };
return { view, box, cachedVisibleSize: json.visible === false ? json.size : undefined };
}
throw new Error('Invalid JSON: \'type\' property must be either \'branch\' or \'leaf\'.');
}
private static getFirstLeaf<T extends IView>(node: GridNode<T>): GridLeafNode<T> | undefined {
private static getFirstLeaf<T extends IView>(node: GridNode<T>): GridLeafNode<T> {
if (!isGridBranchNode(node)) {
return node;
}
@@ -473,6 +527,10 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
throw new Error('Invalid serialized state, first leaf not found');
}
if (typeof firstLeaf.cachedVisibleSize === 'number') {
options = { ...options, firstViewVisibleCachedSize: firstLeaf.cachedVisibleSize };
}
const result = new SerializableGrid<T>(firstLeaf.view, options);
result.orientation = orientation;
result.restoreViews(firstLeaf.view, orientation, root);
@@ -522,13 +580,16 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c));
for (let i = 1; i < firstLeaves.length; i++) {
const size = orientation === Orientation.VERTICAL ? firstLeaves[i]!.box.height : firstLeaves[i]!.box.width;
this.addView(firstLeaves[i]!.view, size, referenceView, direction);
referenceView = firstLeaves[i]!.view;
const node = firstLeaves[i];
const size: number | InvisibleSizing = typeof node.cachedVisibleSize === 'number'
? GridViewSizing.Invisible(node.cachedVisibleSize)
: (orientation === Orientation.VERTICAL ? node.box.height : node.box.width);
this.addView(node.view, size, referenceView, direction);
referenceView = node.view;
}
for (let i = 0; i < node.children.length; i++) {
this.restoreViews(firstLeaves[i]!.view, orthogonal(orientation), node.children[i]);
this.restoreViews(firstLeaves[i].view, orthogonal(orientation), node.children[i]);
}
}
+33 -4
View File
@@ -47,6 +47,7 @@ export interface Box {
export interface GridLeafNode {
readonly view: IView;
readonly box: Box;
readonly cachedVisibleSize: number | undefined;
}
export interface GridBranchNode {
@@ -343,6 +344,14 @@ class BranchNode implements ISplitView, IDisposable {
this.splitview.setViewVisible(index, visible);
}
getChildCachedVisibleSize(index: number): number | undefined {
if (index < 0 || index >= this.children.length) {
throw new Error('Invalid index');
}
return this.splitview.getViewCachedVisibleSize(index);
}
private onDidChildrenChange(): void {
const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined);
this.childrenChangeDisposable.dispose();
@@ -424,6 +433,9 @@ class LeafNode implements ISplitView, IDisposable {
private _size: number = 0;
get size(): number { return this._size; }
private _cachedVisibleSize: number | undefined;
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
private _orthogonalSize: number;
get orthogonalSize(): number { return this._orthogonalSize; }
@@ -528,6 +540,12 @@ class LeafNode implements ISplitView, IDisposable {
}
setVisible(visible: boolean): void {
if (visible) {
this._cachedVisibleSize = undefined;
} else {
this._cachedVisibleSize = this._size;
}
if (this.view.setVisible) {
this.view.setVisible(visible);
}
@@ -658,6 +676,14 @@ export class GridView implements IDisposable {
} else {
const [, grandParent] = tail(pathToParent);
const [, parentIndex] = tail(rest);
let newSiblingSize: number | Sizing = 0;
const newSiblingCachedVisibleSize = grandParent.getChildCachedVisibleSize(parentIndex);
if (typeof newSiblingCachedVisibleSize === 'number') {
newSiblingSize = Sizing.Invisible(newSiblingCachedVisibleSize);
}
grandParent.removeChild(parentIndex);
const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize);
@@ -665,7 +691,7 @@ export class GridView implements IDisposable {
newParent.orthogonalLayout(parent.orthogonalSize);
const newSibling = new LeafNode(parent.view, grandParent.orientation, parent.size);
newParent.addChild(newSibling, 0, 0);
newParent.addChild(newSibling, newSiblingSize, 0);
if (typeof size !== 'number' && size.type === 'split') {
size = Sizing.Split(0);
@@ -877,13 +903,16 @@ export class GridView implements IDisposable {
parent.setChildVisible(index, visible);
}
getViews(): GridBranchNode {
return this._getViews(this.root, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }) as GridBranchNode;
getView(): GridBranchNode;
getView(location?: number[]): GridNode;
getView(location?: number[]): GridNode {
const node = location ? this.getNode(location)[1] : this._root;
return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height });
}
private _getViews(node: Node, orientation: Orientation, box: Box): GridNode {
if (node instanceof LeafNode) {
return { view: node.view, box };
return { view: node.view, box, cachedVisibleSize: node.cachedVisibleSize };
}
const children: GridNode[] = [];
+1 -1
View File
@@ -236,7 +236,7 @@ class KeyboardController<T> implements IDisposable {
private view: ListView<T>,
options: IListOptions<T>
) {
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
const multipleSelectionSupport = options.multipleSelectionSupport !== false;
this.openController = options.openController || DefaultOpenController;
+1 -1
View File
@@ -190,4 +190,4 @@ export class RangeMap {
dispose() {
this.groups = null!; // StrictNullOverride: nulling out ok in dispose
}
}
}
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/scrollbars';
import { isEdgeOrIE } from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
@@ -312,7 +313,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._onMouseWheel(new StandardWheelEvent(browserEvent));
};
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel));
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel));
}
}
+68 -12
View File
@@ -59,8 +59,11 @@ interface ISashEvent {
alt: boolean;
}
type ViewItemSize = number | { cachedVisibleSize: number };
abstract class ViewItem {
private _size: number;
set size(size: number) {
this._size = size;
}
@@ -69,10 +72,11 @@ abstract class ViewItem {
return this._size;
}
private cachedSize: number | undefined = undefined;
private _cachedVisibleSize: number | undefined = undefined;
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
get visible(): boolean {
return typeof this.cachedSize === 'undefined';
return typeof this._cachedVisibleSize === 'undefined';
}
set visible(visible: boolean) {
@@ -81,10 +85,10 @@ abstract class ViewItem {
}
if (visible) {
this.size = this.cachedSize!;
this.cachedSize = undefined;
this.size = clamp(this._cachedVisibleSize!, this.viewMinimumSize, this.viewMaximumSize);
this._cachedVisibleSize = undefined;
} else {
this.cachedSize = this.size;
this._cachedVisibleSize = this.size;
this.size = 0;
}
@@ -104,7 +108,20 @@ abstract class ViewItem {
get priority(): LayoutPriority | undefined { return this.view.priority; }
get snap(): boolean { return !!this.view.snap; }
constructor(protected container: HTMLElement, private view: IView, private _size: number, private disposable: IDisposable) {
constructor(
protected container: HTMLElement,
private view: IView,
size: ViewItemSize,
private disposable: IDisposable
) {
if (typeof size === 'number') {
this._size = size;
this._cachedVisibleSize = undefined;
} else {
this._size = 0;
this._cachedVisibleSize = size.cachedVisibleSize;
}
dom.addClass(container, 'visible');
}
@@ -166,11 +183,13 @@ enum State {
export type DistributeSizing = { type: 'distribute' };
export type SplitSizing = { type: 'split', index: number };
export type Sizing = DistributeSizing | SplitSizing;
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
export namespace Sizing {
export const Distribute: DistributeSizing = { type: 'distribute' };
export function Split(index: number): SplitSizing { return { type: 'split', index }; }
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
}
export class SplitView extends Disposable {
@@ -279,12 +298,14 @@ export class SplitView extends Disposable {
const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));
const disposable = combinedDisposable(onChangeDisposable, containerDisposable);
let viewSize: number;
let viewSize: ViewItemSize;
if (typeof size === 'number') {
viewSize = size;
} else if (size.type === 'split') {
viewSize = this.getViewSize(size.index) / 2;
} else if (size.type === 'invisible') {
viewSize = { cachedVisibleSize: size.cachedVisibleSize };
} else {
viewSize = view.minimumSize;
}
@@ -315,7 +336,24 @@ export class SplitView extends Disposable {
const onChangeDisposable = onChange(this.onSashChange, this);
const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash));
const onEndDisposable = onEnd(this.onSashEnd, this);
const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash)));
const onDidResetDisposable = sash.onDidReset(() => {
const index = firstIndex(this.sashItems, item => item.sash === sash);
const upIndexes = range(index, -1);
const downIndexes = range(index + 1, this.viewItems.length);
const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
if (typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible) {
return;
}
if (typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible) {
return;
}
this._onDidSashReset.fire(index);
});
const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash);
const sashItem: ISashItem = { sash, disposable };
@@ -420,6 +458,15 @@ export class SplitView extends Disposable {
this.layoutViews();
}
getViewCachedVisibleSize(index: number): number | undefined {
if (index < 0 || index >= this.viewItems.length) {
throw new Error('Index out of bounds');
}
const viewItem = this.viewItems[index];
return viewItem.cachedVisibleSize;
}
layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
@@ -600,10 +647,19 @@ export class SplitView extends Disposable {
}
distributeViewSizes(): void {
const size = Math.floor(this.size / this.viewItems.length);
const flexibleViewItems: ViewItem[] = [];
let flexibleSize = 0;
for (let i = 0; i < this.viewItems.length; i++) {
const item = this.viewItems[i];
for (const item of this.viewItems) {
if (item.maximumSize - item.minimumSize > 0) {
flexibleViewItems.push(item);
flexibleSize += item.size;
}
}
const size = Math.floor(flexibleSize / flexibleViewItems.length);
for (const item of flexibleViewItems) {
item.size = clamp(size, item.minimumSize, item.maximumSize);
}
+1 -1
View File
@@ -1183,7 +1183,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
renderers: ITreeRenderer<T, TFilterData, any>[],
private _options: IAbstractTreeOptions<T, TFilterData> = {}
) {
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
+2 -2
View File
@@ -320,7 +320,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
get onDidFocus(): Event<void> { return this.tree.onDidFocus; }
get onDidBlur(): Event<void> { return this.tree.onDidBlur; }
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T>, TFilterData>> { return this.tree.onDidChangeCollapseState; }
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T> | null, TFilterData>> { return this.tree.onDidChangeCollapseState; }
get onDidUpdateOptions(): Event<IAsyncDataTreeOptionsUpdate> { return this.tree.onDidUpdateOptions; }
@@ -340,7 +340,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
renderers: ITreeRenderer<T, TFilterData, any>[],
private dataSource: IAsyncDataSource<TInput, T>,
options: IAsyncDataTreeOptions<T, TFilterData> = {}
) {
@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event';
import { CompressedTreeModel, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
sorter?: ITreeSorter<T>;
}
export class CompressedObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<ICompressedTreeNode<T> | null, TFilterData, T | null> {
protected model: CompressedTreeModel<T, TFilterData>;
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<ICompressedTreeNode<T> | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<ICompressedTreeNode<T>>,
renderers: ITreeRenderer<ICompressedTreeNode<T>, TFilterData, any>[],
options: IObjectTreeOptions<ICompressedTreeNode<T>, TFilterData> = {}
) {
super(container, delegate, renderers, options);
}
setChildren(
element: T | null,
children?: ISequence<ITreeElement<T>>
): Iterator<ITreeElement<T | null>> {
return this.model.setChildren(element, children);
}
rerender(element?: T): void {
if (element === undefined) {
this.view.rerender();
return;
}
this.model.rerender(element);
}
resort(element: T, recursive = true): void {
this.model.resort(element, recursive);
}
protected createModel(view: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: IObjectTreeOptions<ICompressedTreeNode<T>, TFilterData>): ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
return new CompressedTreeModel(view, options);
}
}
@@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence';
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { IObjectTreeModelOptions, ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
export interface ICompressedTreeElement<T> extends ITreeElement<T> {
readonly children?: Iterator<ICompressedTreeElement<T>> | ICompressedTreeElement<T>[];
@@ -80,9 +80,9 @@ export function splice<T>(treeElement: ICompressedTreeElement<T>, element: T, ch
};
}
export interface ICompressedObjectTreeModelOptions<T, TFilterData> extends IObjectTreeModelOptions<ICompressedTreeNode<T>, TFilterData> { }
export interface ICompressedTreeModelOptions<T, TFilterData> extends IObjectTreeModelOptions<ICompressedTreeNode<T>, TFilterData> { }
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
export class CompressedTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
readonly rootRef = null;
@@ -95,7 +95,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
get size(): number { return this.nodes.size; }
constructor(list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}) {
constructor(list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: ICompressedTreeModelOptions<T, TFilterData> = {}) {
this.model = new ObjectTreeModel(list, options);
}
@@ -289,11 +289,11 @@ function createNodeMapper<T, TFilterData>(elementMapper: ElementMapper<T>): Node
return node => mapNode(elementMapper, node);
}
export interface ILinearCompressedObjectTreeModelOptions<T, TFilterData> extends ICompressedObjectTreeModelOptions<T, TFilterData> {
export interface ICompressedObjectTreeModelOptions<T, TFilterData> extends ICompressedTreeModelOptions<T, TFilterData> {
readonly elementMapper?: ElementMapper<T>;
}
export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements IObjectTreeModel<T, TFilterData> {
readonly rootRef = null;
@@ -317,15 +317,25 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
private mapElement: ElementMapper<T | null>;
private mapNode: NodeMapper<T | null, TFilterData>;
private model: CompressedObjectTreeModel<T, TFilterData>;
private model: CompressedTreeModel<T, TFilterData>;
constructor(
list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
options: ILinearCompressedObjectTreeModelOptions<T, TFilterData> = {}
options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}
) {
this.mapElement = options.elementMapper || DefaultElementMapper;
this.mapNode = createNodeMapper(this.mapElement);
this.model = new CompressedObjectTreeModel(list, options);
this.model = new CompressedTreeModel(list, options);
}
setChildren(
element: T | null,
children: ISequence<ITreeElement<T>> | undefined
): Iterator<ITreeElement<T>> {
this.model.setChildren(element, children);
// TODO
return Iterator.empty();
}
getListIndex(location: T | null): number {
@@ -401,4 +411,8 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
refilter(): void {
return this.model.refilter();
}
resort(element: T | null = null, recursive = true): void {
return this.model.resort(element, recursive);
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
renderers: ITreeRenderer<T, TFilterData, any>[],
private dataSource: IDataSource<TInput, T>,
options: IDataTreeOptions<T, TFilterData> = {}
) {
+1 -1
View File
@@ -20,7 +20,7 @@ export class IndexTree<T, TFilterData = void> extends AbstractTree<T, TFilterDat
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
renderers: ITreeRenderer<T, TFilterData, any>[],
private rootElement: T,
options: IIndexTreeOptions<T, TFilterData> = {}
) {
+6 -8
View File
@@ -7,7 +7,7 @@ import { Iterator, ISequence } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event';
@@ -17,14 +17,14 @@ export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTree
export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
protected model: ObjectTreeModel<T, TFilterData>;
protected model: IObjectTreeModel<T, TFilterData>;
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T, TFilterData>> { return this.model.onDidChangeCollapseState; }
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
renderers: ITreeRenderer<T, TFilterData, any>[],
options: IObjectTreeOptions<T, TFilterData> = {}
) {
super(container, delegate, renderers, options);
@@ -32,11 +32,9 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
setChildren(
element: T | null,
children?: ISequence<ITreeElement<T>>,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
children?: ISequence<ITreeElement<T>>
): Iterator<ITreeElement<T | null>> {
return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode);
return this.model.setChildren(element, children);
}
rerender(element?: T): void {
+12 -5
View File
@@ -10,12 +10,19 @@ import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
export type ITreeNodeCallback<T, TFilterData> = (node: ITreeNode<T, TFilterData>) => void;
export interface IObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> extends ITreeModel<T | null, TFilterData, T | null> {
setChildren(element: T | null, children: ISequence<ITreeElement<T>> | undefined): Iterator<ITreeElement<T>>;
resort(element?: T | null, recursive?: boolean): void;
}
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>;
readonly identityProvider?: IIdentityProvider<T>;
}
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements IObjectTreeModel<T, TFilterData> {
readonly rootRef = null;
@@ -51,8 +58,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
setChildren(
element: T | null,
children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
): Iterator<ITreeElement<T>> {
const location = this.getElementLocation(element);
return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode);
@@ -61,8 +68,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
private _setChildren(
location: number[],
children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
): Iterator<ITreeElement<T>> {
const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>();
+14
View File
@@ -326,6 +326,20 @@ export namespace Event {
return result.event;
}
export interface DOMEventEmitter {
addEventListener(event: string | symbol, listener: Function): void;
removeEventListener(event: string | symbol, listener: Function): void;
}
export function fromDOMEventEmitter<T>(emitter: DOMEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
const fn = (...args: any[]) => result.fire(map(...args));
const onFirstListenerAdd = () => emitter.addEventListener(eventName, fn);
const onLastListenerRemove = () => emitter.removeEventListener(eventName, fn);
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
return result.event;
}
export function fromPromise<T = any>(promise: Promise<T>): Event<undefined> {
const emitter = new Emitter<undefined>();
let shouldEmit = false;
+1 -1
View File
@@ -414,7 +414,7 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] {
return res;
}
const _maxLen = 53;
const _maxLen = 128;
function initTable() {
const table: number[][] = [];
-1
View File
@@ -334,7 +334,6 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
if (!extpath.isEqualOrParent(path, arg2.base)) {
return null;
}
return parsedPattern(paths.relative(arg2.base, path), basename);
};
}
+4 -4
View File
@@ -374,12 +374,12 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
let code = text.charCodeAt(pos);
// trivia: whitespace
if (isWhiteSpace(code)) {
if (isWhitespace(code)) {
do {
pos++;
value += String.fromCharCode(code);
code = text.charCodeAt(pos);
} while (isWhiteSpace(code));
} while (isWhitespace(code));
return token = SyntaxKind.Trivia;
}
@@ -517,7 +517,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
}
function isUnknownContentCharacter(code: CharacterCodes) {
if (isWhiteSpace(code) || isLineBreak(code)) {
if (isWhitespace(code) || isLineBreak(code)) {
return false;
}
switch (code) {
@@ -555,7 +555,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
};
}
function isWhiteSpace(ch: number): boolean {
function isWhitespace(ch: number): boolean {
return ch === CharacterCodes.space || ch === CharacterCodes.tab || ch === CharacterCodes.verticalTab || ch === CharacterCodes.formFeed ||
ch === CharacterCodes.nonBreakingSpace || ch === CharacterCodes.ogham || ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace ||
ch === CharacterCodes.narrowNoBreakSpace || ch === CharacterCodes.mathematicalSpace || ch === CharacterCodes.ideographicSpace || ch === CharacterCodes.byteOrderMark;
@@ -1,13 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
suite('Grid', function () {
// {{SQL CARBON EDIT}}
test('getRelativeLocation', function () {
assert.equal(0, 0);
});
});
@@ -22,7 +22,7 @@ suite('Gridview', function () {
});
test('empty gridview is empty', function () {
assert.deepEqual(nodesToArrays(gridview.getViews()), []);
assert.deepEqual(nodesToArrays(gridview.getView()), []);
gridview.dispose();
});
@@ -43,7 +43,7 @@ suite('Gridview', function () {
gridview.addView(views[1], 200, [1]);
gridview.addView(views[2], 200, [2]);
assert.deepEqual(nodesToArrays(gridview.getViews()), views);
assert.deepEqual(nodesToArrays(gridview.getView()), views);
gridview.dispose();
});
@@ -62,7 +62,7 @@ suite('Gridview', function () {
gridview.addView((views[1] as TestView[])[0] as IView, 200, [1]);
gridview.addView((views[1] as TestView[])[1] as IView, 200, [1, 1]);
assert.deepEqual(nodesToArrays(gridview.getViews()), views);
assert.deepEqual(nodesToArrays(gridview.getView()), views);
gridview.dispose();
});
@@ -71,35 +71,35 @@ suite('Gridview', function () {
const view1 = new TestView(20, 20, 20, 20);
gridview.addView(view1 as IView, 200, [0]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1]);
const view2 = new TestView(20, 20, 20, 20);
gridview.addView(view2 as IView, 200, [1]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, view2]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, view2]);
const view3 = new TestView(20, 20, 20, 20);
gridview.addView(view3 as IView, 200, [1, 0]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view3, view2]]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view3, view2]]);
const view4 = new TestView(20, 20, 20, 20);
gridview.addView(view4 as IView, 200, [1, 0, 0]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [[view4, view3], view2]]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [[view4, view3], view2]]);
const view5 = new TestView(20, 20, 20, 20);
gridview.addView(view5 as IView, 200, [1, 0]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view4, view3], view2]]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2]]);
const view6 = new TestView(20, 20, 20, 20);
gridview.addView(view6 as IView, 200, [2]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view4, view3], view2], view6]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2], view6]);
const view7 = new TestView(20, 20, 20, 20);
gridview.addView(view7 as IView, 200, [1, 1]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, view7, [view4, view3], view2], view6]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, view7, [view4, view3], view2], view6]);
const view8 = new TestView(20, 20, 20, 20);
gridview.addView(view8 as IView, 200, [1, 1, 0]);
assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view8, view7], [view4, view3], view2], view6]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view8, view7], [view4, view3], view2], view6]);
gridview.dispose();
});
+3 -2
View File
@@ -5,7 +5,8 @@
import * as assert from 'assert';
import { Emitter, Event } from 'vs/base/common/event';
import { IView, GridNode, isGridBranchNode, } from 'vs/base/browser/ui/grid/gridview';
import { GridNode, isGridBranchNode } from 'vs/base/browser/ui/grid/gridview';
import { IView } from 'vs/base/browser/ui/grid/grid';
export class TestView implements IView {
@@ -78,4 +79,4 @@ export function nodesToArrays(node: GridNode): any {
} else {
return node.view;
}
}
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { Iterator } from 'vs/base/common/iterator';
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
@@ -305,7 +305,7 @@ suite('CompressedObjectTree', function () {
test('ctor', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
const model = new CompressedTreeModel<number>(toSpliceable(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
@@ -313,7 +313,7 @@ suite('CompressedObjectTree', function () {
test('flat', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{ element: 0 },
@@ -340,7 +340,7 @@ suite('CompressedObjectTree', function () {
test('nested', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{
@@ -376,7 +376,7 @@ suite('CompressedObjectTree', function () {
test('compressed', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list));
const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([
{
+15
View File
@@ -470,4 +470,19 @@ suite('Filters', () => {
test('List highlight filter: Not all characters from match are highlighterd #66923', () => {
assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore);
});
test('Autocompletion is matched against truncated filterText to 54 characters #74133', () => {
assertMatches(
'foo',
'ffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo',
'ffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o',
fuzzyScore
);
assertMatches(
'foo',
'Gffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo',
undefined,
fuzzyScore
);
});
});
@@ -37,7 +37,7 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza
import { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc';
import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { DownloadService } from 'vs/platform/download/common/downloadService';
import { IDownloadService } from 'vs/platform/download/common/download';
import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
@@ -154,7 +154,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) {
if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) {
appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService);
disposables.add(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data
disposables.add(toDisposable(() => appInsightsAppender!.flush())); // Ensure the AI appender is disposed so that it flushes remaining data
}
const config: ITelemetryServiceConfig = {
appender: combinedAppender(appInsightsAppender, new LogAppender(logService)),
+7 -5
View File
@@ -77,7 +77,7 @@ import { HistoryMainService } from 'vs/platform/history/electron-main/historyMai
import { URLService } from 'vs/platform/url/common/urlService';
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory';
import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory';
import { VSBuffer } from 'vs/base/common/buffer';
import { statSync } from 'fs';
import { ISignService } from 'vs/platform/sign/common/sign';
@@ -167,9 +167,11 @@ export class CodeApplication extends Disposable {
event.preventDefault();
});
app.on('remote-get-current-web-contents', event => {
this.logService.trace(`App#on(remote-get-current-web-contents): prevented`);
event.preventDefault();
// The driver needs access to web contents
if (!this.environmentService.args.driver) {
this.logService.trace(`App#on(remote-get-current-web-contents): prevented`);
event.preventDefault();
}
});
app.on('web-contents-created', (_event: Electron.Event, contents) => {
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
@@ -708,7 +710,7 @@ export class CodeApplication extends Disposable {
const options: IConnectionOptions = {
isBuilt,
commit: product.commit,
webSocketFactory: nodeWebSocketFactory,
socketFactory: nodeSocketFactory,
addressProvider: {
getAddress: () => {
return Promise.resolve({ host, port });
+14 -5
View File
@@ -86,7 +86,7 @@ export class Main {
await this.setInstallSource(argv['install-source']);
} else if (argv['list-extensions']) {
await this.listExtensions(!!argv['show-versions']);
await this.listExtensions(!!argv['show-versions'], argv['category']);
} else if (argv['install-extension']) {
const arg = argv['install-extension'];
@@ -110,8 +110,17 @@ export class Main {
return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
}
private async listExtensions(showVersions: boolean): Promise<void> {
const extensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
private async listExtensions(showVersions: boolean, category?: string): Promise<void> {
let extensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
if (category) {
extensions = extensions.filter(e => {
if (e.manifest.categories) {
const lowerCaseCategories: string[] = e.manifest.categories.map(c => c.toLowerCase());
return lowerCaseCategories.indexOf(category.toLowerCase()) > -1;
}
return false;
});
}
extensions.forEach(e => console.log(getId(e.manifest, showVersions)));
}
@@ -345,8 +354,6 @@ export async function main(argv: ParsedArgs): Promise<void> {
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
// Dispose the AI adapter so that remaining data gets flushed.
disposables.add(combinedAppender(...appenders));
} else {
services.set(ITelemetryService, NullTelemetryService);
}
@@ -356,6 +363,8 @@ export async function main(argv: ParsedArgs): Promise<void> {
try {
await main.run(argv);
// Flush the remaining data in AI adapter.
await combinedAppender(...appenders).flush();
} finally {
disposables.dispose();
}
@@ -123,7 +123,7 @@ export class MouseHandler extends ViewEventHandler {
e.stopPropagation();
}
};
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, 'mousewheel', onMouseWheel, true));
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, true));
this._context.addEventHandler(this);
}
@@ -407,7 +407,12 @@ class HitTestRequest extends BareHitTestRequest {
}
public fulfill(type: MouseTargetType, position: Position | null = null, range: EditorRange | null = null, detail: any = null): MouseTarget {
return new MouseTarget(this.target, type, this.mouseColumn, position, range, detail);
let mouseColumn = this.mouseColumn;
if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) {
// Most likely, the line contains foreign decorations...
mouseColumn = position.column;
}
return new MouseTarget(this.target, type, mouseColumn, position, range, detail);
}
public withTarget(target: Element | null): HitTestRequest {
+13 -10
View File
@@ -24,6 +24,10 @@ import { withNullAsUndefined } from 'vs/base/common/types';
export type ServicesAccessor = ServicesAccessor;
export type IEditorContributionCtor = IConstructorSignature1<ICodeEditor, IEditorContribution>;
export type EditorTelemetryDataFragment = {
target: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
snippet: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
//#region Command
@@ -219,16 +223,15 @@ export abstract class EditorAction extends EditorCommand {
}
protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) {
/* __GDPR__
"editorActionInvoked" : {
"name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"${include}": [
"${EditorTelemetryData}"
]
}
*/
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id, ...editor.getTelemetryData() });
type EditorActionInvokedClassification = {
name: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
type EditorActionInvokedEvent = {
name: string;
id: string;
};
accessor.get(ITelemetryService).publicLog2<EditorActionInvokedEvent, EditorActionInvokedClassification>('editorActionInvoked', { name: this.label, id: this.id });
}
public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void>;
@@ -1508,9 +1508,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return this._codeEditorService.resolveDecorationOptions(typeKey, writable);
}
/* __GDPR__FRAGMENT__
"EditorTelemetryData" : {}
*/
public getTelemetryData(): { [key: string]: any; } | undefined {
return this._telemetryData;
}
@@ -1851,4 +1848,6 @@ registerThemingParticipant((theme, collector) => {
if (unnecessaryBorder) {
collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryDecoration} { border-bottom: 2px dashed ${unnecessaryBorder}; }`);
}
collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; }`);
});
@@ -39,4 +39,10 @@
.monaco-editor .view-overlays {
position: absolute;
top: 0;
}
}
/*
.monaco-editor .auto-closed-character {
opacity: 0.3;
}
*/
@@ -696,7 +696,7 @@ const editorConfiguration: IConfigurationNode = {
},
'editor.suggest.filteredTypes': {
type: 'object',
default: { keyword: true },
default: { keyword: true, snippet: true },
markdownDescription: nls.localize('suggest.filtered', "Controls whether some suggestion types should be filtered from IntelliSense. A list of suggestion types can be found here: https://code.visualstudio.com/docs/editor/intellisense#_types-of-completions."),
properties: {
method: {
@@ -904,7 +904,7 @@ const editorConfiguration: IConfigurationNode = {
'enum': ['none', 'boundary', 'selection', 'all'],
'enumDescriptions': [
'',
nls.localize('renderWhiteSpace.boundary', "Render whitespace characters except for single spaces between words."),
nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."),
nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."),
''
],
+120 -4
View File
@@ -10,15 +10,16 @@ import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon';
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
import { TypeOperations, TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model';
import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { dispose } from 'vs/base/common/lifecycle';
function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean {
for (let i = 0, len = events.length; i < len; i++) {
@@ -83,6 +84,64 @@ export class CursorModelState {
}
}
class AutoClosedAction {
private readonly _model: ITextModel;
private _autoClosedCharactersDecorations: string[];
private _autoClosedEnclosingDecorations: string[];
constructor(model: ITextModel, autoClosedCharactersDecorations: string[], autoClosedEnclosingDecorations: string[]) {
this._model = model;
this._autoClosedCharactersDecorations = autoClosedCharactersDecorations;
this._autoClosedEnclosingDecorations = autoClosedEnclosingDecorations;
}
public dispose(): void {
this._autoClosedCharactersDecorations = this._model.deltaDecorations(this._autoClosedCharactersDecorations, []);
this._autoClosedEnclosingDecorations = this._model.deltaDecorations(this._autoClosedEnclosingDecorations, []);
}
public getAutoClosedCharactersRanges(): Range[] {
let result: Range[] = [];
for (let i = 0; i < this._autoClosedCharactersDecorations.length; i++) {
const decorationRange = this._model.getDecorationRange(this._autoClosedCharactersDecorations[i]);
if (decorationRange) {
result.push(decorationRange);
}
}
return result;
}
public isValid(selections: Range[]): boolean {
let enclosingRanges: Range[] = [];
for (let i = 0; i < this._autoClosedEnclosingDecorations.length; i++) {
const decorationRange = this._model.getDecorationRange(this._autoClosedEnclosingDecorations[i]);
if (decorationRange) {
enclosingRanges.push(decorationRange);
if (decorationRange.startLineNumber !== decorationRange.endLineNumber) {
// Stop tracking if the range becomes multiline...
return false;
}
}
}
enclosingRanges.sort(Range.compareRangesUsingStarts);
selections.sort(Range.compareRangesUsingStarts);
for (let i = 0; i < selections.length; i++) {
if (i >= enclosingRanges.length) {
return false;
}
if (!enclosingRanges[i].strictContainsRange(selections[i])) {
return false;
}
}
return true;
}
}
export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
public static MAX_CURSOR_COUNT = 10000;
@@ -106,6 +165,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
private _isHandling: boolean;
private _isDoingComposition: boolean;
private _columnSelectData: IColumnSelectData | null;
private _autoClosedActions: AutoClosedAction[];
private _prevEditOperationType: EditOperationType;
constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: IViewModel) {
@@ -120,6 +180,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._isHandling = false;
this._isDoingComposition = false;
this._columnSelectData = null;
this._autoClosedActions = [];
this._prevEditOperationType = EditOperationType.Other;
this._register(this._model.onDidChangeRawContent((e) => {
@@ -173,9 +234,24 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
public dispose(): void {
this._cursors.dispose();
this._autoClosedActions = dispose(this._autoClosedActions);
super.dispose();
}
private _validateAutoClosedActions(): void {
if (this._autoClosedActions.length > 0) {
let selections: Range[] = this._cursors.getSelections();
for (let i = 0; i < this._autoClosedActions.length; i++) {
const autoClosedAction = this._autoClosedActions[i];
if (!autoClosedAction.isValid(selections)) {
autoClosedAction.dispose();
this._autoClosedActions.splice(i, 1);
i--;
}
}
}
}
// ------ some getters/setters
public getPrimaryCursor(): CursorState {
@@ -202,6 +278,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.normalize();
this._columnSelectData = null;
this._validateAutoClosedActions();
this._emitStateChangedIfNecessary(source, reason, oldState);
}
@@ -296,7 +374,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// a model.setValue() was called
this._cursors.dispose();
this._cursors = new CursorCollection(this.context);
this._validateAutoClosedActions();
this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null);
} else {
const selectionsFromMarkers = this._cursors.readSelectionFromMarkers();
@@ -367,6 +445,35 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// The commands were applied correctly
this._interpretCommandResult(result);
// Check for auto-closing closed characters
let autoClosedCharactersRanges: IModelDeltaDecoration[] = [];
let autoClosedEnclosingRanges: IModelDeltaDecoration[] = [];
for (let i = 0; i < opResult.commands.length; i++) {
const command = opResult.commands[i];
if (command instanceof TypeWithAutoClosingCommand && command.enclosingRange && command.closeCharacterRange) {
autoClosedCharactersRanges.push({
range: command.closeCharacterRange,
options: {
inlineClassName: 'auto-closed-character',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
autoClosedEnclosingRanges.push({
range: command.enclosingRange,
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
}
}
if (autoClosedCharactersRanges.length > 0) {
const autoClosedCharactersDecorations = this._model.deltaDecorations([], autoClosedCharactersRanges);
const autoClosedEnclosingDecorations = this._model.deltaDecorations([], autoClosedEnclosingRanges);
this._autoClosedActions.push(new AutoClosedAction(this._model, autoClosedCharactersDecorations, autoClosedEnclosingDecorations));
}
this._prevEditOperationType = opResult.type;
}
@@ -540,6 +647,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.startTrackingSelections();
}
this._validateAutoClosedActions();
if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) {
this._revealRange(RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
}
@@ -566,8 +675,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
chr = text.charAt(i);
}
let autoClosedCharacters: Range[] = [];
if (this._autoClosedActions.length > 0) {
for (let i = 0, len = this._autoClosedActions.length; i < len; i++) {
autoClosedCharacters = autoClosedCharacters.concat(this._autoClosedActions[i].getAutoClosedCharactersRanges());
}
}
// Here we must interpret each typed character individually, that's why we create a new context
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), chr));
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr));
}
} else {
@@ -13,7 +13,7 @@ import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationT
import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand } from 'vs/editor/common/editorCommon';
import { ICommand, ICursorStateComputerData } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
@@ -430,7 +430,7 @@ export class TypeOperations {
return null;
}
private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean {
private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): boolean {
const autoCloseConfig = isQuote(ch) ? config.autoClosingQuotes : config.autoClosingBrackets;
if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) {
@@ -461,6 +461,19 @@ export class TypeOperations {
return false;
}
}
// Must over-type a closing character typed by the editor
let found = false;
for (let j = 0, lenJ = autoClosedCharacters.length; j < lenJ; j++) {
const autoClosedCharacter = autoClosedCharacters[j];
if (position.lineNumber === autoClosedCharacter.startLineNumber && position.column === autoClosedCharacter.startColumn) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
@@ -573,7 +586,7 @@ export class TypeOperations {
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
const closeCharacter = config.autoClosingPairsOpen[ch];
commands[i] = new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length);
commands[i] = new TypeWithAutoClosingCommand(selection, ch, closeCharacter);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: true,
@@ -802,7 +815,7 @@ export class TypeOperations {
});
}
public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult {
public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult {
if (ch === '\n') {
let commands: ICommand[] = [];
@@ -833,7 +846,7 @@ export class TypeOperations {
}
}
if (this._isAutoClosingCloseCharType(config, model, selections, ch)) {
if (this._isAutoClosingCloseCharType(config, model, selections, autoClosedCharacters, ch)) {
return this._runAutoClosingCloseCharType(prevEditOperationType, config, model, selections, ch);
}
@@ -923,3 +936,24 @@ export class TypeOperations {
return commands;
}
}
export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorState {
private _closeCharacter: string;
public closeCharacterRange: Range | null;
public enclosingRange: Range | null;
constructor(selection: Selection, openCharacter: string, closeCharacter: string) {
super(selection, openCharacter + closeCharacter, 0, -closeCharacter.length);
this._closeCharacter = closeCharacter;
this.closeCharacterRange = null;
}
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
let inverseEditOperations = helper.getInverseEditOperations();
let range = inverseEditOperations[0].range;
this.closeCharacterRange = new Range(range.startLineNumber, range.endColumn - this._closeCharacter.length, range.endLineNumber, range.endColumn);
this.enclosingRange = range;
return super.computeCursorState(model, helper);
}
}
+26
View File
@@ -126,6 +126,32 @@ export class Range {
return true;
}
/**
* Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true.
*/
public strictContainsRange(range: IRange): boolean {
return Range.strictContainsRange(this, range);
}
/**
* Test if `otherRange` is strinctly in `range` (must start after, and end before). If the ranges are equal, will return false.
*/
public static strictContainsRange(range: IRange, otherRange: IRange): boolean {
if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) {
return false;
}
if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) {
return false;
}
if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn <= range.startColumn) {
return false;
}
if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn >= range.endColumn) {
return false;
}
return true;
}
/**
* A reunion of the two ranges.
* The smallest position will be used as the start point, and the largest one as the end point.
+2 -1
View File
@@ -17,7 +17,8 @@ export const enum ClassName {
EditorWarningDecoration = 'squiggly-warning',
EditorErrorDecoration = 'squiggly-error',
EditorUnnecessaryDecoration = 'squiggly-unnecessary',
EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary'
EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary',
EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated'
}
export const enum NodeColor {
@@ -545,6 +545,12 @@ export class Searcher {
const matchStartIndex = m.index;
const matchLength = m[0].length;
if (matchStartIndex === this._prevMatchStartIndex && matchLength === this._prevMatchLength) {
if (matchLength === 0) {
// the search result is an empty string and won't advance `regex.lastIndex`, so `regex.exec` will stuck here
// we attempt to recover from that by advancing by one
this._searchRegex.lastIndex += 1;
continue;
}
// Exit early if the regex matches the same range twice
return null;
}
@@ -221,6 +221,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
}
if (marker.tags.indexOf(MarkerTag.Deprecated) !== -1) {
inlineClassName = ClassName.EditorDeprecatedInlineDecoration;
}
}
return {
@@ -7,7 +7,8 @@
export enum MarkerTag {
Unnecessary = 1
Unnecessary = 1,
Deprecated = 2
}
export enum MarkerSeverity {
@@ -148,12 +148,6 @@ class ExecCommandCopyAction extends ExecCommandAction {
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
return;
}
// Prevent copying an empty line by accident
if (editor.getSelections().length === 1 && editor.getSelection().isEmpty()) {
if (editor.getModel().getLineFirstNonWhitespaceColumn(editor.getSelection().positionLineNumber) === 0) {
return;
}
}
super.run(accessor, editor);
}
@@ -13,7 +13,7 @@ import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons';
import { Range } from 'vs/editor/common/core/range';
import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { localize } from 'vs/nls';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -38,7 +38,7 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
getId(element: TreeElement): { toString(): string; } {
getId(element: OutlineItem): { toString(): string; } {
return element.id;
}
}
+12 -2
View File
@@ -112,7 +112,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
searchScope: null,
matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, false),
wholeWord: this._storageService.getBoolean('editor.wholeWord', StorageScope.WORKSPACE, false),
isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, false)
isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, false),
preserveCase: this._storageService.getBoolean('editor.preserveCase', StorageScope.WORKSPACE, false)
}, false);
if (shouldRestartFind) {
@@ -170,13 +171,17 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
if (e.matchCase) {
this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE);
}
if (e.preserveCase) {
this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, StorageScope.WORKSPACE);
}
}
private loadQueryState() {
this._state.change({
matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, this._state.matchCase),
wholeWord: this._storageService.getBoolean('editor.wholeWord', StorageScope.WORKSPACE, this._state.wholeWord),
isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, this._state.isRegex)
isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, this._state.isRegex),
preserveCase: this._storageService.getBoolean('editor.preserveCase', StorageScope.WORKSPACE, this._state.preserveCase)
}, false);
}
@@ -217,6 +222,11 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}
}
public togglePreserveCase(): void {
this._state.change({ preserveCase: !this._state.preserveCase }, false);
this.highlightFindOptions();
}
public toggleSearchScope(): void {
if (this._state.searchScope) {
this._state.change({ searchScope: null }, true);
+8 -5
View File
@@ -59,6 +59,7 @@ export const FIND_IDS = {
ToggleWholeWordCommand: 'toggleFindWholeWord',
ToggleRegexCommand: 'toggleFindRegex',
ToggleSearchScopeCommand: 'toggleFindInSelection',
TogglePreserveCaseCommand: 'togglePreserveCase',
ReplaceOneAction: 'editor.action.replaceOne',
ReplaceAllAction: 'editor.action.replaceAll',
SelectAllMatchesAction: 'editor.action.selectAllMatches'
@@ -416,11 +417,11 @@ export class FindModelBoundToEditorModel {
let replacePattern = this._getReplacePattern();
let selection = this._editor.getSelection();
let nextMatch = this._getNextMatch(selection.getStartPosition(), replacePattern.hasReplacementPatterns, false);
let nextMatch = this._getNextMatch(selection.getStartPosition(), true, false);
if (nextMatch) {
if (selection.equalsRange(nextMatch.range)) {
// selection sits on a find match => replace it!
let replaceString = replacePattern.buildReplaceString(nextMatch.matches);
let replaceString = replacePattern.buildReplaceString(nextMatch.matches, this._state.preserveCase);
let command = new ReplaceCommand(selection, replaceString);
@@ -482,12 +483,14 @@ export class FindModelBoundToEditorModel {
const replacePattern = this._getReplacePattern();
let resultText: string;
const preserveCase = this._state.preserveCase;
if (replacePattern.hasReplacementPatterns) {
resultText = modelText.replace(searchRegex, function () {
return replacePattern.buildReplaceString(<string[]><any>arguments);
return replacePattern.buildReplaceString(<string[]><any>arguments, preserveCase);
});
} else {
resultText = modelText.replace(searchRegex, replacePattern.buildReplaceString(null));
resultText = modelText.replace(searchRegex, replacePattern.buildReplaceString(null, preserveCase));
}
let command = new ReplaceCommandThatPreservesSelection(fullModelRange, resultText, this._editor.getSelection());
@@ -501,7 +504,7 @@ export class FindModelBoundToEditorModel {
let replaceStrings: string[] = [];
for (let i = 0, len = matches.length; i < len; i++) {
replaceStrings[i] = replacePattern.buildReplaceString(matches[i].matches);
replaceStrings[i] = replacePattern.buildReplaceString(matches[i].matches, this._state.preserveCase);
}
let command = new ReplaceAllCommand(this._editor.getSelection(), matches.map(m => m.range), replaceStrings);
+21
View File
@@ -18,6 +18,7 @@ export interface FindReplaceStateChangedEvent {
isRegex: boolean;
wholeWord: boolean;
matchCase: boolean;
preserveCase: boolean;
searchScope: boolean;
matchesPosition: boolean;
matchesCount: boolean;
@@ -41,6 +42,8 @@ export interface INewFindReplaceState {
wholeWordOverride?: FindOptionOverride;
matchCase?: boolean;
matchCaseOverride?: FindOptionOverride;
preserveCase?: boolean;
preserveCaseOverride?: FindOptionOverride;
searchScope?: Range | null;
}
@@ -65,6 +68,8 @@ export class FindReplaceState implements IDisposable {
private _wholeWordOverride: FindOptionOverride;
private _matchCase: boolean;
private _matchCaseOverride: FindOptionOverride;
private _preserveCase: boolean;
private _preserveCaseOverride: FindOptionOverride;
private _searchScope: Range | null;
private _matchesPosition: number;
private _matchesCount: number;
@@ -78,10 +83,12 @@ export class FindReplaceState implements IDisposable {
public get isRegex(): boolean { return effectiveOptionValue(this._isRegexOverride, this._isRegex); }
public get wholeWord(): boolean { return effectiveOptionValue(this._wholeWordOverride, this._wholeWord); }
public get matchCase(): boolean { return effectiveOptionValue(this._matchCaseOverride, this._matchCase); }
public get preserveCase(): boolean { return effectiveOptionValue(this._preserveCaseOverride, this._preserveCase); }
public get actualIsRegex(): boolean { return this._isRegex; }
public get actualWholeWord(): boolean { return this._wholeWord; }
public get actualMatchCase(): boolean { return this._matchCase; }
public get actualPreserveCase(): boolean { return this._preserveCase; }
public get searchScope(): Range | null { return this._searchScope; }
public get matchesPosition(): number { return this._matchesPosition; }
@@ -100,6 +107,8 @@ export class FindReplaceState implements IDisposable {
this._wholeWordOverride = FindOptionOverride.NotSet;
this._matchCase = false;
this._matchCaseOverride = FindOptionOverride.NotSet;
this._preserveCase = false;
this._preserveCaseOverride = FindOptionOverride.NotSet;
this._searchScope = null;
this._matchesPosition = 0;
this._matchesCount = 0;
@@ -120,6 +129,7 @@ export class FindReplaceState implements IDisposable {
isRegex: false,
wholeWord: false,
matchCase: false,
preserveCase: false,
searchScope: false,
matchesPosition: false,
matchesCount: false,
@@ -169,6 +179,7 @@ export class FindReplaceState implements IDisposable {
isRegex: false,
wholeWord: false,
matchCase: false,
preserveCase: false,
searchScope: false,
matchesPosition: false,
matchesCount: false,
@@ -179,6 +190,7 @@ export class FindReplaceState implements IDisposable {
const oldEffectiveIsRegex = this.isRegex;
const oldEffectiveWholeWords = this.wholeWord;
const oldEffectiveMatchCase = this.matchCase;
const oldEffectivePreserveCase = this.preserveCase;
if (typeof newState.searchString !== 'undefined') {
if (this._searchString !== newState.searchString) {
@@ -217,6 +229,9 @@ export class FindReplaceState implements IDisposable {
if (typeof newState.matchCase !== 'undefined') {
this._matchCase = newState.matchCase;
}
if (typeof newState.preserveCase !== 'undefined') {
this._preserveCase = newState.preserveCase;
}
if (typeof newState.searchScope !== 'undefined') {
if (!Range.equalsRange(this._searchScope, newState.searchScope)) {
this._searchScope = newState.searchScope;
@@ -229,6 +244,7 @@ export class FindReplaceState implements IDisposable {
this._isRegexOverride = (typeof newState.isRegexOverride !== 'undefined' ? newState.isRegexOverride : FindOptionOverride.NotSet);
this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet);
this._matchCaseOverride = (typeof newState.matchCaseOverride !== 'undefined' ? newState.matchCaseOverride : FindOptionOverride.NotSet);
this._preserveCaseOverride = (typeof newState.preserveCaseOverride !== 'undefined' ? newState.preserveCaseOverride : FindOptionOverride.NotSet);
if (oldEffectiveIsRegex !== this.isRegex) {
somethingChanged = true;
@@ -243,6 +259,11 @@ export class FindReplaceState implements IDisposable {
changeEvent.matchCase = true;
}
if (oldEffectivePreserveCase !== this.preserveCase) {
somethingChanged = true;
changeEvent.preserveCase = true;
}
if (somethingChanged) {
this._onFindReplaceStateChange.fire(changeEvent);
}
+21
View File
@@ -39,6 +39,11 @@
transition: top 200ms linear;
padding: 0 4px;
}
.monaco-editor .find-widget.hiddenEditor {
display: none;
}
/* Find widget when replace is toggled on */
.monaco-editor .find-widget.replaceToggled {
top: -74px; /* find input height + replace input height + shadow (10px) */
@@ -79,6 +84,15 @@
height: 25px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input {
width: 100% !important;
padding-right: 66px;
}
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
padding-right: 22px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
padding-top: 2px;
@@ -224,12 +238,19 @@
}
.monaco-editor .find-widget > .replace-part > .replace-input {
position: relative;
display: flex;
display: -webkit-flex;
vertical-align: middle;
width: auto !important;
}
.monaco-editor .find-widget > .replace-part > .replace-input > .controls {
position: absolute;
top: 3px;
right: 2px;
}
/* REDUCED */
.monaco-editor .find-widget.reduced-find-widget .matchesCount,
.monaco-editor .find-widget.reduced-find-widget .monaco-checkbox {
+36 -2
View File
@@ -13,6 +13,7 @@ import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findIn
import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { Delayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -47,6 +48,7 @@ const NLS_TOGGLE_SELECTION_FIND_TITLE = nls.localize('label.toggleSelectionFind'
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace");
const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace");
const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case");
const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
@@ -101,6 +103,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _nextBtn: SimpleButton;
private _toggleSelectionFind: SimpleCheckbox;
private _closeBtn: SimpleButton;
private _preserveCase: Checkbox;
private _replaceBtn: SimpleButton;
private _replaceAllBtn: SimpleButton;
@@ -590,14 +593,26 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
};
this._findInput.style(inputStyles);
this._replaceInputBox.style(inputStyles);
this._preserveCase.style(inputStyles);
}
private _tryUpdateWidgetWidth() {
if (!this._isVisible) {
return;
}
let editorWidth = this._codeEditor.getConfiguration().layoutInfo.width;
let minimapWidth = this._codeEditor.getConfiguration().layoutInfo.minimapWidth;
const editorContentWidth = this._codeEditor.getConfiguration().layoutInfo.contentWidth;
if (editorContentWidth <= 0) {
// for example, diff view original editor
dom.addClass(this._domNode, 'hiddenEditor');
return;
} else if (dom.hasClass(this._domNode, 'hiddenEditor')) {
dom.removeClass(this._domNode, 'hiddenEditor');
}
const editorWidth = this._codeEditor.getConfiguration().layoutInfo.width;
const minimapWidth = this._codeEditor.getConfiguration().layoutInfo.minimapWidth;
let collapsedFindWidget = false;
let reducedFindWidget = false;
let narrowFindWidget = false;
@@ -913,6 +928,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._state.change({ replaceString: this._replaceInputBox.value }, false);
}));
this._preserveCase = this._register(new Checkbox({
actionClassName: 'monaco-case-sensitive',
title: NLS_PRESERVE_CASE_LABEL,
isChecked: false,
}));
this._preserveCase.checked = !!this._state.preserveCase;
this._register(this._preserveCase.onChange(viaKeyboard => {
if (!viaKeyboard) {
this._state.change({ preserveCase: !this._state.preserveCase }, false);
this._replaceInputBox.focus();
}
}));
// Replace one button
this._replaceBtn = this._register(new SimpleButton({
label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction),
@@ -937,6 +965,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
let controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = 'block';
controls.appendChild(this._preserveCase.domNode);
replaceInput.appendChild(controls);
let replacePart = document.createElement('div');
replacePart.className = 'replace-part';
replacePart.appendChild(replaceInput);
+16 -2
View File
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { containsUppercaseCharacter } from 'vs/base/common/strings';
const enum ReplacePatternKind {
StaticValue = 0,
@@ -48,9 +49,22 @@ export class ReplacePattern {
}
}
public buildReplaceString(matches: string[] | null): string {
public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string {
if (this._state.kind === ReplacePatternKind.StaticValue) {
return this._state.staticValue;
if (preserveCase && matches && (matches[0] !== '')) {
if (matches[0].toUpperCase() === matches[0]) {
return this._state.staticValue.toUpperCase();
} else if (matches[0].toLowerCase() === matches[0]) {
return this._state.staticValue.toLowerCase();
} else if (containsUppercaseCharacter(matches[0][0])) {
return this._state.staticValue[0].toUpperCase() + this._state.staticValue.substr(1);
} else {
// we don't understand its pattern yet.
return this._state.staticValue;
}
} else {
return this._state.staticValue;
}
}
let result = '';
@@ -41,7 +41,8 @@ export abstract class SimpleFindWidget extends Widget {
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService,
private readonly _state: FindReplaceState = new FindReplaceState(),
showOptionButtons?: boolean
showOptionButtons?: boolean,
private readonly _invertDefaultDirection: boolean = false
) {
super();
@@ -93,13 +94,13 @@ export abstract class SimpleFindWidget extends Widget {
this._register(this._findInput.onKeyDown((e) => {
if (e.equals(KeyCode.Enter)) {
this.find(false);
this.find(this._invertDefaultDirection);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this.find(true);
this.find(!this._invertDefaultDirection);
e.preventDefault();
return;
}
@@ -295,4 +296,4 @@ registerThemingParticipant((theme, collector) => {
if (widgetShadowColor) {
collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
}
});
});
@@ -153,4 +153,26 @@ suite('Replace Pattern test', () => {
let actual = replacePattern.buildReplaceString(matches);
assert.equal(actual, 'a{}');
});
test('preserve case', () => {
let replacePattern = parseReplaceString('Def');
let actual = replacePattern.buildReplaceString(['abc'], true);
assert.equal(actual, 'def');
actual = replacePattern.buildReplaceString(['Abc'], true);
assert.equal(actual, 'Def');
actual = replacePattern.buildReplaceString(['ABC'], true);
assert.equal(actual, 'DEF');
actual = replacePattern.buildReplaceString(['abc', 'Abc'], true);
assert.equal(actual, 'def');
actual = replacePattern.buildReplaceString(['Abc', 'abc'], true);
assert.equal(actual, 'Def');
actual = replacePattern.buildReplaceString(['ABC', 'abc'], true);
assert.equal(actual, 'DEF');
actual = replacePattern.buildReplaceString(['AbC'], true);
assert.equal(actual, 'Def');
actual = replacePattern.buildReplaceString(['aBC'], true);
assert.equal(actual, 'Def');
});
});
@@ -589,13 +589,13 @@ export class AutoIndentOnPaste implements IEditorContribution {
private shouldIgnoreLine(model: ITextModel, lineNumber: number): boolean {
model.forceTokenization(lineNumber);
let nonWhiteSpaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
if (nonWhiteSpaceColumn === 0) {
let nonWhitespaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
if (nonWhitespaceColumn === 0) {
return true;
}
let tokens = model.getLineTokens(lineNumber);
if (tokens.getCount() > 0) {
let firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhiteSpaceColumn);
let firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhitespaceColumn);
if (firstNonWhitespaceTokenIndex >= 0 && tokens.getStandardTokenType(firstNonWhitespaceTokenIndex) === StandardTokenType.Comment) {
return true;
}
@@ -36,10 +36,17 @@ export class CommitCharacterController {
private _onItem(selected: ISelectedSuggestion | undefined): void {
if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) {
// no item or no commit characters
this.reset();
return;
}
if (this._active && this._active.item.item === selected.item) {
// still the same item
return;
}
// keep item and its commit characters
const acceptCharacters = new CharacterSet();
for (const ch of selected.item.completion.commitCharacters) {
if (ch.length > 0) {
+17 -44
View File
@@ -50,7 +50,7 @@ interface ISuggestionTemplateData {
iconLabel: IconLabel;
typeLabel: HTMLElement;
readMore: HTMLElement;
disposables: IDisposable[];
disposables: DisposableStore;
}
/**
@@ -106,8 +106,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
renderTemplate(container: HTMLElement): ISuggestionTemplateData {
const data = <ISuggestionTemplateData>Object.create(null);
const disposables = new DisposableStore();
data.disposables = [disposables];
data.disposables = new DisposableStore();
data.root = container;
addClass(data.root, 'show-file-icons');
@@ -119,7 +118,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
const main = append(text, $('.main'));
data.iconLabel = new IconLabel(main, { supportHighlights: true });
disposables.add(data.iconLabel);
data.disposables.add(data.iconLabel);
data.typeLabel = append(main, $('span.type-label'));
@@ -147,7 +146,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
configureFont();
disposables.add(Event.chain<IConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
data.disposables.add(Event.chain<IConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(e => e.fontInfo || e.contribInfo)
.on(configureFont, null));
@@ -218,7 +217,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
}
disposeTemplate(templateData: ISuggestionTemplateData): void {
templateData.disposables = dispose(templateData.disposables);
templateData.disposables.dispose();
}
}
@@ -253,7 +252,7 @@ class SuggestionDetails {
private type: HTMLElement;
private docs: HTMLElement;
private ariaLabel: string | null;
private disposables: IDisposable[];
private readonly disposables: DisposableStore;
private renderDisposeable: IDisposable;
private borderWidth: number = 1;
@@ -264,16 +263,16 @@ class SuggestionDetails {
private readonly markdownRenderer: MarkdownRenderer,
private readonly triggerKeybindingLabel: string,
) {
this.disposables = [];
this.disposables = new DisposableStore();
this.el = append(container, $('.details'));
this.disposables.push(toDisposable(() => container.removeChild(this.el)));
this.disposables.add(toDisposable(() => container.removeChild(this.el)));
this.body = $('.body');
this.scrollbar = new DomScrollableElement(this.body, {});
append(this.el, this.scrollbar.getDomNode());
this.disposables.push(this.scrollbar);
this.disposables.add(this.scrollbar);
this.header = append(this.body, $('.header'));
this.close = append(this.header, $('span.close'));
@@ -414,7 +413,7 @@ class SuggestionDetails {
}
dispose(): void {
this.disposables = dispose(this.disposables);
this.disposables.dispose();
this.renderDisposeable = dispose(this.renderDisposeable);
}
}
@@ -672,7 +671,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
});
this.currentSuggestionDetails.then(() => {
if (this.list.length < index) {
if (index >= this.list.length || item !== this.list.element(index)) {
return;
}
@@ -689,11 +688,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
}
this._ariaAlert(this._getSuggestionAriaAlertLabel(item));
}).catch(onUnexpectedError).then(() => {
if (this.focusedItem === item) {
this.currentSuggestionDetails = null;
}
});
}).catch(onUnexpectedError);
}
// emit an event
@@ -810,12 +805,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
"suggestWidget" : {
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${include}": [
"${ICompletionStats}",
"${EditorTelemetryData}"
"${ICompletionStats}"
]
}
*/
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
this.telemetryService.publicLog('suggestWidget', { ...stats });
}
this.focusedItem = null;
@@ -949,14 +943,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.details.element.style.borderColor = this.detailsFocusBorderColor;
}
}
/* __GDPR__
"suggestWidget:toggleDetailsFocus" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:toggleDetailsFocus', this.editor.getTelemetryData());
this.telemetryService.publicLog2('suggestWidget:toggleDetailsFocus');
}
toggleDetails(): void {
@@ -970,14 +957,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
removeClass(this.element, 'docs-side');
removeClass(this.element, 'docs-below');
this.editor.layoutContentWidget(this);
/* __GDPR__
"suggestWidget:collapseDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData());
this.telemetryService.publicLog2('suggestWidget:collapseDetails');
} else {
if (this.state !== State.Open && this.state !== State.Details && this.state !== State.Frozen) {
return;
@@ -986,14 +966,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.updateExpandDocsSetting(true);
this.showDetails(false);
this._ariaAlert(this.details.getAriaLabel());
/* __GDPR__
"suggestWidget:expandDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData());
this.telemetryService.publicLog2('suggestWidget:expandDetails');
}
}
@@ -137,6 +137,22 @@ suite('WordOperations', () => {
assert.deepEqual(actual, EXPECTED);
});
test('cursorWordLeftSelect - issue #74369: cursorWordLeft and cursorWordLeftSelect do not behave consistently', () => {
const EXPECTED = [
'|this.|is.|a.|test',
].join('\n');
const [text,] = deserializePipePositions(EXPECTED);
const actualStops = testRepeatedActionAndExtractPositions(
text,
new Position(1, 15),
ed => cursorWordLeft(ed, true),
ed => ed.getPosition()!,
ed => ed.getPosition()!.equals(new Position(1, 1))
);
const actual = serializePipePositions(text, actualStops);
assert.deepEqual(actual, EXPECTED);
});
test('cursorWordStartLeft', () => {
// This is the behaviour observed in Visual Studio, please do not touch test
const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/| '].join('\n');
@@ -163,7 +163,7 @@ export class CursorWordLeftSelect extends WordLeftCommand {
constructor() {
super({
inSelectionMode: true,
wordNavigationType: WordNavigationType.WordStart,
wordNavigationType: WordNavigationType.WordStartFast,
id: 'cursorWordLeftSelect',
precondition: undefined
});
@@ -234,7 +234,9 @@ export class StandaloneCommandService implements ICommandService {
private readonly _dynamicCommands: { [id: string]: ICommand; };
private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;
constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService;
@@ -256,8 +258,10 @@ export class StandaloneCommandService implements ICommandService {
}
try {
this._onWillExecuteCommand.fire({ commandId: id });
this._onWillExecuteCommand.fire({ commandId: id, args });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
@@ -352,7 +356,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] {
let result: ResolvedKeybindingItem[] = [], resultLen = 0;
for (const item of items) {
const when = (item.when ? item.when.normalize() : undefined);
const when = item.when || undefined;
const keybinding = item.keybinding;
if (!keybinding) {
@@ -140,8 +140,6 @@ export module StaticServices {
export const notificationService = define(INotificationService, () => new SimpleNotificationService());
export const accessibilityService = define(IAccessibilityService, () => new BrowserAccessibilityService());
export const markerService = define(IMarkerService, () => new MarkerService());
export const modeService = define(IModeService, (o) => new ModeServiceImpl());
@@ -194,6 +192,8 @@ export class DynamicStandaloneServices extends Disposable {
let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService)));
ensure(IAccessibilityService, () => new BrowserAccessibilityService(contextKeyService, configurationService));
ensure(IListService, () => new ListService(contextKeyService));
let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService));
@@ -4344,12 +4344,12 @@ suite('autoClosingPairs', () => {
let autoClosePositions = [
'var a |=| [|]|;|',
'var b |=| |`asd`|;|',
'var c |=| |\'asd!\'|;|',
'var c |=| |\'asd\'|;|',
'var d |=| |"asd"|;|',
'var e |=| /*3*/| 3;|',
'var f |=| /**| 3 */3;|',
'var g |=| (3+5)|;|',
'var h |=| {| a:| |\'value!\'| |}|;|',
'var h |=| {| a:| |\'value\'| |}|;|',
];
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
const lineNumber = i + 1;
@@ -4494,6 +4494,107 @@ suite('autoClosingPairs', () => {
mode.dispose();
});
test('issue #37315 - overtypes only those characters that it inserted', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'',
'y=();'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
assertCursor(cursor, new Position(1, 1));
cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=()');
cursorCommand(cursor, H.Type, { text: 'asd' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
// overtype!
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
// do not overtype!
cursor.setSelections('test', [new Selection(2, 4, 2, 4)]);
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(2), 'y=());');
});
mode.dispose();
});
test('issue #37315 - stops overtyping once cursor leaves area', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'',
'y=();'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
assertCursor(cursor, new Position(1, 1));
cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=()');
cursor.setSelections('test', [new Selection(1, 5, 1, 5)]);
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=())');
});
mode.dispose();
});
test('issue #37315 - it overtypes only once', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'',
'y=();'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
assertCursor(cursor, new Position(1, 1));
cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=()');
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=()');
cursor.setSelections('test', [new Selection(1, 4, 1, 4)]);
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=())');
});
mode.dispose();
});
test('issue #37315 - it can remember multiple auto-closed instances', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'',
'y=();'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
assertCursor(cursor, new Position(1, 1));
cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=()');
cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=(())');
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=(())');
cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), 'x=(())');
});
mode.dispose();
});
test('issue #15825: accents on mac US intl keyboard', () => {
let mode = new AutoClosingMode();
usingCursor({
@@ -132,7 +132,7 @@ suite('Cursor move command test', () => {
test('move to first non white space character of line from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
@@ -140,7 +140,7 @@ suite('Cursor move command test', () => {
test('move to first non white space character of line from first non white space character', () => {
moveTo(thisCursor, 1, 6);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
@@ -148,7 +148,7 @@ suite('Cursor move command test', () => {
test('move to first non white space character of line from first character', () => {
moveTo(thisCursor, 1, 1);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor);
moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6);
});
@@ -180,7 +180,7 @@ suite('Cursor move command test', () => {
test('move to last non white space character from middle', () => {
moveTo(thisCursor, 1, 8);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
@@ -188,7 +188,7 @@ suite('Cursor move command test', () => {
test('move to last non white space character from last non white space character', () => {
moveTo(thisCursor, 1, 19);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
@@ -196,7 +196,7 @@ suite('Cursor move command test', () => {
test('move to last non white space character from line end', () => {
moveTo(thisCursor, 1, 21);
moveToLineLastNonWhiteSpaceCharacter(thisCursor);
moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19);
});
@@ -415,7 +415,7 @@ function moveToLineStart(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineStart });
}
function moveToLineFirstNonWhiteSpaceCharacter(cursor: Cursor) {
function moveToLineFirstNonWhitespaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter });
}
@@ -427,7 +427,7 @@ function moveToLineEnd(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd });
}
function moveToLineLastNonWhiteSpaceCharacter(cursor: Cursor) {
function moveToLineLastNonWhitespaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter });
}
@@ -32,6 +32,9 @@ export class TestCommandService implements ICommandService {
private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;
constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService;
}
@@ -43,8 +46,9 @@ export class TestCommandService implements ICommandService {
}
try {
this._onWillExecuteCommand.fire({ commandId: id });
this._onWillExecuteCommand.fire({ commandId: id, args });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
@@ -17,6 +17,7 @@ suite('OpenerService', function () {
const commandService = new class implements ICommandService {
_serviceBrand: any;
onWillExecuteCommand = () => ({ dispose: () => { } });
onDidExecuteCommand = () => ({ dispose: () => { } });
executeCommand(id: string, ...args: any[]): Promise<any> {
lastCommand = { id, args };
return Promise.resolve(undefined);
@@ -733,4 +733,23 @@ suite('TextModelSearch', () => {
assert(isMultilineRegexSource('\\n'));
assert(isMultilineRegexSource('foo\\W'));
});
test('issue #74715. \\d* finds empty string and stops searching.', () => {
let model = TextModel.createFromString('10.243.30.10');
let searchParams = new SearchParams('\\d*', true, false, null);
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100);
assert.deepEqual(actual, [
new FindMatch(new Range(1, 1, 1, 3), ['10']),
new FindMatch(new Range(1, 3, 1, 3), ['']),
new FindMatch(new Range(1, 4, 1, 7), ['243']),
new FindMatch(new Range(1, 7, 1, 7), ['']),
new FindMatch(new Range(1, 8, 1, 10), ['30']),
new FindMatch(new Range(1, 10, 1, 10), ['']),
new FindMatch(new Range(1, 11, 1, 13), ['10'])
]);
model.dispose();
});
});

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