Merge from vscode d5e9aa0227e057a60c82568bf31c04730dc15dcd (#11276)

* Merge from vscode d5e9aa0227e057a60c82568bf31c04730dc15dcd

* fix tests
This commit is contained in:
Anthony Dresser
2020-07-09 13:40:02 -07:00
committed by GitHub
parent f812e4fa5d
commit 047c221834
135 changed files with 1772 additions and 954 deletions

1
.vscode/launch.json vendored
View File

@@ -92,6 +92,7 @@
"env": { "env": {
"VSCODE_EXTHOST_WILL_SEND_SOCKET": null "VSCODE_EXTHOST_WILL_SEND_SOCKET": null
}, },
"cleanUp": "wholeBrowser",
"breakOnLoad": false, "breakOnLoad": false,
"urlFilter": "*workbench.html*", "urlFilter": "*workbench.html*",
"runtimeArgs": [ "runtimeArgs": [

View File

@@ -2,12 +2,14 @@
{ {
"kind": 1, "kind": 1,
"language": "markdown", "language": "markdown",
"value": "##### `Config`: defines the inbox query" "value": "##### `Config`: defines the inbox query",
"editable": true
}, },
{ {
"kind": 2, "kind": 2,
"language": "github-issues", "language": "github-issues",
"value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item " "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ",
"editable": true
}, },
{ {
"kind": 1, "kind": 1,
@@ -18,7 +20,7 @@
{ {
"kind": 2, "kind": 2,
"language": "github-issues", "language": "github-issues",
"value": "$inbox -label:\"needs more info\"", "value": "$inbox -label:\"needs more info\" -label:emmet",
"editable": true "editable": true
}, },
{ {
@@ -31,6 +33,6 @@
"kind": 2, "kind": 2,
"language": "github-issues", "language": "github-issues",
"value": "$inbox", "value": "$inbox",
"editable": true "editable": false
} }
] ]

View File

@@ -20,7 +20,7 @@
{ {
"kind": 2, "kind": 2,
"language": "github-issues", "language": "github-issues",
"value": "$repos $milestone assignee:@me is:open\n", "value": "$repos $milestone assignee:@me is:open",
"editable": false "editable": false
}, },
{ {

View File

@@ -0,0 +1,55 @@
[
{
"kind": 1,
"language": "markdown",
"value": "### Bug Verification Queries\n\nBefore shipping we want to verify _all_ bugs. That means when a bug is fixed we check that the fix actually works. It's always best to start with bugs that you have filed and the proceed with bugs that have been filed from users outside the development team. ",
"editable": true
},
{
"kind": 1,
"language": "markdown",
"value": "#### Config: update list of `repos` and the `milestone`",
"editable": true
},
{
"kind": 2,
"language": "github-issues",
"value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"June 2020\"",
"editable": true
},
{
"kind": 1,
"language": "markdown",
"value": "### Bugs You Filed",
"editable": true
},
{
"kind": 2,
"language": "github-issues",
"value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate author:@me",
"editable": false
},
{
"kind": 1,
"language": "markdown",
"value": "### Bugs From Outside",
"editable": true
},
{
"kind": 2,
"language": "github-issues",
"value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand",
"editable": false
},
{
"kind": 1,
"language": "markdown",
"value": "### All"
},
{
"kind": 2,
"language": "github-issues",
"value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate",
"editable": false
}
]

4
.vscode/tasks.json vendored
View File

@@ -26,8 +26,8 @@
"message": 3 "message": 3
}, },
"background": { "background": {
"beginsPattern": "Starting compilation", "beginsPattern": "\\[watch-client\\].*Starting compilation",
"endsPattern": "Finished compilation" "endsPattern": "\\[watch-client\\].*Finished compilation"
} }
} }
}, },

View File

@@ -75,6 +75,7 @@ const vscodeResources = [
'out-build/paths.js', 'out-build/paths.js',
'out-build/vs/**/*.{svg,png,html}', 'out-build/vs/**/*.{svg,png,html}',
'!out-build/vs/code/browser/**/*.html', '!out-build/vs/code/browser/**/*.html',
'!out-build/vs/editor/standalone/**/*.svg',
'out-build/vs/base/common/performance.js', 'out-build/vs/base/common/performance.js',
'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/languagePacks.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',

View File

@@ -73,3 +73,5 @@ yarnInstall('test/automation'); // node modules required for smoketest
yarnInstall('test/smoke'); // node modules required for smoketest yarnInstall('test/smoke'); // node modules required for smoketest
yarnInstall('test/integration/browser'); // node modules required for integration yarnInstall('test/integration/browser'); // node modules required for integration
yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron
cp.execSync('git config pull.rebase true');

View File

@@ -40,7 +40,7 @@
"gulp-bom": "^1.0.0", "gulp-bom": "^1.0.0",
"gulp-sourcemaps": "^1.11.0", "gulp-sourcemaps": "^1.11.0",
"gulp-uglify": "^3.0.0", "gulp-uglify": "^3.0.0",
"iconv-lite-umd": "0.6.5", "iconv-lite-umd": "0.6.7",
"mime": "^1.3.4", "mime": "^1.3.4",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"minimist": "^1.2.3", "minimist": "^1.2.3",

View File

@@ -1817,10 +1817,10 @@ https-proxy-agent@^4.0.0:
agent-base "5" agent-base "5"
debug "4" debug "4"
iconv-lite-umd@0.6.5: iconv-lite-umd@0.6.7:
version "0.6.5" version "0.6.7"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.7.tgz#ee437e34b30f15dc00ec93ea65065e672770777c"
integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== integrity sha512-DT90zb7wL1B3I6DmYUMcfJeVdY19XigzDj5AtXbXEw9Jfi0+AVAxfn7ytvY7Xhr+GFn7nd7hPonapC37oo7iAQ==
iconv-lite@^0.4.4: iconv-lite@^0.4.4:
version "0.4.24" version "0.4.24"

View File

@@ -533,7 +533,7 @@
"git": { "git": {
"name": "ripgrep", "name": "ripgrep",
"repositoryUrl": "https://github.com/BurntSushi/ripgrep", "repositoryUrl": "https://github.com/BurntSushi/ripgrep",
"commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" "commitHash": "973de50c9ef451da2cfcdfa86f2b2711d8d6ff48"
} }
}, },
"isOnlyProductionDependency": true, "isOnlyProductionDependency": true,

View File

@@ -1878,7 +1878,7 @@
"dependencies": { "dependencies": {
"byline": "^5.0.0", "byline": "^5.0.0",
"file-type": "^7.2.0", "file-type": "^7.2.0",
"iconv-lite-umd": "0.6.5", "iconv-lite-umd": "0.6.7",
"jschardet": "2.1.1", "jschardet": "2.1.1",
"vscode-extension-telemetry": "0.1.1", "vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0", "vscode-nls": "^4.0.0",

View File

@@ -7,7 +7,6 @@ import { Model } from '../model';
import { GitExtension, Repository, API } from './git'; import { GitExtension, Repository, API } from './git';
import { ApiRepository, ApiImpl } from './api1'; import { ApiRepository, ApiImpl } from './api1';
import { Event, EventEmitter } from 'vscode'; import { Event, EventEmitter } from 'vscode';
import { latchEvent } from '../util';
export function deprecated(_target: any, key: string, descriptor: any): void { export function deprecated(_target: any, key: string, descriptor: any): void {
if (typeof descriptor.value !== 'function') { if (typeof descriptor.value !== 'function') {
@@ -26,14 +25,20 @@ export class GitExtensionImpl implements GitExtension {
enabled: boolean = false; enabled: boolean = false;
private _onDidChangeEnablement = new EventEmitter<boolean>(); private _onDidChangeEnablement = new EventEmitter<boolean>();
readonly onDidChangeEnablement: Event<boolean> = latchEvent(this._onDidChangeEnablement.event); readonly onDidChangeEnablement: Event<boolean> = this._onDidChangeEnablement.event;
private _model: Model | undefined = undefined; private _model: Model | undefined = undefined;
set model(model: Model | undefined) { set model(model: Model | undefined) {
this._model = model; this._model = model;
this.enabled = !!model; const enabled = !!model;
if (this.enabled === enabled) {
return;
}
this.enabled = enabled;
this._onDidChangeEnablement.fire(this.enabled); this._onDidChangeEnablement.fire(this.enabled);
} }
@@ -73,4 +78,4 @@ export class GitExtensionImpl implements GitExtension {
return new ApiImpl(this._model); return new ApiImpl(this._model);
} }
} }

View File

@@ -176,8 +176,7 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
return result; return result;
} }
// @ts-expect-error async function checkGitv1(info: IGit): Promise<void> {
async function checkGitVersion(info: IGit): Promise<void> {
const config = workspace.getConfiguration('git'); const config = workspace.getConfiguration('git');
const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true; const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true;
@@ -204,3 +203,27 @@ async function checkGitVersion(info: IGit): Promise<void> {
await config.update('ignoreLegacyWarning', true, true); await config.update('ignoreLegacyWarning', true, true);
} }
} }
async function checkGitWindows(info: IGit): Promise<void> {
if (!/^2\.(25|26)\./.test(info.version)) {
return;
}
const update = localize('updateGit', "Update Git");
const choice = await window.showWarningMessage(
localize('git2526', "There are known issues with the installed Git {0}. Please update to Git >= 2.27 for the git features to work correctly.", info.version),
update
);
if (choice === update) {
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
}
}
// @ts-expect-error
async function checkGitVersion(info: IGit): Promise<void> {
await checkGitv1(info);
if (process.platform === 'win32') {
await checkGitWindows(info);
}
}

View File

