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
+2 -1
View File
@@ -59,5 +59,6 @@
"git.ignoreLimitWarning": true, "git.ignoreLimitWarning": true,
"remote.extensionKind": { "remote.extensionKind": {
"msjsdiag.debugger-for-chrome": "workspace" "msjsdiag.debugger-for-chrome": "workspace"
} },
"files.insertFinalNewline": true
} }
+1 -1
View File
@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron" disturl "https://atom.io/download/electron"
target "4.2.5" target "4.2.7"
runtime "electron" runtime "electron"
+2 -2
View File
@@ -31,8 +31,8 @@ steps:
git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git"
git fetch distro git fetch distro
# Push master branch into master and oss/master # Push master branch into oss/master
git push distro origin/master:refs/heads/master origin/master:refs/heads/oss/master git push distro origin/master:refs/heads/oss/master
# Push every release branch into oss/release # 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 for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro
+1 -1
View File
@@ -17,7 +17,7 @@ const gunzip = require('gulp-gunzip');
const untar = require('gulp-untar'); const untar = require('gulp-untar');
const File = require('vinyl'); const File = require('vinyl');
const fs = require('fs'); const fs = require('fs');
const remote = require('gulp-remote-src'); const remote = require('gulp-remote-retry-src');
const rename = require('gulp-rename'); const rename = require('gulp-rename');
const filter = require('gulp-filter'); const filter = require('gulp-filter');
const cp = require('child_process'); const cp = require('child_process');
+1 -1
View File
@@ -13,7 +13,7 @@ const File = require("vinyl");
const vsce = require("vsce"); const vsce = require("vsce");
const stats_1 = require("./stats"); const stats_1 = require("./stats");
const util2 = require("./util"); const util2 = require("./util");
const remote = require("gulp-remote-src"); const remote = require("gulp-remote-retry-src");
const vzip = require('gulp-vinyl-zip'); const vzip = require('gulp-vinyl-zip');
const filter = require("gulp-filter"); const filter = require("gulp-filter");
const rename = require("gulp-rename"); const rename = require("gulp-rename");
+1 -1
View File
@@ -13,7 +13,7 @@ import * as File from 'vinyl';
import * as vsce from 'vsce'; import * as vsce from 'vsce';
import { createStatsStream } from './stats'; import { createStatsStream } from './stats';
import * as util2 from './util'; import * as util2 from './util';
import remote = require('gulp-remote-src'); import remote = require('gulp-remote-retry-src');
const vzip = require('gulp-vinyl-zip'); const vzip = require('gulp-vinyl-zip');
import filter = require('gulp-filter'); import filter = require('gulp-filter');
import rename = require('gulp-rename'); import rename = require('gulp-rename');
+1 -1
View File
@@ -1,4 +1,4 @@
declare module 'gulp-remote-src' { declare module 'gulp-remote-retry-src' {
import stream = require("stream"); import stream = require("stream");
+2
View File
@@ -5,6 +5,8 @@
declare namespace monaco { declare namespace monaco {
// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.
export type Thenable<T> = PromiseLike<T>; export type Thenable<T> = PromiseLike<T>;
export interface IDisposable { 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 = (<ServiceIdentifier<any>>b).type;
a = (<IHighlight>b).start; a = (<IHighlight>b).start;
a = (<IHighlight>b).end; a = (<IHighlight>b).end;
a = (<SimpleWorkerClient<any>>b).getProxyObject; // IWorkerClient a = (<SimpleWorkerClient<any, any>>b).getProxyObject; // IWorkerClient
a = create1; a = create1;
a = create2; a = create2;
a = (<DocumentRangeFormattingEditProvider>b).extensionId; a = (<DocumentRangeFormattingEditProvider>b).extensionId;
+3 -3
View File
@@ -20,7 +20,7 @@
"@types/minimatch": "^3.0.3", "@types/minimatch": "^3.0.3",
"@types/minimist": "^1.2.0", "@types/minimist": "^1.2.0",
"@types/mocha": "2.2.39", "@types/mocha": "2.2.39",
"@types/node": "8.0.33", "@types/node": "^10.14.8",
"@types/pump": "^1.0.1", "@types/pump": "^1.0.1",
"@types/request": "^2.47.0", "@types/request": "^2.47.0",
"@types/rimraf": "^2.0.2", "@types/rimraf": "^2.0.2",
@@ -29,7 +29,7 @@
"@types/uglify-es": "^3.0.0", "@types/uglify-es": "^3.0.0",
"@types/underscore": "^1.8.9", "@types/underscore": "^1.8.9",
"@types/xml2js": "0.0.33", "@types/xml2js": "0.0.33",
"applicationinsights": "1.0.6", "applicationinsights": "1.0.8",
"azure-storage": "^2.1.0", "azure-storage": "^2.1.0",
"del": "^3.0.0", "del": "^3.0.0",
"documentdb": "1.13.0", "documentdb": "1.13.0",
@@ -44,7 +44,7 @@
"service-downloader": "github:anthonydresser/service-downloader#0.1.5", "service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"typescript": "3.5.2", "typescript": "3.5.2",
"vsce": "1.48.0", "vsce": "1.48.0",
"vscode-telemetry-extractor": "1.4.3", "vscode-telemetry-extractor": "^1.5.1",
"xml2js": "^0.4.17" "xml2js": "^0.4.17"
}, },
"scripts": { "scripts": {
+22 -17
View File
@@ -187,10 +187,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==
"@types/node@8.0.33": "@types/node@^10.14.8":
version "8.0.33" version "10.14.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.13.tgz#ac786d623860adf39a3f51d629480aacd6a6eec7"
integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== integrity sha512-yN/FNNW1UYsRR1wwAoyOwqvDuLDtVXnaJTZ898XIw/Q5cCaeVAlVwvsmXLX5PuiScBYwZsZU4JYSHB3TvfdwvQ==
"@types/pump@^1.0.1": "@types/pump@^1.0.1":
version "1.0.1" version "1.0.1"
@@ -374,10 +374,10 @@ anymatch@^2.0.0:
micromatch "^3.1.4" micromatch "^3.1.4"
normalize-path "^2.1.1" normalize-path "^2.1.1"
applicationinsights@1.0.6: applicationinsights@1.0.8:
version "1.0.6" version "1.0.8"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
dependencies: dependencies:
diagnostic-channel "0.2.0" diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1" diagnostic-channel-publishers "0.2.1"
@@ -1847,7 +1847,12 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" 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" version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
@@ -3388,7 +3393,7 @@ tough-cookie@~2.3.3:
dependencies: dependencies:
punycode "^1.4.1" punycode "^1.4.1"
ts-morph@^3.1.2: ts-morph@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-3.1.3.tgz#bbfa1d14481ee23bdd1c030340ccf4a243cfc844" resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-3.1.3.tgz#bbfa1d14481ee23bdd1c030340ccf4a243cfc844"
integrity sha512-CwjgyJTtd3f8vBi7Vr0IOgdOY6Wi/Tq0MhieXOE2B5ns5WWRD7BwMNHtv+ZufKI/S2U/lMrh+Q3bOauE4tsv2g== integrity sha512-CwjgyJTtd3f8vBi7Vr0IOgdOY6Wi/Tq0MhieXOE2B5ns5WWRD7BwMNHtv+ZufKI/S2U/lMrh+Q3bOauE4tsv2g==
@@ -3613,19 +3618,19 @@ vsce@1.48.0:
yauzl "^2.3.1" yauzl "^2.3.1"
yazl "^2.2.2" yazl "^2.2.2"
vscode-ripgrep@^1.4.0: vscode-ripgrep@^1.5.5:
version "1.5.5" version "1.5.5"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961" resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961"
integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg== integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg==
vscode-telemetry-extractor@1.4.3: vscode-telemetry-extractor@^1.5.1:
version "1.4.3" version "1.5.1"
resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.4.3.tgz#e4af380beb4e2a63d6e4fa819b25ba7ef6dc4a10" resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.5.1.tgz#67249e4ca9c65a21800ca53880732f8cef98d0fa"
integrity sha512-OFklPErZnUBjrKte3hg+irQXue5rzgz4qnvE8kdaOnW1E/wynHUEXW9t5vF5q0hu2lodpGMkybwLkSjONZVzvg== integrity sha512-B5SnEdRiDrI4o6NMG9iHmengoaW1rxUQmS/sCaripgnchm+P79JURmKxhfXr5eRo4Mr1QSenFT/SDNaEop7aoQ==
dependencies: dependencies:
command-line-args "^5.1.1" command-line-args "^5.1.1"
ts-morph "^3.1.2" ts-morph "^3.1.3"
vscode-ripgrep "^1.4.0" vscode-ripgrep "^1.5.5"
vso-node-api@6.1.2-preview: vso-node-api@6.1.2-preview:
version "6.1.2-preview" version "6.1.2-preview"
+2 -2
View File
@@ -60,12 +60,12 @@
"git": { "git": {
"name": "electron", "name": "electron",
"repositoryUrl": "https://github.com/electron/electron", "repositoryUrl": "https://github.com/electron/electron",
"commitHash": "5d67ec3da5376a5058990e8a9557bc9124ad59a8" "commitHash": "36ea114ac0616e469e75ae94e6d53af48925e036"
} }
}, },
"isOnlyProductionDependency": true, "isOnlyProductionDependency": true,
"license": "MIT", "license": "MIT",
"version": "4.2.5" "version": "4.2.7"
}, },
{ {
"component": { "component": {
+9 -1
View File
@@ -5,7 +5,7 @@
import { Model } from '../model'; import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository'; 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 { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode';
import { mapEvent } from '../util'; import { mapEvent } from '../util';
@@ -214,6 +214,14 @@ export class ApiImpl implements API {
readonly git = new ApiGit(this._model); 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> { get onDidOpenRepository(): Event<Repository> {
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r)); return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
} }
+4
View File
@@ -176,7 +176,11 @@ export interface Repository {
log(options?: LogOptions): Promise<Commit[]>; log(options?: LogOptions): Promise<Commit[]>;
} }
export type APIState = 'uninitialized' | 'initialized';
export interface API { export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly git: Git; readonly git: Git;
readonly repositories: Repository[]; readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>; readonly onDidOpenRepository: Event<Repository>;
+3 -3
View File
@@ -1115,7 +1115,7 @@ export class CommandCenter {
if (scmResources.length === 1) { if (scmResources.length === 1) {
if (untrackedCount > 0) { 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"); yes = localize('delete file', "Delete file");
} else { } else {
if (scmResources[0].type === Status.DELETED) { if (scmResources[0].type === Status.DELETED) {
@@ -1134,7 +1134,7 @@ export class CommandCenter {
} }
if (untrackedCount > 0) { 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)); await repository.clean(resources.map(r => r.resourceUri));
return; return;
} else if (resources.length === 1) { } 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 yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes); const pick = await window.showWarningMessage(message, { modal: true }, yes);
+27 -11
View File
@@ -12,7 +12,7 @@ import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import { fromGitUri } from './uri'; import { fromGitUri } from './uri';
import { GitErrorCodes } from './api/git'; import { GitErrorCodes, APIState as State } from './api/git';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -63,15 +63,22 @@ export class Model {
private possibleGitRepositoryPaths = new Set<string>(); 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[] = []; private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) { constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] });
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
this.onDidChangeVisibleTextEditors(window.visibleTextEditors);
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
const fsWatcher = workspace.createFileSystemWatcher('**'); const fsWatcher = workspace.createFileSystemWatcher('**');
@@ -82,7 +89,15 @@ export class Model {
const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));
onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); 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 => !activeRepositories.has(r!.repository))
.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; .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()); openRepositoriesToDispose.forEach(r => r.dispose());
await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));
} }
private onDidChangeConfiguration(): void { private onDidChangeConfiguration(): void {
@@ -175,7 +190,7 @@ export class Model {
openRepositoriesToDispose.forEach(r => r.dispose()); openRepositoriesToDispose.forEach(r => r.dispose());
} }
private onDidChangeVisibleTextEditors(editors: TextEditor[]): void { private async onDidChangeVisibleTextEditors(editors: TextEditor[]): Promise<void> {
const config = workspace.getConfiguration('git'); const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection'); const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
@@ -183,7 +198,7 @@ export class Model {
return; return;
} }
editors.forEach(editor => { await Promise.all(editors.map(async editor => {
const uri = editor.document.uri; const uri = editor.document.uri;
if (uri.scheme !== 'file') { if (uri.scheme !== 'file') {
@@ -196,8 +211,8 @@ export class Model {
return; return;
} }
this.openRepository(path.dirname(uri.fsPath)); await this.openRepository(path.dirname(uri.fsPath));
}); }));
} }
@sequentialize @sequentialize
@@ -236,6 +251,7 @@ export class Model {
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel); const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
this.open(repository); this.open(repository);
await repository.status();
} catch (err) { } catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
return; return;
+18 -8
View File
@@ -519,8 +519,8 @@ class DotGitWatcher implements IFileWatcher {
this.transientDisposables.push(upstreamWatcher); this.transientDisposables.push(upstreamWatcher);
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) { } catch (err) {
if (env.logLevel <= LogLevel.Info) { if (env.logLevel <= LogLevel.Error) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`); 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 onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path)); const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); let onDotGitFileChange: Event<Uri>;
this.disposables.push(dotGitFileWatcher);
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`: // FS changes should trigger `git status`:
// - any change inside the repository working tree // - any change inside the repository working tree
// - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock` // - 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); onFileChange(this.onFileChange, this, this.disposables);
// Relevate repository changes should trigger virtual document change events // 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); const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root); this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -713,7 +724,6 @@ export class Repository implements Disposable {
this.disposables.push(progressManager); this.disposables.push(progressManager);
this.updateCommitTemplate(); this.updateCommitTemplate();
this.status();
} }
validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined { validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined {
@@ -17,7 +17,7 @@
"vscode-json-languageservice": "^3.3.0", "vscode-json-languageservice": "^3.3.0",
"vscode-languageserver": "^5.3.0-next.8", "vscode-languageserver": "^5.3.0-next.8",
"vscode-nls": "^4.1.1", "vscode-nls": "^4.1.1",
"vscode-uri": "^2.0.1" "vscode-uri": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/mocha": "2.2.33", "@types/mocha": "2.2.33",
@@ -154,7 +154,8 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
documentSymbolProvider: true, documentSymbolProvider: true,
documentRangeFormattingProvider: false, documentRangeFormattingProvider: false,
colorProvider: {}, colorProvider: {},
foldingRangeProvider: true foldingRangeProvider: true,
selectionRangeProvider: true
}; };
return { capabilities }; return { capabilities };
@@ -134,3 +134,8 @@ vscode-uri@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.1.tgz#5448e4f77d21d93ffa34b96f84c6c5e09e3f5a9b" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.1.tgz#5448e4f77d21d93ffa34b96f84c6c5e09e3f5a9b"
integrity sha512-s/k0zsYr6y+tsocFyxT/+G5aq8mEdpDZuph3LZ+UmCs7LNhx/xomiCy5kyP+jOAKC7RMCUvb6JbPD1/TgAvq0g== 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": [ "statusBar/windowIndicator": [
{ {
"command": "vscode-testresolver.newWindow", "command": "vscode-testresolver.newWindow",
"when": "!remoteAuthority", "when": "!remoteName",
"group": "9_local_testresolver@2" "group": "9_local_testresolver@2"
}, },
{ {
"command": "vscode-testresolver.showLog", "command": "vscode-testresolver.showLog",
"when": "remoteAuthority =~ /^test\\+.*$/", "when": "remoteName == test",
"group": "1_remote_testresolver_open@3" "group": "1_remote_testresolver_open@3"
}, },
{ {
"command": "vscode-testresolver.newWindow", "command": "vscode-testresolver.newWindow",
"when": "remoteAuthority =~ /^test\\+.*$/", "when": "remoteName == test",
"group": "1_remote_testresolver_open@1" "group": "1_remote_testresolver_open@1"
} }
] ]
+25 -22
View File
@@ -1,31 +1,34 @@
{ {
"comments": { "comments": {
"lineComment": "", "blockComment": [ "<!--", "-->" ]
"blockComment": ["<!--", "-->"]
}, },
"brackets": [ "brackets": [
["<", ">"] ["<!--", "-->"],
["<", ">"],
["{", "}"],
["(", ")"]
], ],
"autoClosingPairs": [ "autoClosingPairs": [
["<", ">"], { "open": "{", "close": "}"},
["'", "'"], { "open": "[", "close": "]"},
["\"", "\""] { "open": "(", "close": ")" },
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]},
{ "open": "<![CDATA[", "close": "]]>", "notIn": [ "comment", "string" ]}
], ],
"surroundingPairs": [ "surroundingPairs": [
["<", ">"], { "open": "'", "close": "'" },
["'", "'"], { "open": "\"", "close": "\"" },
["\"", "\""] { "open": "{", "close": "}"},
] { "open": "[", "close": "]"},
{ "open": "(", "close": ")" },
// enhancedBrackets: [{ { "open": "<", "close": ">" }
// tokenType: 'tag.tag-$1.xml', ],
// openTrigger: '>', "folding": {
// open: /<(\w[\w\d]*)([^\/>]*(?!\/)>)[^<>]*$/i, "markers": {
// closeComplete: '</$1>', "start": "^\\s*<!--\\s*#region\\b.*-->",
// closeTrigger: '>', "end": "^\\s*<!--\\s*#endregion\\b.*-->"
// close: /<\/(\w[\w\d]*)\s*>$/i }
// }], }
// autoClosingPairs: [['\'', '\''], ['"', '"'] ]
} }
+3 -3
View File
@@ -76,8 +76,8 @@
"vscode-ripgrep": "^1.5.5", "vscode-ripgrep": "^1.5.5",
"vscode-sqlite3": "4.0.8", "vscode-sqlite3": "4.0.8",
"vscode-textmate": "^4.2.2", "vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta71", "xterm": "3.15.0-beta89",
"xterm-addon-search": "0.2.0-beta2", "xterm-addon-search": "0.2.0-beta3",
"xterm-addon-web-links": "0.1.0-beta10", "xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2", "yauzl": "^2.9.2",
"yazl": "^2.4.3", "yazl": "^2.4.3",
@@ -124,7 +124,7 @@
"gulp-gunzip": "^1.0.0", "gulp-gunzip": "^1.0.0",
"gulp-json-editor": "^2.5.0", "gulp-json-editor": "^2.5.0",
"gulp-plumber": "^1.2.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-rename": "^1.2.0",
"gulp-replace": "^0.5.4", "gulp-replace": "^0.5.4",
"gulp-shell": "^0.6.5", "gulp-shell": "^0.6.5",
+2 -2
View File
@@ -20,8 +20,8 @@
"vscode-proxy-agent": "0.4.0", "vscode-proxy-agent": "0.4.0",
"vscode-ripgrep": "^1.5.5", "vscode-ripgrep": "^1.5.5",
"vscode-textmate": "^4.2.2", "vscode-textmate": "^4.2.2",
"xterm": "3.15.0-beta71", "xterm": "3.15.0-beta89",
"xterm-addon-search": "0.2.0-beta2", "xterm-addon-search": "0.2.0-beta3",
"xterm-addon-web-links": "0.1.0-beta10", "xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2", "yauzl": "^2.9.2",
"yazl": "^2.4.3" "yazl": "^2.4.3"
+8 -8
View File
@@ -1149,20 +1149,20 @@ vscode-windows-registry@1.0.1:
dependencies: dependencies:
nan "^2.12.1" nan "^2.12.1"
xterm-addon-search@0.2.0-beta2: xterm-addon-search@0.2.0-beta3:
version "0.2.0-beta2" version "0.2.0-beta3"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta2.tgz#c3173f0a6f207ee9f1848849174ee5d6b6ce8262" resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b"
integrity sha512-XEcwi2TeFGk2MuIFjiI/OpVXSNO5dGQBvHH3o+9KzqG3ooVqhhDqzwxs092QGNcNCGh8hGn/PWZiczaBBnKm/g== integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow==
xterm-addon-web-links@0.1.0-beta10: xterm-addon-web-links@0.1.0-beta10:
version "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" resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23"
integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg==
xterm@3.15.0-beta71: xterm@3.15.0-beta89:
version "3.15.0-beta71" version "3.15.0-beta89"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta71.tgz#2728c9800ca3b08423e835e9504bd1f4b5de6253" resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta89.tgz#255962e2595deefb42b8c0043001256526163a3f"
integrity sha512-8M/cLaxZ+iDopRxLPdPfKuDGaNNyYTdCeytdxjMSH0N7dZzbx6fbaEygQdCrV5pO9cGnT92MefSjVPGRXRiBLA== integrity sha512-rNaoUamacPRg+ejbKDGRDNqR3SZ3Uf/pUW0mO+FF25/lIgdLq8x7RgZVBgFweCZ/dijPjxoyMcgfNDTH9h8LOg==
yauzl@^2.9.2: yauzl@^2.9.2:
version "2.10.0" version "2.10.0"
+1
View File
@@ -17,6 +17,7 @@ arguments=(
'(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]' '(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]'
'--extensions-dir[set the root path for extensions]:root path:_directories' '--extensions-dir[set the root path for extensions]:root path:_directories'
'--list-extensions[list the installed extensions]' '--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]' '--show-versions[show versions of installed extensions, when using --list-extension]'
'--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"'
'--uninstall-extension[uninstall 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 if [ -n "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC # replace \r\n with \n in WSL_EXT_WLOC
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh 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 $? exit $?
fi fi
else else
@@ -31,7 +31,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
title: localize('disconnect', "Disconnect") title: localize('disconnect', "Disconnect")
}, },
when: ContextKeyExpr.and(NodeContextKey.IsConnected, when: ContextKeyExpr.and(NodeContextKey.IsConnected,
new ContextKeyNotEqualsExpr('nodeType', NodeType.Folder)) ContextKeyNotEqualsExpr.create('nodeType', NodeType.Folder))
}); });
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, { MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
@@ -214,7 +214,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
}, },
when: ContextKeyExpr.and(MssqlNodeContext.NodeProvider.isEqualTo(mssqlProviderName), when: ContextKeyExpr.and(MssqlNodeContext.NodeProvider.isEqualTo(mssqlProviderName),
MssqlNodeContext.IsWindows, 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 ///////////////// //////////////// Scripting Actions /////////////////
@@ -80,7 +80,7 @@ if (isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: RunQueryKeyboardAction.ID, title: RunQueryKeyboardAction.LABEL }, command: { id: RunQueryKeyboardAction.ID, title: RunQueryKeyboardAction.LABEL },
group: 'query', 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/ // Project: http://electronjs.org/
// Definitions by: The Electron Team <https://github.com/electron/electron> // Definitions by: The Electron Team <https://github.com/electron/electron>
// Definitions: https://github.com/electron/electron-typescript-definitions // 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 * 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 * 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 * 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 * 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 * some cases, the NSImageName doesn't match its string representation; one example
* of this is NSFolderImageName, whose string representation would actually be * of this is NSFolderImageName, whose string representation would actually be
* NSFolder. Therefore, you'll need to determine the correct string representation * 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 * 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); }' | * -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' |
* clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME * clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME
* should be replaced with any value from this list. * should be replaced with any value from this list.
*/ */
static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage; static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage;
/** /**
@@ -3737,6 +3737,17 @@ declare namespace Electron {
once(event: 'unlock-screen', listener: Function): this; once(event: 'unlock-screen', listener: Function): this;
addListener(event: 'unlock-screen', listener: Function): this; addListener(event: 'unlock-screen', listener: Function): this;
removeListener(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 { interface PowerSaveBlocker extends EventEmitter {
@@ -4418,30 +4429,6 @@ declare namespace Electron {
* The new RGBA color the user assigned to be their system accent color. * The new RGBA color the user assigned to be their system accent color.
*/ */
newColor: string) => void): this; 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; on(event: 'color-changed', listener: (event: Event) => void): this;
once(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; 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 * The type of media access being requested, can be video, audio or unknown
*/ */
mediaType: ('video' | 'audio' | '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 { interface PermissionRequestHandlerDetails {
/** /**
* The url of the openExternal request. * The url of the openExternal request.
*/ */
externalURL: string; externalURL?: string;
/** /**
* The types of media access being requested, elements can be video or audio * 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 { 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'; 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. * A string representing a renderer type.
*/ */
@@ -107,6 +112,18 @@ declare module 'xterm' {
*/ */
lineHeight?: number; 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. * Whether to treat option as the meta key.
*/ */
@@ -177,6 +194,12 @@ declare module 'xterm' {
* not whitespace. * not whitespace.
*/ */
windowsMode?: boolean; 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, cursor?: string,
/** The accent color of the cursor (fg color for a block cursor) */ /** The accent color of the cursor (fg color for a block cursor) */
cursorAccent?: string, cursorAccent?: string,
/** The selection color (can be transparent) */ /** The selection background color (can be transparent) */
selection?: string, selection?: string,
/** ANSI black (eg. `\x1b[30m`) */ /** ANSI black (eg. `\x1b[30m`) */
black?: string, black?: string,
@@ -483,12 +506,14 @@ declare module 'xterm' {
* final character (e.g "m" for SGR) of the CSI sequence. * final character (e.g "m" for SGR) of the CSI sequence.
* @param callback The function to handle the escape sequence. The callback * @param callback The function to handle the escape sequence. The callback
* is called with the numerical params, as well as the special characters * 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). * we should try a previous handler (set by addCsiHandler or setCsiHandler).
* The most recently-added handler is tried first. * The most recently-added handler is tried first.
* @return An IDisposable you can call to remove this handler. * @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. * (EXPERIMENTAL) Adds a handler for OSC escape sequences.
@@ -668,12 +693,12 @@ declare module 'xterm' {
* Retrieves an option's value from the terminal. * Retrieves an option's value from the terminal.
* @param key The option key. * @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. * Retrieves an option's value from the terminal.
* @param key The option key. * @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. * Retrieves an option's value from the terminal.
* @param key The option key. * @param key The option key.
@@ -700,13 +725,19 @@ declare module 'xterm' {
* @param key The option key. * @param key The option key.
* @param value The option value. * @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. * Sets an option on the terminal.
* @param key The option key. * @param key The option key.
* @param value The option value. * @param value The option value.
*/ */
setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; 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. * Sets an option on the terminal.
* @param key The option key. * @param key The option key.
@@ -724,7 +755,7 @@ declare module 'xterm' {
* @param key The option key. * @param key The option key.
* @param value The option value. * @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. * Sets an option on the terminal.
* @param key The option key. * @param key The option key.
@@ -928,16 +959,16 @@ declare module 'xterm' {
// Modifications to official .d.ts below // Modifications to official .d.ts below
declare module 'xterm' { declare module 'xterm' {
interface TerminalCore { interface TerminalCore {
debug: boolean;
handler(text: string): void;
_onScroll: IEventEmitter<number>; _onScroll: IEventEmitter<number>;
_onKey: IEventEmitter<{ key: string }>; _onKey: IEventEmitter<{ key: string }>;
_charSizeService: { _charSizeService: {
width: number; width: number;
height: number; height: number;
};
_coreService: {
triggerDataEvent(data: string, wasUserInput?: boolean): void;
} }
_renderService: { _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 isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0); export const isIPad = (userAgent.indexOf('iPad') >= 0);
export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 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() { export function hasClipboardSupport() {
if (isIE) { if (isIE) {
+1 -1
View File
@@ -1200,7 +1200,7 @@ export function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) { if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location // rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker // 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; return uri;
} }
+4
View File
@@ -160,6 +160,8 @@ export class StandardWheelEvent {
this.deltaY = e1.wheelDeltaY / 120; this.deltaY = e1.wheelDeltaY / 120;
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) { } else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3; this.deltaY = -e2.detail / 3;
} else {
this.deltaY = -e.deltaY / 40;
} }
// horizontal delta scroll // horizontal delta scroll
@@ -171,6 +173,8 @@ export class StandardWheelEvent {
} }
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) { } else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3; this.deltaX = -e.detail / 3;
} else {
this.deltaX = -e.deltaX / 40;
} }
// Assume a vertical scroll if nothing else worked // Assume a vertical scroll if nothing else worked
@@ -6,7 +6,7 @@
import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview';
import { $ } from 'vs/base/browser/dom'; import { $ } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event'; 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 { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color'; 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 { Orientation } from 'vs/base/browser/ui/sash/sash';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { tail2 as tail, equals } from 'vs/base/common/arrays'; 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 { 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 { export const enum Direction {
Up, 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> { export interface GridLeafNode<T extends IView> {
readonly view: T; readonly view: T;
readonly box: Box; readonly box: Box;
readonly cachedVisibleSize: number | undefined;
} }
export interface GridBranchNode<T extends IView> { export interface GridBranchNode<T extends IView> {
@@ -173,16 +180,23 @@ function getGridLocation(element: HTMLElement): number[] {
return [...getGridLocation(ancestor), index]; return [...getGridLocation(ancestor), index];
} }
export const enum Sizing { export type DistributeSizing = { type: 'distribute' };
Distribute = 'distribute', export type SplitSizing = { type: 'split' };
Split = '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 IGridStyles extends IGridViewStyles { }
export interface IGridOptions { export interface IGridOptions {
styles?: IGridStyles; readonly styles?: IGridStyles;
proportionalLayout?: boolean; readonly proportionalLayout?: boolean;
readonly firstViewVisibleCachedSize?: number;
} }
export class Grid<T extends IView = IView> extends Disposable { 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.gridview = new GridView(options);
this._register(this.gridview); 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 { style(styles: IGridStyles): void {
@@ -241,10 +259,12 @@ export class Grid<T extends IView = IView> extends Disposable {
let viewSize: number | GridViewSizing; let viewSize: number | GridViewSizing;
if (size === Sizing.Split) { if (typeof size === 'number') {
viewSize = size;
} else if (size.type === 'split') {
const [, index] = tail(referenceLocation); const [, index] = tail(referenceLocation);
viewSize = GridViewSizing.Split(index); viewSize = GridViewSizing.Split(index);
} else if (size === Sizing.Distribute) { } else if (size.type === 'distribute') {
viewSize = GridViewSizing.Distribute; viewSize = GridViewSizing.Distribute;
} else { } else {
viewSize = size; viewSize = size;
@@ -264,7 +284,7 @@ export class Grid<T extends IView = IView> extends Disposable {
} }
const location = this.getViewLocation(view); 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); this.views.delete(view);
} }
@@ -320,7 +340,7 @@ export class Grid<T extends IView = IView> extends Disposable {
} }
getViews(): GridBranchNode<T> { 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[] { 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); return getGridLocation(element);
} }
private doResetViewSize(location: number[]): void { private onDidSashReset(location: number[]): void {
const [parentLocation,] = tail(location); 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); this.gridview.distributeViewSizes(parentLocation);
} }
} }
@@ -379,6 +427,7 @@ export interface ISerializedLeafNode {
type: 'leaf'; type: 'leaf';
data: object | null; data: object | null;
size: number; size: number;
visible?: boolean;
} }
export interface ISerializedBranchNode { 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; const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
if (!isGridBranchNode(node)) { 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 }; 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.'); 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 const childBox: Box = orientation === Orientation.HORIZONTAL
? { top: box.top, left: box.left + offset, width: child.size, height: box.height } ? { top: box.top, left: box.left + offset, width: childSize, height: box.height }
: { top: box.top + offset, left: box.left, width: box.width, height: child.size }; : { top: box.top + offset, left: box.left, width: box.width, height: childSize };
children.push(SerializableGrid.deserializeNode(child, orthogonal(orientation), childBox, deserializer)); children.push(SerializableGrid.deserializeNode(child, orthogonal(orientation), childBox, deserializer));
offset += child.size; offset += childSize;
} }
return { children, box }; return { children, box };
} else if (json.type === 'leaf') { } else if (json.type === 'leaf') {
const view: T = deserializer.fromJSON(json.data); 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\'.'); 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)) { if (!isGridBranchNode(node)) {
return 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'); 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); const result = new SerializableGrid<T>(firstLeaf.view, options);
result.orientation = orientation; result.orientation = orientation;
result.restoreViews(firstLeaf.view, orientation, root); 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)); const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c));
for (let i = 1; i < firstLeaves.length; i++) { for (let i = 1; i < firstLeaves.length; i++) {
const size = orientation === Orientation.VERTICAL ? firstLeaves[i]!.box.height : firstLeaves[i]!.box.width; const node = firstLeaves[i];
this.addView(firstLeaves[i]!.view, size, referenceView, direction); const size: number | InvisibleSizing = typeof node.cachedVisibleSize === 'number'
referenceView = firstLeaves[i]!.view; ? 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++) { 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 { export interface GridLeafNode {
readonly view: IView; readonly view: IView;
readonly box: Box; readonly box: Box;
readonly cachedVisibleSize: number | undefined;
} }
export interface GridBranchNode { export interface GridBranchNode {
@@ -343,6 +344,14 @@ class BranchNode implements ISplitView, IDisposable {
this.splitview.setViewVisible(index, visible); 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 { private onDidChildrenChange(): void {
const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined); const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined);
this.childrenChangeDisposable.dispose(); this.childrenChangeDisposable.dispose();
@@ -424,6 +433,9 @@ class LeafNode implements ISplitView, IDisposable {
private _size: number = 0; private _size: number = 0;
get size(): number { return this._size; } get size(): number { return this._size; }
private _cachedVisibleSize: number | undefined;
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
private _orthogonalSize: number; private _orthogonalSize: number;
get orthogonalSize(): number { return this._orthogonalSize; } get orthogonalSize(): number { return this._orthogonalSize; }
@@ -528,6 +540,12 @@ class LeafNode implements ISplitView, IDisposable {
} }
setVisible(visible: boolean): void { setVisible(visible: boolean): void {
if (visible) {
this._cachedVisibleSize = undefined;
} else {
this._cachedVisibleSize = this._size;
}
if (this.view.setVisible) { if (this.view.setVisible) {
this.view.setVisible(visible); this.view.setVisible(visible);
} }
@@ -658,6 +676,14 @@ export class GridView implements IDisposable {
} else { } else {
const [, grandParent] = tail(pathToParent); const [, grandParent] = tail(pathToParent);
const [, parentIndex] = tail(rest); 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); grandParent.removeChild(parentIndex);
const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); 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); newParent.orthogonalLayout(parent.orthogonalSize);
const newSibling = new LeafNode(parent.view, grandParent.orientation, parent.size); 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') { if (typeof size !== 'number' && size.type === 'split') {
size = Sizing.Split(0); size = Sizing.Split(0);
@@ -877,13 +903,16 @@ export class GridView implements IDisposable {
parent.setChildVisible(index, visible); parent.setChildVisible(index, visible);
} }
getViews(): GridBranchNode { getView(): GridBranchNode;
return this._getViews(this.root, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }) as 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 { private _getViews(node: Node, orientation: Orientation, box: Box): GridNode {
if (node instanceof LeafNode) { if (node instanceof LeafNode) {
return { view: node.view, box }; return { view: node.view, box, cachedVisibleSize: node.cachedVisibleSize };
} }
const children: GridNode[] = []; const children: GridNode[] = [];
+1 -1
View File
@@ -236,7 +236,7 @@ class KeyboardController<T> implements IDisposable {
private view: ListView<T>, private view: ListView<T>,
options: IListOptions<T> options: IListOptions<T>
) { ) {
const multipleSelectionSupport = !(options.multipleSelectionSupport === false); const multipleSelectionSupport = options.multipleSelectionSupport !== false;
this.openController = options.openController || DefaultOpenController; this.openController = options.openController || DefaultOpenController;
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/scrollbars'; import 'vs/css!./media/scrollbars';
import { isEdgeOrIE } from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; 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._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; alt: boolean;
} }
type ViewItemSize = number | { cachedVisibleSize: number };
abstract class ViewItem { abstract class ViewItem {
private _size: number;
set size(size: number) { set size(size: number) {
this._size = size; this._size = size;
} }
@@ -69,10 +72,11 @@ abstract class ViewItem {
return this._size; return this._size;
} }
private cachedSize: number | undefined = undefined; private _cachedVisibleSize: number | undefined = undefined;
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
get visible(): boolean { get visible(): boolean {
return typeof this.cachedSize === 'undefined'; return typeof this._cachedVisibleSize === 'undefined';
} }
set visible(visible: boolean) { set visible(visible: boolean) {
@@ -81,10 +85,10 @@ abstract class ViewItem {
} }
if (visible) { if (visible) {
this.size = this.cachedSize!; this.size = clamp(this._cachedVisibleSize!, this.viewMinimumSize, this.viewMaximumSize);
this.cachedSize = undefined; this._cachedVisibleSize = undefined;
} else { } else {
this.cachedSize = this.size; this._cachedVisibleSize = this.size;
this.size = 0; this.size = 0;
} }
@@ -104,7 +108,20 @@ abstract class ViewItem {
get priority(): LayoutPriority | undefined { return this.view.priority; } get priority(): LayoutPriority | undefined { return this.view.priority; }
get snap(): boolean { return !!this.view.snap; } 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'); dom.addClass(container, 'visible');
} }
@@ -166,11 +183,13 @@ enum State {
export type DistributeSizing = { type: 'distribute' }; export type DistributeSizing = { type: 'distribute' };
export type SplitSizing = { type: 'split', index: number }; 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 namespace Sizing {
export const Distribute: DistributeSizing = { type: 'distribute' }; export const Distribute: DistributeSizing = { type: 'distribute' };
export function Split(index: number): SplitSizing { return { type: 'split', index }; } 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 { export class SplitView extends Disposable {
@@ -279,12 +298,14 @@ export class SplitView extends Disposable {
const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container)); const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));
const disposable = combinedDisposable(onChangeDisposable, containerDisposable); const disposable = combinedDisposable(onChangeDisposable, containerDisposable);
let viewSize: number; let viewSize: ViewItemSize;
if (typeof size === 'number') { if (typeof size === 'number') {
viewSize = size; viewSize = size;
} else if (size.type === 'split') { } else if (size.type === 'split') {
viewSize = this.getViewSize(size.index) / 2; viewSize = this.getViewSize(size.index) / 2;
} else if (size.type === 'invisible') {
viewSize = { cachedVisibleSize: size.cachedVisibleSize };
} else { } else {
viewSize = view.minimumSize; viewSize = view.minimumSize;
} }
@@ -315,7 +336,24 @@ export class SplitView extends Disposable {
const onChangeDisposable = onChange(this.onSashChange, this); const onChangeDisposable = onChange(this.onSashChange, this);
const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash));
const onEndDisposable = onEnd(this.onSashEnd, this); 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 disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash);
const sashItem: ISashItem = { sash, disposable }; const sashItem: ISashItem = { sash, disposable };
@@ -420,6 +458,15 @@ export class SplitView extends Disposable {
this.layoutViews(); 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 { layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize); const previousSize = Math.max(this.size, this.contentSize);
this.size = size; this.size = size;
@@ -600,10 +647,19 @@ export class SplitView extends Disposable {
} }
distributeViewSizes(): void { 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++) { for (const item of this.viewItems) {
const item = this.viewItems[i]; 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); 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( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IListVirtualDelegate<T>, delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[], renderers: ITreeRenderer<T, TFilterData, any>[],
private _options: IAbstractTreeOptions<T, TFilterData> = {} private _options: IAbstractTreeOptions<T, TFilterData> = {}
) { ) {
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate); 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 onDidFocus(): Event<void> { return this.tree.onDidFocus; }
get onDidBlur(): Event<void> { return this.tree.onDidBlur; } 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; } get onDidUpdateOptions(): Event<IAsyncDataTreeOptionsUpdate> { return this.tree.onDidUpdateOptions; }
@@ -340,7 +340,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
constructor( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IListVirtualDelegate<T>, delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[], renderers: ITreeRenderer<T, TFilterData, any>[],
private dataSource: IAsyncDataSource<TInput, T>, private dataSource: IAsyncDataSource<TInput, T>,
options: IAsyncDataTreeOptions<T, TFilterData> = {} 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 { Iterator, ISequence } from 'vs/base/common/iterator';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; 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> { export interface ICompressedTreeElement<T> extends ITreeElement<T> {
readonly children?: Iterator<ICompressedTreeElement<T>> | ICompressedTreeElement<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; readonly rootRef = null;
@@ -95,7 +95,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
get size(): number { return this.nodes.size; } 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); this.model = new ObjectTreeModel(list, options);
} }
@@ -289,11 +289,11 @@ function createNodeMapper<T, TFilterData>(elementMapper: ElementMapper<T>): Node
return node => mapNode(elementMapper, 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>; 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; readonly rootRef = null;
@@ -317,15 +317,25 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
private mapElement: ElementMapper<T | null>; private mapElement: ElementMapper<T | null>;
private mapNode: NodeMapper<T | null, TFilterData>; private mapNode: NodeMapper<T | null, TFilterData>;
private model: CompressedObjectTreeModel<T, TFilterData>; private model: CompressedTreeModel<T, TFilterData>;
constructor( constructor(
list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
options: ILinearCompressedObjectTreeModelOptions<T, TFilterData> = {} options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}
) { ) {
this.mapElement = options.elementMapper || DefaultElementMapper; this.mapElement = options.elementMapper || DefaultElementMapper;
this.mapNode = createNodeMapper(this.mapElement); 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 { getListIndex(location: T | null): number {
@@ -401,4 +411,8 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
refilter(): void { refilter(): void {
return this.model.refilter(); 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( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IListVirtualDelegate<T>, delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[], renderers: ITreeRenderer<T, TFilterData, any>[],
private dataSource: IDataSource<TInput, T>, private dataSource: IDataSource<TInput, T>,
options: IDataTreeOptions<T, TFilterData> = {} options: IDataTreeOptions<T, TFilterData> = {}
) { ) {
+1 -1
View File
@@ -20,7 +20,7 @@ export class IndexTree<T, TFilterData = void> extends AbstractTree<T, TFilterDat
constructor( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IListVirtualDelegate<T>, delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[], renderers: ITreeRenderer<T, TFilterData, any>[],
private rootElement: T, private rootElement: T,
options: IIndexTreeOptions<T, TFilterData> = {} 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 { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence'; import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; 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 { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event'; 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> { 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( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IListVirtualDelegate<T>, delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[], renderers: ITreeRenderer<T, TFilterData, any>[],
options: IObjectTreeOptions<T, TFilterData> = {} options: IObjectTreeOptions<T, TFilterData> = {}
) { ) {
super(container, delegate, renderers, options); super(container, delegate, renderers, options);
@@ -32,11 +32,9 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
setChildren( setChildren(
element: T | null, element: T | null,
children?: ISequence<ITreeElement<T>>, children?: ISequence<ITreeElement<T>>
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> { ): Iterator<ITreeElement<T | null>> {
return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode); return this.model.setChildren(element, children);
} }
rerender(element?: T): void { 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 { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; 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> { export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>; readonly sorter?: ITreeSorter<T>;
readonly identityProvider?: IIdentityProvider<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; readonly rootRef = null;
@@ -51,8 +58,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
setChildren( setChildren(
element: T | null, element: T | null,
children: ISequence<ITreeElement<T>> | undefined, children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void, onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
): Iterator<ITreeElement<T>> { ): Iterator<ITreeElement<T>> {
const location = this.getElementLocation(element); const location = this.getElementLocation(element);
return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode); 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( private _setChildren(
location: number[], location: number[],
children: ISequence<ITreeElement<T>> | undefined, children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void, onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
): Iterator<ITreeElement<T>> { ): Iterator<ITreeElement<T>> {
const insertedElements = new Set<T | null>(); const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>(); const insertedElementIds = new Set<string>();
+14
View File
@@ -326,6 +326,20 @@ export namespace Event {
return result.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> { export function fromPromise<T = any>(promise: Promise<T>): Event<undefined> {
const emitter = new Emitter<undefined>(); const emitter = new Emitter<undefined>();
let shouldEmit = false; let shouldEmit = false;
+1 -1
View File
@@ -414,7 +414,7 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] {
return res; return res;
} }
const _maxLen = 53; const _maxLen = 128;
function initTable() { function initTable() {
const table: number[][] = []; const table: number[][] = [];
-1
View File
@@ -334,7 +334,6 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
if (!extpath.isEqualOrParent(path, arg2.base)) { if (!extpath.isEqualOrParent(path, arg2.base)) {
return null; return null;
} }
return parsedPattern(paths.relative(arg2.base, path), basename); 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); let code = text.charCodeAt(pos);
// trivia: whitespace // trivia: whitespace
if (isWhiteSpace(code)) { if (isWhitespace(code)) {
do { do {
pos++; pos++;
value += String.fromCharCode(code); value += String.fromCharCode(code);
code = text.charCodeAt(pos); code = text.charCodeAt(pos);
} while (isWhiteSpace(code)); } while (isWhitespace(code));
return token = SyntaxKind.Trivia; return token = SyntaxKind.Trivia;
} }
@@ -517,7 +517,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
} }
function isUnknownContentCharacter(code: CharacterCodes) { function isUnknownContentCharacter(code: CharacterCodes) {
if (isWhiteSpace(code) || isLineBreak(code)) { if (isWhitespace(code) || isLineBreak(code)) {
return false; return false;
} }
switch (code) { 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 || 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.nonBreakingSpace || ch === CharacterCodes.ogham || ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace ||
ch === CharacterCodes.narrowNoBreakSpace || ch === CharacterCodes.mathematicalSpace || ch === CharacterCodes.ideographicSpace || ch === CharacterCodes.byteOrderMark; 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 () { test('empty gridview is empty', function () {
assert.deepEqual(nodesToArrays(gridview.getViews()), []); assert.deepEqual(nodesToArrays(gridview.getView()), []);
gridview.dispose(); gridview.dispose();
}); });
@@ -43,7 +43,7 @@ suite('Gridview', function () {
gridview.addView(views[1], 200, [1]); gridview.addView(views[1], 200, [1]);
gridview.addView(views[2], 200, [2]); gridview.addView(views[2], 200, [2]);
assert.deepEqual(nodesToArrays(gridview.getViews()), views); assert.deepEqual(nodesToArrays(gridview.getView()), views);
gridview.dispose(); 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[])[0] as IView, 200, [1]);
gridview.addView((views[1] as TestView[])[1] as IView, 200, [1, 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(); gridview.dispose();
}); });
@@ -71,35 +71,35 @@ suite('Gridview', function () {
const view1 = new TestView(20, 20, 20, 20); const view1 = new TestView(20, 20, 20, 20);
gridview.addView(view1 as IView, 200, [0]); 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); const view2 = new TestView(20, 20, 20, 20);
gridview.addView(view2 as IView, 200, [1]); 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); const view3 = new TestView(20, 20, 20, 20);
gridview.addView(view3 as IView, 200, [1, 0]); 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); const view4 = new TestView(20, 20, 20, 20);
gridview.addView(view4 as IView, 200, [1, 0, 0]); 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); const view5 = new TestView(20, 20, 20, 20);
gridview.addView(view5 as IView, 200, [1, 0]); 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); const view6 = new TestView(20, 20, 20, 20);
gridview.addView(view6 as IView, 200, [2]); 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); const view7 = new TestView(20, 20, 20, 20);
gridview.addView(view7 as IView, 200, [1, 1]); 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); const view8 = new TestView(20, 20, 20, 20);
gridview.addView(view8 as IView, 200, [1, 1, 0]); 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(); gridview.dispose();
}); });
+2 -1
View File
@@ -5,7 +5,8 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { Emitter, Event } from 'vs/base/common/event'; 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 { export class TestView implements IView {
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; 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 { Iterator } from 'vs/base/common/iterator';
import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence'; import { ISpliceable } from 'vs/base/common/sequence';
@@ -305,7 +305,7 @@ suite('CompressedObjectTree', function () {
test('ctor', () => { test('ctor', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = []; const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list)); const model = new CompressedTreeModel<number>(toSpliceable(list));
assert(model); assert(model);
assert.equal(list.length, 0); assert.equal(list.length, 0);
assert.equal(model.size, 0); assert.equal(model.size, 0);
@@ -313,7 +313,7 @@ suite('CompressedObjectTree', function () {
test('flat', () => { test('flat', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = []; const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list)); const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([ model.setChildren(null, Iterator.fromArray([
{ element: 0 }, { element: 0 },
@@ -340,7 +340,7 @@ suite('CompressedObjectTree', function () {
test('nested', () => { test('nested', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = []; const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list)); const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([ model.setChildren(null, Iterator.fromArray([
{ {
@@ -376,7 +376,7 @@ suite('CompressedObjectTree', function () {
test('compressed', () => { test('compressed', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = []; const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>(toSpliceable(list)); const model = new CompressedTreeModel<number>(toSpliceable(list));
model.setChildren(null, Iterator.fromArray([ 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', () => { test('List highlight filter: Not all characters from match are highlighterd #66923', () => {
assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); 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 { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc';
import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc'; import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; 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 { DownloadService } from 'vs/platform/download/common/downloadService';
import { IDownloadService } from 'vs/platform/download/common/download'; import { IDownloadService } from 'vs/platform/download/common/download';
import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; 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 (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) {
if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) {
appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); 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 = { const config: ITelemetryServiceConfig = {
appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), 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 { URLService } from 'vs/platform/url/common/urlService';
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; 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 { VSBuffer } from 'vs/base/common/buffer';
import { statSync } from 'fs'; import { statSync } from 'fs';
import { ISignService } from 'vs/platform/sign/common/sign'; import { ISignService } from 'vs/platform/sign/common/sign';
@@ -167,9 +167,11 @@ export class CodeApplication extends Disposable {
event.preventDefault(); event.preventDefault();
}); });
app.on('remote-get-current-web-contents', event => { app.on('remote-get-current-web-contents', event => {
this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); // The driver needs access to web contents
if (!this.environmentService.args.driver) {
event.preventDefault(); this.logService.trace(`App#on(remote-get-current-web-contents): prevented`);
event.preventDefault();
}
}); });
app.on('web-contents-created', (_event: Electron.Event, contents) => { app.on('web-contents-created', (_event: Electron.Event, contents) => {
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
@@ -708,7 +710,7 @@ export class CodeApplication extends Disposable {
const options: IConnectionOptions = { const options: IConnectionOptions = {
isBuilt, isBuilt,
commit: product.commit, commit: product.commit,
webSocketFactory: nodeWebSocketFactory, socketFactory: nodeSocketFactory,
addressProvider: { addressProvider: {
getAddress: () => { getAddress: () => {
return Promise.resolve({ host, port }); return Promise.resolve({ host, port });
+14 -5
View File
@@ -86,7 +86,7 @@ export class Main {
await this.setInstallSource(argv['install-source']); await this.setInstallSource(argv['install-source']);
} else if (argv['list-extensions']) { } else if (argv['list-extensions']) {
await this.listExtensions(!!argv['show-versions']); await this.listExtensions(!!argv['show-versions'], argv['category']);
} else if (argv['install-extension']) { } else if (argv['install-extension']) {
const arg = argv['install-extension']; const arg = argv['install-extension'];
@@ -110,8 +110,17 @@ export class Main {
return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30)); return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
} }
private async listExtensions(showVersions: boolean): Promise<void> { private async listExtensions(showVersions: boolean, category?: string): Promise<void> {
const extensions = await this.extensionManagementService.getInstalled(ExtensionType.User); 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))); 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])); services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
// Dispose the AI adapter so that remaining data gets flushed.
disposables.add(combinedAppender(...appenders));
} else { } else {
services.set(ITelemetryService, NullTelemetryService); services.set(ITelemetryService, NullTelemetryService);
} }
@@ -356,6 +363,8 @@ export async function main(argv: ParsedArgs): Promise<void> {
try { try {
await main.run(argv); await main.run(argv);
// Flush the remaining data in AI adapter.
await combinedAppender(...appenders).flush();
} finally { } finally {
disposables.dispose(); disposables.dispose();
} }
@@ -123,7 +123,7 @@ export class MouseHandler extends ViewEventHandler {
e.stopPropagation(); 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); 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 { 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 { 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 ServicesAccessor = ServicesAccessor;
export type IEditorContributionCtor = IConstructorSignature1<ICodeEditor, IEditorContribution>; export type IEditorContributionCtor = IConstructorSignature1<ICodeEditor, IEditorContribution>;
export type EditorTelemetryDataFragment = {
target: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
snippet: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
//#region Command //#region Command
@@ -219,16 +223,15 @@ export abstract class EditorAction extends EditorCommand {
} }
protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) { protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) {
/* __GDPR__ type EditorActionInvokedClassification = {
"editorActionInvoked" : { name: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
"name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
"id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, };
"${include}": [ type EditorActionInvokedEvent = {
"${EditorTelemetryData}" name: string;
] id: string;
} };
*/ accessor.get(ITelemetryService).publicLog2<EditorActionInvokedEvent, EditorActionInvokedClassification>('editorActionInvoked', { name: this.label, id: this.id });
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id, ...editor.getTelemetryData() });
} }
public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void>; 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); return this._codeEditorService.resolveDecorationOptions(typeKey, writable);
} }
/* __GDPR__FRAGMENT__
"EditorTelemetryData" : {}
*/
public getTelemetryData(): { [key: string]: any; } | undefined { public getTelemetryData(): { [key: string]: any; } | undefined {
return this._telemetryData; return this._telemetryData;
} }
@@ -1851,4 +1848,6 @@ registerThemingParticipant((theme, collector) => {
if (unnecessaryBorder) { if (unnecessaryBorder) {
collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryDecoration} { border-bottom: 2px dashed ${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; }`);
}); });
@@ -40,3 +40,9 @@
position: absolute; position: absolute;
top: 0; top: 0;
} }
/*
.monaco-editor .auto-closed-character {
opacity: 0.3;
}
*/
@@ -696,7 +696,7 @@ const editorConfiguration: IConfigurationNode = {
}, },
'editor.suggest.filteredTypes': { 'editor.suggest.filteredTypes': {
type: 'object', 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."), 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: { properties: {
method: { method: {
@@ -904,7 +904,7 @@ const editorConfiguration: IConfigurationNode = {
'enum': ['none', 'boundary', 'selection', 'all'], 'enum': ['none', 'boundary', 'selection', 'all'],
'enumDescriptions': [ '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."), 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 { 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 { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; 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 { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon'; 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 { RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
import * as viewEvents from 'vs/editor/common/view/viewEvents'; import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { dispose } from 'vs/base/common/lifecycle';
function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean { function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean {
for (let i = 0, len = events.length; i < len; i++) { 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 { export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
public static MAX_CURSOR_COUNT = 10000; public static MAX_CURSOR_COUNT = 10000;
@@ -106,6 +165,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
private _isHandling: boolean; private _isHandling: boolean;
private _isDoingComposition: boolean; private _isDoingComposition: boolean;
private _columnSelectData: IColumnSelectData | null; private _columnSelectData: IColumnSelectData | null;
private _autoClosedActions: AutoClosedAction[];
private _prevEditOperationType: EditOperationType; private _prevEditOperationType: EditOperationType;
constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: IViewModel) { constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: IViewModel) {
@@ -120,6 +180,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._isHandling = false; this._isHandling = false;
this._isDoingComposition = false; this._isDoingComposition = false;
this._columnSelectData = null; this._columnSelectData = null;
this._autoClosedActions = [];
this._prevEditOperationType = EditOperationType.Other; this._prevEditOperationType = EditOperationType.Other;
this._register(this._model.onDidChangeRawContent((e) => { this._register(this._model.onDidChangeRawContent((e) => {
@@ -173,9 +234,24 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
public dispose(): void { public dispose(): void {
this._cursors.dispose(); this._cursors.dispose();
this._autoClosedActions = dispose(this._autoClosedActions);
super.dispose(); 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 // ------ some getters/setters
public getPrimaryCursor(): CursorState { public getPrimaryCursor(): CursorState {
@@ -202,6 +278,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.normalize(); this._cursors.normalize();
this._columnSelectData = null; this._columnSelectData = null;
this._validateAutoClosedActions();
this._emitStateChangedIfNecessary(source, reason, oldState); this._emitStateChangedIfNecessary(source, reason, oldState);
} }
@@ -296,7 +374,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// a model.setValue() was called // a model.setValue() was called
this._cursors.dispose(); this._cursors.dispose();
this._cursors = new CursorCollection(this.context); this._cursors = new CursorCollection(this.context);
this._validateAutoClosedActions();
this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null); this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null);
} else { } else {
const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); const selectionsFromMarkers = this._cursors.readSelectionFromMarkers();
@@ -367,6 +445,35 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// The commands were applied correctly // The commands were applied correctly
this._interpretCommandResult(result); 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; this._prevEditOperationType = opResult.type;
} }
@@ -540,6 +647,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.startTrackingSelections(); this._cursors.startTrackingSelections();
} }
this._validateAutoClosedActions();
if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) { if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) {
this._revealRange(RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); 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); 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 // 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 { } else {
@@ -13,7 +13,7 @@ import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationT
import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection'; 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 { ITextModel } from 'vs/editor/common/model';
import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
@@ -430,7 +430,7 @@ export class TypeOperations {
return null; 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; const autoCloseConfig = isQuote(ch) ? config.autoClosingQuotes : config.autoClosingBrackets;
if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) { if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) {
@@ -461,6 +461,19 @@ export class TypeOperations {
return false; 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; return true;
@@ -573,7 +586,7 @@ export class TypeOperations {
for (let i = 0, len = selections.length; i < len; i++) { for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i]; const selection = selections[i];
const closeCharacter = config.autoClosingPairsOpen[ch]; 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, { return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: true, 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') { if (ch === '\n') {
let commands: ICommand[] = []; 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); return this._runAutoClosingCloseCharType(prevEditOperationType, config, model, selections, ch);
} }
@@ -923,3 +936,24 @@ export class TypeOperations {
return commands; 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; 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. * A reunion of the two ranges.
* The smallest position will be used as the start point, and the largest one as the end point. * 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', EditorWarningDecoration = 'squiggly-warning',
EditorErrorDecoration = 'squiggly-error', EditorErrorDecoration = 'squiggly-error',
EditorUnnecessaryDecoration = 'squiggly-unnecessary', EditorUnnecessaryDecoration = 'squiggly-unnecessary',
EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary' EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary',
EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated'
} }
export const enum NodeColor { export const enum NodeColor {
@@ -545,6 +545,12 @@ export class Searcher {
const matchStartIndex = m.index; const matchStartIndex = m.index;
const matchLength = m[0].length; const matchLength = m[0].length;
if (matchStartIndex === this._prevMatchStartIndex && matchLength === this._prevMatchLength) { 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 // Exit early if the regex matches the same range twice
return null; return null;
} }
@@ -221,6 +221,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) { if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration; inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
} }
if (marker.tags.indexOf(MarkerTag.Deprecated) !== -1) {
inlineClassName = ClassName.EditorDeprecatedInlineDecoration;
}
} }
return { return {
@@ -7,7 +7,8 @@
export enum MarkerTag { export enum MarkerTag {
Unnecessary = 1 Unnecessary = 1,
Deprecated = 2
} }
export enum MarkerSeverity { export enum MarkerSeverity {
@@ -148,12 +148,6 @@ class ExecCommandCopyAction extends ExecCommandAction {
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
return; 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); super.run(accessor, editor);
} }
@@ -13,7 +13,7 @@ import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons'; import 'vs/css!./media/symbol-icons';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; 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 { localize } from 'vs/nls';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -38,7 +38,7 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> { export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
getId(element: TreeElement): { toString(): string; } { getId(element: OutlineItem): { toString(): string; } {
return element.id; return element.id;
} }
} }
+12 -2
View File
@@ -112,7 +112,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
searchScope: null, searchScope: null,
matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, false), matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, false),
wholeWord: this._storageService.getBoolean('editor.wholeWord', 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); }, false);
if (shouldRestartFind) { if (shouldRestartFind) {
@@ -170,13 +171,17 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
if (e.matchCase) { if (e.matchCase) {
this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE); 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() { private loadQueryState() {
this._state.change({ this._state.change({
matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, this._state.matchCase), matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, this._state.matchCase),
wholeWord: this._storageService.getBoolean('editor.wholeWord', StorageScope.WORKSPACE, this._state.wholeWord), 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); }, 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 { public toggleSearchScope(): void {
if (this._state.searchScope) { if (this._state.searchScope) {
this._state.change({ searchScope: null }, true); this._state.change({ searchScope: null }, true);
+8 -5
View File
@@ -59,6 +59,7 @@ export const FIND_IDS = {
ToggleWholeWordCommand: 'toggleFindWholeWord', ToggleWholeWordCommand: 'toggleFindWholeWord',
ToggleRegexCommand: 'toggleFindRegex', ToggleRegexCommand: 'toggleFindRegex',
ToggleSearchScopeCommand: 'toggleFindInSelection', ToggleSearchScopeCommand: 'toggleFindInSelection',
TogglePreserveCaseCommand: 'togglePreserveCase',
ReplaceOneAction: 'editor.action.replaceOne', ReplaceOneAction: 'editor.action.replaceOne',
ReplaceAllAction: 'editor.action.replaceAll', ReplaceAllAction: 'editor.action.replaceAll',
SelectAllMatchesAction: 'editor.action.selectAllMatches' SelectAllMatchesAction: 'editor.action.selectAllMatches'
@@ -416,11 +417,11 @@ export class FindModelBoundToEditorModel {
let replacePattern = this._getReplacePattern(); let replacePattern = this._getReplacePattern();
let selection = this._editor.getSelection(); 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 (nextMatch) {
if (selection.equalsRange(nextMatch.range)) { if (selection.equalsRange(nextMatch.range)) {
// selection sits on a find match => replace it! // 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); let command = new ReplaceCommand(selection, replaceString);
@@ -482,12 +483,14 @@ export class FindModelBoundToEditorModel {
const replacePattern = this._getReplacePattern(); const replacePattern = this._getReplacePattern();
let resultText: string; let resultText: string;
const preserveCase = this._state.preserveCase;
if (replacePattern.hasReplacementPatterns) { if (replacePattern.hasReplacementPatterns) {
resultText = modelText.replace(searchRegex, function () { resultText = modelText.replace(searchRegex, function () {
return replacePattern.buildReplaceString(<string[]><any>arguments); return replacePattern.buildReplaceString(<string[]><any>arguments, preserveCase);
}); });
} else { } 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()); let command = new ReplaceCommandThatPreservesSelection(fullModelRange, resultText, this._editor.getSelection());
@@ -501,7 +504,7 @@ export class FindModelBoundToEditorModel {
let replaceStrings: string[] = []; let replaceStrings: string[] = [];
for (let i = 0, len = matches.length; i < len; i++) { 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); 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; isRegex: boolean;
wholeWord: boolean; wholeWord: boolean;
matchCase: boolean; matchCase: boolean;
preserveCase: boolean;
searchScope: boolean; searchScope: boolean;
matchesPosition: boolean; matchesPosition: boolean;
matchesCount: boolean; matchesCount: boolean;
@@ -41,6 +42,8 @@ export interface INewFindReplaceState {
wholeWordOverride?: FindOptionOverride; wholeWordOverride?: FindOptionOverride;
matchCase?: boolean; matchCase?: boolean;
matchCaseOverride?: FindOptionOverride; matchCaseOverride?: FindOptionOverride;
preserveCase?: boolean;
preserveCaseOverride?: FindOptionOverride;
searchScope?: Range | null; searchScope?: Range | null;
} }
@@ -65,6 +68,8 @@ export class FindReplaceState implements IDisposable {
private _wholeWordOverride: FindOptionOverride; private _wholeWordOverride: FindOptionOverride;
private _matchCase: boolean; private _matchCase: boolean;
private _matchCaseOverride: FindOptionOverride; private _matchCaseOverride: FindOptionOverride;
private _preserveCase: boolean;
private _preserveCaseOverride: FindOptionOverride;
private _searchScope: Range | null; private _searchScope: Range | null;
private _matchesPosition: number; private _matchesPosition: number;
private _matchesCount: 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 isRegex(): boolean { return effectiveOptionValue(this._isRegexOverride, this._isRegex); }
public get wholeWord(): boolean { return effectiveOptionValue(this._wholeWordOverride, this._wholeWord); } public get wholeWord(): boolean { return effectiveOptionValue(this._wholeWordOverride, this._wholeWord); }
public get matchCase(): boolean { return effectiveOptionValue(this._matchCaseOverride, this._matchCase); } 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 actualIsRegex(): boolean { return this._isRegex; }
public get actualWholeWord(): boolean { return this._wholeWord; } public get actualWholeWord(): boolean { return this._wholeWord; }
public get actualMatchCase(): boolean { return this._matchCase; } public get actualMatchCase(): boolean { return this._matchCase; }
public get actualPreserveCase(): boolean { return this._preserveCase; }
public get searchScope(): Range | null { return this._searchScope; } public get searchScope(): Range | null { return this._searchScope; }
public get matchesPosition(): number { return this._matchesPosition; } public get matchesPosition(): number { return this._matchesPosition; }
@@ -100,6 +107,8 @@ export class FindReplaceState implements IDisposable {
this._wholeWordOverride = FindOptionOverride.NotSet; this._wholeWordOverride = FindOptionOverride.NotSet;
this._matchCase = false; this._matchCase = false;
this._matchCaseOverride = FindOptionOverride.NotSet; this._matchCaseOverride = FindOptionOverride.NotSet;
this._preserveCase = false;
this._preserveCaseOverride = FindOptionOverride.NotSet;
this._searchScope = null; this._searchScope = null;
this._matchesPosition = 0; this._matchesPosition = 0;
this._matchesCount = 0; this._matchesCount = 0;
@@ -120,6 +129,7 @@ export class FindReplaceState implements IDisposable {
isRegex: false, isRegex: false,
wholeWord: false, wholeWord: false,
matchCase: false, matchCase: false,
preserveCase: false,
searchScope: false, searchScope: false,
matchesPosition: false, matchesPosition: false,
matchesCount: false, matchesCount: false,
@@ -169,6 +179,7 @@ export class FindReplaceState implements IDisposable {
isRegex: false, isRegex: false,
wholeWord: false, wholeWord: false,
matchCase: false, matchCase: false,
preserveCase: false,
searchScope: false, searchScope: false,
matchesPosition: false, matchesPosition: false,
matchesCount: false, matchesCount: false,
@@ -179,6 +190,7 @@ export class FindReplaceState implements IDisposable {
const oldEffectiveIsRegex = this.isRegex; const oldEffectiveIsRegex = this.isRegex;
const oldEffectiveWholeWords = this.wholeWord; const oldEffectiveWholeWords = this.wholeWord;
const oldEffectiveMatchCase = this.matchCase; const oldEffectiveMatchCase = this.matchCase;
const oldEffectivePreserveCase = this.preserveCase;
if (typeof newState.searchString !== 'undefined') { if (typeof newState.searchString !== 'undefined') {
if (this._searchString !== newState.searchString) { if (this._searchString !== newState.searchString) {
@@ -217,6 +229,9 @@ export class FindReplaceState implements IDisposable {
if (typeof newState.matchCase !== 'undefined') { if (typeof newState.matchCase !== 'undefined') {
this._matchCase = newState.matchCase; this._matchCase = newState.matchCase;
} }
if (typeof newState.preserveCase !== 'undefined') {
this._preserveCase = newState.preserveCase;
}
if (typeof newState.searchScope !== 'undefined') { if (typeof newState.searchScope !== 'undefined') {
if (!Range.equalsRange(this._searchScope, newState.searchScope)) { if (!Range.equalsRange(this._searchScope, newState.searchScope)) {
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._isRegexOverride = (typeof newState.isRegexOverride !== 'undefined' ? newState.isRegexOverride : FindOptionOverride.NotSet);
this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet); this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet);
this._matchCaseOverride = (typeof newState.matchCaseOverride !== 'undefined' ? newState.matchCaseOverride : 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) { if (oldEffectiveIsRegex !== this.isRegex) {
somethingChanged = true; somethingChanged = true;
@@ -243,6 +259,11 @@ export class FindReplaceState implements IDisposable {
changeEvent.matchCase = true; changeEvent.matchCase = true;
} }
if (oldEffectivePreserveCase !== this.preserveCase) {
somethingChanged = true;
changeEvent.preserveCase = true;
}
if (somethingChanged) { if (somethingChanged) {
this._onFindReplaceStateChange.fire(changeEvent); this._onFindReplaceStateChange.fire(changeEvent);
} }
+21
View File
@@ -39,6 +39,11 @@
transition: top 200ms linear; transition: top 200ms linear;
padding: 0 4px; padding: 0 4px;
} }
.monaco-editor .find-widget.hiddenEditor {
display: none;
}
/* Find widget when replace is toggled on */ /* Find widget when replace is toggled on */
.monaco-editor .find-widget.replaceToggled { .monaco-editor .find-widget.replaceToggled {
top: -74px; /* find input height + replace input height + shadow (10px) */ top: -74px; /* find input height + replace input height + shadow (10px) */
@@ -79,6 +84,15 @@
height: 25px; 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 > .find-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input { .monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
padding-top: 2px; padding-top: 2px;
@@ -224,12 +238,19 @@
} }
.monaco-editor .find-widget > .replace-part > .replace-input { .monaco-editor .find-widget > .replace-part > .replace-input {
position: relative;
display: flex; display: flex;
display: -webkit-flex; display: -webkit-flex;
vertical-align: middle; vertical-align: middle;
width: auto !important; width: auto !important;
} }
.monaco-editor .find-widget > .replace-part > .replace-input > .controls {
position: absolute;
top: 3px;
right: 2px;
}
/* REDUCED */ /* REDUCED */
.monaco-editor .find-widget.reduced-find-widget .matchesCount, .monaco-editor .find-widget.reduced-find-widget .matchesCount,
.monaco-editor .find-widget.reduced-find-widget .monaco-checkbox { .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 { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget'; 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 { Delayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors'; 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_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace"); const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace");
const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.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_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All"); 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"); 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 _nextBtn: SimpleButton;
private _toggleSelectionFind: SimpleCheckbox; private _toggleSelectionFind: SimpleCheckbox;
private _closeBtn: SimpleButton; private _closeBtn: SimpleButton;
private _preserveCase: Checkbox;
private _replaceBtn: SimpleButton; private _replaceBtn: SimpleButton;
private _replaceAllBtn: SimpleButton; private _replaceAllBtn: SimpleButton;
@@ -590,14 +593,26 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}; };
this._findInput.style(inputStyles); this._findInput.style(inputStyles);
this._replaceInputBox.style(inputStyles); this._replaceInputBox.style(inputStyles);
this._preserveCase.style(inputStyles);
} }
private _tryUpdateWidgetWidth() { private _tryUpdateWidgetWidth() {
if (!this._isVisible) { if (!this._isVisible) {
return; 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 collapsedFindWidget = false;
let reducedFindWidget = false; let reducedFindWidget = false;
let narrowFindWidget = 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._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 // Replace one button
this._replaceBtn = this._register(new SimpleButton({ this._replaceBtn = this._register(new SimpleButton({
label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), 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'); let replacePart = document.createElement('div');
replacePart.className = 'replace-part'; replacePart.className = 'replace-part';
replacePart.appendChild(replaceInput); replacePart.appendChild(replaceInput);
+16 -2
View File
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { containsUppercaseCharacter } from 'vs/base/common/strings';
const enum ReplacePatternKind { const enum ReplacePatternKind {
StaticValue = 0, 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) { 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 = ''; let result = '';
@@ -41,7 +41,8 @@ export abstract class SimpleFindWidget extends Widget {
@IContextViewService private readonly _contextViewService: IContextViewService, @IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService, @IContextKeyService contextKeyService: IContextKeyService,
private readonly _state: FindReplaceState = new FindReplaceState(), private readonly _state: FindReplaceState = new FindReplaceState(),
showOptionButtons?: boolean showOptionButtons?: boolean,
private readonly _invertDefaultDirection: boolean = false
) { ) {
super(); super();
@@ -93,13 +94,13 @@ export abstract class SimpleFindWidget extends Widget {
this._register(this._findInput.onKeyDown((e) => { this._register(this._findInput.onKeyDown((e) => {
if (e.equals(KeyCode.Enter)) { if (e.equals(KeyCode.Enter)) {
this.find(false); this.find(this._invertDefaultDirection);
e.preventDefault(); e.preventDefault();
return; return;
} }
if (e.equals(KeyMod.Shift | KeyCode.Enter)) { if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this.find(true); this.find(!this._invertDefaultDirection);
e.preventDefault(); e.preventDefault();
return; return;
} }
@@ -153,4 +153,26 @@ suite('Replace Pattern test', () => {
let actual = replacePattern.buildReplaceString(matches); let actual = replacePattern.buildReplaceString(matches);
assert.equal(actual, 'a{}'); 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 { private shouldIgnoreLine(model: ITextModel, lineNumber: number): boolean {
model.forceTokenization(lineNumber); model.forceTokenization(lineNumber);
let nonWhiteSpaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber); let nonWhitespaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
if (nonWhiteSpaceColumn === 0) { if (nonWhitespaceColumn === 0) {
return true; return true;
} }
let tokens = model.getLineTokens(lineNumber); let tokens = model.getLineTokens(lineNumber);
if (tokens.getCount() > 0) { if (tokens.getCount() > 0) {
let firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhiteSpaceColumn); let firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhitespaceColumn);
if (firstNonWhitespaceTokenIndex >= 0 && tokens.getStandardTokenType(firstNonWhitespaceTokenIndex) === StandardTokenType.Comment) { if (firstNonWhitespaceTokenIndex >= 0 && tokens.getStandardTokenType(firstNonWhitespaceTokenIndex) === StandardTokenType.Comment) {
return true; return true;
} }
@@ -36,10 +36,17 @@ export class CommitCharacterController {
private _onItem(selected: ISelectedSuggestion | undefined): void { private _onItem(selected: ISelectedSuggestion | undefined): void {
if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) { if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) {
// no item or no commit characters
this.reset(); this.reset();
return; 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(); const acceptCharacters = new CharacterSet();
for (const ch of selected.item.completion.commitCharacters) { for (const ch of selected.item.completion.commitCharacters) {
if (ch.length > 0) { if (ch.length > 0) {
+17 -44
View File
@@ -50,7 +50,7 @@ interface ISuggestionTemplateData {
iconLabel: IconLabel; iconLabel: IconLabel;
typeLabel: HTMLElement; typeLabel: HTMLElement;
readMore: HTMLElement; readMore: HTMLElement;
disposables: IDisposable[]; disposables: DisposableStore;
} }
/** /**
@@ -106,8 +106,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
renderTemplate(container: HTMLElement): ISuggestionTemplateData { renderTemplate(container: HTMLElement): ISuggestionTemplateData {
const data = <ISuggestionTemplateData>Object.create(null); const data = <ISuggestionTemplateData>Object.create(null);
const disposables = new DisposableStore(); data.disposables = new DisposableStore();
data.disposables = [disposables];
data.root = container; data.root = container;
addClass(data.root, 'show-file-icons'); addClass(data.root, 'show-file-icons');
@@ -119,7 +118,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
const main = append(text, $('.main')); const main = append(text, $('.main'));
data.iconLabel = new IconLabel(main, { supportHighlights: true }); data.iconLabel = new IconLabel(main, { supportHighlights: true });
disposables.add(data.iconLabel); data.disposables.add(data.iconLabel);
data.typeLabel = append(main, $('span.type-label')); data.typeLabel = append(main, $('span.type-label'));
@@ -147,7 +146,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
configureFont(); 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) .filter(e => e.fontInfo || e.contribInfo)
.on(configureFont, null)); .on(configureFont, null));
@@ -218,7 +217,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
} }
disposeTemplate(templateData: ISuggestionTemplateData): void { disposeTemplate(templateData: ISuggestionTemplateData): void {
templateData.disposables = dispose(templateData.disposables); templateData.disposables.dispose();
} }
} }
@@ -253,7 +252,7 @@ class SuggestionDetails {
private type: HTMLElement; private type: HTMLElement;
private docs: HTMLElement; private docs: HTMLElement;
private ariaLabel: string | null; private ariaLabel: string | null;
private disposables: IDisposable[]; private readonly disposables: DisposableStore;
private renderDisposeable: IDisposable; private renderDisposeable: IDisposable;
private borderWidth: number = 1; private borderWidth: number = 1;
@@ -264,16 +263,16 @@ class SuggestionDetails {
private readonly markdownRenderer: MarkdownRenderer, private readonly markdownRenderer: MarkdownRenderer,
private readonly triggerKeybindingLabel: string, private readonly triggerKeybindingLabel: string,
) { ) {
this.disposables = []; this.disposables = new DisposableStore();
this.el = append(container, $('.details')); 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.body = $('.body');
this.scrollbar = new DomScrollableElement(this.body, {}); this.scrollbar = new DomScrollableElement(this.body, {});
append(this.el, this.scrollbar.getDomNode()); append(this.el, this.scrollbar.getDomNode());
this.disposables.push(this.scrollbar); this.disposables.add(this.scrollbar);
this.header = append(this.body, $('.header')); this.header = append(this.body, $('.header'));
this.close = append(this.header, $('span.close')); this.close = append(this.header, $('span.close'));
@@ -414,7 +413,7 @@ class SuggestionDetails {
} }
dispose(): void { dispose(): void {
this.disposables = dispose(this.disposables); this.disposables.dispose();
this.renderDisposeable = dispose(this.renderDisposeable); this.renderDisposeable = dispose(this.renderDisposeable);
} }
} }
@@ -672,7 +671,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
}); });
this.currentSuggestionDetails.then(() => { this.currentSuggestionDetails.then(() => {
if (this.list.length < index) { if (index >= this.list.length || item !== this.list.element(index)) {
return; return;
} }
@@ -689,11 +688,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
} }
this._ariaAlert(this._getSuggestionAriaAlertLabel(item)); this._ariaAlert(this._getSuggestionAriaAlertLabel(item));
}).catch(onUnexpectedError).then(() => { }).catch(onUnexpectedError);
if (this.focusedItem === item) {
this.currentSuggestionDetails = null;
}
});
} }
// emit an event // emit an event
@@ -810,12 +805,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
"suggestWidget" : { "suggestWidget" : {
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${include}": [ "${include}": [
"${ICompletionStats}", "${ICompletionStats}"
"${EditorTelemetryData}"
] ]
} }
*/ */
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() }); this.telemetryService.publicLog('suggestWidget', { ...stats });
} }
this.focusedItem = null; this.focusedItem = null;
@@ -949,14 +943,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.details.element.style.borderColor = this.detailsFocusBorderColor; this.details.element.style.borderColor = this.detailsFocusBorderColor;
} }
} }
/* __GDPR__ this.telemetryService.publicLog2('suggestWidget:toggleDetailsFocus');
"suggestWidget:toggleDetailsFocus" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:toggleDetailsFocus', this.editor.getTelemetryData());
} }
toggleDetails(): void { toggleDetails(): void {
@@ -970,14 +957,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
removeClass(this.element, 'docs-side'); removeClass(this.element, 'docs-side');
removeClass(this.element, 'docs-below'); removeClass(this.element, 'docs-below');
this.editor.layoutContentWidget(this); this.editor.layoutContentWidget(this);
/* __GDPR__ this.telemetryService.publicLog2('suggestWidget:collapseDetails');
"suggestWidget:collapseDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData());
} else { } else {
if (this.state !== State.Open && this.state !== State.Details && this.state !== State.Frozen) { if (this.state !== State.Open && this.state !== State.Details && this.state !== State.Frozen) {
return; return;
@@ -986,14 +966,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.updateExpandDocsSetting(true); this.updateExpandDocsSetting(true);
this.showDetails(false); this.showDetails(false);
this._ariaAlert(this.details.getAriaLabel()); this._ariaAlert(this.details.getAriaLabel());
/* __GDPR__ this.telemetryService.publicLog2('suggestWidget:expandDetails');
"suggestWidget:expandDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData());
} }
} }
@@ -137,6 +137,22 @@ suite('WordOperations', () => {
assert.deepEqual(actual, EXPECTED); 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', () => { test('cursorWordStartLeft', () => {
// This is the behaviour observed in Visual Studio, please do not touch test // 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'); const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/| '].join('\n');
@@ -163,7 +163,7 @@ export class CursorWordLeftSelect extends WordLeftCommand {
constructor() { constructor() {
super({ super({
inSelectionMode: true, inSelectionMode: true,
wordNavigationType: WordNavigationType.WordStart, wordNavigationType: WordNavigationType.WordStartFast,
id: 'cursorWordLeftSelect', id: 'cursorWordLeftSelect',
precondition: undefined precondition: undefined
}); });
@@ -234,7 +234,9 @@ export class StandaloneCommandService implements ICommandService {
private readonly _dynamicCommands: { [id: string]: ICommand; }; private readonly _dynamicCommands: { [id: string]: ICommand; };
private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>(); private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event; public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;
constructor(instantiationService: IInstantiationService) { constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService; this._instantiationService = instantiationService;
@@ -256,8 +258,10 @@ export class StandaloneCommandService implements ICommandService {
} }
try { 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; const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result); return Promise.resolve(result);
} catch (err) { } catch (err) {
return Promise.reject(err); return Promise.reject(err);
@@ -352,7 +356,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] {
let result: ResolvedKeybindingItem[] = [], resultLen = 0; let result: ResolvedKeybindingItem[] = [], resultLen = 0;
for (const item of items) { for (const item of items) {
const when = (item.when ? item.when.normalize() : undefined); const when = item.when || undefined;
const keybinding = item.keybinding; const keybinding = item.keybinding;
if (!keybinding) { if (!keybinding) {
@@ -140,8 +140,6 @@ export module StaticServices {
export const notificationService = define(INotificationService, () => new SimpleNotificationService()); export const notificationService = define(INotificationService, () => new SimpleNotificationService());
export const accessibilityService = define(IAccessibilityService, () => new BrowserAccessibilityService());
export const markerService = define(IMarkerService, () => new MarkerService()); export const markerService = define(IMarkerService, () => new MarkerService());
export const modeService = define(IModeService, (o) => new ModeServiceImpl()); 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))); let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService)));
ensure(IAccessibilityService, () => new BrowserAccessibilityService(contextKeyService, configurationService));
ensure(IListService, () => new ListService(contextKeyService)); ensure(IListService, () => new ListService(contextKeyService));
let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService)); let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService));
@@ -4344,12 +4344,12 @@ suite('autoClosingPairs', () => {
let autoClosePositions = [ let autoClosePositions = [
'var a |=| [|]|;|', 'var a |=| [|]|;|',
'var b |=| |`asd`|;|', 'var b |=| |`asd`|;|',
'var c |=| |\'asd!\'|;|', 'var c |=| |\'asd\'|;|',
'var d |=| |"asd"|;|', 'var d |=| |"asd"|;|',
'var e |=| /*3*/| 3;|', 'var e |=| /*3*/| 3;|',
'var f |=| /**| 3 */3;|', 'var f |=| /**| 3 */3;|',
'var g |=| (3+5)|;|', 'var g |=| (3+5)|;|',
'var h |=| {| a:| |\'value!\'| |}|;|', 'var h |=| {| a:| |\'value\'| |}|;|',
]; ];
for (let i = 0, len = autoClosePositions.length; i < len; i++) { for (let i = 0, len = autoClosePositions.length; i < len; i++) {
const lineNumber = i + 1; const lineNumber = i + 1;
@@ -4494,6 +4494,107 @@ suite('autoClosingPairs', () => {
mode.dispose(); 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', () => { test('issue #15825: accents on mac US intl keyboard', () => {
let mode = new AutoClosingMode(); let mode = new AutoClosingMode();
usingCursor({ usingCursor({
@@ -132,7 +132,7 @@ suite('Cursor move command test', () => {
test('move to first non white space character of line from middle', () => { test('move to first non white space character of line from middle', () => {
moveTo(thisCursor, 1, 8); moveTo(thisCursor, 1, 8);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor); moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6); 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', () => { test('move to first non white space character of line from first non white space character', () => {
moveTo(thisCursor, 1, 6); moveTo(thisCursor, 1, 6);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor); moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6); 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', () => { test('move to first non white space character of line from first character', () => {
moveTo(thisCursor, 1, 1); moveTo(thisCursor, 1, 1);
moveToLineFirstNonWhiteSpaceCharacter(thisCursor); moveToLineFirstNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 6); cursorEqual(thisCursor, 1, 6);
}); });
@@ -180,7 +180,7 @@ suite('Cursor move command test', () => {
test('move to last non white space character from middle', () => { test('move to last non white space character from middle', () => {
moveTo(thisCursor, 1, 8); moveTo(thisCursor, 1, 8);
moveToLineLastNonWhiteSpaceCharacter(thisCursor); moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19); 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', () => { test('move to last non white space character from last non white space character', () => {
moveTo(thisCursor, 1, 19); moveTo(thisCursor, 1, 19);
moveToLineLastNonWhiteSpaceCharacter(thisCursor); moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19); cursorEqual(thisCursor, 1, 19);
}); });
@@ -196,7 +196,7 @@ suite('Cursor move command test', () => {
test('move to last non white space character from line end', () => { test('move to last non white space character from line end', () => {
moveTo(thisCursor, 1, 21); moveTo(thisCursor, 1, 21);
moveToLineLastNonWhiteSpaceCharacter(thisCursor); moveToLineLastNonWhitespaceCharacter(thisCursor);
cursorEqual(thisCursor, 1, 19); cursorEqual(thisCursor, 1, 19);
}); });
@@ -415,7 +415,7 @@ function moveToLineStart(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineStart }); move(cursor, { to: CursorMove.RawDirection.WrappedLineStart });
} }
function moveToLineFirstNonWhiteSpaceCharacter(cursor: Cursor) { function moveToLineFirstNonWhitespaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter }); move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter });
} }
@@ -427,7 +427,7 @@ function moveToLineEnd(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd }); move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd });
} }
function moveToLineLastNonWhiteSpaceCharacter(cursor: Cursor) { function moveToLineLastNonWhitespaceCharacter(cursor: Cursor) {
move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter }); move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter });
} }
@@ -32,6 +32,9 @@ export class TestCommandService implements ICommandService {
private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>(); private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event; 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) { constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService; this._instantiationService = instantiationService;
} }
@@ -43,8 +46,9 @@ export class TestCommandService implements ICommandService {
} }
try { 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; const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result); return Promise.resolve(result);
} catch (err) { } catch (err) {
return Promise.reject(err); return Promise.reject(err);
@@ -17,6 +17,7 @@ suite('OpenerService', function () {
const commandService = new class implements ICommandService { const commandService = new class implements ICommandService {
_serviceBrand: any; _serviceBrand: any;
onWillExecuteCommand = () => ({ dispose: () => { } }); onWillExecuteCommand = () => ({ dispose: () => { } });
onDidExecuteCommand = () => ({ dispose: () => { } });
executeCommand(id: string, ...args: any[]): Promise<any> { executeCommand(id: string, ...args: any[]): Promise<any> {
lastCommand = { id, args }; lastCommand = { id, args };
return Promise.resolve(undefined); return Promise.resolve(undefined);
@@ -733,4 +733,23 @@ suite('TextModelSearch', () => {
assert(isMultilineRegexSource('\\n')); assert(isMultilineRegexSource('\\n'));
assert(isMultilineRegexSource('foo\\W')); 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();
});
}); });
+12 -1
View File
@@ -5,6 +5,8 @@
declare namespace monaco { declare namespace monaco {
// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.
export type Thenable<T> = PromiseLike<T>; export type Thenable<T> = PromiseLike<T>;
export interface IDisposable { export interface IDisposable {
@@ -27,7 +29,8 @@ declare namespace monaco {
export enum MarkerTag { export enum MarkerTag {
Unnecessary = 1 Unnecessary = 1,
Deprecated = 2
} }
export enum MarkerSeverity { export enum MarkerSeverity {
@@ -583,6 +586,14 @@ declare namespace monaco {
* Test if `otherRange` is in `range`. If the ranges are equal, will return true. * Test if `otherRange` is in `range`. If the ranges are equal, will return true.
*/ */
static containsRange(range: IRange, otherRange: IRange): boolean; static containsRange(range: IRange, otherRange: IRange): boolean;
/**
* Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true.
*/
strictContainsRange(range: IRange): boolean;
/**
* Test if `otherRange` is strinctly in `range` (must start after, and end before). If the ranges are equal, will return false.
*/
static strictContainsRange(range: IRange, otherRange: IRange): boolean;
/** /**
* A reunion of the two ranges. * A reunion of the two ranges.
* The smallest position will be used as the start point, and the largest one as the end point. * The smallest position will be used as the start point, and the largest one as the end point.

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