@@ -865,7 +865,7 @@ export class Repository implements Disposable {
} }
async getInputTemplate(): Promise<string> { async getInputTemplate(): Promise<string> {
const commitMessage = (await Promise.all([this.repository.getMergeMessage(), this.repository.getSquashMessage()])).find(msg => msg !== undefined); const commitMessage = (await Promise.all([this.repository.getMergeMessage(), this.repository.getSquashMessage()])).find(msg => !!msg);
if (commitMessage) { if (commitMessage) {
return commitMessage; return commitMessage;

View File

@@ -44,18 +44,6 @@ export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Even
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
} }
export function latchEvent<T>(event: Event<T>): Event<T> {
let firstCall = true;
let cache: T;
return filterEvent(event, value => {
let shouldEmit = firstCall || value !== cache;
firstCall = false;
cache = value;
return shouldEmit;
});
}
export function anyEvent<T>(...events: Event<T>[]): Event<T> { export function anyEvent<T>(...events: Event<T>[]): Event<T> {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));

View File

@@ -425,10 +425,10 @@ https-proxy-agent@^2.2.1:
agent-base "^4.3.0" agent-base "^4.3.0"
debug "^3.1.0" debug "^3.1.0"
iconv-lite-umd@0.6.5: iconv-lite-umd@0.6.7:
version "0.6.5" version "0.6.7"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.7.tgz#ee437e34b30f15dc00ec93ea65065e672770777c"
integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== integrity sha512-DT90zb7wL1B3I6DmYUMcfJeVdY19XigzDj5AtXbXEw9Jfi0+AVAxfn7ytvY7Xhr+GFn7nd7hPonapC37oo7iAQ==
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"

View File

@@ -21,6 +21,11 @@
"main": "./out/extension.js", "main": "./out/extension.js",
"contributes": { "contributes": {
"commands": [ "commands": [
{
"command": "githubBrowser.openRepository",
"title": "Open GitHub Repository...",
"category": "GitHub Browser"
},
{ {
"command": "githubBrowser.commit", "command": "githubBrowser.commit",
"title": "Commit", "title": "Commit",
@@ -48,6 +53,10 @@
], ],
"menus": { "menus": {
"commandPalette": [ "commandPalette": [
{
"command": "githubBrowser.openRepository",
"when": "config.githubBrowser.openRepository"
},
{ {
"command": "githubBrowser.commit", "command": "githubBrowser.commit",
"when": "false" "when": "false"

View File

@@ -47,31 +47,17 @@ function fromSerialized(operations: StoredOperation): Operation {
return { ...operations, uri: Uri.parse(operations.uri) }; return { ...operations, uri: Uri.parse(operations.uri) };
} }
interface CreatedFileChangeStoreEvent { export interface ChangeStoreEvent {
type: 'created'; type: 'created' | 'changed' | 'deleted';
rootUri: Uri; rootUri: Uri;
uri: Uri; uri: Uri;
} }
interface ChangedFileChangeStoreEvent {
type: 'changed';
rootUri: Uri;
uri: Uri;
}
interface DeletedFileChangeStoreEvent {
type: 'deleted';
rootUri: Uri;
uri: Uri;
}
type ChangeStoreEvent = CreatedFileChangeStoreEvent | ChangedFileChangeStoreEvent | DeletedFileChangeStoreEvent;
function toChangeStoreEvent(operation: Operation | StoredOperation, rootUri: Uri, uri?: Uri): ChangeStoreEvent { function toChangeStoreEvent(operation: Operation | StoredOperation, rootUri: Uri, uri?: Uri): ChangeStoreEvent {
return { return {
type: operation.type, type: operation.type,
rootUri: rootUri, rootUri: rootUri,
uri: uri ?? (typeof operation.uri === 'string' ? Uri.parse(operation.uri) : operation.uri) uri: uri ?? (typeof operation.uri === 'string' ? Uri.parse(operation.uri) : operation.uri),
}; };
} }
@@ -82,6 +68,8 @@ export interface IChangeStore {
discard(uri: Uri): Promise<void>; discard(uri: Uri): Promise<void>;
discardAll(rootUri: Uri): Promise<void>; discardAll(rootUri: Uri): Promise<void>;
hasChanges(rootUri: Uri): boolean;
getChanges(rootUri: Uri): Operation[]; getChanges(rootUri: Uri): Operation[];
getContent(uri: Uri): string | undefined; getContent(uri: Uri): string | undefined;
@@ -116,9 +104,15 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore {
await this.saveWorkingOperations(rootUri, undefined); await this.saveWorkingOperations(rootUri, undefined);
const events: ChangeStoreEvent[] = [];
for (const operation of operations) { for (const operation of operations) {
await this.discardWorkingContent(operation.uri); await this.discardWorkingContent(operation.uri);
this._onDidChange.fire(toChangeStoreEvent(operation, rootUri)); events.push(toChangeStoreEvent(operation, rootUri));
}
for (const e of events) {
this._onDidChange.fire(e);
} }
} }
@@ -143,7 +137,7 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore {
this._onDidChange.fire({ this._onDidChange.fire({
type: operation.type === 'created' ? 'deleted' : operation.type === 'deleted' ? 'created' : 'changed', type: operation.type === 'created' ? 'deleted' : operation.type === 'deleted' ? 'created' : 'changed',
rootUri: rootUri, rootUri: rootUri,
uri: uri uri: uri,
}); });
} }
@@ -152,9 +146,15 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore {
await this.saveWorkingOperations(rootUri, undefined); await this.saveWorkingOperations(rootUri, undefined);
const events: ChangeStoreEvent[] = [];
for (const operation of operations) { for (const operation of operations) {
await this.discardWorkingContent(operation.uri); await this.discardWorkingContent(operation.uri);
this._onDidChange.fire(toChangeStoreEvent(operation, rootUri)); events.push(toChangeStoreEvent(operation, rootUri));
}
for (const e of events) {
this._onDidChange.fire(e);
} }
} }

View File

@@ -4,9 +4,13 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
'use strict'; 'use strict';
import { Event, EventEmitter, Memento, Uri } from 'vscode'; import { Event, EventEmitter, Memento, Uri, workspace } from 'vscode';
export const contextKeyPrefix = 'github.context|'; export interface WorkspaceFolderContext<T> {
context: T;
name: string;
folderUri: Uri;
}
export class ContextStore<T> { export class ContextStore<T> {
private _onDidChange = new EventEmitter<Uri>(); private _onDidChange = new EventEmitter<Uri>();
@@ -14,23 +18,36 @@ export class ContextStore<T> {
return this._onDidChange.event; return this._onDidChange.event;
} }
constructor(private readonly memento: Memento, private readonly scheme: string) { } constructor(
private readonly scheme: string,
private readonly originalScheme: string,
private readonly memento: Memento,
) { }
delete(uri: Uri) { delete(uri: Uri) {
return this.set(uri, undefined); return this.set(uri, undefined);
} }
get(uri: Uri): T | undefined { get(uri: Uri): T | undefined {
return this.memento.get<T>(`${contextKeyPrefix}${uri.toString()}`); return this.memento.get<T>(`${this.originalScheme}.context|${this.getOriginalResource(uri).toString()}`);
} }
getForWorkspace(): WorkspaceFolderContext<T>[] {
const folders = workspace.workspaceFolders?.filter(f => f.uri.scheme === this.scheme || f.uri.scheme === this.originalScheme) ?? [];
return folders.map(f => ({ context: this.get(f.uri)!, name: f.name, folderUri: f.uri })).filter(c => c.context !== undefined);
}
async set(uri: Uri, context: T | undefined) { async set(uri: Uri, context: T | undefined) {
if (uri.scheme !== this.scheme) { uri = this.getOriginalResource(uri);
throw new Error(`Invalid context scheme: ${uri.scheme}`); await this.memento.update(`${this.originalScheme}.context|${uri.toString()}`, context);
}
await this.memento.update(`${contextKeyPrefix}${uri.toString()}`, context);
this._onDidChange.fire(uri); this._onDidChange.fire(uri);
} }
getOriginalResource(uri: Uri): Uri {
return uri.with({ scheme: this.originalScheme });
}
getWorkspaceResource(uri: Uri): Uri {
return uri.with({ scheme: this.scheme });
}
} }

View File

@@ -3,48 +3,50 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionContext, Uri, workspace } from 'vscode'; import { commands, ExtensionContext, Uri, window, workspace } from 'vscode';
import { ChangeStore } from './changeStore'; import { ChangeStore } from './changeStore';
import { ContextStore } from './contextStore'; import { ContextStore } from './contextStore';
import { VirtualFS } from './fs'; import { VirtualFS } from './fs';
import { GitHubApiContext, GitHubApi } from './github/api'; import { GitHubApiContext, GitHubApi } from './github/api';
import { GitHubFS } from './github/fs'; import { GitHubFS } from './github/fs';
import { VirtualSCM } from './scm'; import { VirtualSCM } from './scm';
import { StatusBar } from './statusbar';
// const repositoryRegex = /^(?:(?:https:\/\/)?github.com\/)?([^\/]+)\/([^\/]+?)(?:\/|.git|$)/i; const repositoryRegex = /^(?:(?:https:\/\/)?github.com\/)?([^\/]+)\/([^\/]+?)(?:\/|.git|$)/i;
export function activate(context: ExtensionContext) { export async function activate(context: ExtensionContext) {
const contextStore = new ContextStore<GitHubApiContext>(context.workspaceState, GitHubFS.scheme); const contextStore = new ContextStore<GitHubApiContext>('codespace', GitHubFS.scheme, context.workspaceState);
const changeStore = new ChangeStore(context.workspaceState); const changeStore = new ChangeStore(context.workspaceState);
const githubApi = new GitHubApi(contextStore); const githubApi = new GitHubApi(contextStore);
const gitHubFS = new GitHubFS(githubApi); const gitHubFS = new GitHubFS(githubApi);
const virtualFS = new VirtualFS('codespace', GitHubFS.scheme, contextStore, changeStore, gitHubFS); const virtualFS = new VirtualFS('codespace', contextStore, changeStore, gitHubFS);
context.subscriptions.push( context.subscriptions.push(
githubApi, githubApi,
gitHubFS, gitHubFS,
virtualFS, virtualFS,
new VirtualSCM(GitHubFS.scheme, githubApi, changeStore) new VirtualSCM(GitHubFS.scheme, githubApi, changeStore),
new StatusBar(contextStore, changeStore),
); );
// commands.registerCommand('githubBrowser.openRepository', async () => { commands.registerCommand('githubBrowser.openRepository', async () => {
// const value = await window.showInputBox({ const value = await window.showInputBox({
// placeHolder: 'e.g. https://github.com/microsoft/vscode', placeHolder: 'e.g. https://github.com/microsoft/vscode',
// prompt: 'Enter a GitHub repository url', prompt: 'Enter a GitHub repository url',
// validateInput: value => repositoryRegex.test(value) ? undefined : 'Invalid repository url' validateInput: value => repositoryRegex.test(value) ? undefined : 'Invalid repository url'
// }); });
// if (value) { if (value) {
// const match = repositoryRegex.exec(value); const match = repositoryRegex.exec(value);
// if (match) { if (match) {
// const [, owner, repo] = match; const [, owner, repo] = match;
// const uri = Uri.parse(`codespace://HEAD/${owner}/${repo}`); const uri = Uri.parse(`codespace://HEAD/${owner}/${repo}`);
// openWorkspace(uri, repo, 'currentWindow'); openWorkspace(uri, repo, 'currentWindow');
// } }
// } }
// }); });
} }
export function getRelativePath(rootUri: Uri, uri: Uri) { export function getRelativePath(rootUri: Uri, uri: Uri) {
@@ -63,11 +65,16 @@ export function isDescendent(folderPath: string, filePath: string) {
return folderPath.length === 0 || filePath.startsWith(folderPath.endsWith('/') ? folderPath : `${folderPath}/`); return folderPath.length === 0 || filePath.startsWith(folderPath.endsWith('/') ? folderPath : `${folderPath}/`);
} }
// function openWorkspace(uri: Uri, name: string, location: 'currentWindow' | 'newWindow' | 'addToCurrentWorkspace') { const shaRegex = /^[0-9a-f]{40}$/;
// if (location === 'addToCurrentWorkspace') { export function isSha(ref: string) {
// const count = (workspace.workspaceFolders && workspace.workspaceFolders.length) || 0; return shaRegex.test(ref);
// return workspace.updateWorkspaceFolders(count, 0, { uri: uri, name: name }); }
// }
// return commands.executeCommand('vscode.openFolder', uri, location === 'newWindow'); function openWorkspace(uri: Uri, name: string, location: 'currentWindow' | 'newWindow' | 'addToCurrentWorkspace') {
// } if (location === 'addToCurrentWorkspace') {
const count = (workspace.workspaceFolders && workspace.workspaceFolders.length) || 0;
return workspace.updateWorkspaceFolders(count, 0, { uri: uri, name: name });
}
return commands.executeCommand('vscode.openFolder', uri, location === 'newWindow');
}

View File

@@ -43,26 +43,22 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe
constructor( constructor(
readonly scheme: string, readonly scheme: string,
private readonly originalScheme: string, private readonly contextStore: ContextStore<GitHubApiContext>,
contextStore: ContextStore<GitHubApiContext>,
private readonly changeStore: IWritableChangeStore, private readonly changeStore: IWritableChangeStore,
private readonly fs: FileSystemProvider & FileSearchProvider & TextSearchProvider private readonly fs: FileSystemProvider & FileSearchProvider & TextSearchProvider
) { ) {
// TODO@eamodio listen for workspace folder changes // TODO@eamodio listen for workspace folder changes
for (const folder of workspace.workspaceFolders ?? []) { for (const context of contextStore.getForWorkspace()) {
const uri = this.getOriginalResource(folder.uri);
// If we have a saved context, but no longer have any changes, reset the context // If we have a saved context, but no longer have any changes, reset the context
// We only do this on startup/reload to keep things consistent // We only do this on startup/reload to keep things consistent
if (contextStore.get(uri) !== undefined && !changeStore.hasChanges(folder.uri)) { if (!changeStore.hasChanges(context.folderUri)) {
contextStore.delete(uri); console.log('Clear context', context.folderUri.toString());
contextStore.delete(context.folderUri);
} }
} }
this.disposable = Disposable.from( this.disposable = Disposable.from(
workspace.registerFileSystemProvider(scheme, this, { workspace.registerFileSystemProvider(scheme, this, { isCaseSensitive: true }),
isCaseSensitive: true,
}),
workspace.registerFileSearchProvider(scheme, this), workspace.registerFileSearchProvider(scheme, this),
workspace.registerTextSearchProvider(scheme, this), workspace.registerTextSearchProvider(scheme, this),
changeStore.onDidChange(e => { changeStore.onDidChange(e => {
@@ -86,11 +82,11 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe
} }
private getOriginalResource(uri: Uri): Uri { private getOriginalResource(uri: Uri): Uri {
return uri.with({ scheme: this.originalScheme }); return this.contextStore.getOriginalResource(uri);
} }
private getVirtualResource(uri: Uri): Uri { private getWorkspaceResource(uri: Uri): Uri {
return uri.with({ scheme: this.scheme }); return this.contextStore.getWorkspaceResource(uri);
} }
//#region FileSystemProvider //#region FileSystemProvider
@@ -211,7 +207,7 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe
return this.fs.provideTextSearchResults( return this.fs.provideTextSearchResults(
query, query,
{ ...options, folder: this.getOriginalResource(options.folder) }, { ...options, folder: this.getOriginalResource(options.folder) },
{ report: (result: TextSearchResult) => progress.report({ ...result, uri: this.getVirtualResource(result.uri) }) }, { report: (result: TextSearchResult) => progress.report({ ...result, uri: this.getWorkspaceResource(result.uri) }) },
token token
); );
} }

View File

@@ -6,14 +6,16 @@
import { authentication, AuthenticationSession, Disposable, Event, EventEmitter, Range, Uri } from 'vscode'; import { authentication, AuthenticationSession, Disposable, Event, EventEmitter, Range, Uri } from 'vscode';
import { graphql } from '@octokit/graphql'; import { graphql } from '@octokit/graphql';
import { Octokit } from '@octokit/rest'; import { Octokit } from '@octokit/rest';
import { fromGitHubUri } from './fs';
import { ContextStore } from '../contextStore'; import { ContextStore } from '../contextStore';
import { fromGitHubUri } from './fs';
import { isSha } from '../extension';
import { Iterables } from '../iterables'; import { Iterables } from '../iterables';
export const shaRegex = /^[0-9a-f]{40}$/;
export interface GitHubApiContext { export interface GitHubApiContext {
sha: string; requestRef: string;
branch: string;
sha: string | undefined;
timestamp: number; timestamp: number;
} }
@@ -110,19 +112,12 @@ export class GitHubApi implements Disposable {
} }
async commit(rootUri: Uri, message: string, operations: CommitOperation[]): Promise<string | undefined> { async commit(rootUri: Uri, message: string, operations: CommitOperation[]): Promise<string | undefined> {
let { owner, repo, ref } = fromGitHubUri(rootUri); const { owner, repo } = fromGitHubUri(rootUri);
try { try {
if (ref === undefined || ref === 'HEAD') {
ref = await this.defaultBranchQuery(rootUri);
if (ref === undefined) {
throw new Error('Cannot commit — invalid ref');
}
}
const context = await this.getContext(rootUri); const context = await this.getContext(rootUri);
if (context.sha === undefined) { if (context.sha === undefined) {
throw new Error('Cannot commit — invalid context'); throw new Error(`Cannot commit to Uri(${rootUri.toString(true)}); Invalid context sha`);
} }
const hasDeletes = operations.some(op => op.type === 'deleted'); const hasDeletes = operations.some(op => op.type === 'deleted');
@@ -204,14 +199,14 @@ export class GitHubApi implements Disposable {
parents: [context.sha] parents: [context.sha]
}); });
this.updateContext(rootUri, { sha: resp.data.sha, timestamp: Date.now() }); this.updateContext(rootUri, { ...context, sha: resp.data.sha, timestamp: Date.now() });
// TODO@eamodio need to send a file change for any open files // TODO@eamodio need to send a file change for any open files
await github.git.updateRef({ await github.git.updateRef({
owner: owner, owner: owner,
repo: repo, repo: repo,
ref: `heads/${ref}`, ref: `heads/${context.branch}`,
sha: resp.data.sha sha: resp.data.sha
}); });
@@ -256,7 +251,7 @@ export class GitHubApi implements Disposable {
owner: owner, owner: owner,
repo: repo, repo: repo,
recursive: '1', recursive: '1',
tree_sha: context?.sha ?? ref ?? 'HEAD', tree_sha: context?.sha ?? ref,
}); });
return Iterables.filterMap(resp.data.tree, p => p.type === 'blob' ? p.path : undefined); return Iterables.filterMap(resp.data.tree, p => p.type === 'blob' ? p.path : undefined);
} catch (ex) { } catch (ex) {
@@ -283,7 +278,7 @@ export class GitHubApi implements Disposable {
}>(query, { }>(query, {
owner: owner, owner: owner,
repo: repo, repo: repo,
path: `${context.sha ?? ref ?? 'HEAD'}:${path}`, path: `${context.sha ?? ref}:${path}`,
}); });
return rsp?.repository?.object ?? undefined; return rsp?.repository?.object ?? undefined;
} catch (ex) { } catch (ex) {
@@ -295,7 +290,7 @@ export class GitHubApi implements Disposable {
const { owner, repo, ref } = fromGitHubUri(uri); const { owner, repo, ref } = fromGitHubUri(uri);
try { try {
if (ref === undefined || ref === 'HEAD') { if (ref === 'HEAD') {
const query = `query latest($owner: String!, $repo: String!) { const query = `query latest($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) { repository(owner: $owner, name: $repo) {
defaultBranchRef { defaultBranchRef {
@@ -322,6 +317,7 @@ export class GitHubApi implements Disposable {
oid oid
} }
} }
}
}`; }`;
const rsp = await this.gqlQuery<{ const rsp = await this.gqlQuery<{
@@ -345,7 +341,7 @@ export class GitHubApi implements Disposable {
const { owner, repo, ref } = fromGitHubUri(uri); const { owner, repo, ref } = fromGitHubUri(uri);
// If we have a specific ref, don't try to search, because GitHub search only works against the default branch // If we have a specific ref, don't try to search, because GitHub search only works against the default branch
if (ref === undefined) { if (ref !== 'HEAD') {
return { matches: [], limitHit: true }; return { matches: [], limitHit: true };
} }
@@ -436,29 +432,46 @@ export class GitHubApi implements Disposable {
private readonly rootUriToContextMap = new Map<string, GitHubApiContext>(); private readonly rootUriToContextMap = new Map<string, GitHubApiContext>();
private async getContextCore(rootUri: Uri): Promise<GitHubApiContext> { private async getContextCore(rootUri: Uri): Promise<GitHubApiContext> {
let context = this.rootUriToContextMap.get(rootUri.toString()); const key = rootUri.toString();
if (context === undefined) { let context = this.rootUriToContextMap.get(key);
const { ref } = fromGitHubUri(rootUri);
if (ref !== undefined && shaRegex.test(ref)) {
context = { sha: ref, timestamp: Date.now() };
} else {
context = this.context.get(rootUri);
if (context?.sha === undefined) {
const sha = await this.latestCommitQuery(rootUri);
if (sha !== undefined) {
context = { sha: sha, timestamp: Date.now() };
} else {
context = undefined;
}
}
}
if (context !== undefined) { // Check if we have a cached a context
this.updateContext(rootUri, context); if (context?.sha !== undefined) {
} return context;
} }
return context ?? { sha: rootUri.authority, timestamp: Date.now() }; // Check if we have a saved context
context = this.context.get(rootUri);
if (context?.sha !== undefined) {
this.rootUriToContextMap.set(key, context);
return context;
}
const { ref } = fromGitHubUri(rootUri);
// If the requested ref looks like a sha, then use it
if (isSha(ref)) {
context = { requestRef: ref, branch: ref, sha: ref, timestamp: Date.now() };
} else {
let branch;
if (ref === 'HEAD') {
branch = await this.defaultBranchQuery(rootUri);
if (branch === undefined) {
throw new Error(`Cannot get context for Uri(${rootUri.toString(true)}); unable to get default branch`);
}
} else {
branch = ref;
}
// Query for the latest sha for the give ref
const sha = await this.latestCommitQuery(rootUri);
context = { requestRef: ref, branch: branch, sha: sha, timestamp: Date.now() };
}
this.updateContext(rootUri, context);
return context;
} }
private updateContext(rootUri: Uri, context: GitHubApiContext) { private updateContext(rootUri: Uri, context: GitHubApiContext) {

View File

@@ -299,7 +299,7 @@ function typenameToFileType(typename: string | undefined | null) {
} }
} }
type RepoInfo = { owner: string; repo: string; path: string | undefined; ref?: string }; type RepoInfo = { owner: string; repo: string; path: string | undefined; ref: string };
export function fromGitHubUri(uri: Uri): RepoInfo { export function fromGitHubUri(uri: Uri): RepoInfo {
const [, owner, repo, ...rest] = uri.path.split('/'); const [, owner, repo, ...rest] = uri.path.split('/');
@@ -311,7 +311,7 @@ export function fromGitHubUri(uri: Uri): RepoInfo {
ref = 'HEAD'; ref = 'HEAD';
} }
} }
return { owner: owner, repo: repo, path: rest.join('/'), ref: ref }; return { owner: owner, repo: repo, path: rest.join('/'), ref: ref ?? 'HEAD' };
} }
function getHashCode(s: string): number { function getHashCode(s: string): number {

View File

@@ -32,17 +32,15 @@ export class VirtualSCM implements Disposable {
// TODO@eamodio listen for workspace folder changes // TODO@eamodio listen for workspace folder changes
for (const folder of workspace.workspaceFolders ?? []) { for (const folder of workspace.workspaceFolders ?? []) {
this.createScmProvider(folder.uri, folder.name); this.createScmProvider(folder.uri, folder.name);
for (const operation of changeStore.getChanges(folder.uri)) {
this.update(folder.uri, operation.uri);
}
} }
this.disposable = Disposable.from( this.disposable = Disposable.from(
changeStore.onDidChange(e => this.update(e.rootUri, e.uri)), changeStore.onDidChange(e => this.update(e.rootUri, e.uri)),
); );
for (const { uri } of workspace.workspaceFolders ?? []) {
for (const operation of changeStore.getChanges(uri)) {
this.update(uri, operation.uri);
}
}
} }
dispose() { dispose() {
@@ -50,7 +48,18 @@ export class VirtualSCM implements Disposable {
} }
private registerCommands() { private registerCommands() {
commands.registerCommand('githubBrowser.commit', (...args: any[]) => this.commitChanges(args[0])); commands.registerCommand('githubBrowser.commit', (sourceControl: SourceControl | undefined) => {
// TODO@eamodio remove this hack once I figure out why the args are missing
if (sourceControl === undefined && this.providers.length === 1) {
sourceControl = this.providers[0].sourceControl;
}
if (sourceControl === undefined) {
return;
}
this.commitChanges(sourceControl);
});
commands.registerCommand('githubBrowser.discardChanges', (resourceState: SourceControlResourceState) => commands.registerCommand('githubBrowser.discardChanges', (resourceState: SourceControlResourceState) =>
this.discardChanges(resourceState.resourceUri) this.discardChanges(resourceState.resourceUri)

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Disposable, StatusBarAlignment, StatusBarItem, Uri, window, workspace } from 'vscode';
import { ChangeStoreEvent, IChangeStore } from './changeStore';
import { GitHubApiContext } from './github/api';
import { isSha } from './extension';
import { ContextStore, WorkspaceFolderContext } from './contextStore';
export class StatusBar implements Disposable {
private readonly disposable: Disposable;
private readonly items = new Map<string, StatusBarItem>();
constructor(
private readonly contextStore: ContextStore<GitHubApiContext>,
private readonly changeStore: IChangeStore
) {
this.disposable = Disposable.from(
contextStore.onDidChange(this.onContextsChanged, this),
changeStore.onDidChange(this.onChanged, this)
);
for (const context of this.contextStore.getForWorkspace()) {
this.createOrUpdateStatusBarItem(context);
}
}
dispose() {
this.disposable?.dispose();
this.items.forEach(i => i.dispose());
}
private createOrUpdateStatusBarItem(wc: WorkspaceFolderContext<GitHubApiContext>) {
let item = this.items.get(wc.folderUri.toString());
if (item === undefined) {
item = window.createStatusBarItem({
id: `githubBrowser.branch:${wc.folderUri.toString()}`,
name: `GitHub Browser: ${wc.name}`,
alignment: StatusBarAlignment.Left,
priority: 1000
});
}
if (isSha(wc.context.branch)) {
item.text = `$(git-commit) ${wc.context.branch.substr(0, 8)}`;
item.tooltip = `${wc.name} \u2022 ${wc.context.branch.substr(0, 8)}`;
} else {
item.text = `$(git-branch) ${wc.context.branch}`;
item.tooltip = `${wc.name} \u2022 ${wc.context.branch}${wc.context.sha ? ` @ ${wc.context.sha?.substr(0, 8)}` : ''}`;
}
const hasChanges = this.changeStore.hasChanges(wc.folderUri);
if (hasChanges) {
item.text += '*';
}
item.show();
this.items.set(wc.folderUri.toString(), item);
}
private onContextsChanged(uri: Uri) {
const folder = workspace.getWorkspaceFolder(this.contextStore.getWorkspaceResource(uri));
if (folder === undefined) {
return;
}
const context = this.contextStore.get(uri);
if (context === undefined) {
return;
}
this.createOrUpdateStatusBarItem({
context: context,
name: folder.name,
folderUri: folder.uri,
});
}
private onChanged(e: ChangeStoreEvent) {
const item = this.items.get(e.rootUri.toString());
if (item !== undefined) {
const hasChanges = this.changeStore.hasChanges(e.rootUri);
if (hasChanges) {
if (!item.text.endsWith('*')) {
item.text += '*';
}
} else {
if (item.text.endsWith('*')) {
item.text = item.text.substr(0, item.text.length - 1);
}
}
}
}
}

View File

@@ -6,9 +6,10 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { API as GitAPI } from './typings/git'; import { API as GitAPI } from './typings/git';
import { publishRepository } from './publish'; import { publishRepository } from './publish';
import { combinedDisposable } from './util';
export function registerCommands(gitAPI: GitAPI): vscode.Disposable[] { export function registerCommands(gitAPI: GitAPI): vscode.Disposable {
const disposables = []; const disposables: vscode.Disposable[] = [];
disposables.push(vscode.commands.registerCommand('github.publish', async () => { disposables.push(vscode.commands.registerCommand('github.publish', async () => {
try { try {
@@ -18,5 +19,5 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable[] {
} }
})); }));
return disposables; return combinedDisposable(disposables);
} }

View File

@@ -3,23 +3,41 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode'; import { Disposable, ExtensionContext, extensions } from 'vscode';
import { GithubRemoteSourceProvider } from './remoteSourceProvider'; import { GithubRemoteSourceProvider } from './remoteSourceProvider';
import { GitExtension } from './typings/git'; import { GitExtension } from './typings/git';
import { registerCommands } from './commands'; import { registerCommands } from './commands';
import { GithubCredentialProviderManager } from './credentialProvider'; import { GithubCredentialProviderManager } from './credentialProvider';
import { dispose, combinedDisposable } from './util';
export async function activate(context: vscode.ExtensionContext) { export function activate(context: ExtensionContext): void {
const gitExtension = vscode.extensions.getExtension<GitExtension>('vscode.git')!.exports; const disposables = new Set<Disposable>();
context.subscriptions.push(combinedDisposable(disposables));
try { const init = () => {
const gitAPI = gitExtension.getAPI(1); try {
const gitAPI = gitExtension.getAPI(1);
context.subscriptions.push(...registerCommands(gitAPI)); disposables.add(registerCommands(gitAPI));
context.subscriptions.push(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI))); disposables.add(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI)));
context.subscriptions.push(new GithubCredentialProviderManager(gitAPI)); disposables.add(new GithubCredentialProviderManager(gitAPI));
} catch (err) { } catch (err) {
console.error('Could not initialize GitHub extension'); console.error('Could not initialize GitHub extension');
console.warn(err); console.warn(err);
} }
};
const onDidChangeGitExtensionEnablement = (enabled: boolean) => {
if (!enabled) {
dispose(disposables);
disposables.clear();
} else {
init();
}
};
const gitExtension = extensions.getExtension<GitExtension>('vscode.git')!.exports;
context.subscriptions.push(gitExtension.onDidChangeEnablement(onDidChangeGitExtensionEnablement));
onDidChangeGitExtensionEnablement(gitExtension.enabled);
} }

View File

@@ -5,10 +5,10 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import * as path from 'path';
import { promises as fs } from 'fs';
import { API as GitAPI, Repository } from './typings/git'; import { API as GitAPI, Repository } from './typings/git';
import { getOctokit } from './auth'; import { getOctokit } from './auth';
import { TextEncoder } from 'util';
import { basename } from 'path';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -28,10 +28,12 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
return; return;
} }
let folder: vscode.WorkspaceFolder; let folder: vscode.Uri;
if (vscode.workspace.workspaceFolders.length === 1) { if (repository) {
folder = vscode.workspace.workspaceFolders[0]; folder = repository.rootUri;
} else if (vscode.workspace.workspaceFolders.length === 1) {
folder = vscode.workspace.workspaceFolders[0].uri;
} else { } else {
const picks = vscode.workspace.workspaceFolders.map(folder => ({ label: folder.name, folder })); const picks = vscode.workspace.workspaceFolders.map(folder => ({ label: folder.name, folder }));
const placeHolder = localize('pick folder', "Pick a folder to publish to GitHub"); const placeHolder = localize('pick folder', "Pick a folder to publish to GitHub");
@@ -41,14 +43,14 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
return; return;
} }
folder = pick.folder; folder = pick.folder.uri;
} }
let quickpick = vscode.window.createQuickPick<vscode.QuickPickItem & { repo?: string, auth?: 'https' | 'ssh' }>(); let quickpick = vscode.window.createQuickPick<vscode.QuickPickItem & { repo?: string, auth?: 'https' | 'ssh' }>();
quickpick.ignoreFocusOut = true; quickpick.ignoreFocusOut = true;
quickpick.placeholder = 'Repository Name'; quickpick.placeholder = 'Repository Name';
quickpick.value = folder.name; quickpick.value = basename(folder.fsPath);
quickpick.show(); quickpick.show();
quickpick.busy = true; quickpick.busy = true;
@@ -97,37 +99,49 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
return; return;
} }
quickpick = vscode.window.createQuickPick(); if (!repository) {
quickpick.placeholder = localize('ignore', "Select which files should be included in the repository."); const gitignore = vscode.Uri.joinPath(folder, '.gitignore');
quickpick.canSelectMany = true; let shouldGenerateGitignore = false;
quickpick.show();
try { try {
quickpick.busy = true; await vscode.workspace.fs.stat(gitignore);
} catch (err) {
const repositoryPath = folder.uri.fsPath; shouldGenerateGitignore = true;
const currentPath = path.join(repositoryPath);
const children = await fs.readdir(currentPath);
quickpick.items = children.map(name => ({ label: name }));
quickpick.selectedItems = quickpick.items;
quickpick.busy = false;
const result = await Promise.race([
new Promise<readonly vscode.QuickPickItem[]>(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))),
new Promise<undefined>(c => quickpick.onDidHide(() => c(undefined)))
]);
if (!result) {
return;
} }
const ignored = new Set(children); if (shouldGenerateGitignore) {
result.forEach(c => ignored.delete(c.label)); quickpick = vscode.window.createQuickPick();
quickpick.placeholder = localize('ignore', "Select which files should be included in the repository.");
quickpick.canSelectMany = true;
quickpick.show();
const raw = [...ignored].map(i => `/${i}`).join('\n'); try {
await fs.writeFile(path.join(repositoryPath, '.gitignore'), raw, 'utf8'); quickpick.busy = true;
} finally {
quickpick.dispose(); const children = (await vscode.workspace.fs.readDirectory(folder)).map(([name]) => name);
quickpick.items = children.map(name => ({ label: name }));
quickpick.selectedItems = quickpick.items;
quickpick.busy = false;
const result = await Promise.race([
new Promise<readonly vscode.QuickPickItem[]>(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))),
new Promise<undefined>(c => quickpick.onDidHide(() => c(undefined)))
]);
if (!result) {
return;
}
const ignored = new Set(children);
result.forEach(c => ignored.delete(c.label));
const raw = [...ignored].map(i => `/${i}`).join('\n');
const encoder = new TextEncoder();
await vscode.workspace.fs.writeFile(gitignore, encoder.encode(raw));
} finally {
quickpick.dispose();
}
}
} }
const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => { const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => {
@@ -143,7 +157,7 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
progress.report({ message: 'Creating first commit', increment: 25 }); progress.report({ message: 'Creating first commit', increment: 25 });
if (!repository) { if (!repository) {
repository = await gitAPI.init(folder.uri) || undefined; repository = await gitAPI.init(folder) || undefined;
if (!repository) { if (!repository) {
return; return;

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export function dispose(arg: vscode.Disposable | Iterable<vscode.Disposable>): void {
if (arg instanceof vscode.Disposable) {
arg.dispose();
} else {
for (const disposable of arg) {
disposable.dispose();
}
}
}
export function combinedDisposable(disposables: Iterable<vscode.Disposable>): vscode.Disposable {
return {
dispose() {
dispose(disposables);
}
};
}

View File

@@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"description": "Dependencies shared by all extensions", "description": "Dependencies shared by all extensions",
"dependencies": { "dependencies": {
"typescript": "3.9.5" "typescript": "3.9.6"
}, },
"scripts": { "scripts": {
"postinstall": "node ./postinstall" "postinstall": "node ./postinstall"

View File

@@ -122,5 +122,11 @@
"foreground": "#CBEDCB", "foreground": "#CBEDCB",
} }
} }
] ],
"semanticTokenColors": {
"newOperator": "#FFFFFF",
"stringLiteral": "#ce9178",
"customLiteral": "#DCDCAA",
"numberLiteral": "#b5cea8",
}
} }

View File

@@ -20,7 +20,7 @@
"statusBarItem.remoteBackground": "#16825D", "statusBarItem.remoteBackground": "#16825D",
"sideBarSectionHeader.background": "#0000", "sideBarSectionHeader.background": "#0000",
"sideBarSectionHeader.border": "#61616130", "sideBarSectionHeader.border": "#61616130",
"notebook.cellFocusBackground": "#c8ddf150", "notebook.focusedCellBackground": "#c8ddf150",
"notebook.cellBorderColor": "#dae3e9", "notebook.cellBorderColor": "#dae3e9",
"notebook.outputContainerBackgroundColor": "#c8ddf150", "notebook.outputContainerBackgroundColor": "#c8ddf150",
"notebook.focusedCellShadow": "#00315040" "notebook.focusedCellShadow": "#00315040"

View File

@@ -76,10 +76,10 @@ rimraf@^3.0.2:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
typescript@3.9.5: typescript@3.9.6:
version "3.9.5" version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"

View File

@@ -14,7 +14,7 @@
"preinstall": "node build/npm/preinstall.js", "preinstall": "node build/npm/preinstall.js",
"postinstall": "node build/npm/postinstall.js", "postinstall": "node build/npm/postinstall.js",
"compile": "gulp compile --max_old_space_size=4095", "compile": "gulp compile --max_old_space_size=4095",
"watch": "gulp watch --max_old_space_size=4095", "watch": "concurrently \"npm:watch-client\" \"npm:watch-extensions\"",
"watchd": "deemon yarn watch", "watchd": "deemon yarn watch",
"watch-webd": "deemon yarn watch-web", "watch-webd": "deemon yarn watch-web",
"kill-watchd": "deemon --kill yarn watch", "kill-watchd": "deemon --kill yarn watch",
@@ -22,6 +22,7 @@
"restart-watchd": "deemon --restart yarn watch", "restart-watchd": "deemon --restart yarn watch",
"restart-watch-webd": "deemon --restart yarn watch-web", "restart-watch-webd": "deemon --restart yarn watch-web",
"watch-client": "gulp watch-client --max_old_space_size=4095", "watch-client": "gulp watch-client --max_old_space_size=4095",
"watch-extensions": "gulp watch-extensions --max_old_space_size=4095",
"mocha": "mocha test/unit/node/all.js --delay", "mocha": "mocha test/unit/node/all.js --delay",
"precommit": "node build/gulpfile.hygiene.js", "precommit": "node build/gulpfile.hygiene.js",
"gulp": "gulp --max_old_space_size=8192", "gulp": "gulp --max_old_space_size=8192",
@@ -61,7 +62,7 @@
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6",
"http-proxy-agent": "^2.1.0", "http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3", "https-proxy-agent": "^2.2.3",
"iconv-lite-umd": "0.6.5", "iconv-lite-umd": "0.6.7",
"jquery": "3.5.0", "jquery": "3.5.0",
"jschardet": "2.1.1", "jschardet": "2.1.1",
"keytar": "^5.5.0", "keytar": "^5.5.0",
@@ -123,6 +124,7 @@
"ansi-colors": "^3.2.3", "ansi-colors": "^3.2.3",
"asar": "^0.14.0", "asar": "^0.14.0",
"chromium-pickle-js": "^0.2.0", "chromium-pickle-js": "^0.2.0",
"concurrently": "^5.2.0",
"copy-webpack-plugin": "^4.5.2", "copy-webpack-plugin": "^4.5.2",
"cson-parser": "^1.3.3", "cson-parser": "^1.3.3",
"css-loader": "^3.2.0", "css-loader": "^3.2.0",

View File

@@ -20,7 +20,7 @@
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6",
"http-proxy-agent": "^2.1.0", "http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3", "https-proxy-agent": "^2.2.3",
"iconv-lite-umd": "0.6.5", "iconv-lite-umd": "0.6.7",
"jquery": "3.5.0", "jquery": "3.5.0",
"jschardet": "2.1.1", "jschardet": "2.1.1",
"minimist": "^1.2.5", "minimist": "^1.2.5",

View File

@@ -14,7 +14,7 @@
"ansi_up": "^3.0.0", "ansi_up": "^3.0.0",
"chart.js": "^2.6.0", "chart.js": "^2.6.0",
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6",
"iconv-lite-umd": "0.6.5", "iconv-lite-umd": "0.6.7",
"jschardet": "2.1.1", "jschardet": "2.1.1",
"jquery": "3.5.0", "jquery": "3.5.0",
"ng2-charts": "^1.6.0", "ng2-charts": "^1.6.0",

View File

@@ -182,10 +182,10 @@ htmlparser2@^3.10.0:
inherits "^2.0.1" inherits "^2.0.1"
readable-stream "^3.1.1" readable-stream "^3.1.1"
iconv-lite-umd@0.6.5: iconv-lite-umd@0.6.7:
version "0.6.5" version "0.6.7"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.7.tgz#ee437e34b30f15dc00ec93ea65065e672770777c"
integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== integrity sha512-DT90zb7wL1B3I6DmYUMcfJeVdY19XigzDj5AtXbXEw9Jfi0+AVAxfn7ytvY7Xhr+GFn7nd7hPonapC37oo7iAQ==
inherits@^2.0.1, inherits@^2.0.3: inherits@^2.0.1, inherits@^2.0.3:
version "2.0.4" version "2.0.4"

View File

@@ -376,10 +376,10 @@ https-proxy-agent@^4.0.0:
agent-base "5" agent-base "5"
debug "4" debug "4"
iconv-lite-umd@0.6.5: iconv-lite-umd@0.6.7:
version "0.6.5" version "0.6.7"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.7.tgz#ee437e34b30f15dc00ec93ea65065e672770777c"
integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== integrity sha512-DT90zb7wL1B3I6DmYUMcfJeVdY19XigzDj5AtXbXEw9Jfi0+AVAxfn7ytvY7Xhr+GFn7nd7hPonapC37oo7iAQ==
inherits@^2.0.1, inherits@^2.0.3: inherits@^2.0.1, inherits@^2.0.3:
version "2.0.4" version "2.0.4"

View File

@@ -232,20 +232,23 @@ async function handleRoot(req, res) {
if (match) { if (match) {
const qs = new URLSearchParams(match[1]); const qs = new URLSearchParams(match[1]);
let ghPath = qs.get('gh'); let gh = qs.get('gh');
if (ghPath) { if (gh) {
if (!ghPath.startsWith('/')) { if (gh.startsWith('/')) {
ghPath = '/' + ghPath; gh = gh.substr(1);
} }
folderUri = { scheme: 'github', authority: 'HEAD', path: ghPath };
} else {
let csPath = qs.get('cs'); const [owner, repo, ...branch] = gh.split('/', 3);
if (csPath) { folderUri = { scheme: 'github', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` };
if (!csPath.startsWith('/')) { } else {
csPath = '/' + csPath; let cs = qs.get('cs');
if (cs) {
if (cs.startsWith('/')) {
cs = cs.substr(1);
} }
folderUri = { scheme: 'codespace', authority: 'HEAD', path: csPath };
const [owner, repo, ...branch] = cs.split('/');
folderUri = { scheme: 'codespace', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` };
} }
} }
} }

View File

@@ -31,7 +31,7 @@ import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/t
import { UNSAVED_GROUP_ID, mssqlProviderName } from 'sql/platform/connection/common/constants'; import { UNSAVED_GROUP_ID, mssqlProviderName } from 'sql/platform/connection/common/constants';
import { $ } from 'vs/base/browser/dom'; import { $ } from 'vs/base/browser/dom';
import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions'; import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions';
import { IViewsService, IView, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneContainer } from 'vs/workbench/common/views';
import { ConsoleLogService } from 'vs/platform/log/common/log'; import { ConsoleLogService } from 'vs/platform/log/common/log';
import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import { IProgressIndicator } from 'vs/platform/progress/common/progress';
import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPaneComposite } from 'vs/workbench/common/panecomposite';
@@ -111,6 +111,9 @@ suite('SQL Connection Tree Action tests', () => {
}); });
const viewsService = new class implements IViewsService { const viewsService = new class implements IViewsService {
getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer {
throw new Error('Method not implemented.');
}
getViewProgressIndicator(id: string): IProgressIndicator { getViewProgressIndicator(id: string): IProgressIndicator {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@@ -854,6 +854,7 @@ export interface IListOptions<T> {
readonly horizontalScrolling?: boolean; readonly horizontalScrolling?: boolean;
readonly additionalScrollHeight?: number; readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean; readonly transformOptimization?: boolean;
readonly smoothScrolling?: boolean;
} }
export interface IListStyles { export interface IListStyles {

View File

@@ -17,6 +17,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { getZoomFactor } from 'vs/base/browser/browser';
const HIDE_TIMEOUT = 500; const HIDE_TIMEOUT = 500;
const SCROLL_WHEEL_SENSITIVITY = 50; const SCROLL_WHEEL_SENSITIVITY = 50;
@@ -130,13 +131,18 @@ export class MouseWheelClassifier {
// } // }
} }
if (Math.abs(item.deltaX - Math.round(item.deltaX)) > 0 || Math.abs(item.deltaY - Math.round(item.deltaY)) > 0) { if (!this._isAlmostInt(item.deltaX) || !this._isAlmostInt(item.deltaY)) {
// non-integer deltas => indicator that this is not a physical mouse wheel // non-integer deltas => indicator that this is not a physical mouse wheel
score += 0.25; score += 0.25;
} }
return Math.min(Math.max(score, 0), 1); return Math.min(Math.max(score, 0), 1);
} }
private _isAlmostInt(value: number): boolean {
const delta = Math.abs(Math.round(value) - value);
return (delta < 0.01);
}
} }
export abstract class AbstractScrollableElement extends Widget { export abstract class AbstractScrollableElement extends Widget {
@@ -343,10 +349,11 @@ export abstract class AbstractScrollableElement extends Widget {
const classifier = MouseWheelClassifier.INSTANCE; const classifier = MouseWheelClassifier.INSTANCE;
if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) { if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {
if (platform.isWindows) { const osZoomFactor = window.devicePixelRatio / getZoomFactor();
// On Windows, the incoming delta events are multiplied with the device pixel ratio, if (platform.isWindows || platform.isLinux) {
// so to get a better classification, simply undo that. // On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor.
classifier.accept(Date.now(), e.deltaX / window.devicePixelRatio, e.deltaY / window.devicePixelRatio); // The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account.
classifier.accept(Date.now(), e.deltaX / osZoomFactor, e.deltaY / osZoomFactor);
} else { } else {
classifier.accept(Date.now(), e.deltaX, e.deltaY); classifier.accept(Date.now(), e.deltaX, e.deltaY);
} }

View File

@@ -951,6 +951,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly filterOnType?: boolean; readonly filterOnType?: boolean;
readonly smoothScrolling?: boolean; readonly smoothScrolling?: boolean;
readonly horizontalScrolling?: boolean; readonly horizontalScrolling?: boolean;
readonly expandOnlyOnDoubleClick?: boolean;
} }
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> { export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
@@ -1094,7 +1095,10 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
return super.onViewPointer(e); return super.onViewPointer(e);
} }
const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); const target = e.browserEvent.target as HTMLElement;
const onTwistie = hasClass(target, 'monaco-tl-twistie')
|| (hasClass(target, 'monaco-icon-label') && hasClass(target, 'folder-icon') && e.browserEvent.offsetX < 16);
let expandOnlyOnTwistieClick = false; let expandOnlyOnTwistieClick = false;
if (typeof this.tree.expandOnlyOnTwistieClick === 'function') { if (typeof this.tree.expandOnlyOnTwistieClick === 'function') {
@@ -1107,6 +1111,10 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
return super.onViewPointer(e); return super.onViewPointer(e);
} }
if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) {
return super.onViewPointer(e);
}
if (node.collapsible) { if (node.collapsible) {
const model = ((this.tree as any).model as ITreeModel<T, TFilterData, TRef>); // internal const model = ((this.tree as any).model as ITreeModel<T, TFilterData, TRef>); // internal
const location = model.getNodeLocation(node); const location = model.getNodeLocation(node);
@@ -1244,6 +1252,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
get filterOnType(): boolean { return !!this._options.filterOnType; } get filterOnType(): boolean { return !!this._options.filterOnType; }
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
private readonly _onDidUpdateOptions = new Emitter<IAbstractTreeOptions<T, TFilterData>>(); private readonly _onDidUpdateOptions = new Emitter<IAbstractTreeOptions<T, TFilterData>>();

View File

@@ -472,6 +472,8 @@ export namespace Codicon {
export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' }); export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' });
export const playCircle = new Codicon('play-circle', { character: '\\eba6' }); export const playCircle = new Codicon('play-circle', { character: '\\eba6' });
export const record = new Codicon('record', { character: '\\eba7' }); export const record = new Codicon('record', { character: '\\eba7' });
export const debugAltSmall = new Codicon('debug-alt-small', { character: '\\eba8' });
export const vmConnect = new Codicon('vm-connect', { character: '\\eba9' });
} }

View File

@@ -36,8 +36,7 @@ export interface IPopupOptions {
x?: number; x?: number;
y?: number; y?: number;
positioningItem?: number; positioningItem?: number;
onHide?: () => void;
} }
export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu'; export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu';
export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu';

View File

@@ -8,7 +8,7 @@ import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHAN
let contextMenuIdPool = 0; let contextMenuIdPool = 0;
export function popup(items: IContextMenuItem[], options?: IPopupOptions): void { export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide?: () => void): void {
const processedItems: IContextMenuItem[] = []; const processedItems: IContextMenuItem[] = [];
const contextMenuId = contextMenuIdPool++; const contextMenuId = contextMenuIdPool++;
@@ -28,8 +28,8 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void
ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); ipcRenderer.removeListener(onClickChannel, onClickChannelHandler);
if (options?.onHide) { if (onHide) {
options.onHide(); onHide();
} }
}); });

View File

@@ -1230,10 +1230,10 @@ export class QuickInputController extends Disposable {
this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined;
}, true)); }, true));
this._register(focusTracker.onDidBlur(() => { this._register(focusTracker.onDidBlur(() => {
this.previousFocusElement = undefined;
if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) {
this.hide(true); this.hide();
} }
this.previousFocusElement = undefined;
})); }));
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => {
inputBox.setFocus(); inputBox.setFocus();
@@ -1574,13 +1574,14 @@ export class QuickInputController extends Disposable {
} }
} }
hide(focusLost?: boolean) { hide() {
const controller = this.controller; const controller = this.controller;
if (controller) { if (controller) {
const focusChanged = !this.ui?.container.contains(document.activeElement);
this.controller = null; this.controller = null;
this.onHideEmitter.fire(); this.onHideEmitter.fire();
this.getUI().container.style.display = 'none'; this.getUI().container.style.display = 'none';
if (!focusLost) { if (!focusChanged) {
if (this.previousFocusElement && this.previousFocusElement.offsetParent) { if (this.previousFocusElement && this.previousFocusElement.offsetParent) {
this.previousFocusElement.focus(); this.previousFocusElement.focus();
this.previousFocusElement = undefined; this.previousFocusElement = undefined;

View File

@@ -277,6 +277,9 @@ class WorkspaceProvider implements IWorkspaceProvider {
(function () { (function () {
// Mark start of workbench
performance.mark('workbench-start');
// Find config by checking for DOM // Find config by checking for DOM
const configElement = document.getElementById('vscode-workbench-web-configuration'); const configElement = document.getElementById('vscode-workbench-web-configuration');
const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined;

View File

@@ -55,6 +55,8 @@ bootstrapWindow.load([
'vs/css!vs/workbench/workbench.desktop.main' 'vs/css!vs/workbench/workbench.desktop.main'
], ],
function (workbench, configuration) { function (workbench, configuration) {
// Mark start of workbench
perf.mark('didLoadWorkbenchMain'); perf.mark('didLoadWorkbenchMain');
performance.mark('workbench-start'); performance.mark('workbench-start');

View File

@@ -280,6 +280,12 @@ export class CommonFindController extends Disposable implements IEditorContribut
if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) { if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) {
let selectionSearchString = await this.getGlobalBufferTerm(); let selectionSearchString = await this.getGlobalBufferTerm();
if (!this._editor.hasModel()) {
// the editor has lost its model in the meantime
return;
}
if (selectionSearchString) { if (selectionSearchString) {
stateChanges.searchString = selectionSearchString; stateChanges.searchString = selectionSearchString;
} }
@@ -364,13 +370,14 @@ export class CommonFindController extends Disposable implements IEditorContribut
return ''; return '';
} }
public async setGlobalBufferTerm(text: string) { public setGlobalBufferTerm(text: string): void {
if (this._editor.getOption(EditorOption.find).globalFindClipboard if (this._editor.getOption(EditorOption.find).globalFindClipboard
&& this._clipboardService && this._clipboardService
&& this._editor.hasModel() && this._editor.hasModel()
&& !this._editor.getModel().isTooLargeForSyncing() && !this._editor.getModel().isTooLargeForSyncing()
) { ) {
await this._clipboardService.writeFindText(text); // intentionally not awaited
this._clipboardService.writeFindText(text);
} }
} }
} }
@@ -424,10 +431,12 @@ export class FindController extends CommonFindController implements IFindControl
await super._start(opts); await super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) { if (this._widget) {
this._widget!.focusReplaceInput(); if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
} else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) { this._widget.focusReplaceInput();
this._widget!.focusFindInput(); } else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) {
this._widget.focusFindInput();
}
} }
} }
@@ -470,10 +479,10 @@ export class StartFindAction extends EditorAction {
}); });
} }
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
let controller = CommonFindController.get(editor); let controller = CommonFindController.get(editor);
if (controller) { if (controller) {
controller.start({ await controller.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard, seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard,
@@ -508,7 +517,7 @@ export class StartFindWithSelectionAction extends EditorAction {
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> { public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
let controller = CommonFindController.get(editor); let controller = CommonFindController.get(editor);
if (controller) { if (controller) {
controller.start({ await controller.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: true, seedSearchStringFromSelection: true,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -518,15 +527,15 @@ export class StartFindWithSelectionAction extends EditorAction {
loop: editor.getOption(EditorOption.find).loop loop: editor.getOption(EditorOption.find).loop
}); });
return controller.setGlobalBufferTerm(controller.getState().searchString); controller.setGlobalBufferTerm(controller.getState().searchString);
} }
} }
} }
export abstract class MatchFindAction extends EditorAction { export abstract class MatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
let controller = CommonFindController.get(editor); let controller = CommonFindController.get(editor);
if (controller && !this._run(controller)) { if (controller && !this._run(controller)) {
controller.start({ await controller.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection, seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: true, seedSearchStringFromGlobalClipboard: true,
@@ -629,7 +638,7 @@ export class PreviousMatchFindAction2 extends MatchFindAction {
} }
export abstract class SelectionMatchFindAction extends EditorAction { export abstract class SelectionMatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
let controller = CommonFindController.get(editor); let controller = CommonFindController.get(editor);
if (!controller) { if (!controller) {
return; return;
@@ -639,7 +648,7 @@ export abstract class SelectionMatchFindAction extends EditorAction {
controller.setSearchString(selectionSearchString); controller.setSearchString(selectionSearchString);
} }
if (!this._run(controller)) { if (!this._run(controller)) {
controller.start({ await controller.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -720,7 +729,7 @@ export class StartFindReplaceAction extends EditorAction {
}); });
} }
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly)) { if (!editor.hasModel() || editor.getOption(EditorOption.readOnly)) {
return; return;
} }
@@ -745,7 +754,7 @@ export class StartFindReplaceAction extends EditorAction {
if (controller) { if (controller) {
controller.start({ await controller.start({
forceRevealReplace: true, forceRevealReplace: true,
seedSearchStringFromSelection: seedSearchStringFromSelection, seedSearchStringFromSelection: seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection,

View File

@@ -14,7 +14,7 @@ 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 { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController';
import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { withAsyncTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -55,7 +55,7 @@ function fromSelection(slc: Selection): number[] {
return [slc.startLineNumber, slc.startColumn, slc.endLineNumber, slc.endColumn]; return [slc.startLineNumber, slc.startColumn, slc.endLineNumber, slc.endColumn];
} }
suite.skip('FindController', () => { suite.skip('FindController', async () => {
let queryState: { [key: string]: any; } = {}; let queryState: { [key: string]: any; } = {};
let clipboardState = ''; let clipboardState = '';
let serviceCollection = new ServiceCollection(); let serviceCollection = new ServiceCollection();
@@ -77,13 +77,13 @@ suite.skip('FindController', () => {
}); });
} }
/* test('stores to the global clipboard buffer on start find action', () => { /* test('stores to the global clipboard buffer on start find action', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'ABC', 'ABC',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
if (!platform.isMacintosh) { if (!platform.isMacintosh) {
assert.ok(true); assert.ok(true);
@@ -101,13 +101,13 @@ suite.skip('FindController', () => {
}); });
}); });
test('reads from the global clipboard buffer on next find action if buffer exists', () => { test('reads from the global clipboard buffer on next find action if buffer exists', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'ABC', 'ABC',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = 'ABC'; clipboardState = 'ABC';
if (!platform.isMacintosh) { if (!platform.isMacintosh) {
@@ -128,13 +128,13 @@ suite.skip('FindController', () => {
}); });
}); });
test('writes to the global clipboard buffer when text changes', () => { test('writes to the global clipboard buffer when text changes', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'ABC', 'ABC',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
if (!platform.isMacintosh) { if (!platform.isMacintosh) {
assert.ok(true); assert.ok(true);
@@ -152,13 +152,13 @@ suite.skip('FindController', () => {
}); });
}); */ }); */
test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => { test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'ABC', 'ABC',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
// The cursor is at the very top, of the file, at the first ABC // The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
@@ -167,7 +167,7 @@ suite.skip('FindController', () => {
let nextMatchFindAction = new NextMatchFindAction(); let nextMatchFindAction = new NextMatchFindAction();
// I hit Ctrl+F to show the Find dialog // I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor); await startFindAction.run(null, editor);
// I type ABC. // I type ABC.
findState.change({ searchString: 'A' }, true); findState.change({ searchString: 'A' }, true);
@@ -201,7 +201,7 @@ suite.skip('FindController', () => {
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 4, 1, 4]); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 4, 1, 4]);
// I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ. // I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ.
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
assert.equal(findState.searchString, 'ABC'); assert.equal(findState.searchString, 'ABC');
assert.equal(findController.hasFocus, false); assert.equal(findController.hasFocus, false);
@@ -210,10 +210,10 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #3090: F3 does not loop with two matches on a single line', () => { test('issue #3090: F3 does not loop with two matches on a single line', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'import nls = require(\'vs/nls\');' 'import nls = require(\'vs/nls\');'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let nextMatchFindAction = new NextMatchFindAction(); let nextMatchFindAction = new NextMatchFindAction();
@@ -223,22 +223,22 @@ suite.skip('FindController', () => {
column: 9 column: 9
}); });
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 26, 1, 29]); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 26, 1, 29]);
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 8, 1, 11]); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 8, 1, 11]);
findController.dispose(); findController.dispose();
}); });
}); });
test('issue #6149: Auto-escape highlighted text for search and replace regex mode', () => { test('issue #6149: Auto-escape highlighted text for search and replace regex mode', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
@@ -247,22 +247,22 @@ suite.skip('FindController', () => {
editor.setSelection(new Selection(1, 9, 1, 13)); editor.setSelection(new Selection(1, 9, 1, 13));
findController.toggleRegex(); findController.toggleRegex();
startFindAction.run(null, editor); await startFindAction.run(null, editor);
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]); assert.deepEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]);
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 9, 1, 13]); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 9, 1, 13]);
findController.dispose(); findController.dispose();
}); });
}); });
test('issue #41027: Don\'t replace find input value on replace action if find input is active', () => { test('issue #41027: Don\'t replace find input value on replace action if find input is active', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'test', 'test',
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
let testRegexString = 'tes.'; let testRegexString = 'tes.';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let nextMatchFindAction = new NextMatchFindAction(); let nextMatchFindAction = new NextMatchFindAction();
@@ -270,7 +270,7 @@ suite.skip('FindController', () => {
findController.toggleRegex(); findController.toggleRegex();
findController.setSearchString(testRegexString); findController.setSearchString(testRegexString);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -279,8 +279,8 @@ suite.skip('FindController', () => {
updateSearchScope: false, updateSearchScope: false,
loop: true loop: true
}); });
nextMatchFindAction.run(null, editor); await nextMatchFindAction.run(null, editor);
startFindReplaceAction.run(null, editor); await startFindReplaceAction.run(null, editor);
assert.equal(findController.getState().searchString, testRegexString); assert.equal(findController.getState().searchString, testRegexString);
@@ -288,15 +288,15 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #9043: Clear search scope when find widget is hidden', () => { test('issue #9043: Clear search scope when find widget is hidden', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -319,15 +319,15 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #18111: Regex replace with single space replaces with no space', () => { test('issue #18111: Regex replace with single space replaces with no space', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'HRESULT OnAmbientPropertyChange(DISPID dispid);' 'HRESULT OnAmbientPropertyChange(DISPID dispid);'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
startFindAction.run(null, editor); await startFindAction.run(null, editor);
findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false); findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false);
findController.moveToNextMatch(); findController.moveToNextMatch();
@@ -344,17 +344,17 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #24714: Regular expression with ^ in search & replace', () => { test('issue #24714: Regular expression with ^ in search & replace', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'', '',
'line2', 'line2',
'line3' 'line3'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
startFindAction.run(null, editor); await startFindAction.run(null, editor);
findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false); findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false);
findController.moveToNextMatch(); findController.moveToNextMatch();
@@ -371,12 +371,12 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #38232: Find Next Selection, regex enabled', () => { test('issue #38232: Find Next Selection, regex enabled', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'([funny]', '([funny]',
'', '',
'([funny]' '([funny]'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction(); let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
@@ -388,7 +388,7 @@ suite.skip('FindController', () => {
editor.setSelection(new Selection(1, 1, 1, 9)); editor.setSelection(new Selection(1, 1, 1, 9));
// cmd+f3 // cmd+f3
nextSelectionMatchFindAction.run(null, editor); await nextSelectionMatchFindAction.run(null, editor);
assert.deepEqual(editor.getSelections()!.map(fromSelection), [ assert.deepEqual(editor.getSelections()!.map(fromSelection), [
[3, 1, 3, 9] [3, 1, 3, 9]
@@ -398,19 +398,19 @@ suite.skip('FindController', () => {
}); });
}); });
test('issue #38232: Find Next Selection, regex enabled, find widget open', () => { test('issue #38232: Find Next Selection, regex enabled, find widget open', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'([funny]', '([funny]',
'', '',
'([funny]' '([funny]'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
clipboardState = ''; clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction(); let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
// cmd+f - open find widget // cmd+f - open find widget
startFindAction.run(null, editor); await startFindAction.run(null, editor);
// toggle regex // toggle regex
findController.getState().change({ isRegex: true }, false); findController.getState().change({ isRegex: true }, false);
@@ -419,7 +419,7 @@ suite.skip('FindController', () => {
editor.setSelection(new Selection(1, 1, 1, 9)); editor.setSelection(new Selection(1, 1, 1, 9));
// cmd+f3 // cmd+f3
nextSelectionMatchFindAction.run(null, editor); await nextSelectionMatchFindAction.run(null, editor);
assert.deepEqual(editor.getSelections()!.map(fromSelection), [ assert.deepEqual(editor.getSelections()!.map(fromSelection), [
[3, 1, 3, 9] [3, 1, 3, 9]
@@ -430,7 +430,7 @@ suite.skip('FindController', () => {
}); });
}); });
suite.skip('FindController query options persistence', () => { suite.skip('FindController query options persistence', async () => {
let queryState: { [key: string]: any; } = {}; let queryState: { [key: string]: any; } = {};
queryState['editor.isRegex'] = false; queryState['editor.isRegex'] = false;
queryState['editor.matchCase'] = false; queryState['editor.matchCase'] = false;
@@ -447,13 +447,13 @@ suite.skip('FindController query options persistence', () => {
remove: () => undefined remove: () => undefined
} as any); } as any);
test('matchCase', () => { test('matchCase', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'abc', 'abc',
'ABC', 'ABC',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false }; queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false };
// The cursor is at the very top, of the file, at the first ABC // The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
@@ -461,7 +461,7 @@ suite.skip('FindController query options persistence', () => {
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
// I hit Ctrl+F to show the Find dialog // I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor); await startFindAction.run(null, editor);
// I type ABC. // I type ABC.
findState.change({ searchString: 'ABC' }, true); findState.change({ searchString: 'ABC' }, true);
@@ -474,13 +474,13 @@ suite.skip('FindController query options persistence', () => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
test('wholeWord', () => { test('wholeWord', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'AB', 'AB',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// The cursor is at the very top, of the file, at the first ABC // The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
@@ -488,7 +488,7 @@ suite.skip('FindController query options persistence', () => {
let startFindAction = new StartFindAction(); let startFindAction = new StartFindAction();
// I hit Ctrl+F to show the Find dialog // I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor); await startFindAction.run(null, editor);
// I type AB. // I type AB.
findState.change({ searchString: 'AB' }, true); findState.change({ searchString: 'AB' }, true);
@@ -499,13 +499,13 @@ suite.skip('FindController query options persistence', () => {
}); });
}); });
test('toggling options is saved', () => { test('toggling options is saved', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'ABC', 'ABC',
'AB', 'AB',
'XYZ', 'XYZ',
'ABC' 'ABC'
], { serviceCollection: serviceCollection }, (editor) => { ], { serviceCollection: serviceCollection }, async (editor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// The cursor is at the very top, of the file, at the first ABC // The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
@@ -516,17 +516,17 @@ suite.skip('FindController query options persistence', () => {
}); });
}); });
test('issue #27083: Update search scope once find widget becomes visible', () => { test('issue #27083: Update search scope once find widget becomes visible', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor) => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
// clipboardState = ''; // clipboardState = '';
editor.setSelection(new Range(1, 1, 2, 1)); editor.setSelection(new Range(1, 1, 2, 1));
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -540,17 +540,17 @@ suite.skip('FindController query options persistence', () => {
}); });
}); });
test('issue #58604: Do not update searchScope if it is empty', () => { test('issue #58604: Do not update searchScope if it is empty', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor) => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
// clipboardState = ''; // clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 2)); editor.setSelection(new Range(1, 2, 1, 2));
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -564,17 +564,17 @@ suite.skip('FindController query options persistence', () => {
}); });
}); });
test('issue #58604: Update searchScope if it is not empty', () => { test('issue #58604: Update searchScope if it is not empty', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor) => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
// clipboardState = ''; // clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 3)); editor.setSelection(new Range(1, 2, 1, 3));
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,
@@ -589,17 +589,17 @@ suite.skip('FindController query options persistence', () => {
}); });
test('issue #27083: Find in selection when multiple lines are selected', () => { test('issue #27083: Find in selection when multiple lines are selected', async () => {
withTestCodeEditor([ await withAsyncTestCodeEditor([
'var x = (3 * 5)', 'var x = (3 * 5)',
'var y = (3 * 5)', 'var y = (3 * 5)',
'var z = (3 * 5)', 'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, (editor) => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, async (editor) => {
// clipboardState = ''; // clipboardState = '';
editor.setSelection(new Range(1, 6, 2, 1)); editor.setSelection(new Range(1, 6, 2, 1));
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.start({ await findController.start({
forceRevealReplace: false, forceRevealReplace: false,
seedSearchStringFromSelection: false, seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false, seedSearchStringFromGlobalClipboard: false,

View File

@@ -96,6 +96,24 @@ export function withTestCodeEditor(text: string | string[] | null, options: Test
editor.dispose(); editor.dispose();
} }
export async function withAsyncTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => Promise<void>): Promise<void> {
// create a model if necessary and remember it in order to dispose it.
if (!options.model) {
if (typeof text === 'string') {
options.model = createTextModel(text);
} else if (text) {
options.model = createTextModel(text.join('\n'));
}
}
const editor = createTestCodeEditor(options);
const viewModel = editor.getViewModel()!;
viewModel.setHasFocus(true);
await callback(<ITestCodeEditor>editor, editor.getViewModel()!);
editor.dispose();
}
export function createTestCodeEditor(options: TestCodeEditorCreationOptions): ITestCodeEditor { export function createTestCodeEditor(options: TestCodeEditorCreationOptions): ITestCodeEditor {
const model = options.model; const model = options.model;

View File

@@ -870,6 +870,12 @@ var AMDLoader;
} }
var cachedData = script.createCachedData(); var cachedData = script.createCachedData();
if (cachedData.length === 0 || cachedData.length === lastSize || iteration >= 5) { if (cachedData.length === 0 || cachedData.length === lastSize || iteration >= 5) {
// done
return;
}
if (cachedData.length < lastSize) {
// less data than before: skip, try again next round
createLoop();
return; return;
} }
lastSize = cachedData.length; lastSize = cachedData.length;

View File

@@ -23,4 +23,3 @@ export class ThemableCheckboxActionViewItem extends CheckboxActionViewItem {
} }
} }

View File

@@ -0,0 +1,246 @@
/*---------------------------------------------------------------------------------------------
* 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 { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { URI } from 'vs/base/common/uri';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Event } from 'vs/base/common/event';
import { NullLogService } from 'vs/platform/log/common/log';
import { FileService } from 'vs/platform/files/common/fileService';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
suite('ConfigurationService', () => {
let fileService: IFileService;
let settingsResource: URI;
const disposables: DisposableStore = new DisposableStore();
setup(async () => {
fileService = disposables.add(new FileService(new NullLogService()));
const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider());
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
settingsResource = URI.file('settings.json');
});
teardown(() => disposables.clear());
test('simple', async () => {
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await testObject.initialize();
const config = testObject.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
});
test('config gets flattened', async () => {
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await testObject.initialize();
const config = testObject.getValue<{
testworkbench: {
editor: {
tabs: boolean;
};
};
}>();
assert.ok(config);
assert.ok(config.testworkbench);
assert.ok(config.testworkbench.editor);
assert.equal(config.testworkbench.editor.tabs, true);
});
test('error case does not explode', async () => {
await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,'));
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await testObject.initialize();
const config = testObject.getValue<{
foo: string;
}>();
assert.ok(config);
});
test('missing file does not explode', async () => {
const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
await testObject.initialize();
const config = testObject.getValue<{ foo: string }>();
assert.ok(config);
});
test('trigger configuration change event when file does not exist', async () => {
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await testObject.initialize();
return new Promise(async (c, e) => {
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => {
assert.equal(testObject.getValue('foo'), 'bar');
c();
}));
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
});
});
test('trigger configuration change event when file exists', async () => {
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
await testObject.initialize();
return new Promise((c, e) => {
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
assert.equal(testObject.getValue('foo'), 'barz');
c();
}));
fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }'));
});
});
test('reloadConfiguration', async () => {
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
await testObject.initialize();
let config = testObject.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "changed" }'));
// force a reload to get latest
await testObject.reloadConfiguration();
config = testObject.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'changed');
});
test('model defaults', async () => {
interface ITestSetting {
configuration: {
service: {
testSetting: string;
}
};
}
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_test',
'type': 'object',
'properties': {
'configuration.service.testSetting': {
'type': 'string',
'default': 'isSet'
}
}
});
let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
await testObject.initialize();
let setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }'));
await testObject.reloadConfiguration();
setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isChanged');
});
test('lookup', async () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_test',
'type': 'object',
'properties': {
'lookup.service.testSetting': {
'type': 'string',
'default': 'isSet'
}
}
});
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
testObject.initialize();
let res = testObject.inspect('something.missing');
assert.strictEqual(res.value, undefined);
assert.strictEqual(res.defaultValue, undefined);
assert.strictEqual(res.userValue, undefined);
res = testObject.inspect('lookup.service.testSetting');
assert.strictEqual(res.defaultValue, 'isSet');
assert.strictEqual(res.value, 'isSet');
assert.strictEqual(res.userValue, undefined);
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testSetting": "bar" }'));
await testObject.reloadConfiguration();
res = testObject.inspect('lookup.service.testSetting');
assert.strictEqual(res.defaultValue, 'isSet');
assert.strictEqual(res.userValue, 'bar');
assert.strictEqual(res.value, 'bar');
});
test('lookup with null', async () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_testNull',
'type': 'object',
'properties': {
'lookup.service.testNullSetting': {
'type': 'null',
}
}
});
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
testObject.initialize();
let res = testObject.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.defaultValue, null);
assert.strictEqual(res.value, null);
assert.strictEqual(res.userValue, undefined);
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testNullSetting": null }'));
await testObject.reloadConfiguration();
res = testObject.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.defaultValue, null);
assert.strictEqual(res.value, null);
assert.strictEqual(res.userValue, null);
});
});

View File

@@ -1,304 +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';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as fs from 'fs';
import { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { testFile } from 'vs/base/test/node/utils';
import { URI } from 'vs/base/common/uri';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Event } from 'vs/base/common/event';
import { NullLogService } from 'vs/platform/log/common/log';
import { FileService } from 'vs/platform/files/common/fileService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
suite('ConfigurationService - Node', () => {
let fileService: IFileService;
const disposables: IDisposable[] = [];
setup(() => {
const logService = new NullLogService();
fileService = new FileService(logService);
disposables.push(fileService);
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
disposables.push(diskFileSystemProvider);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
});
test('simple', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
service.dispose();
return res.cleanUp();
});
test('config gets flattened', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
testworkbench: {
editor: {
tabs: boolean;
};
};
}>();
assert.ok(config);
assert.ok(config.testworkbench);
assert.ok(config.testworkbench.editor);
assert.equal(config.testworkbench.editor.tabs, true);
service.dispose();
return res.cleanUp();
});
test('error case does not explode', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, ',,,,');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
foo: string;
}>();
assert.ok(config);
service.dispose();
return res.cleanUp();
});
test('missing file does not explode', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'config', id);
const testFile = path.join(newDir, 'config.json');
const service = new ConfigurationService(URI.file(testFile), fileService);
await service.initialize();
const config = service.getValue<{ foo: string }>();
assert.ok(config);
service.dispose();
});
test('trigger configuration change event when file does not exist', async () => {
const res = await testFile('config', 'config.json');
const settingsFile = URI.file(res.testFile);
const service = new ConfigurationService(settingsFile, fileService);
await service.initialize();
return new Promise(async (c, e) => {
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
disposable.dispose();
assert.equal(service.getValue('foo'), 'bar');
service.dispose();
await res.cleanUp();
c();
});
await fileService.writeFile(settingsFile, VSBuffer.fromString('{ "foo": "bar" }'));
});
});
test('trigger configuration change event when file exists', async () => {
const res = await testFile('config', 'config.json');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
await service.initialize();
return new Promise((c, e) => {
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
disposable.dispose();
assert.equal(service.getValue('foo'), 'barz');
service.dispose();
await res.cleanUp();
c();
});
fs.writeFileSync(res.testFile, '{ "foo": "barz" }');
});
});
test('reloadConfiguration', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
let config = service.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
fs.writeFileSync(res.testFile, '{ "foo": "changed" }');
// still outdated
config = service.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
// force a reload to get latest
await service.reloadConfiguration();
config = service.getValue<{
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'changed');
service.dispose();
return res.cleanUp();
});
test('model defaults', async () => {
interface ITestSetting {
configuration: {
service: {
testSetting: string;
}
};
}
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_test',
'type': 'object',
'properties': {
'configuration.service.testSetting': {
'type': 'string',
'default': 'isSet'
}
}
});
let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'), fileService);
await serviceWithoutFile.initialize();
let setting = serviceWithoutFile.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
return testFile('config', 'config.json').then(async res => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
let setting = service.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
fs.writeFileSync(res.testFile, '{ "configuration.service.testSetting": "isChanged" }');
await service.reloadConfiguration();
let setting_1 = service.getValue<ITestSetting>();
assert.ok(setting_1);
assert.equal(setting_1.configuration.service.testSetting, 'isChanged');
service.dispose();
serviceWithoutFile.dispose();
return res.cleanUp();
});
});
test('lookup', async () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_test',
'type': 'object',
'properties': {
'lookup.service.testSetting': {
'type': 'string',
'default': 'isSet'
}
}
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(URI.file(r.testFile), fileService);
service.initialize();
let res = service.inspect('something.missing');
assert.strictEqual(res.value, undefined);
assert.strictEqual(res.defaultValue, undefined);
assert.strictEqual(res.userValue, undefined);
res = service.inspect('lookup.service.testSetting');
assert.strictEqual(res.defaultValue, 'isSet');
assert.strictEqual(res.value, 'isSet');
assert.strictEqual(res.userValue, undefined);
fs.writeFileSync(r.testFile, '{ "lookup.service.testSetting": "bar" }');
await service.reloadConfiguration();
res = service.inspect('lookup.service.testSetting');
assert.strictEqual(res.defaultValue, 'isSet');
assert.strictEqual(res.userValue, 'bar');
assert.strictEqual(res.value, 'bar');
service.dispose();
return r.cleanUp();
});
test('lookup with null', async () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_testNull',
'type': 'object',
'properties': {
'lookup.service.testNullSetting': {
'type': 'null',
}
}
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(URI.file(r.testFile), fileService);
service.initialize();
let res = service.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.defaultValue, null);
assert.strictEqual(res.value, null);
assert.strictEqual(res.userValue, undefined);
fs.writeFileSync(r.testFile, '{ "lookup.service.testNullSetting": null }');
await service.reloadConfiguration();
res = service.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.defaultValue, null);
assert.strictEqual(res.value, null);
assert.strictEqual(res.userValue, null);
service.dispose();
return r.cleanUp();
});
});

View File

@@ -63,8 +63,6 @@ export class IndexedDB {
} }
class IndexedDBFileSystemProvider extends KeyValueFileSystemProvider { class IndexedDBFileSystemProvider extends KeyValueFileSystemProvider {
constructor(scheme: string, private readonly database: IDBDatabase, private readonly store: string) { constructor(scheme: string, private readonly database: IDBDatabase, private readonly store: string) {

View File

@@ -26,19 +26,19 @@ export class FileService extends Disposable implements IFileService {
private readonly BUFFER_SIZE = 64 * 1024; private readonly BUFFER_SIZE = 64 * 1024;
constructor(@ILogService private logService: ILogService) { constructor(@ILogService private readonly logService: ILogService) {
super(); super();
} }
//#region File System Provider //#region File System Provider
private _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter<IFileSystemProviderRegistrationEvent>()); private readonly _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter<IFileSystemProviderRegistrationEvent>());
readonly onDidChangeFileSystemProviderRegistrations = this._onDidChangeFileSystemProviderRegistrations.event; readonly onDidChangeFileSystemProviderRegistrations = this._onDidChangeFileSystemProviderRegistrations.event;
private _onWillActivateFileSystemProvider = this._register(new Emitter<IFileSystemProviderActivationEvent>()); private readonly _onWillActivateFileSystemProvider = this._register(new Emitter<IFileSystemProviderActivationEvent>());
readonly onWillActivateFileSystemProvider = this._onWillActivateFileSystemProvider.event; readonly onWillActivateFileSystemProvider = this._onWillActivateFileSystemProvider.event;
private _onDidChangeFileSystemProviderCapabilities = this._register(new Emitter<IFileSystemProviderCapabilitiesChangeEvent>()); private readonly _onDidChangeFileSystemProviderCapabilities = this._register(new Emitter<IFileSystemProviderCapabilitiesChangeEvent>());
readonly onDidChangeFileSystemProviderCapabilities = this._onDidChangeFileSystemProviderCapabilities.event; readonly onDidChangeFileSystemProviderCapabilities = this._onDidChangeFileSystemProviderCapabilities.event;
private readonly provider = new Map<string, IFileSystemProvider>(); private readonly provider = new Map<string, IFileSystemProvider>();
@@ -146,10 +146,10 @@ export class FileService extends Disposable implements IFileService {
//#endregion //#endregion
private _onDidRunOperation = this._register(new Emitter<FileOperationEvent>()); private readonly _onDidRunOperation = this._register(new Emitter<FileOperationEvent>());
readonly onDidRunOperation = this._onDidRunOperation.event; readonly onDidRunOperation = this._onDidRunOperation.event;
private _onError = this._register(new Emitter<Error>()); private readonly _onError = this._register(new Emitter<Error>());
readonly onError = this._onError.event; readonly onError = this._onError.event;
//#region File Metadata Resolving //#region File Metadata Resolving
@@ -881,10 +881,10 @@ export class FileService extends Disposable implements IFileService {
//#region File Watching //#region File Watching
private _onDidFilesChange = this._register(new Emitter<FileChangesEvent>()); private readonly _onDidFilesChange = this._register(new Emitter<FileChangesEvent>());
readonly onDidFilesChange = this._onDidFilesChange.event; readonly onDidFilesChange = this._onDidFilesChange.event;
private activeWatchers = new Map<string, { disposable: IDisposable, count: number }>(); private readonly activeWatchers = new Map<string, { disposable: IDisposable, count: number }>();
watch(resource: URI, options: IWatchOptions = { recursive: false, excludes: [] }): IDisposable { watch(resource: URI, options: IWatchOptions = { recursive: false, excludes: [] }): IDisposable {
let watchDisposed = false; let watchDisposed = false;
@@ -950,7 +950,7 @@ export class FileService extends Disposable implements IFileService {
//#region Helpers //#region Helpers
private writeQueues: Map<string, Queue<void>> = new Map(); private readonly writeQueues: Map<string, Queue<void>> = new Map();
private ensureWriteQueue(provider: IFileSystemProvider, resource: URI): Queue<void> { private ensureWriteQueue(provider: IFileSystemProvider, resource: URI): Queue<void> {
const { extUri } = this.getExtUri(provider); const { extUri } = this.getExtUri(provider);

View File

@@ -15,7 +15,7 @@ export class DiskFileSystemProvider extends NodeDiskFileSystemProvider {
constructor( constructor(
logService: ILogService, logService: ILogService,
private electronService: IElectronService, private readonly electronService: IElectronService,
options?: IDiskFileSystemProviderOptions options?: IDiskFileSystemProviderOptions
) { ) {
super(logService, options); super(logService, options);

View File

@@ -46,7 +46,10 @@ export class DiskFileSystemProvider extends Disposable implements
private readonly BUFFER_SIZE = this.options?.bufferSize || 64 * 1024; private readonly BUFFER_SIZE = this.options?.bufferSize || 64 * 1024;
constructor(private logService: ILogService, private options?: IDiskFileSystemProviderOptions) { constructor(
private readonly logService: ILogService,
private readonly options?: IDiskFileSystemProviderOptions
) {
super(); super();
} }
@@ -198,9 +201,9 @@ export class DiskFileSystemProvider extends Disposable implements
} }
} }
private mapHandleToPos: Map<number, number> = new Map(); private readonly mapHandleToPos: Map<number, number> = new Map();
private writeHandles: Set<number> = new Set(); private readonly writeHandles: Set<number> = new Set();
private canFlush: boolean = true; private canFlush: boolean = true;
async open(resource: URI, opts: FileOpenOptions): Promise<number> { async open(resource: URI, opts: FileOpenOptions): Promise<number> {
@@ -502,14 +505,14 @@ export class DiskFileSystemProvider extends Disposable implements
//#region File Watching //#region File Watching
private _onDidWatchErrorOccur = this._register(new Emitter<string>()); private readonly _onDidWatchErrorOccur = this._register(new Emitter<string>());
readonly onDidErrorOccur = this._onDidWatchErrorOccur.event; readonly onDidErrorOccur = this._onDidWatchErrorOccur.event;
private _onDidChangeFile = this._register(new Emitter<readonly IFileChange[]>()); private readonly _onDidChangeFile = this._register(new Emitter<readonly IFileChange[]>());
readonly onDidChangeFile = this._onDidChangeFile.event; readonly onDidChangeFile = this._onDidChangeFile.event;
private recursiveWatcher: WindowsWatcherService | UnixWatcherService | NsfwWatcherService | undefined; private recursiveWatcher: WindowsWatcherService | UnixWatcherService | NsfwWatcherService | undefined;
private recursiveFoldersToWatch: { path: string, excludes: string[] }[] = []; private readonly recursiveFoldersToWatch: { path: string, excludes: string[] }[] = [];
private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer<void>(0)); private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer<void>(0));
private recursiveWatcherLogLevelListener: IDisposable | undefined; private recursiveWatcherLogLevelListener: IDisposable | undefined;

View File

@@ -179,6 +179,8 @@ function toWorkbenchListOptions<T>(options: IListOptions<T>, configurationServic
} }
}; };
result.smoothScrolling = configurationService.getValue<boolean>(listSmoothScrolling);
return [result, disposables]; return [result, disposables];
} }
@@ -193,7 +195,6 @@ export interface IWorkbenchListOptions<T> extends IWorkbenchListOptionsUpdate, I
export class WorkbenchList<T> extends List<T> { export class WorkbenchList<T> extends List<T> {
readonly contextKeyService: IContextKeyService; readonly contextKeyService: IContextKeyService;
private readonly configurationService: IConfigurationService;
private readonly themeService: IThemeService; private readonly themeService: IThemeService;
private listHasSelectionOrFocus: IContextKey<boolean>; private listHasSelectionOrFocus: IContextKey<boolean>;
@@ -231,7 +232,6 @@ export class WorkbenchList<T> extends List<T> {
this.disposables.add(workbenchListOptionsDisposable); this.disposables.add(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this); this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
this.configurationService = configurationService;
this.themeService = themeService; this.themeService = themeService;
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
@@ -265,8 +265,25 @@ export class WorkbenchList<T> extends List<T> {
this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
})); }));
this.disposables.add(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
this.registerListeners(); let options: IListOptionsUpdate = {};
if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) {
const horizontalScrolling = configurationService.getValue<boolean>(horizontalScrollingKey);
options = { ...options, horizontalScrolling };
}
if (e.affectsConfiguration(listSmoothScrolling)) {
const smoothScrolling = configurationService.getValue<boolean>(listSmoothScrolling);
options = { ...options, smoothScrolling };
}
if (Object.keys(options).length > 0) {
this.updateOptions(options);
}
}));
} }
updateOptions(options: IWorkbenchListOptionsUpdate): void { updateOptions(options: IWorkbenchListOptionsUpdate): void {
@@ -292,18 +309,6 @@ export class WorkbenchList<T> extends List<T> {
this._styler = attachListStyler(this, this.themeService, styles); this._styler = attachListStyler(this, this.themeService, styles);
} }
private registerListeners(): void {
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
}
if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) {
const horizontalScrolling = this.configurationService.getValue<boolean>(horizontalScrollingKey);
this.updateOptions({ horizontalScrolling });
}
}));
}
get useAltAsMultipleSelectionModifier(): boolean { get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier; return this._useAltAsMultipleSelectionModifier;
} }
@@ -316,7 +321,6 @@ export interface IWorkbenchPagedListOptions<T> extends IWorkbenchListOptionsUpda
export class WorkbenchPagedList<T> extends PagedList<T> { export class WorkbenchPagedList<T> extends PagedList<T> {
readonly contextKeyService: IContextKeyService; readonly contextKeyService: IContextKeyService;
private readonly configurationService: IConfigurationService;
private readonly disposables: DisposableStore; private readonly disposables: DisposableStore;
@@ -350,7 +354,6 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
this.disposables.add(workbenchListOptionsDisposable); this.disposables.add(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this); this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
this.configurationService = configurationService;
this.horizontalScrolling = options.horizontalScrolling; this.horizontalScrolling = options.horizontalScrolling;
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
@@ -365,17 +368,23 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
this.disposables.add(attachListStyler(this, themeService, options.overrideStyles)); this.disposables.add(attachListStyler(this, themeService, options.overrideStyles));
} }
this.registerListeners(); this.disposables.add(configurationService.onDidChangeConfiguration(e => {
}
private registerListeners(): void {
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(multiSelectModifierSettingKey)) { if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
} }
let options: IListOptionsUpdate = {};
if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) { if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) {
const horizontalScrolling = this.configurationService.getValue<boolean>(horizontalScrollingKey); const horizontalScrolling = configurationService.getValue<boolean>(horizontalScrollingKey);
this.updateOptions({ horizontalScrolling }); options = { ...options, horizontalScrolling };
}
if (e.affectsConfiguration(listSmoothScrolling)) {
const smoothScrolling = configurationService.getValue<boolean>(listSmoothScrolling);
options = { ...options, smoothScrolling };
}
if (Object.keys(options).length > 0) {
this.updateOptions(options);
} }
})); }));
} }
@@ -836,7 +845,8 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
horizontalScrolling, horizontalScrolling,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService), keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService),
additionalScrollHeight, additionalScrollHeight,
hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements,
expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick'
} as TOptions } as TOptions
}; };
} }
@@ -933,6 +943,9 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
const horizontalScrolling = configurationService.getValue<boolean>(horizontalScrollingKey); const horizontalScrolling = configurationService.getValue<boolean>(horizontalScrollingKey);
newOptions = { ...newOptions, horizontalScrolling }; newOptions = { ...newOptions, horizontalScrolling };
} }
if (e.affectsConfiguration(openModeSettingKey)) {
newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' };
}
if (Object.keys(newOptions).length > 0) { if (Object.keys(newOptions).length > 0) {
tree.updateOptions(newOptions); tree.updateOptions(newOptions);
} }

View File

@@ -263,7 +263,7 @@ export class NativeStorageService extends Disposable implements IStorageService
async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> { async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {
if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) {
return Promise.resolve(); // no migration needed if running in memory return; // no migration needed if running in memory
} }
// Close workspace DB to be able to copy // Close workspace DB to be able to copy

View File

@@ -219,8 +219,14 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this.telemetryService.publicLog2<{ code: string }, AutoSyncErrorClassification>(`autosync/error`, { code: userDataSyncError.code }); this.telemetryService.publicLog2<{ code: string }, AutoSyncErrorClassification>(`autosync/error`, { code: userDataSyncError.code });
} }
// Turned off from another device or session got expired // Session got expired
if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff || userDataSyncError.code === UserDataSyncErrorCode.SessionExpired) { if (userDataSyncError.code === UserDataSyncErrorCode.SessionExpired) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because current session is expired');
}
// Turned off from another device
if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) {
await this.turnOff(false, true /* force soft turnoff on error */); await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because sync is turned off in the cloud'); this.logService.info('Auto Sync: Turned off sync because sync is turned off in the cloud');
} }

View File

@@ -156,8 +156,7 @@ export class WebviewProtocolProvider extends Disposable {
rewriteUri = (uri) => { rewriteUri = (uri) => {
if (metadata.remoteConnectionData) { if (metadata.remoteConnectionData) {
if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) { if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
const scheme = metadata.remoteConnectionData.host === 'localhost' || metadata.remoteConnectionData.host === '127.0.0.1' ? 'http' : 'https'; return URI.parse(`http://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
return URI.parse(`${scheme}://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
path: '/vscode-remote-resource', path: '/vscode-remote-resource',
query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`, query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`,
}); });

8
src/vs/vscode.d.ts vendored
View File

@@ -1734,8 +1734,8 @@ declare module 'vscode' {
/** /**
* Dialog title. * Dialog title.
* *
* Depending on the underlying operating system this parameter might be ignored, since some * This parameter might be ignored, as not all operating systems display a title on open dialogs
* systems do not present title on open dialogs. * (for example, macOS).
*/ */
title?: string; title?: string;
} }
@@ -1769,8 +1769,8 @@ declare module 'vscode' {
/** /**
* Dialog title. * Dialog title.
* *
* Depending on the underlying operating system this parameter might be ignored, since some * This parameter might be ignored, as not all operating systems display a title on save dialogs
* systems do not present title on save dialogs. * (for example, macOS).
*/ */
title?: string; title?: string;
} }

View File

@@ -830,12 +830,11 @@ declare module 'vscode' {
noDebug?: boolean; noDebug?: boolean;
/** /**
* Controls if the debug session created will be compacted with the parent in the CALL STACK view. * Controls if the debug session's parent session is shown in the CALL STACK view even if it has only a single child.
* Compact with the parent is only done if the session is the only child of it's parent session. * By default, the debug session will never hide its parent.
* Default is to compact. * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact.
*
*/ */
noCompact?: boolean; compact?: boolean;
} }
// deprecated debug API // deprecated debug API
@@ -1053,7 +1052,9 @@ declare module 'vscode' {
export interface TerminalLinkProvider<T extends TerminalLink = TerminalLink> { export interface TerminalLinkProvider<T extends TerminalLink = TerminalLink> {
/** /**
* Provide terminal links for the given context. * Provide terminal links for the given context. Note that this can be called multiple times
* even before previous calls resolve, make sure to not share global objects (eg. `RegExp`)
* that could have problems when asynchronous usage may overlap.
* @param context Information about what links are being provided for. * @param context Information about what links are being provided for.
*/ */
provideTerminalLinks(context: TerminalLinkContext): ProviderResult<T[]> provideTerminalLinks(context: TerminalLinkContext): ProviderResult<T[]>

View File

@@ -232,7 +232,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
noDebug: options.noDebug, noDebug: options.noDebug,
parentSession: this.getSession(options.parentSessionID), parentSession: this.getSession(options.parentSessionID),
repl: options.repl, repl: options.repl,
noCompact: options.noCompact compact: options.compact
}; };
return this.debugService.startDebugging(launch, nameOrConfig, debugOptions).then(success => { return this.debugService.startDebugging(launch, nameOrConfig, debugOptions).then(success => {
return success; return success;

View File

@@ -30,12 +30,14 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
// if there are icons in the text use the tooltip for the aria label // if there are icons in the text use the tooltip for the aria label
let ariaLabel: string; let ariaLabel: string;
let role: string | undefined = undefined;
if (accessibilityInformation) { if (accessibilityInformation) {
ariaLabel = accessibilityInformation.label; ariaLabel = accessibilityInformation.label;
role = accessibilityInformation.role;
} else { } else {
ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : ''; ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : '';
} }
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel }; const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role };
if (typeof priority === 'undefined') { if (typeof priority === 'undefined') {
priority = 0; priority = 0;

View File

@@ -368,7 +368,7 @@ namespace TaskDTO {
const label = nls.localize('task.label', '{0}: {1}', source.label, task.name); const label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!; const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!;
const id = `${task.source.extensionId}.${definition._key}`; const id = (CustomExecutionDTO.is(task.execution!) && task._id) ? task._id : `${task.source.extensionId}.${definition._key}`;
const result: ContributedTask = new ContributedTask( const result: ContributedTask = new ContributedTask(
id, // uuidMap.getUUID(identifier) id, // uuidMap.getUUID(identifier)
source, source,
@@ -534,20 +534,47 @@ export class MainThreadTask implements MainThreadTaskShape {
} }
} }
public $executeTask(value: TaskDTO): Promise<TaskExecutionDTO> { // Passing in a TaskHandleDTO will cause the task to get re-resolved, which is important for tasks are coming from the core,
// such as those gotten from a fetchTasks, since they can have missing configuration properties.
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
return new Promise<TaskExecutionDTO>((resolve, reject) => { return new Promise<TaskExecutionDTO>((resolve, reject) => {
const task = TaskDTO.to(value, this._workspaceContextServer, true)!; if (TaskHandleDTO.is(value)) {
this._taskService.run(task).then(undefined, reason => { const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
// eat the error, it has already been surfaced to the user and we don't care about it here if (workspaceFolder) {
}); this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => {
const result: TaskExecutionDTO = { if (!task) {
id: task._id, reject(new Error('Task not found'));
task: TaskDTO.from(task) } else {
}; this._taskService.run(task).then(undefined, reason => {
resolve(result); // eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
};
resolve(result);
}
}, (_error) => {
reject(new Error('Task not found'));
});
} else {
reject(new Error('No workspace folder'));
}
} else {
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
};
resolve(result);
}
}); });
} }
public $customExecutionComplete(id: string, result?: number): Promise<void> { public $customExecutionComplete(id: string, result?: number): Promise<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
this._taskService.getActiveTasks().then((tasks) => { this._taskService.getActiveTasks().then((tasks) => {

View File

@@ -784,7 +784,7 @@ export interface MainThreadTaskShape extends IDisposable {
$unregisterTaskProvider(handle: number): Promise<void>; $unregisterTaskProvider(handle: number): Promise<void>;
$fetchTasks(filter?: tasks.TaskFilterDTO): Promise<tasks.TaskDTO[]>; $fetchTasks(filter?: tasks.TaskFilterDTO): Promise<tasks.TaskDTO[]>;
$getTaskExecution(value: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>; $getTaskExecution(value: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
$executeTask(task: tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>; $executeTask(task: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
$terminateTask(id: string): Promise<void>; $terminateTask(id: string): Promise<void>;
$registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void; $registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void;
$customExecutionComplete(id: string, result?: number): Promise<void>; $customExecutionComplete(id: string, result?: number): Promise<void>;
@@ -862,7 +862,7 @@ export interface IStartDebuggingOptions {
parentSessionID?: DebugSessionUUID; parentSessionID?: DebugSessionUUID;
repl?: IDebugSessionReplMode; repl?: IDebugSessionReplMode;
noDebug?: boolean; noDebug?: boolean;
noCompact?: boolean; compact?: boolean;
} }
export interface MainThreadDebugServiceShape extends IDisposable { export interface MainThreadDebugServiceShape extends IDisposable {

View File

@@ -297,7 +297,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
parentSessionID: options.parentSession ? options.parentSession.id : undefined, parentSessionID: options.parentSession ? options.parentSession.id : undefined,
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate', repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate',
noDebug: options.noDebug, noDebug: options.noDebug,
noCompact: options.noCompact compact: options.compact
}); });
} }

View File

@@ -117,6 +117,11 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
this.update(); this.update();
} }
public set accessibilityInformation(accessibilityInformation: vscode.AccessibilityInformation | undefined) {
this._accessibilityInformation = accessibilityInformation;
this.update();
}
public show(): void { public show(): void {
this._visible = true; this._visible = true;
this.update(); this.update();

View File

@@ -367,7 +367,7 @@ export class ExtHostTreeView<T> extends Disposable {
if (node) { if (node) {
const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem); const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem);
// Resolvable elements. Currently only tooltip. // Resolvable elements. Currently only tooltip.
node.item.tooltip = resolve.tooltip; node.item.tooltip = this.getTooltip(resolve.tooltip);
return node.item; return node.item;
} }
} }

View File

@@ -345,7 +345,7 @@ namespace schema {
type: 'string' type: 'string'
}, },
icon: { icon: {
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `$(zap)`'), description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
anyOf: [{ anyOf: [{
type: 'string' type: 'string'
}, },

View File

@@ -54,12 +54,13 @@ export class ExtHostTask extends ExtHostTaskBase {
// We have a preserved ID. So the task didn't change. // We have a preserved ID. So the task didn't change.
if (tTask._id !== undefined) { if (tTask._id !== undefined) {
// Always get the task execution first to prevent timing issues when retrieving it later // Always get the task execution first to prevent timing issues when retrieving it later
const executionDTO = await this._proxy.$getTaskExecution(TaskHandleDTO.from(tTask)); const handleDto = TaskHandleDTO.from(tTask);
const executionDTO = await this._proxy.$getTaskExecution(handleDto);
if (executionDTO.task === undefined) { if (executionDTO.task === undefined) {
throw new Error('Task from execution DTO is undefined'); throw new Error('Task from execution DTO is undefined');
} }
const execution = await this.getTaskExecution(executionDTO, task); const execution = await this.getTaskExecution(executionDTO, task);
this._proxy.$executeTask(executionDTO.task).catch(() => { /* The error here isn't actionable. */ }); this._proxy.$executeTask(handleDto).catch(() => { /* The error here isn't actionable. */ });
return execution; return execution;
} else { } else {
const dto = TaskDTO.from(task, extension); const dto = TaskDTO.from(task, extension);

View File

@@ -210,7 +210,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
this.activeEditorContext.set(activeEditorPane.getId()); this.activeEditorContext.set(activeEditorPane.getId());
this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly()); this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly());
const editors = activeEditorPane.input.resource ? this.editorService.getEditorOverrides(activeEditorPane.input.resource, undefined, activeGroup, undefined) : []; const editors = activeEditorPane.input.resource ? this.editorService.getEditorOverrides(activeEditorPane.input.resource, undefined, activeGroup) : [];
this.activeEditorAvailableEditorIds.set(editors.map(([_, entry]) => entry.id).join(',')); this.activeEditorAvailableEditorIds.set(editors.map(([_, entry]) => entry.id).join(','));
} else { } else {
this.activeEditorContext.reset(); this.activeEditorContext.reset();

View File

@@ -418,7 +418,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
// Case: we closed the last visible composite // Case: we closed the last visible composite
// Solv: we hide the part // Solv: we hide the part
else if (this.visibleComposites.length === 1) { else if (this.visibleComposites.length === 0) {
this.options.hidePart(); this.options.hidePart();
} }

View File

@@ -38,7 +38,7 @@ import { ResourceLabel } from 'vs/workbench/browser/labels';
import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
import { IEditorPartOptions, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { IEditorPartOptions, toResource, SideBySideEditor, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions } from 'vs/workbench/common/editor';
import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -49,6 +49,7 @@ import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label'; import { ILabelService } from 'vs/platform/label/common/label';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
import { Registry } from 'vs/platform/registry/common/platform';
class Item extends BreadcrumbsItem { class Item extends BreadcrumbsItem {
@@ -248,12 +249,23 @@ export class BreadcrumbsControl {
} }
} }
// display uri which can be derived from file input
let fileInfoUri = uri;
let input = this._editorGroup.activeEditor;
if (input instanceof SideBySideEditorInput) {
input = input.primary;
}
if (Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories).getFileEditorInputFactory().isFileEditorInput(input)) {
fileInfoUri = input.label;
}
this.domNode.classList.toggle('hidden', false); this.domNode.classList.toggle('hidden', false);
this._ckBreadcrumbsVisible.set(true); this._ckBreadcrumbsVisible.set(true);
this._ckBreadcrumbsPossible.set(true); this._ckBreadcrumbsPossible.set(true);
const editor = this._getActiveCodeEditor(); const editor = this._getActiveCodeEditor();
const model = new EditorBreadcrumbsModel( const model = new EditorBreadcrumbsModel(
fileInfoUri,
uri, editor, uri, editor,
this._configurationService, this._configurationService,
this._textResourceConfigurationService, this._textResourceConfigurationService,

View File

@@ -52,6 +52,7 @@ export class EditorBreadcrumbsModel {
readonly onDidUpdate: Event<this> = this._onDidUpdate.event; readonly onDidUpdate: Event<this> = this._onDidUpdate.event;
constructor( constructor(
fileInfoUri: URI,
private readonly _uri: URI, private readonly _uri: URI,
private readonly _editor: ICodeEditor | undefined, private readonly _editor: ICodeEditor | undefined,
@IConfigurationService private readonly _configurationService: IConfigurationService, @IConfigurationService private readonly _configurationService: IConfigurationService,
@@ -64,7 +65,7 @@ export class EditorBreadcrumbsModel {
this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService); this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(fileInfoUri, workspaceService);
this._bindToEditor(); this._bindToEditor();
this._onDidUpdate.fire(this); this._onDidUpdate.fire(this);
} }

View File

@@ -457,7 +457,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '6_reopen', order: 20, when: ActiveEditorAvailableEditorIdsContext });
// Editor Title Menu // Editor Title Menu
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });

View File

@@ -765,6 +765,9 @@ class StatusbarEntryItem extends Disposable {
this.container.setAttribute('aria-label', entry.ariaLabel); this.container.setAttribute('aria-label', entry.ariaLabel);
this.labelContainer.setAttribute('aria-label', entry.ariaLabel); this.labelContainer.setAttribute('aria-label', entry.ariaLabel);
} }
if (!this.entry || entry.role !== this.entry.role) {
this.labelContainer.setAttribute('role', entry.role || 'button');
}
// Update: Tooltip (on the container, because label can be disabled) // Update: Tooltip (on the container, because label can be disabled)
if (!this.entry || entry.tooltip !== this.entry.tooltip) { if (!this.entry || entry.tooltip !== this.entry.tooltip) {

View File

@@ -275,6 +275,11 @@ export class ViewsService extends Disposable implements IViewsService {
return viewContainerId ? this.viewDescriptorService.getViewContainerById(viewContainerId) : null; return viewContainerId ? this.viewDescriptorService.getViewContainerById(viewContainerId) : null;
} }
getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null {
const viewContainer = this.viewDescriptorService.getViewContainerById(viewContainerId);
return viewContainer ? this.getActiveViewPaneContainer(viewContainer) : null;
}
async openViewContainer(id: string, focus?: boolean): Promise<IPaneComposite | null> { async openViewContainer(id: string, focus?: boolean): Promise<IPaneComposite | null> {
const viewContainer = this.viewDescriptorService.getViewContainerById(id); const viewContainer = this.viewDescriptorService.getViewContainerById(id);
if (viewContainer) { if (viewContainer) {

View File

@@ -484,6 +484,7 @@ export interface IViewsService {
openViewContainer(id: string, focus?: boolean): Promise<IPaneComposite | null>; openViewContainer(id: string, focus?: boolean): Promise<IPaneComposite | null>;
closeViewContainer(id: string): void; closeViewContainer(id: string): void;
getVisibleViewContainer(location: ViewContainerLocation): ViewContainer | null; getVisibleViewContainer(location: ViewContainerLocation): ViewContainer | null;
getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null;
// View APIs // View APIs
readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }>; readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }>;

View File

@@ -451,7 +451,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
open: (editor, options, group) => { open: (editor, options, group) => {
return this.onEditorOpening(editor, options, group); return this.onEditorOpening(editor, options, group);
}, },
getEditorOverrides: (resource: URI, _options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined): IOpenEditorOverrideEntry[] => { getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => {
const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource)); const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource));
const defaultEditorOverride: IOpenEditorOverrideEntry = { const defaultEditorOverride: IOpenEditorOverrideEntry = {
@@ -468,14 +468,14 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
}; };
}; };
if (typeof id === 'string') { if (typeof options?.override === 'string') {
// A specific override was requested. Only return it. // A specific override was requested. Only return it.
if (id === defaultEditorOverride.id) { if (options.override === defaultEditorOverride.id) {
return [defaultEditorOverride]; return [defaultEditorOverride];
} }
const matchingEditor = this.customEditorService.getCustomEditor(id); const matchingEditor = this.customEditorService.getCustomEditor(options.override);
return matchingEditor ? [toOverride(matchingEditor)] : []; return matchingEditor ? [toOverride(matchingEditor)] : [];
} }

View File

@@ -217,7 +217,7 @@ export class CallStackView extends ViewPane {
this.dataSource = new CallStackDataSource(this.debugService); this.dataSource = new CallStackDataSource(this.debugService);
const sessionsRenderer = this.instantiationService.createInstance(SessionsRenderer, this.menu); const sessionsRenderer = this.instantiationService.createInstance(SessionsRenderer, this.menu);
this.tree = <WorkbenchCompressibleAsyncDataTree<IDebugModel, CallStackItem, FuzzyScore>>this.instantiationService.createInstance(WorkbenchCompressibleAsyncDataTree, 'CallStackView', treeContainer, new CallStackDelegate(), new CallStackCompressionDelegate(), [ this.tree = <WorkbenchCompressibleAsyncDataTree<IDebugModel, CallStackItem, FuzzyScore>>this.instantiationService.createInstance(WorkbenchCompressibleAsyncDataTree, 'CallStackView', treeContainer, new CallStackDelegate(), new CallStackCompressionDelegate(this.debugService), [
sessionsRenderer, sessionsRenderer,
new ThreadsRenderer(this.instantiationService), new ThreadsRenderer(this.instantiationService),
this.instantiationService.createInstance(StackFramesRenderer), this.instantiationService.createInstance(StackFramesRenderer),
@@ -355,6 +355,10 @@ export class CallStackView extends ViewPane {
const sessionListeners: IDisposable[] = []; const sessionListeners: IDisposable[] = [];
sessionListeners.push(s.onDidChangeName(() => this.tree.rerender(s))); sessionListeners.push(s.onDidChangeName(() => this.tree.rerender(s)));
sessionListeners.push(s.onDidEndAdapter(() => dispose(sessionListeners))); sessionListeners.push(s.onDidEndAdapter(() => dispose(sessionListeners)));
if (s.parentSession) {
// A session we already expanded has a new child session, allow to expand it again.
this.autoExpandedSessions.delete(s.parentSession);
}
})); }));
} }
@@ -517,18 +521,18 @@ class SessionsRenderer implements ICompressibleTreeRenderer<IDebugSession, Fuzzy
} }
renderElement(element: ITreeNode<IDebugSession, FuzzyScore>, _: number, data: ISessionTemplateData): void { renderElement(element: ITreeNode<IDebugSession, FuzzyScore>, _: number, data: ISessionTemplateData): void {
this.doRenderElement(element.element, element.element.getLabel(), createMatches(element.filterData), data); this.doRenderElement(element.element, createMatches(element.filterData), data);
} }
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IDebugSession>, FuzzyScore>, index: number, templateData: ISessionTemplateData, height: number | undefined): void { renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IDebugSession>, FuzzyScore>, index: number, templateData: ISessionTemplateData, height: number | undefined): void {
const lastElement = node.element.elements[node.element.elements.length - 1]; const lastElement = node.element.elements[node.element.elements.length - 1];
const matches = createMatches(node.filterData); const matches = createMatches(node.filterData);
this.doRenderElement(lastElement, node.element.elements[0].getLabel(), matches, templateData); this.doRenderElement(lastElement, matches, templateData);
} }
private doRenderElement(session: IDebugSession, label: string, matches: IMatch[], data: ISessionTemplateData): void { private doRenderElement(session: IDebugSession, matches: IMatch[], data: ISessionTemplateData): void {
data.session.title = nls.localize({ key: 'session', comment: ['Session is a noun'] }, "Session"); data.session.title = nls.localize({ key: 'session', comment: ['Session is a noun'] }, "Session");
data.label.set(label, matches); data.label.set(session.getLabel(), matches);
const thread = session.getAllThreads().find(t => t.stopped); const thread = session.getAllThreads().find(t => t.stopped);
const setActionBar = () => { const setActionBar = () => {
@@ -1100,9 +1104,20 @@ class ContinueAction extends Action {
} }
class CallStackCompressionDelegate implements ITreeCompressionDelegate<CallStackItem> { class CallStackCompressionDelegate implements ITreeCompressionDelegate<CallStackItem> {
constructor(private readonly debugService: IDebugService) { }
isIncompressible(stat: CallStackItem): boolean { isIncompressible(stat: CallStackItem): boolean {
if (isDebugSession(stat)) { if (isDebugSession(stat)) {
return stat.noCompact; if (stat.compact) {
return false;
}
const sessions = this.debugService.getModel().getSessions();
if (sessions.some(s => s.parentSession === stat && s.compact)) {
return false;
}
return true;
} }
return true; return true;

View File

@@ -348,8 +348,8 @@ export class DebugService implements IDebugService {
} }
if (configOrName && !config) { if (configOrName && !config) {
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) : const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : configOrName.name) :
nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist."); nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist for passed workspace folder.");
throw new Error(message); throw new Error(message);
} }
@@ -358,6 +358,7 @@ export class DebugService implements IDebugService {
return result; return result;
} catch (err) { } catch (err) {
// make sure to get out of initializing state, and propagate the result // make sure to get out of initializing state, and propagate the result
this.notificationService.error(err);
this.endInitializingState(); this.endInitializingState();
return Promise.reject(err); return Promise.reject(err);
} }

View File

@@ -129,8 +129,8 @@ export class DebugSession implements IDebugSession {
return this._options.parentSession; return this._options.parentSession;
} }
get noCompact(): boolean { get compact(): boolean {
return !!this._options.noCompact; return !!this._options.compact;
} }
setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }) { setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }) {

View File

@@ -142,6 +142,7 @@
.debug-pane .debug-call-stack .monaco-list-row .monaco-action-bar { .debug-pane .debug-call-stack .monaco-list-row .monaco-action-bar {
display: none; display: none;
flex-shrink: 0; flex-shrink: 0;
margin-right: 1px;
} }
.debug-pane .debug-call-stack .monaco-list-row:hover .monaco-action-bar { .debug-pane .debug-call-stack .monaco-list-row:hover .monaco-action-bar {

View File

@@ -606,10 +606,17 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
await this.clipboardService.writeText(this.getVisibleContent()); await this.clipboardService.writeText(this.getVisibleContent());
return Promise.resolve(); return Promise.resolve();
})); }));
actions.push(new Action('debug.replPaste', localize('paste', "Paste"), undefined, true, async () => { actions.push(new Action('debug.replPaste', localize('paste', "Paste"), undefined, this.debugService.state !== State.Inactive, async () => {
const clipboardText = await this.clipboardService.readText(); const clipboardText = await this.clipboardService.readText();
if (clipboardText) { if (clipboardText) {
this.replInput.setValue(this.replInput.getValue().concat(clipboardText)); this.replInput.setValue(this.replInput.getValue().concat(clipboardText));
this.replInput.focus();
const model = this.replInput.getModel();
const lineNumber = model ? model.getLineCount() : 0;
const column = model?.getLineMaxColumn(lineNumber);
if (typeof lineNumber === 'number' && typeof column === 'number') {
this.replInput.setPosition({ lineNumber, column });
}
} }
})); }));
actions.push(new Separator()); actions.push(new Separator());

View File

@@ -157,7 +157,7 @@ export interface IDebugSessionOptions {
parentSession?: IDebugSession; parentSession?: IDebugSession;
repl?: IDebugSessionReplMode; repl?: IDebugSessionReplMode;
compoundRoot?: DebugCompoundRoot; compoundRoot?: DebugCompoundRoot;
noCompact?: boolean; compact?: boolean;
} }
export interface IDebugSession extends ITreeElement { export interface IDebugSession extends ITreeElement {
@@ -168,7 +168,7 @@ export interface IDebugSession extends ITreeElement {
readonly root: IWorkspaceFolder | undefined; readonly root: IWorkspaceFolder | undefined;
readonly parentSession: IDebugSession | undefined; readonly parentSession: IDebugSession | undefined;
readonly subId: string | undefined; readonly subId: string | undefined;
readonly noCompact: boolean; readonly compact: boolean;
setSubId(subId: string | undefined): void; setSubId(subId: string | undefined): void;

View File

@@ -157,7 +157,7 @@ export class MockSession implements IDebugSession {
subId: string | undefined; subId: string | undefined;
get noCompact(): boolean { get compact(): boolean {
return false; return false;
} }

View File

@@ -225,24 +225,23 @@ export class ExtensionEditor extends BaseEditor {
builtin.textContent = localize('builtin', "Built-in"); builtin.textContent = localize('builtin', "Built-in");
const subtitle = append(details, $('.subtitle')); const subtitle = append(details, $('.subtitle'));
const publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 })); const publisher = append(append(subtitle, $('.subtitle-entry')), $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 }));
// {{SQL CARBON EDIT}} remove rating and install count widgets // {{SQL CARBON EDIT}} remove rating and install count widgets
// const installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); // const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 }));
// const rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); // const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 }));
const repository = append(subtitle, $('span.repository.clickable')); const repository = append(append(subtitle, $('.subtitle-entry')), $('span.repository.clickable'));
repository.textContent = localize('repository', 'Repository'); repository.textContent = localize('repository', 'Repository');
repository.style.display = 'none'; repository.style.display = 'none';
repository.tabIndex = 0; repository.tabIndex = 0;
const license = append(subtitle, $('span.license.clickable')); const license = append(append(subtitle, $('.subtitle-entry')), $('span.license.clickable'));
license.textContent = localize('license', 'License'); license.textContent = localize('license', 'License');
license.style.display = 'none'; license.style.display = 'none';
license.tabIndex = 0; license.tabIndex = 0;
const version = append(subtitle, $('span.version')); const version = append(append(subtitle, $('.subtitle-entry')), $('span.version'));
version.textContent = localize('version', 'Version'); version.textContent = localize('version', 'Version');
const description = append(details, $('.description')); const description = append(details, $('.description'));

View File

@@ -19,7 +19,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow
import { import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
@@ -150,6 +150,7 @@ actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: En
const checkForUpdatesAction = SyncActionDescriptor.from(CheckForUpdatesAction); const checkForUpdatesAction = SyncActionDescriptor.from(CheckForUpdatesAction);
actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Extension Updates`, ExtensionsLabel); actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Extension Updates`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ClearExtensionsSearchResultsAction), 'Extensions: Clear Extensions Search Results', ExtensionsLabel);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAutoUpdateAction), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAutoUpdateAction), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAutoUpdateAction), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAutoUpdateAction), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallSpecificVersionOfExtensionAction), 'Install Specific Version of Extension...', ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallSpecificVersionOfExtensionAction), 'Install Specific Version of Extension...', ExtensionsLabel);

View File

@@ -60,6 +60,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { Codicon } from 'vs/base/common/codicons'; import { Codicon } from 'vs/base/common/codicons';
import { IViewsService } from 'vs/workbench/common/views';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; // {{SQL CARBON EDIT}} import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; // {{SQL CARBON EDIT}}
import product from 'vs/platform/product/common/product'; import product from 'vs/platform/product/common/product';
@@ -1662,19 +1663,39 @@ export class ShowDisabledExtensionsAction extends Action {
} }
} }
export class ClearExtensionsInputAction extends Action { export class ClearExtensionsSearchResultsAction extends Action {
static readonly ID = 'workbench.extensions.action.clearExtensionsInput'; static readonly ID = 'workbench.extensions.action.clearExtensionsSearchResults';
static readonly LABEL = localize('clearExtensionsInput', "Clear Extensions Search Results"); static readonly LABEL = localize('clearExtensionsSearchResults', "Clear Extensions Search Results");
constructor(
id: string,
label: string,
@IViewsService private readonly viewsService: IViewsService
) {
super(id, label, 'codicon-clear-all', true);
}
async run(): Promise<void> {
const viewPaneContainer = this.viewsService.getActiveViewPaneContainerWithId(VIEWLET_ID);
if (viewPaneContainer) {
const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer;
extensionsViewPaneContainer.search('');
extensionsViewPaneContainer.focus();
}
}
}
export class ClearExtensionsInputAction extends ClearExtensionsSearchResultsAction {
constructor( constructor(
id: string, id: string,
label: string, label: string,
onSearchChange: Event<string>, onSearchChange: Event<string>,
value: string, value: string,
@IViewletService private readonly viewletService: IViewletService @IViewsService viewsService: IViewsService
) { ) {
super(id, label, 'codicon-clear-all', true); super(id, label, viewsService);
this.onSearchChange(value); this.onSearchChange(value);
this._register(onSearchChange(this.onSearchChange, this)); this._register(onSearchChange(this.onSearchChange, this));
} }
@@ -1683,14 +1704,6 @@ export class ClearExtensionsInputAction extends Action {
this.enabled = !!value; this.enabled = !!value;
} }
run(): Promise<void> {
return this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
.then(viewlet => {
viewlet.search('');
viewlet.focus();
});
}
} }
export class ShowBuiltInExtensionsAction extends Action { export class ShowBuiltInExtensionsAction extends Action {

View File

@@ -113,7 +113,7 @@
line-height: 20px; line-height: 20px;
} }
.extension-editor > .header > .details > .subtitle > .publisher { .extension-editor > .header > .details > .subtitle .publisher {
font-size: 18px; font-size: 18px;
} }
@@ -124,12 +124,11 @@
align-items: center; align-items: center;
} }
.extension-editor > .header > .details > .subtitle > .install > .count { .extension-editor > .header > .details > .subtitle .install > .count {
margin-left: 6px; margin-left: 6px;
} }
.extension-editor > .header > .details > .subtitle > span:not(:first-child):not(:empty), .extension-editor > .header > .details > .subtitle > div:not(:first-child):not(:empty) {
.extension-editor > .header > .details > .subtitle > a:not(:first-child):not(:empty) {
border-left: 1px solid rgba(128, 128, 128, 0.7); border-left: 1px solid rgba(128, 128, 128, 0.7);
margin-left: 14px; margin-left: 14px;
padding-left: 14px; padding-left: 14px;

View File

@@ -374,7 +374,12 @@ export class RuntimeExtensionsEditor extends BaseEditor {
] ]
}, "Activated by {1} because searching for {0} took too long", glob, activationId); }, "Activated by {1} because searching for {0} took too long", glob, activationId);
} else if (activationEvent === 'onStartupFinished') { } else if (activationEvent === 'onStartupFinished') {
title = nls.localize('startupFinishedActivation', "Activated by {0} after start-up finished", activationId); title = nls.localize({
key: 'startupFinishedActivation',
comment: [
'This refers to an extension. {0} will be an activation event.'
]
}, "Activated by {0} after start-up finished", activationId);
} else if (/^onLanguage:/.test(activationEvent)) { } else if (/^onLanguage:/.test(activationEvent)) {
let language = activationEvent.substr('onLanguage:'.length); let language = activationEvent.substr('onLanguage:'.length);
title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId);

View File

@@ -486,7 +486,7 @@ export class ExplorerView extends ViewPane {
this.rootContext.set(!stat || (stat && stat.isRoot)); this.rootContext.set(!stat || (stat && stat.isRoot));
if (resource) { if (resource) {
const overrides = resource ? this.editorService.getEditorOverrides(resource, undefined, undefined, undefined) : []; const overrides = resource ? this.editorService.getEditorOverrides(resource, undefined, undefined) : [];
this.availableEditorIdsContext.set(overrides.map(([, entry]) => entry.id).join(',')); this.availableEditorIdsContext.set(overrides.map(([, entry]) => entry.id).join(','));
} else { } else {
this.availableEditorIdsContext.reset(); this.availableEditorIdsContext.reset();

View File

@@ -8,19 +8,20 @@
export const SCROLLABLE_ELEMENT_PADDING_TOP = 20; export const SCROLLABLE_ELEMENT_PADDING_TOP = 20;
// Cell sizing related // Cell sizing related
export const CELL_MARGIN = 24; export const CELL_MARGIN = 8;
export const CELL_RUN_GUTTER = 32; export const CELL_RUN_GUTTER = 28;
export const CODE_CELL_LEFT_MARGIN = 45; export const CODE_CELL_LEFT_MARGIN = 32;
export const EDITOR_TOOLBAR_HEIGHT = 0; export const EDITOR_TOOLBAR_HEIGHT = 0;
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 32; export const BOTTOM_CELL_TOOLBAR_HEIGHT = 28;
export const CELL_STATUSBAR_HEIGHT = 22; export const CELL_STATUSBAR_HEIGHT = 22;
// Margin above editor // Margin above editor
export const EDITOR_TOP_MARGIN = 8; export const EDITOR_TOP_MARGIN = 6;
export const CELL_BOTTOM_MARGIN = 8; export const CELL_BOTTOM_MARGIN = 6;
// Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight`
export const EDITOR_TOP_PADDING = 12; export const EDITOR_TOP_PADDING = 12;
export const EDITOR_BOTTOM_PADDING = 12; export const EDITOR_BOTTOM_PADDING = 4;
export const CELL_OUTPUT_PADDING = 14;

View File

@@ -211,6 +211,48 @@
outline: none !important; outline: none !important;
} }
/* top and bottom borders on cells */
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after {
content: "";
position: absolute;
width: 100%;
height: 1px;
}
/* top border */
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before {
border-top: 1px solid transparent;
}
.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-top:before,
.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:before {
border-top-style: dashed;
}
/* bottom border */
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after {
border-bottom: 1px solid transparent;
}
.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-bottom:before,
.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:after {
border-bottom-style: dashed;
}
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before {
top: 0;
}
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before,
.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after {
bottom: 0px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover,
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover { .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover {
cursor: pointer; cursor: pointer;
@@ -314,7 +356,7 @@
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon { .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon {
margin: 0; margin: 0;
padding: 0 4px; padding-right: 4px;
} }
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .actions-container { .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .actions-container {
@@ -398,8 +440,7 @@
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
top: 0px; top: 0px;
visibility: hidden; opacity: 0;
opacity: 1;
} }
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side {
@@ -428,7 +469,7 @@
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.cell-output-hover .cell-focus-indicator, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.cell-output-hover .cell-focus-indicator,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator { .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator {
visibility: visible; opacity: 1;
} }
.monaco-workbench .notebookOverlay .monaco-list-row .cell-editor-part:before { .monaco-workbench .notebookOverlay .monaco-list-row .cell-editor-part:before {
@@ -582,7 +623,7 @@
/* Adjust margin of first item in markdown cell */ /* Adjust margin of first item in markdown cell */
.monaco-workbench .notebookOverlay .cell.markdown div *:first-child { .monaco-workbench .notebookOverlay .cell.markdown div *:first-child {
margin-top: 4px; margin-top: 0px;
} }
/* h1 tags don't need top margin */ /* h1 tags don't need top margin */
@@ -594,11 +635,12 @@
.monaco-workbench .notebookOverlay .cell.markdown div *:only-child, .monaco-workbench .notebookOverlay .cell.markdown div *:only-child,
.monaco-workbench .notebookOverlay .cell.markdown div *:last-child { .monaco-workbench .notebookOverlay .cell.markdown div *:last-child {
margin-bottom: 0; margin-bottom: 0;
padding-bottom: 0;
} }
/* makes all markdown cells consistent */ /* makes all markdown cells consistent */
.monaco-workbench .notebookOverlay .cell.markdown div { .monaco-workbench .notebookOverlay .cell.markdown div {
min-height: 32px; min-height: 24px;
} }
.monaco-workbench .notebookOverlay .cell.markdown table { .monaco-workbench .notebookOverlay .cell.markdown table {
@@ -666,15 +708,15 @@
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator {
position: absolute; position: absolute;
top: 11px; top: 0;
left: 0px; left: 0;
right: 0; right: 0;
height: 100%;
} }
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon { .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon {
visibility: visible; visibility: visible;
padding: 4px; padding: 8px 0 0 10px;
width: calc(100% - 30px);
} }
/** Theming */ /** Theming */

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