Merge from vscode 591842cc4b71958c81947b254924a215fe3edcbd (#4886)

This commit is contained in:
Karl Burtram
2019-04-05 14:14:26 -07:00
committed by GitHub
parent 657adafb7d
commit 0532346f4f
117 changed files with 1691 additions and 1191 deletions
@@ -3,12 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import * as vscode from 'vscode';
import { getLocation, visit, parse, ParseErrorCode } from 'jsonc-parser';
import { getLocation, parse, visit } from 'jsonc-parser';
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { SettingsDocument } from './settingsDocumentHelper';
const localize = nls.loadMessageBundle();
const fadedDecoration = vscode.window.createTextEditorDecorationType({
light: {
@@ -45,50 +45,6 @@ export function activate(context: vscode.ExtensionContext): void {
}
}, null, context.subscriptions));
updateLaunchJsonDecorations(vscode.window.activeTextEditor);
context.subscriptions.push(vscode.workspace.onWillSaveTextDocument(e => {
if (!e.document.fileName.endsWith('/settings.json')) {
return;
}
autoFixSettingsJSON(e);
}));
}
function autoFixSettingsJSON(willSaveEvent: vscode.TextDocumentWillSaveEvent): void {
const document = willSaveEvent.document;
const text = document.getText();
const edit = new vscode.WorkspaceEdit();
let lastEndOfSomething = -1;
visit(text, {
onArrayEnd(offset: number, length: number): void {
lastEndOfSomething = offset + length;
},
onLiteralValue(_value: any, offset: number, length: number): void {
lastEndOfSomething = offset + length;
},
onObjectEnd(offset: number, length: number): void {
lastEndOfSomething = offset + length;
},
onError(error: ParseErrorCode, _offset: number, _length: number): void {
if (error === ParseErrorCode.CommaExpected && lastEndOfSomething > -1) {
const fixPosition = document.positionAt(lastEndOfSomething);
// Don't insert a comma immediately before a : or ' :'
const colonRange = document.getWordRangeAtPosition(fixPosition, / *:/);
if (!colonRange) {
edit.insert(document.uri, fixPosition, ',');
}
}
}
});
willSaveEvent.waitUntil(
vscode.workspace.applyEdit(edit));
}
function registerSettingsCompletions(): vscode.Disposable {
+1 -1
View File
@@ -6,7 +6,7 @@
"git": {
"name": "MagicStack/MagicPython",
"repositoryUrl": "https://github.com/MagicStack/MagicPython",
"commitHash": "8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc"
"commitHash": "38422d302fe0b3e7716d26ce8cd7d0b9685f3a38"
}
},
"license": "MIT",
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/MagicStack/MagicPython/commit/8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc",
"version": "https://github.com/MagicStack/MagicPython/commit/38422d302fe0b3e7716d26ce8cd7d0b9685f3a38",
"name": "MagicPython",
"scopeName": "source.python",
"patterns": [
@@ -826,7 +826,7 @@
]
},
"f-expression": {
"comment": "All valid Python expressions, except comments and line cont",
"comment": "All valid Python expressions, except comments and line continuation",
"patterns": [
{
"include": "#expression-bare"
+10
View File
@@ -30,6 +30,16 @@ exports.load = function (modulePaths, resultCallback, options) {
const path = require('path');
const args = parseURLQueryArgs();
/**
* // configuration: IWindowConfiguration
* @type {{
* zoomLevel?: number,
* extensionDevelopmentPath?: string | string[],
* extensionTestsPath?: string,
* userEnv?: { [key: string]: string | undefined },
* appRoot?: string,
* nodeCachedDataDir?: string
* }} */
const configuration = JSON.parse(args['config'] || '{}') || {};
// Apply zoom level early to avoid glitches
@@ -107,7 +107,6 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo
}
this._webview = this.instantiationService.createInstance(WebviewElement,
this.layoutService.getContainer(Parts.EDITOR_PART),
{},
{
allowScripts: true
@@ -104,7 +104,6 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
}
this._webview = this.instantiationService.createInstance(WebviewElement,
this.layoutService.getContainer(Parts.EDITOR_PART),
{},
{
allowScripts: true,
@@ -77,7 +77,6 @@ export default class WebViewComponent extends ComponentBase implements IComponen
private _createWebview(): void {
this._webview = this.instantiationService.createInstance(WebviewElement,
this.layoutService.getContainer(Parts.EDITOR_PART),
{
allowSvgs: true
},
@@ -88,7 +88,6 @@ export class WebViewDialog extends Modal {
this._body = DOM.append(container, DOM.$('div.webview-dialog'));
this._webview = this._instantiationService.createInstance(WebviewElement,
this.layoutService.getContainer(Parts.EDITOR_PART),
{},
{
allowScripts: true
@@ -274,7 +274,7 @@ export class BreadcrumbsWidget {
this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload });
}
getItems(): ReadonlyArray<BreadcrumbsItem> {
getItems(): readonly BreadcrumbsItem[] {
return this._items;
}
+3 -4
View File
@@ -5,10 +5,10 @@
import { URI } from 'vs/base/common/uri';
import { sep, posix, normalize } from 'vs/base/common/path';
import { endsWith, ltrim, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
import { endsWith, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
import { isEqual, basename } from 'vs/base/common/resources';
import { isEqual, basename, relativePath } from 'vs/base/common/resources';
import { CharCode } from 'vs/base/common/charCode';
export interface IWorkspaceFolderProvider {
@@ -40,8 +40,7 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom
if (isEqual(baseResource.uri, resource)) {
pathLabel = ''; // no label if paths are identical
} else {
// TODO: isidor use resources.relative
pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), posix.sep)!);
pathLabel = relativePath(baseResource.uri, resource)!;
}
if (hasMultipleRoots) {
@@ -917,6 +917,18 @@ export class IssueReporter extends Disposable {
<tr><td>VM</td><td>${systemInfo.vmHint}</td></tr>
</table>`;
systemInfo.remoteData.forEach(remote => {
renderedData += `
<hr>
<table>
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
<tr><td>OS</td><td>${remote.machineInfo.os}</td></tr>
<tr><td>CPUs</td><td>${remote.machineInfo.cpus}</td></tr>
<tr><td>Memory (System)</td><td>${remote.machineInfo.memory}</td></tr>
<tr><td>VM</td><td>${remote.machineInfo.vmHint}</td></tr>
</table>`;
});
target.innerHTML = renderedData;
}
}
@@ -69,11 +69,19 @@ ${this._data.issueDescription}
Azure Data Studio version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion}
OS version: ${this._data.versionInfo && this._data.versionInfo.os}
${this.getRemoteOSes()}
${this.getInfos()}
<!-- generated by issue reporter -->`;
}
private getRemoteOSes(): string {
if (this._data.systemInfo && this._data.systemInfo.remoteData.length) {
return this._data.systemInfo.remoteData.map(remote => `Remote OS version: ${remote.machineInfo.os}`).join('\n') + '\n';
}
return '';
}
fileOnExtension(): boolean | undefined {
const fileOnExtensionSupported = this._data.issueType === IssueType.Bug
|| this._data.issueType === IssueType.PerformanceIssue
@@ -159,6 +167,18 @@ ${this.getInfos()}
|Process Argv|${this._data.systemInfo.processArgs}|
|Screen Reader|${this._data.systemInfo.screenReader}|
|VM|${this._data.systemInfo.vmHint}|`;
this._data.systemInfo.remoteData.forEach(remote => {
md += `
|Item|Value|
|---|---|
|Remote|${remote.hostName}|
|OS|${remote.machineInfo.os}|
|CPUs|${remote.machineInfo.cpus}|
|Memory (System)|${remote.machineInfo.memory}|
|VM|${remote.machineInfo.vmHint}|`;
});
}
md += '\n</details>';
@@ -50,6 +50,7 @@ Extensions: none
vmHint: '0%',
processArgs: '',
screenReader: 'no',
remoteData: [],
gpuStatus: {
'2d_canvas': 'enabled',
'checker_imaging': 'disabled_off'
@@ -82,6 +83,67 @@ OS version: undefined
<!-- generated by issue reporter -->`);
});
test('serializes remote information when data is provided', () => {
const issueReporterModel = new IssueReporterModel({
issueType: 0,
systemInfo: {
os: 'Darwin',
cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)',
memory: '16.00GB',
vmHint: '0%',
processArgs: '',
screenReader: 'no',
gpuStatus: {
'2d_canvas': 'enabled',
'checker_imaging': 'disabled_off'
},
remoteData: [
{
hostName: 'SSH: Pineapple',
machineInfo: {
os: 'Linux x64 4.18.0',
cpus: 'Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz (2 x 2294)',
memory: '8GB',
vmHint: '100%'
}
}
]
}
});
assert.equal(issueReporterModel.serialize(),
`
Issue Type: <b>Bug</b>
undefined
VS Code version: undefined
OS version: undefined
Remote OS version: Linux x64 4.18.0
<details>
<summary>System Info</summary>
|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)|
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off|
|Load (avg)|undefined|
|Memory (System)|16.00GB|
|Process Argv||
|Screen Reader|no|
|VM|0%|
|Item|Value|
|---|---|
|Remote|SSH: Pineapple|
|OS|Linux x64 4.18.0|
|CPUs|Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz (2 x 2294)|
|Memory (System)|8GB|
|VM|100%|
</details>Extensions: none
<!-- generated by issue reporter -->`);
});
test('should normalize GitHub urls', () => {
[
'https://github.com/repo',
@@ -64,7 +64,14 @@ bootstrapWindow.load([
});
/**
* @param {object} configuration
* // configuration: IWindowConfiguration
* @param {{
* partsSplashPath?: string,
* highContrast?: boolean,
* extensionDevelopmentPath?: string | string[],
* folderUri?: object,
* workspace?: object
* }} configuration
*/
function showPartsSplash(configuration) {
perf.mark('willShowPartsSplash');
+4 -2
View File
@@ -30,7 +30,7 @@ import { endsWith } from 'vs/base/common/strings';
export interface IWindowCreationOptions {
state: IWindowState;
extensionDevelopmentPath?: string;
extensionDevelopmentPath?: string | string[];
isExtensionTestHost?: boolean;
}
@@ -220,9 +220,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return !!this.config.extensionTestsPath;
}
get extensionDevelopmentPath(): string | undefined {
/*
get extensionDevelopmentPaths(): string | string[] | undefined {
return this.config.extensionDevelopmentPath;
}
*/
get config(): IWindowConfiguration {
return this.currentConfig;
+27 -6
View File
@@ -26,7 +26,7 @@ import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService, IRecent } from 'vs/platform/history/common/history';
import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform';
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { Schemas } from 'vs/base/common/network';
@@ -899,7 +899,7 @@ export class WindowsManager implements IWindowsMainService {
for (let f of fileUris) {
const fileUri = this.argToUri(f);
if (fileUri) {
const path = this.parseUri({ fileUri }, parseOptions);
const path = this.parseUri(hasWorkspaceFileExtension(f) ? { workspaceUri: fileUri } : { fileUri }, parseOptions);
if (path) {
pathsToOpen.push(path);
}
@@ -1153,7 +1153,7 @@ export class WindowsManager implements IWindowsMainService {
return { openFolderInNewWindow: !!openFolderInNewWindow, openFilesInNewWindow };
}
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string, openConfig: IOpenConfiguration): void {
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string | string[], openConfig: IOpenConfiguration): void {
// Reload an existing extension development host window on the same path
// We currently do not allow more than one extension development window
@@ -1207,9 +1207,30 @@ export class WindowsManager implements IWindowsMainService {
openConfig.cli['folder-uri'] = folderUris;
openConfig.cli['file-uri'] = fileUris;
const match = extensionDevelopmentPath.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
openConfig.cli['remote'] = URI.parse(extensionDevelopmentPath).authority;
if (Array.isArray(extensionDevelopmentPath)) {
let authority: string | undefined = undefined;
for (let p of extensionDevelopmentPath) {
const match = p.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
const auth = URI.parse(p).authority;
if (authority) {
if (auth !== authority) {
console.log('more than one authority');
}
} else {
authority = auth;
}
}
}
if (authority) {
openConfig.cli['remote'] = authority;
}
} else {
const match = extensionDevelopmentPath.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
openConfig.cli['remote'] = URI.parse(extensionDevelopmentPath).authority;
}
}
// Open it
+25 -5
View File
@@ -14,7 +14,7 @@ export interface ISimpleWindow {
openedWorkspace?: IWorkspaceIdentifier;
openedFolderUri?: URI;
extensionDevelopmentPath?: string;
extensionDevelopmentPath?: string | string[];
lastFocusTime: number;
}
@@ -95,13 +95,33 @@ export function findWindowOnWorkspace<W extends ISimpleWindow>(windows: W[], wor
return null;
}
export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string): W | null {
export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string | string[]): W | null {
const matches = (uriString: string): boolean => {
if (Array.isArray(extensionDevelopmentPath)) {
return extensionDevelopmentPath.some(p => extpath.isEqual(p, uriString, !platform.isLinux /* ignorecase */));
} else if (extensionDevelopmentPath) {
return extpath.isEqual(extensionDevelopmentPath, uriString, !platform.isLinux /* ignorecase */);
}
return false;
};
for (const window of windows) {
// match on extension development path. The path can be a path or uri string, using paths.isEqual is not 100% correct but good enough
if (window.extensionDevelopmentPath && extpath.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) {
return window;
// match on extension development path. The path can be one or more paths or uri strings, using paths.isEqual is not 100% correct but good enough
if (window.extensionDevelopmentPath) {
if (Array.isArray(window.extensionDevelopmentPath)) {
if (window.extensionDevelopmentPath.some(p => matches(p))) {
return window;
}
} else if (window.extensionDevelopmentPath) {
if (matches(window.extensionDevelopmentPath)) {
return window;
}
}
}
}
return null;
}
+6 -1
View File
@@ -1001,11 +1001,16 @@ export interface ILink {
range: IRange;
url?: URI | string;
}
export interface ILinksList {
links: ILink[];
dispose?(): void;
}
/**
* A provider of links.
*/
export interface LinkProvider {
provideLinks(model: model.ITextModel, token: CancellationToken): ProviderResult<ILink[]>;
provideLinks(model: model.ITextModel, token: CancellationToken): ProviderResult<ILinksList>;
resolveLink?: (link: ILink, token: CancellationToken) => ProviderResult<ILink>;
}
@@ -58,12 +58,14 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
this._workerManager = this._register(new WorkerManager(this._modelService));
// todo@joh make sure this happens only once
this._register(modes.LinkProviderRegistry.register('*', <modes.LinkProvider>{
this._register(modes.LinkProviderRegistry.register('*', {
provideLinks: (model, token) => {
if (!canSyncModel(this._modelService, model.uri)) {
return Promise.resolve([]); // File too large
return Promise.resolve({ links: [] }); // File too large
}
return this._workerManager.withWorker().then(client => client.computeLinks(model.uri));
return this._workerManager.withWorker().then(client => client.computeLinks(model.uri)).then(links => {
return links && { links };
});
}
}));
this._register(modes.CompletionProviderRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, this._modelService)));
@@ -32,7 +32,7 @@ export class CodeActionSet {
}
}
public readonly actions: ReadonlyArray<CodeAction>;
public readonly actions: readonly CodeAction[];
public constructor(actions: CodeAction[]) {
this.actions = mergeSort(actions, CodeActionSet.codeActionsComparator);
+81 -57
View File
@@ -8,9 +8,11 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { ILink, LinkProvider, LinkProviderRegistry } from 'vs/editor/common/modes';
import { ILink, LinkProvider, LinkProviderRegistry, ILinksList } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { isDisposable, Disposable } from 'vs/base/common/lifecycle';
import { coalesce } from 'vs/base/common/arrays';
export class Link implements ILink {
@@ -66,77 +68,99 @@ export class Link implements ILink {
}
}
export function getLinks(model: ITextModel, token: CancellationToken): Promise<Link[]> {
export class LinksList extends Disposable {
let links: Link[] = [];
readonly links: Link[];
constructor(tuples: [ILinksList, LinkProvider][]) {
super();
let links: Link[] = [];
for (const [list, provider] of tuples) {
// merge all links
const newLinks = list.links.map(link => new Link(link, provider));
links = LinksList._union(links, newLinks);
// register disposables
if (isDisposable(provider)) {
this._register(provider);
}
}
this.links = links;
}
private static _union(oldLinks: Link[], newLinks: Link[]): Link[] {
// reunite oldLinks with newLinks and remove duplicates
let result: Link[] = [];
let oldIndex: number;
let oldLen: number;
let newIndex: number;
let newLen: number;
for (oldIndex = 0, newIndex = 0, oldLen = oldLinks.length, newLen = newLinks.length; oldIndex < oldLen && newIndex < newLen;) {
const oldLink = oldLinks[oldIndex];
const newLink = newLinks[newIndex];
if (Range.areIntersectingOrTouching(oldLink.range, newLink.range)) {
// Remove the oldLink
oldIndex++;
continue;
}
const comparisonResult = Range.compareRangesUsingStarts(oldLink.range, newLink.range);
if (comparisonResult < 0) {
// oldLink is before
result.push(oldLink);
oldIndex++;
} else {
// newLink is before
result.push(newLink);
newIndex++;
}
}
for (; oldIndex < oldLen; oldIndex++) {
result.push(oldLinks[oldIndex]);
}
for (; newIndex < newLen; newIndex++) {
result.push(newLinks[newIndex]);
}
return result;
}
}
export function getLinks(model: ITextModel, token: CancellationToken): Promise<LinksList> {
const lists: [ILinksList, LinkProvider][] = [];
// ask all providers for links in parallel
const promises = LinkProviderRegistry.ordered(model).reverse().map(provider => {
const promises = LinkProviderRegistry.ordered(model).reverse().map((provider, i) => {
return Promise.resolve(provider.provideLinks(model, token)).then(result => {
if (Array.isArray(result)) {
const newLinks = result.map(link => new Link(link, provider));
links = union(links, newLinks);
if (result) {
lists[i] = [result, provider];
}
}, onUnexpectedExternalError);
});
return Promise.all(promises).then(() => {
return links;
});
return Promise.all(promises).then(() => new LinksList(coalesce(lists)));
}
function union(oldLinks: Link[], newLinks: Link[]): Link[] {
// reunite oldLinks with newLinks and remove duplicates
let result: Link[] = [];
let oldIndex: number;
let oldLen: number;
let newIndex: number;
let newLen: number;
for (oldIndex = 0, newIndex = 0, oldLen = oldLinks.length, newLen = newLinks.length; oldIndex < oldLen && newIndex < newLen;) {
const oldLink = oldLinks[oldIndex];
const newLink = newLinks[newIndex];
if (Range.areIntersectingOrTouching(oldLink.range, newLink.range)) {
// Remove the oldLink
oldIndex++;
continue;
}
const comparisonResult = Range.compareRangesUsingStarts(oldLink.range, newLink.range);
if (comparisonResult < 0) {
// oldLink is before
result.push(oldLink);
oldIndex++;
} else {
// newLink is before
result.push(newLink);
newIndex++;
}
}
for (; oldIndex < oldLen; oldIndex++) {
result.push(oldLinks[oldIndex]);
}
for (; newIndex < newLen; newIndex++) {
result.push(newLinks[newIndex]);
}
return result;
}
CommandsRegistry.registerCommand('_executeLinkProvider', (accessor, ...args) => {
CommandsRegistry.registerCommand('_executeLinkProvider', async (accessor, ...args): Promise<ILink[]> => {
const [uri] = args;
if (!(uri instanceof URI)) {
return undefined;
return [];
}
const model = accessor.get(IModelService).getModel(uri);
if (!model) {
return undefined;
return [];
}
return getLinks(model, CancellationToken.None);
const list = await getLinks(model, CancellationToken.None);
if (!list) {
return [];
}
const result = list.links.slice(0);
list.dispose();
return result;
});
+14 -4
View File
@@ -19,7 +19,7 @@ import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeSti
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { LinkProviderRegistry } from 'vs/editor/common/modes';
import { ClickLinkGesture, ClickLinkKeyboardEvent, ClickLinkMouseEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
import { Link, getLinks } from 'vs/editor/contrib/links/getLinks';
import { Link, getLinks, LinksList } from 'vs/editor/contrib/links/getLinks';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
@@ -157,7 +157,8 @@ class LinkDetector implements editorCommon.IEditorContribution {
private enabled: boolean;
private listenersToRemove: IDisposable[];
private readonly timeout: async.TimeoutTimer;
private computePromise: async.CancelablePromise<Link[]> | null;
private computePromise: async.CancelablePromise<LinksList> | null;
private activeLinksList: LinksList | null;
private activeLinkDecorationId: string | null;
private readonly openerService: IOpenerService;
private readonly notificationService: INotificationService;
@@ -210,6 +211,7 @@ class LinkDetector implements editorCommon.IEditorContribution {
this.timeout = new async.TimeoutTimer();
this.computePromise = null;
this.activeLinksList = null;
this.currentOccurrences = {};
this.activeLinkDecorationId = null;
this.beginCompute();
@@ -246,10 +248,15 @@ class LinkDetector implements editorCommon.IEditorContribution {
return;
}
if (this.activeLinksList) {
this.activeLinksList.dispose();
this.activeLinksList = null;
}
this.computePromise = async.createCancelablePromise(token => getLinks(model, token));
try {
const links = await this.computePromise;
this.updateDecorations(links);
this.activeLinksList = await this.computePromise;
this.updateDecorations(this.activeLinksList.links);
} catch (err) {
onUnexpectedError(err);
} finally {
@@ -380,6 +387,9 @@ class LinkDetector implements editorCommon.IEditorContribution {
private stop(): void {
this.timeout.cancel();
if (this.activeLinksList) {
this.activeLinksList.dispose();
}
if (this.computePromise) {
this.computePromise.cancel();
this.computePromise = null;
+6 -1
View File
@@ -5253,11 +5253,16 @@ declare namespace monaco.languages {
url?: Uri | string;
}
export interface ILinksList {
links: ILink[];
dispose?(): void;
}
/**
* A provider of links.
*/
export interface LinkProvider {
provideLinks(model: editor.ITextModel, token: CancellationToken): ProviderResult<ILink[]>;
provideLinks(model: editor.ITextModel, token: CancellationToken): ProviderResult<ILinksList>;
resolveLink?: (link: ILink, token: CancellationToken) => ProviderResult<ILink>;
}
@@ -125,7 +125,6 @@ export interface IConfigurationData {
user: IConfigurationModel;
workspace: IConfigurationModel;
folders: { [folder: string]: IConfigurationModel };
isComplete: boolean;
}
export function compare(from: IConfigurationModel, to: IConfigurationModel): { added: string[], removed: string[], updated: string[] } {
@@ -513,8 +513,7 @@ export class Configuration {
const { contents, overrides, keys } = this._folderConfigurations.get(folder)!;
result[folder.toString()] = { contents, overrides, keys };
return result;
}, Object.create({})),
isComplete: true
}, Object.create({}))
};
}
@@ -22,9 +22,14 @@ export interface SystemInfo extends IMachineInfo {
processArgs: string;
gpuStatus: any;
screenReader: string;
remoteData: IRemoteDiagnosticInfo[];
load?: string;
}
export interface IRemoteDiagnosticInfo extends IDiagnosticInfo {
hostName: string;
}
export interface IDiagnosticInfoOptions {
includeProcesses?: boolean;
folders?: UriComponents[];
@@ -15,7 +15,7 @@ import { app } from 'electron';
import { basename } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { WorkspaceStats, SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IMachineInfo, WorkspaceStats, SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { collectWorkspaceStats, getMachineInfo } from 'vs/platform/diagnostics/node/diagnosticsService';
import { ProcessItem } from 'vs/base/common/processes';
@@ -46,12 +46,21 @@ export interface PerformanceInfo {
processInfo?: string;
workspaceInfo?: string;
}
export class DiagnosticsService implements IDiagnosticsService {
_serviceBrand: any;
formatEnvironment(info: IMainProcessInfo): string {
private formatMachineInfo(info: IMachineInfo): string {
const output: string[] = [];
output.push(`OS Version: ${info.os}`);
output.push(`CPUs: ${info.cpus}`);
output.push(`Memory (System): ${info.memory}`);
output.push(`VM: ${info.vmHint}`);
return output.join('\n');
}
private formatEnvironment(info: IMainProcessInfo): string {
const MB = 1024 * 1024;
const GB = 1024 * MB;
@@ -76,10 +85,40 @@ export class DiagnosticsService implements IDiagnosticsService {
async getPerformanceInfo(launchService: ILaunchService): Promise<PerformanceInfo> {
const info = await launchService.getMainProcessInfo();
return Promise.all<ProcessItem, string>([listProcesses(info.mainPID), this.formatWorkspaceMetadata(info)]).then(result => {
const [rootProcess, workspaceInfo] = result;
return Promise.all<ProcessItem, string>([listProcesses(info.mainPID), this.formatWorkspaceMetadata(info)]).then(async result => {
let [rootProcess, workspaceInfo] = result;
let processInfo = this.formatProcessList(info, rootProcess);
try {
const remoteData = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
remoteData.forEach(diagnostics => {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
}
});
} catch (e) {
processInfo += `\nFetching remote data failed: ${e}`;
workspaceInfo += `\nFetching remote data failed: ${e}`;
}
return {
processInfo: this.formatProcessList(info, rootProcess),
processInfo,
workspaceInfo
};
});
@@ -95,7 +134,8 @@ export class DiagnosticsService implements IDiagnosticsService {
vmHint,
processArgs: `${info.mainArguments.join(' ')}`,
gpuStatus: app.getGPUFeatureStatus(),
screenReader: `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`
screenReader: `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
remoteData: await launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })
};
@@ -120,12 +160,42 @@ export class DiagnosticsService implements IDiagnosticsService {
output.push(this.formatProcessList(info, rootProcess));
// Workspace Stats
if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) {
if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0 && !window.remoteAuthority)) {
output.push('');
output.push('Workspace Stats: ');
output.push(await this.formatWorkspaceMetadata(info));
}
try {
const data = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
data.forEach(diagnostics => {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
}
});
} catch (e) {
output.push('\n\n');
output.push(`Fetching status information from remotes failed: ${e.message}`);
}
output.push('');
output.push('');
@@ -195,7 +265,7 @@ export class DiagnosticsService implements IDiagnosticsService {
const workspaceStatPromises: Promise<void>[] = [];
info.windows.forEach(window => {
if (window.folderURIs.length === 0) {
if (window.folderURIs.length === 0 || !!window.remoteAuthority) {
return;
}
@@ -37,7 +37,7 @@ export interface ParsedArgs {
logExtensionHostCommunication?: boolean;
'extensions-dir'?: string;
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string; // either a local path or a URI
extensionDevelopmentPath?: string | string[]; // one or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
'inspect-extensions'?: string;
'inspect-brk-extensions'?: string;
@@ -124,7 +124,7 @@ export interface IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsLocationURI?: URI;
debugExtensionHost: IExtensionHostDebugParams;
@@ -172,9 +172,16 @@ export class EnvironmentService implements IEnvironmentService {
}
@memoize
get extensionDevelopmentLocationURI(): URI | undefined {
get extensionDevelopmentLocationURI(): URI | URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
if (s) {
if (Array.isArray(s)) {
return s.map(p => {
if (/^[^:/?#]+?:\/\//.test(p)) {
return URI.parse(p);
}
return URI.file(path.normalize(p));
});
} else if (s) {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
}
@@ -245,12 +245,6 @@ export interface IExtensionEnablementService {
*/
onEnablementChanged: Event<IExtension[]>;
/**
* Returns all disabled extension identifiers for current workspace
* Returns an empty array if none exist
*/
getDisabledExtensions(): Promise<IExtensionIdentifier[]>;
/**
* Returns the enablement state for the given extension
*/
@@ -15,10 +15,11 @@ import { whenDeleted } from 'vs/base/node/pfs';
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI, UriComponents } from 'vs/base/common/uri';
import { BrowserWindow } from 'electron';
import { BrowserWindow, ipcMain, Event as IpcEvent } from 'electron';
import { Event } from 'vs/base/common/event';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { coalesce } from 'vs/base/common/arrays';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
@@ -32,6 +33,7 @@ export interface IWindowInfo {
pid: number;
title: string;
folderURIs: UriComponents[];
remoteAuthority?: string;
}
export interface IMainProcessInfo {
@@ -41,6 +43,11 @@ export interface IMainProcessInfo {
windows: IWindowInfo[];
}
export interface IRemoteDiagnosticOptions {
includeProcesses?: boolean;
includeWorkspaceMetadata?: boolean;
}
function parseOpenUrl(args: ParsedArgs): URI[] {
if (args['open-url'] && args._urls && args._urls.length > 0) {
// --open-url must contain -- followed by the url(s)
@@ -64,6 +71,7 @@ export interface ILaunchService {
getMainProcessId(): Promise<number>;
getMainProcessInfo(): Promise<IMainProcessInfo>;
getLogsPath(): Promise<string>;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]>;
}
export class LaunchChannel implements IServerChannel {
@@ -88,6 +96,9 @@ export class LaunchChannel implements IServerChannel {
case 'get-logs-path':
return this.service.getLogsPath();
case 'get-remote-diagnostics':
return this.service.getRemoteDiagnostics(arg);
}
throw new Error(`Call not found: ${command}`);
@@ -115,6 +126,10 @@ export class LaunchChannelClient implements ILaunchService {
getLogsPath(): Promise<string> {
return this.channel.call('get-logs-path', null);
}
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]> {
return this.channel.call('get-remote-diagnostics', options);
}
}
export class LaunchService implements ILaunchService {
@@ -284,7 +299,45 @@ export class LaunchService implements ILaunchService {
return Promise.resolve(this.environmentService.logsPath);
}
private codeWindowToInfo(window: ICodeWindow): IWindowInfo {
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]> {
const windows = this.windowsMainService.getWindows();
const promises: Promise<IDiagnosticInfo | undefined>[] = windows.map(window => {
return new Promise((resolve, reject) => {
if (window.remoteAuthority) {
const replyChannel = `vscode:getDiagnosticInfoResponse${window.id}`;
const args: IDiagnosticInfoOptions = {
includeProcesses: options.includeProcesses,
folders: options.includeWorkspaceMetadata ? this.getFolderURIs(window) : undefined
};
window.sendWhenReady('vscode:getDiagnosticInfo', { replyChannel, args });
ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => {
// No data is returned if getting the connection fails.
if (!data) {
resolve();
}
if (typeof (data) === 'string') {
reject(new Error(data));
}
resolve(data);
});
setTimeout(() => {
resolve();
}, 5000);
} else {
resolve();
}
});
});
return Promise.all(promises).then(diagnostics => diagnostics.filter((x): x is IRemoteDiagnosticInfo => !!x));
}
private getFolderURIs(window: ICodeWindow): URI[] {
const folderURIs: URI[] = [];
if (window.openedFolderUri) {
@@ -303,14 +356,20 @@ export class LaunchService implements ILaunchService {
}
}
return this.browserWindowToInfo(window.win, folderURIs);
return folderURIs;
}
private browserWindowToInfo(win: BrowserWindow, folderURIs: URI[] = []): IWindowInfo {
private codeWindowToInfo(window: ICodeWindow): IWindowInfo {
const folderURIs = this.getFolderURIs(window);
return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority);
}
private browserWindowToInfo(win: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo {
return {
pid: win.webContents.getOSProcessId(),
title: win.getTitle(),
folderURIs
folderURIs,
remoteAuthority
};
}
}
@@ -63,7 +63,6 @@ export interface IRemoteExtensionHostStartParams {
debugId?: string;
break?: boolean;
port?: number | null;
updatePort?: boolean;
}
interface IExtensionHostConnectionResult {
@@ -11,8 +11,6 @@ export interface ResolvedAuthority {
readonly authority: string;
readonly host: string;
readonly port: number;
readonly debugListenPort?: number;
readonly debugConnectPort?: number;
}
export interface IRemoteAuthorityResolverService {
@@ -97,7 +97,7 @@ export interface IWindowsMainService {
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | undefined>;
closeWorkspace(win: ICodeWindow): void;
open(openConfig: IOpenConfiguration): ICodeWindow[];
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string, openConfig: IOpenConfiguration): void;
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string | string[], openConfig: IOpenConfiguration): void;
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void>;
-14
View File
@@ -75,8 +75,6 @@ declare module 'vscode' {
export class ResolvedAuthority {
readonly host: string;
readonly port: number;
debugListenPort?: number;
debugConnectPort?: number;
constructor(host: string, port: number);
}
@@ -473,11 +471,6 @@ declare module 'vscode' {
}
export namespace workspace {
/**
* DEPRECATED
*/
export function registerSearchProvider(): Disposable;
/**
* Register a search provider.
*
@@ -1017,9 +1010,7 @@ declare module 'vscode' {
* Provide a list of ranges which allow new comment threads creation or null for a given document
*/
provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult<Range[]>;
}
export interface EmptyCommentThreadFactory {
/**
* The method `createEmptyCommentThread` is called when users attempt to create new comment thread from the gutter or command palette.
* Extensions still need to call `createCommentThread` inside this call when appropriate.
@@ -1054,11 +1045,6 @@ declare module 'vscode' {
*/
commentingRangeProvider?: CommentingRangeProvider;
/**
* Optional new comment thread factory.
*/
emptyCommentThreadFactory?: EmptyCommentThreadFactory;
/**
* Optional reaction provider
*/
@@ -357,6 +357,7 @@ export class MainThreadCommentController {
return <ICommentInfo>{
owner: this._uniqueId,
label: this.label,
threads: ret,
commentingRanges: commentingRanges ?
{
@@ -377,7 +377,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
command: data.m,
// not-standard
_id: data.x,
_pid: data.y
};
}
@@ -393,14 +392,14 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return {
suggestions: result.b.map(d => MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)),
incomplete: result.c,
dispose: () => this._proxy.$releaseCompletionItems(handle, result.x)
dispose: () => typeof result.x === 'number' && this._proxy.$releaseCompletionItems(handle, result.x)
};
});
}
};
if (supportsResolveDetails) {
provider.resolveCompletionItem = (model, position, suggestion, token) => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, suggestion._pid, token).then(result => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, token).then(result => {
if (!result) {
return suggestion;
}
@@ -428,29 +427,36 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- links
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.LinkProviderRegistry.register(selector, <modes.LinkProvider>{
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[], supportsResolve: boolean): void {
const provider: modes.LinkProvider = {
provideLinks: (model, token) => {
return this._proxy.$provideDocumentLinks(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
MainThreadLanguageFeatures._reviveLinkDTO(obj);
this._heapService.trackObject(obj);
});
if (!dto) {
return undefined;
}
return dto;
});
},
resolveLink: (link, token) => {
return this._proxy.$resolveDocumentLink(handle, link, token).then(obj => {
if (obj) {
MainThreadLanguageFeatures._reviveLinkDTO(obj);
this._heapService.trackObject(obj);
}
return obj;
return {
links: dto.links.map(MainThreadLanguageFeatures._reviveLinkDTO),
dispose: () => {
if (typeof dto.id === 'number') {
this._proxy.$releaseDocumentLinks(handle, dto.id);
}
}
};
});
}
});
};
if (supportsResolve) {
provider.resolveLink = (link, token) => {
const dto: LinkDto = link;
if (!dto.cacheId) {
return link;
}
return this._proxy.$resolveDocumentLink(handle, dto.cacheId, token).then(obj => {
return obj && MainThreadLanguageFeatures._reviveLinkDTO(obj);
});
};
}
this._registrations[handle] = modes.LinkProviderRegistry.register(selector, provider);
}
// --- colors
@@ -335,7 +335,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
}
}
private async applyCodeActions(actionsToRun: ReadonlyArray<CodeAction>) {
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
for (const action of actionsToRun) {
await applyCodeAction(action, this._bulkEditService, this._commandService);
}
+24 -11
View File
@@ -52,9 +52,12 @@ import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
appName: string;
appRoot?: URI;
appLanguage: string;
appUriScheme: string;
appSettingsHome?: URI;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsLocationURI?: URI;
globalStorageHome: URI;
userHome: URI;
@@ -71,6 +74,7 @@ export interface IWorkspaceData extends IStaticWorkspaceData {
}
export interface IInitData {
version: string;
commit?: string;
parentPid: number;
environment: IEnvironment;
@@ -318,8 +322,8 @@ export interface ISerializedDocumentFilter {
}
export interface ISerializedSignatureHelpProviderMetadata {
readonly triggerCharacters: ReadonlyArray<string>;
readonly retriggerCharacters: ReadonlyArray<string>;
readonly triggerCharacters: readonly string[];
readonly retriggerCharacters: readonly string[];
}
export interface MainThreadLanguageFeaturesShape extends IDisposable {
@@ -343,7 +347,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void;
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], metadata: ISerializedSignatureHelpProviderMetadata): void;
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[], supportsResolve: boolean): void;
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -878,12 +882,11 @@ export interface SuggestDataDto {
l/* additionalTextEdits */?: ISingleEditOperation[];
m/* command */?: modes.Command;
// not-standard
x: number;
y: number;
x?: ChainedCacheId;
}
export interface SuggestResultDto {
x: number;
x?: number;
a: IRange;
b: SuggestDataDto[];
c?: boolean;
@@ -961,7 +964,16 @@ export interface CodeActionDto {
isPreferred?: boolean;
}
export interface LinkDto extends ObjectIdentifier {
export type CacheId = number;
export type ChainedCacheId = [CacheId, CacheId];
export interface LinksListDto {
id?: CacheId;
links: LinkDto[];
}
export interface LinkDto {
cacheId?: ChainedCacheId;
range: IRange;
url?: string | UriComponents;
}
@@ -1007,11 +1019,12 @@ export interface ExtHostLanguageFeaturesShape {
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinkDto[] | undefined>;
$resolveDocumentLink(handle: number, link: LinkDto, token: CancellationToken): Promise<LinkDto | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinksListDto | undefined>;
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined>;
$releaseDocumentLinks(handle: number, id: number): void;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[] | undefined>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[] | undefined>;
@@ -25,7 +25,6 @@ import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/we
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { extHostNamedCustomer } from '../common/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
@@ -61,7 +60,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
) {
super();
@@ -136,7 +134,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
// 4) continue to forward messages to the webview
const webview = this._instantiationService.createInstance(
WebviewElement,
this._layoutService.getContainer(Parts.EDITOR_PART),
{
extension: {
location: URI.revive(extensionLocation),
+7 -11
View File
@@ -9,7 +9,6 @@ import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import * as path from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
@@ -17,8 +16,6 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as files from 'vs/platform/files/common/files';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { ExtHostContext, IInitData, IMainContext, MainContext, MainThreadKeytarShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard';
@@ -27,6 +24,8 @@ import { ExtHostComments } from 'vs/workbench/api/node/extHostComments';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration';
// {{SQL CARBON EDIT}} - Remove ExtHostDebugService
// import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
// {{SQL CARBON EDIT}} - Import product
import product from 'vs/platform/product/node/product';
import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
@@ -68,6 +67,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
import { originalFSPath } from 'vs/base/common/resources';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { withNullAsUndefined } from 'vs/base/common/types';
import { values } from 'vs/base/common/collections';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -134,7 +134,7 @@ export function createApiFactory(
}
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
rpcProtocol.assertRegistered(expected);
// Other instances
@@ -234,10 +234,10 @@ export function createApiFactory(
const env: typeof vscode.env = Object.freeze({
get machineId() { return initData.telemetryInfo.machineId; },
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return platform.language!; },
get appName() { return product.nameLong; },
get language() { return initData.environment.appLanguage; },
get appName() { return initData.environment.appName; },
get appRoot() { return initData.environment.appRoot!.fsPath; },
get uriScheme() { return product.urlProtocol; },
get uriScheme() { return initData.environment.appUriScheme; },
get logLevel() {
checkProposedApiEnabled(extension);
return typeConverters.LogLevel.to(extHostLogService.getLevel());
@@ -628,10 +628,6 @@ export function createApiFactory(
registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => {
return extHostSearch.registerFileSearchProvider(scheme, provider);
}),
registerSearchProvider: proposedApiFunction(extension, () => {
// Temp for live share in Insiders
return { dispose: () => { } };
}),
registerTextSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.TextSearchProvider) => {
return extHostSearch.registerTextSearchProvider(scheme, provider);
}),
+15 -4
View File
@@ -149,12 +149,25 @@ export class ExtHostComments implements ExtHostCommentsShape {
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<void> {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.emptyCommentThreadFactory) {
if (!commentController) {
return Promise.resolve();
}
if (!(commentController as any).emptyCommentThreadFactory && !(commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread)) {
return Promise.resolve();
}
const document = this._documents.getDocument(URI.revive(uriComponents));
return asPromise(() => commentController.emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range))).then(() => Promise.resolve());
return asPromise(() => {
// TODO, remove this once GH PR stable deprecates `emptyCommentThreadFactory`.
if ((commentController as any).emptyCommentThreadFactory) {
return (commentController as any).emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
}
if (commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread) {
return commentController.commentingRangeProvider.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
}
}).then(() => Promise.resolve());
}
registerWorkspaceCommentProvider(
@@ -558,8 +571,6 @@ class ExtHostCommentController implements vscode.CommentController {
private _threads: Map<number, ExtHostCommentThread> = new Map<number, ExtHostCommentThread>();
commentingRangeProvider?: vscode.CommentingRangeProvider;
emptyCommentThreadFactory?: vscode.EmptyCommentThreadFactory;
private _commentReactionProvider?: vscode.CommentReactionProvider;
@@ -707,8 +707,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
authority: remoteAuthority,
host: result.host,
port: result.port,
debugListenPort: result.debugListenPort,
debugConnectPort: result.debugConnectPort,
};
}
@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto } from '../common/extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto, LinksListDto, ChainedCacheId } from '../common/extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
@@ -623,8 +623,7 @@ class SuggestAdapter {
private _commands: CommandsConverter;
private _provider: vscode.CompletionItemProvider;
private _cache = new Map<number, vscode.CompletionItem[]>();
private _idPool = 0;
private _cache = new Cache<vscode.CompletionItem>();
constructor(documents: ExtHostDocuments, commands: CommandsConverter, provider: vscode.CompletionItemProvider) {
this._documents = documents;
@@ -639,33 +638,32 @@ class SuggestAdapter {
return asPromise(() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))).then(value => {
const _id = this._idPool++;
if (!value) {
// undefined and null are valid results
return undefined;
}
let list = Array.isArray(value) ? new CompletionList(value) : value;
let pid: number | undefined;
// keep result for providers that support resolving
if (SuggestAdapter.supportsResolving(this._provider)) {
pid = this._cache.add(list.items);
}
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
.with({ end: pos });
const result: SuggestResultDto = {
x: _id,
x: pid,
b: [],
a: typeConvert.Range.from(wordRangeBeforePos),
c: list.isIncomplete || undefined
};
let list: CompletionList;
if (!value) {
// undefined and null are valid results
return undefined;
} else if (Array.isArray(value)) {
list = new CompletionList(value);
} else {
list = value;
result.c = list.isIncomplete;
}
for (let i = 0; i < list.items.length; i++) {
const suggestion = this._convertCompletionItem2(list.items[i], pos, i, _id);
const suggestion = this._convertCompletionItem(list.items[i], pos, pid && [pid, i] || undefined);
// check for bad completion item
// for the converter did warn
if (suggestion) {
@@ -673,21 +671,17 @@ class SuggestAdapter {
}
}
if (SuggestAdapter.supportsResolving(this._provider)) {
this._cache.set(_id, list.items);
}
return result;
});
}
resolveCompletionItem(_resource: URI, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined> {
resolveCompletionItem(_resource: URI, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined> {
if (typeof this._provider.resolveCompletionItem !== 'function') {
return Promise.resolve(undefined);
}
const item = this._cache.has(pid) ? this._cache.get(pid)![id] : undefined;
const item = this._cache.get(...id);
if (!item) {
return Promise.resolve(undefined);
}
@@ -699,7 +693,7 @@ class SuggestAdapter {
}
const pos = typeConvert.Position.to(position);
return this._convertCompletionItem2(resolvedItem, pos, id, pid);
return this._convertCompletionItem(resolvedItem, pos, id);
});
}
@@ -707,7 +701,7 @@ class SuggestAdapter {
this._cache.delete(id);
}
private _convertCompletionItem2(item: vscode.CompletionItem, position: vscode.Position, id: number, pid: number): SuggestDataDto | undefined {
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId | undefined): SuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
return undefined;
@@ -716,7 +710,6 @@ class SuggestAdapter {
const result: SuggestDataDto = {
//
x: id,
y: pid,
//
a: item.label,
b: typeConvert.CompletionItemKind.from(item.kind),
@@ -800,49 +793,78 @@ class SignatureHelpAdapter {
}
}
class Cache<T> {
private _data = new Map<number, T[]>();
private _idPool = 1;
add(item: T[]): number {
const id = this._idPool++;
this._data.set(id, item);
return id;
}
get(pid: number, id: number): T | undefined {
return this._data.has(pid) ? this._data.get(pid)![id] : undefined;
}
delete(id: number) {
this._data.delete(id);
}
}
class LinkProviderAdapter {
private _cache = new Cache<vscode.DocumentLink>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.DocumentLinkProvider
) { }
provideLinks(resource: URI, token: CancellationToken): Promise<LinkDto[] | undefined> {
provideLinks(resource: URI, token: CancellationToken): Promise<LinksListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideDocumentLinks(doc, token)).then(links => {
if (!Array.isArray(links)) {
if (!Array.isArray(links) || links.length === 0) {
// bad result
return undefined;
}
const result: LinkDto[] = [];
for (const link of links) {
const data = typeConvert.DocumentLink.from(link);
const id = this._heapService.keep(link);
result.push(ObjectIdentifier.mixin(data, id));
if (typeof this._provider.resolveDocumentLink !== 'function') {
// no resolve -> no caching
return { links: links.map(typeConvert.DocumentLink.from) };
} else {
// cache links for future resolving
const pid = this._cache.add(links);
const result: LinksListDto = { links: [], id: pid };
for (let i = 0; i < links.length; i++) {
const dto: LinkDto = typeConvert.DocumentLink.from(links[i]);
dto.cacheId = [pid, i];
result.links.push(dto);
}
return result;
}
return result;
});
}
resolveLink(link: LinkDto, token: CancellationToken): Promise<LinkDto | undefined> {
resolveLink(id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined> {
if (typeof this._provider.resolveDocumentLink !== 'function') {
return Promise.resolve(undefined);
}
const id = ObjectIdentifier.of(link);
const item = this._heapService.get<vscode.DocumentLink>(id);
const item = this._cache.get(...id);
if (!item) {
return Promise.resolve(undefined);
}
return asPromise(() => this._provider.resolveDocumentLink!(item, token)).then(value => {
if (value) {
return typeConvert.DocumentLink.from(value);
}
return undefined;
return value && typeConvert.DocumentLink.from(value) || undefined;
});
}
releaseLinks(id: number): any {
this._cache.delete(id);
}
}
class ColorProviderAdapter {
@@ -1373,8 +1395,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined);
}
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, id, pid, token), undefined);
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, id, token), undefined);
}
$releaseCompletionItems(handle: number, id: number): void {
@@ -1400,17 +1422,21 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- links
registerDocumentLinkProvider(extension: IExtensionDescription | undefined, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider), extension);
this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector));
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector), typeof provider.resolveDocumentLink === 'function');
return this._createDisposable(handle);
}
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinkDto[] | undefined> {
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinksListDto | undefined> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token), undefined);
}
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise<LinkDto | undefined> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token), undefined);
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(id, token), undefined);
}
$releaseDocumentLinks(handle: number, id: number): void {
this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.releaseLinks(id), undefined);
}
registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
@@ -432,8 +432,6 @@ export class Selection extends Range {
export class ResolvedAuthority {
readonly host: string;
readonly port: number;
debugListenPort?: number;
debugConnectPort?: number;
constructor(host: string, port: number) {
if (typeof host !== 'string' || host.length === 0) {
+2 -2
View File
@@ -8,7 +8,7 @@ import * as resources from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { toResource, IEditorInput } from 'vs/workbench/common/editor';
import { toResource, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -332,7 +332,7 @@ class ResourceLabelWidget extends IconLabel {
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
this.setResource({
resource: withNullAsUndefined(toResource(editor, { supportSideBySide: true })),
resource: withNullAsUndefined(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })),
name: withNullAsUndefined(editor.getName()),
description: withNullAsUndefined(editor.getDescription())
}, options);
@@ -45,6 +45,11 @@ class CodeRendererMain extends Disposable {
const logService = new SimpleLogService();
serviceCollection.set(ILogService, logService);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE.
// CONTRIBUTE IT VIA WORKBENCH.MAIN.TS AND registerSingleton().
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return { serviceCollection, logService };
}
}
@@ -18,9 +18,9 @@ import { SimpleConfigurationService as StandaloneEditorConfigurationService, Sta
import { IDownloadService } from 'vs/platform/download/common/download';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams } from 'vs/platform/environment/common/environment';
import { IExtensionGalleryService, IQueryOptions, IGalleryExtension, InstallOperation, StatisticType, ITranslation, IGalleryExtensionVersion, IExtensionIdentifier, IReportedExtension, IExtensionManagementService, ILocalExtension, IGalleryMetadata, IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionGalleryService, IQueryOptions, IGalleryExtension, InstallOperation, StatisticType, ITranslation, IGalleryExtensionVersion, IExtensionIdentifier, IReportedExtension, IExtensionManagementService, ILocalExtension, IGalleryMetadata, IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation, IExtensionEnablementService, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IPager } from 'vs/base/common/paging';
import { IExtensionManifest, ExtensionType, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IExtensionManifest, ExtensionType, ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions';
import { NullExtensionService, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -251,7 +251,7 @@ export class SimpleEnvironmentService implements IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsPath?: string;
debugExtensionHost: IExtensionHostDebugParams;
debugSearch: IDebugParams;
@@ -354,6 +354,38 @@ registerSingleton(IExtensionGalleryService, SimpleExtensionGalleryService, true)
//#region Extension Management
//#region Extension Enablement
export class SimpleExtensionEnablementService implements IExtensionEnablementService {
_serviceBrand: any;
readonly onEnablementChanged = Event.None;
readonly allUserExtensionsDisabled = true;
getEnablementState(extension: IExtension): EnablementState {
return EnablementState.Disabled;
}
canChangeEnablement(extension: IExtension): boolean {
return false;
}
setEnablement(extensions: IExtension[], newState: EnablementState): Promise<boolean[]> {
throw new Error('not implemented');
}
isEnabled(extension: IExtension): boolean {
return false;
}
}
registerSingleton(IExtensionEnablementService, SimpleExtensionEnablementService, true);
//#endregion
//#region Extension Tips
export class SimpleExtensionTipsService implements IExtensionTipsService {
@@ -73,6 +73,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
this.visibleComposites = [];
this.compositeSizeInBar = new Map<string, number>();
this.compositeTransfer = LocalSelectionTransfer.getInstance<DraggedCompositeIdentifier>();
this.computeSizes(this.model.visibleItems);
}
getCompositeBarItems(): ICompositeBarItem[] {
@@ -15,7 +15,7 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -50,7 +50,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
}
getResource() {
return toResource(this.editor, { supportSideBySide: true });
return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
}
getAriaLabel(): string {
@@ -14,7 +14,7 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { Language } from 'vs/base/common/platform';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorAction } from 'vs/editor/common/editorCommon';
@@ -772,7 +772,7 @@ export class EditorStatus implements IStatusbarItem {
private onResourceEncodingChange(resource: URI): void {
const activeControl = this.editorService.activeControl;
if (activeControl) {
const activeResource = toResource(activeControl.input, { supportSideBySide: true });
const activeResource = toResource(activeControl.input, { supportSideBySide: SideBySideEditor.MASTER });
if (activeResource && activeResource.toString() === resource.toString()) {
return this.onEncodingChange(<IBaseEditor>activeControl); // only update if the encoding changed for the active resource
}
@@ -850,7 +850,7 @@ export class ChangeModeAction extends Action {
}
const textModel = activeTextEditorWidget.getModel();
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: true }) : null;
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
let hasLanguageSupport = !!resource;
if (resource && resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
@@ -976,7 +976,7 @@ export class ChangeModeAction extends Action {
if (pick === autoDetectMode) {
if (textModel) {
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
const resource = toResource(activeEditor.input, { supportSideBySide: true });
const resource = toResource(activeEditor.input, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
}
@@ -1195,7 +1195,7 @@ export class ChangeEncodingAction extends Action {
return undefined;
}
const resource = toResource(activeControl!.input, { supportSideBySide: true });
const resource = toResource(activeControl!.input, { supportSideBySide: SideBySideEditor.MASTER });
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/notabstitlecontrol';
import { toResource, Verbosity, IEditorInput, IEditorPartOptions } from 'vs/workbench/common/editor';
import { toResource, Verbosity, IEditorInput, IEditorPartOptions, SideBySideEditor } from 'vs/workbench/common/editor';
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
@@ -237,7 +237,7 @@ export class NoTabsTitleControl extends TitleControl {
this.updateEditorDirty(editor);
// Editor Label
const resource = toResource(editor, { supportSideBySide: true });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const name = editor.getName() || '';
const { labelFormat } = this.accessor.partOptions;
@@ -21,6 +21,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
export class SideBySideEditor extends BaseEditor {
static readonly ID: string = 'workbench.editor.sidebysideEditor';
static MASTER: SideBySideEditor | undefined;
get minimumMasterWidth() { return this.masterEditor ? this.masterEditor.minimumWidth : 0; }
get maximumMasterWidth() { return this.masterEditor ? this.masterEditor.maximumWidth : Number.POSITIVE_INFINITY; }
@@ -6,7 +6,7 @@
import 'vs/css!./media/tabstitlecontrol';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions } from 'vs/workbench/common/editor';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions, SideBySideEditor } from 'vs/workbench/common/editor';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -608,7 +608,7 @@ export class TabsTitleControl extends TitleControl {
e.dataTransfer!.effectAllowed = 'copyMove';
// Apply some datatransfer types to allow for dragging the element outside of the application
const resource = toResource(editor, { supportSideBySide: true });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
@@ -899,7 +899,7 @@ export class TabsTitleControl extends TitleControl {
tabContainer.title = title;
// Label
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
// {{SQL CARBON EDIT}} -- Display the editor's tab color
const isTabActive = this.group.isActive(editor);
@@ -33,7 +33,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions } from 'vs/workbench/common/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions, SideBySideEditor } from 'vs/workbench/common/editor';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { Themable } from 'vs/workbench/common/theme';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -219,7 +219,7 @@ export abstract class TitleControl extends Themable {
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
// Update the resource context
this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null);
this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null);
// Editor actions require the editor control to be there, so we retrieve it via service
const activeControl = this.group.activeControl;
@@ -259,7 +259,7 @@ export abstract class TitleControl extends Themable {
// If tabs are disabled, treat dragging as if an editor tab was dragged
if (!this.accessor.partOptions.showTabs) {
const resource = this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null;
const resource = this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
@@ -286,7 +286,7 @@ export abstract class TitleControl extends Themable {
// Update the resource context
const currentContext = this.resourceContext.get();
this.resourceContext.set(toResource(editor, { supportSideBySide: true }));
this.resourceContext.set(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }));
// Find target anchor
let anchor: HTMLElement | { x: number, y: number } = node;
@@ -191,6 +191,10 @@ export class NotificationsToasts extends Themable {
// Update when item height potentially changes due to label changes
itemDisposeables.push(item.onDidLabelChange(e => {
if (!item.expanded) {
return; // dynamic height only applies to expanded notifications
}
if (e.kind === NotificationViewItemLabelKind.ACTIONS || e.kind === NotificationViewItemLabelKind.MESSAGE) {
notificationList.updateNotificationsList(0, 1, [item]);
}
@@ -350,6 +350,9 @@ export class NotificationTemplateRenderer {
case NotificationViewItemLabelKind.PROGRESS:
this.renderProgress(notification);
break;
case NotificationViewItemLabelKind.MESSAGE:
this.renderMessage(notification);
break;
}
}));
}
@@ -45,10 +45,12 @@
}
/* adding padding to the most left status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left {
.monaco-workbench .part.statusbar > .statusbar-item.left:first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left {
padding-left: 7px;
}
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.has-background-color.left {
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left:first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.has-background-color.left {
padding-right: 7px; /* expand padding if background color is configured for the status bar entry to make it look centered properly */
}
/* adding padding to the most right status bar item */
@@ -17,7 +17,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { EditorInput, toResource, Verbosity } from 'vs/workbench/common/editor';
import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
@@ -179,7 +179,7 @@ export class TitlebarPart extends Part implements ITitleService {
}
private updateRepresentedFilename(): void {
const file = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: 'file' });
const file = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: 'file' });
const path = file ? file.fsPath : '';
// Apply to window
@@ -282,7 +282,7 @@ export class TitlebarPart extends Part implements ITitleService {
// Compute folder resource
// Single Root Workspace: always the root single workspace in this case
// Otherwise: root folder of the currently active file if any
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true })!);
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })!);
// Variables
const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
+1 -2
View File
@@ -205,8 +205,7 @@ export class Workbench extends Layout {
// TODO@Sandeep debt around cyclic dependencies
const configurationService = accessor.get(IConfigurationService) as any;
if (typeof configurationService.acquireFileService === 'function') {
configurationService.acquireFileService(fileService);
if (typeof configurationService.acquireInstantiationService === 'function') {
configurationService.acquireInstantiationService(instantiationService);
}
+18 -29
View File
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import * as objects from 'vs/base/common/objects';
import * as types from 'vs/base/common/types';
import { assign } from 'vs/base/common/objects';
import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
@@ -14,7 +14,6 @@ import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITextModel } from 'vs/editor/common/model';
import { Schemas } from 'vs/base/common/network';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ICompositeControl } from 'vs/workbench/common/composite';
import { ActionRunner, IAction } from 'vs/base/common/actions';
@@ -582,7 +581,7 @@ export class SideBySideEditorInput extends EditorInput {
getTelemetryDescriptor(): object {
const descriptor = this.master.getTelemetryDescriptor();
return objects.assign(descriptor, super.getTelemetryDescriptor());
return assign(descriptor, super.getTelemetryDescriptor());
}
private registerListeners(): void {
@@ -838,7 +837,7 @@ export class TextEditorOptions extends EditorOptions {
* Returns if this options object has objects defined for the editor.
*/
hasOptionsDefined(): boolean {
return !!this.editorViewState || (!types.isUndefinedOrNull(this.startLineNumber) && !types.isUndefinedOrNull(this.startColumn));
return !!this.editorViewState || (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn));
}
/**
@@ -886,10 +885,10 @@ export class TextEditorOptions extends EditorOptions {
}
// Otherwise check for selection
else if (!types.isUndefinedOrNull(this.startLineNumber) && !types.isUndefinedOrNull(this.startColumn)) {
else if (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn)) {
// Select
if (!types.isUndefinedOrNull(this.endLineNumber) && !types.isUndefinedOrNull(this.endColumn)) {
if (!isUndefinedOrNull(this.endLineNumber) && !isUndefinedOrNull(this.endColumn)) {
const range = {
startLineNumber: this.startLineNumber,
startColumn: this.startColumn,
@@ -990,9 +989,14 @@ export interface IEditorPartOptions extends IEditorPartConfiguration {
iconTheme?: string;
}
export enum SideBySideEditor {
MASTER = 1,
DETAILS = 2
}
export interface IResourceOptions {
supportSideBySide?: boolean;
filter?: string | string[];
supportSideBySide?: SideBySideEditor;
filterByScheme?: string | string[];
}
export function toResource(editor: IEditorInput | null | undefined, options?: IResourceOptions): URI | null {
@@ -1000,35 +1004,20 @@ export function toResource(editor: IEditorInput | null | undefined, options?: IR
return null;
}
// Check for side by side if we are asked to
if (options && options.supportSideBySide && editor instanceof SideBySideEditorInput) {
editor = editor.master;
editor = options.supportSideBySide === SideBySideEditor.MASTER ? editor.master : editor.details;
}
const resource = editor.getResource();
if (!options || !options.filter) {
return types.withUndefinedAsNull(resource); // return early if no filter is specified
if (!resource || !options || !options.filterByScheme) {
return withUndefinedAsNull(resource);
}
if (!resource) {
return null;
}
let includeFiles: boolean;
let includeUntitled: boolean;
if (Array.isArray(options.filter)) {
includeFiles = (options.filter.indexOf(Schemas.file) >= 0);
includeUntitled = (options.filter.indexOf(Schemas.untitled) >= 0);
} else {
includeFiles = (options.filter === Schemas.file);
includeUntitled = (options.filter === Schemas.untitled);
}
if (includeFiles && resource.scheme === Schemas.file) {
if (Array.isArray(options.filterByScheme) && options.filterByScheme.some(scheme => resource.scheme === scheme)) {
return resource;
}
if (includeUntitled && resource.scheme === Schemas.untitled) {
if (options.filterByScheme === resource.scheme) {
return resource;
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput } from 'vs/workbench/common/editor';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
@@ -157,7 +157,7 @@ export class EditorGroup extends Disposable {
}
for (const editor of this.editors) {
const editorResource = toResource(editor, { supportSideBySide: true });
const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (editorResource && editorResource.toString() === resource.toString()) {
return editor;
}
@@ -541,7 +541,7 @@ export class EditorGroup extends Disposable {
}
private updateResourceMap(editor: EditorInput, remove: boolean): void {
const resource = toResource(editor, { supportSideBySide: true });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
// It is possible to have the same resource opened twice (once as normal input and once as diff input)
@@ -24,6 +24,7 @@ export interface IResourceCommentThreadEvent {
export interface ICommentInfo extends CommentInfo {
owner: string;
label?: string;
}
export interface IWorkspaceCommentThreadsEvent {
@@ -34,6 +34,9 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ctxCommentEditorFocused, SimpleCommentEditor } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IAction, Action } from 'vs/base/common/actions';
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { IQuickInputService, QuickPickInput, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
export const ID = 'editor.contrib.review';
@@ -61,7 +64,7 @@ class CommentingRangeDecoration {
return this._decorationId;
}
constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private commentingRangesInfo?: modes.CommentingRanges) {
constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private commentingRangesInfo?: modes.CommentingRanges) {
const startLineNumber = _range.startLineNumber;
const endLineNumber = _range.endLineNumber;
let commentingRangeDecorations = [{
@@ -78,9 +81,10 @@ class CommentingRangeDecoration {
}
}
public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined } {
public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined } {
return {
extensionId: this._extensionId,
label: this._label,
replyCommand: this._reply,
ownerId: this._ownerId,
commentingRangesInfo: this.commentingRangesInfo
@@ -120,11 +124,11 @@ class CommentingRangeDecorator {
for (const info of commentInfos) {
if (Array.isArray(info.commentingRanges)) {
info.commentingRanges.forEach(range => {
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, range, info.reply, this.decorationOptions));
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, info.reply, this.decorationOptions));
});
} else {
(info.commentingRanges ? info.commentingRanges.ranges : []).forEach(range => {
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, range, (info.commentingRanges as modes.CommentingRanges).newCommentThreadCommand, this.decorationOptions, info.commentingRanges as modes.CommentingRanges));
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, (info.commentingRanges as modes.CommentingRanges).newCommentThreadCommand, this.decorationOptions, info.commentingRanges as modes.CommentingRanges));
});
}
}
@@ -136,14 +140,15 @@ class CommentingRangeDecorator {
}
public getMatchedCommentAction(line: number) {
let result = [];
for (const decoration of this.commentingRangeDecorations) {
const range = decoration.getActiveRange();
if (range && range.startLineNumber <= line && line <= range.endLineNumber) {
return decoration.getCommentAction();
result.push(decoration.getCommentAction());
}
}
return null;
return result;
}
public dispose(): void {
@@ -164,7 +169,7 @@ export class ReviewController implements IEditorContribution {
private _commentingRangeSpaceReserved = false;
private _computePromise: CancelablePromise<Array<ICommentInfo | null>> | null;
private _addInProgress: boolean;
private _emptyThreadsToAddQueue: number[] = [];
private _emptyThreadsToAddQueue: [number, IEditorMouseEvent | undefined][] = [];
private _computeCommentingRangePromise: CancelablePromise<ICommentInfo[]> | null;
private _computeCommentingRangeScheduler: Delayer<Array<ICommentInfo | null>> | null;
private _pendingCommentCache: { [key: number]: { [key: string]: string } };
@@ -178,6 +183,7 @@ export class ReviewController implements IEditorContribution {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
@IContextMenuService readonly contextMenuService: IContextMenuService,
@IQuickInputService private readonly quickInputService: IQuickInputService
) {
this.editor = editor;
this.globalToDispose = [];
@@ -547,11 +553,11 @@ export class ReviewController implements IEditorContribution {
if (e.target.element.className.indexOf('comment-diff-added') >= 0) {
const lineNumber = e.target.position!.lineNumber;
this.addOrToggleCommentAtLine(lineNumber);
this.addOrToggleCommentAtLine(lineNumber, e);
}
}
public async addOrToggleCommentAtLine(lineNumber: number): Promise<void> {
public async addOrToggleCommentAtLine(lineNumber: number, e: IEditorMouseEvent | undefined): Promise<void> {
// If an add is already in progress, queue the next add and process it after the current one finishes to
// prevent empty comment threads from being added to the same line.
if (!this._addInProgress) {
@@ -563,29 +569,98 @@ export class ReviewController implements IEditorContribution {
this.processNextThreadToAdd();
return;
} else {
this.addCommentAtLine(lineNumber);
this.addCommentAtLine(lineNumber, e);
}
} else {
this._emptyThreadsToAddQueue.push(lineNumber);
this._emptyThreadsToAddQueue.push([lineNumber, e]);
}
}
private processNextThreadToAdd(): void {
this._addInProgress = false;
const lineNumber = this._emptyThreadsToAddQueue.shift();
if (lineNumber) {
this.addOrToggleCommentAtLine(lineNumber);
const info = this._emptyThreadsToAddQueue.shift();
if (info) {
this.addOrToggleCommentAtLine(info[0], info[1]);
}
}
public addCommentAtLine(lineNumber: number): Promise<void> {
const newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber);
if (!newCommentInfo || !this.editor.hasModel()) {
public addCommentAtLine(lineNumber: number, e: IEditorMouseEvent | undefined): Promise<void> {
const newCommentInfos = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber);
if (!newCommentInfos.length || !this.editor.hasModel()) {
return Promise.resolve();
}
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = newCommentInfo;
if (newCommentInfos.length > 1) {
if (e) {
const anchor = { x: e.event.posx, y: e.event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this.getContextMenuActions(newCommentInfos, lineNumber),
getActionsContext: () => newCommentInfos.length ? newCommentInfos[0] : undefined,
onHide: () => { this._addInProgress = false; }
});
return Promise.resolve();
} else {
const picks = this.getCommentProvidersQuickPicks(newCommentInfos);
return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickCommentService', "Select Comment Provider"), matchOnDescription: true }).then(pick => {
if (!pick) {
return;
}
const commentInfos = newCommentInfos.filter(info => info.ownerId === pick.id);
if (commentInfos.length) {
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = commentInfos[0];
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
}
}).then(() => {
this._addInProgress = false;
});
}
} else {
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = newCommentInfos[0]!;
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
}
return Promise.resolve();
}
private getCommentProvidersQuickPicks(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined }[]) {
const picks: QuickPickInput[] = commentInfos.map((commentInfo) => {
const { ownerId, extensionId, label } = commentInfo;
return <IQuickPickItem>{
label: label || extensionId,
id: ownerId
};
});
return picks;
}
private getContextMenuActions(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined }[], lineNumber: number): (IAction | ContextSubMenu)[] {
const actions: (IAction | ContextSubMenu)[] = [];
commentInfos.forEach(commentInfo => {
const { replyCommand, ownerId, extensionId, label, commentingRangesInfo } = commentInfo;
actions.push(new Action(
'addCommentThread',
`${label || extensionId}`,
undefined,
true,
() => {
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
return Promise.resolve();
}
));
});
return actions;
}
public addCommentAtLine2(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined) {
if (commentingRangesInfo) {
let range = new Range(lineNumber, 1, lineNumber, 1);
if (commentingRangesInfo.newCommentThreadCommand) {
@@ -597,7 +672,7 @@ export class ReviewController implements IEditorContribution {
this._addInProgress = false;
}
} else if (commentingRangesInfo.newCommentThreadCallback) {
return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel().uri, range)
return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel()!.uri, range)
.then(_ => {
this.processNextThreadToAdd();
})
@@ -621,7 +696,6 @@ export class ReviewController implements IEditorContribution {
return Promise.resolve();
}
private setComments(commentInfos: ICommentInfo[]): void {
if (!this.editor) {
return;
@@ -762,7 +836,7 @@ CommandsRegistry.registerCommand({
}
const position = activeEditor.getPosition();
return controller.addOrToggleCommentAtLine(position.lineNumber);
return controller.addOrToggleCommentAtLine(position.lineNumber, undefined);
}
});
@@ -797,7 +797,7 @@ export class DebugModel implements IDebugModel {
// Make sure to de-dupe if a session is re-intialized. In case of EH debugging we are adding a session again after an attach.
return false;
}
if (s.state === State.Inactive && s.getLabel() === session.getLabel()) {
if (s.state === State.Inactive && s.configuration.name === session.configuration.name) {
// Make sure to remove all inactive sessions that are using the same configuration as the new session
return false;
}
@@ -783,6 +783,7 @@ export class DebugSession implements IDebugSession {
}));
this.rawListeners.push(this.raw.onDidExitAdapter(event => {
this.initialized = true;
this._onDidEndAdapter.fire(event);
}));
}
@@ -14,7 +14,7 @@ import {
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { Emitter } from 'vs/base/common/event';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { URLService } from 'vs/platform/url/common/urlService';
import { IURLService } from 'vs/platform/url/common/url';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -83,30 +83,30 @@ suite('Experimental Prompts', () => {
});
test('Show experimental prompt if experiment should be run. Choosing option with link should mark experiment as complete', () => {
// test('Show experimental prompt if experiment should be run. Choosing option with link should mark experiment as complete', () => {
storageData = {
enabled: true,
state: ExperimentState.Run
};
// storageData = {
// enabled: true,
// state: ExperimentState.Run
// };
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[0].run();
return undefined!;
}
});
// instantiationService.stub(INotificationService, {
// prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
// assert.equal(b, promptText);
// assert.equal(c.length, 2);
// c[0].run();
// return undefined!;
// }
// });
experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
onExperimentEnabledEvent.fire(experiment);
// experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
// onExperimentEnabledEvent.fire(experiment);
return Promise.resolve(null).then(result => {
assert.equal(storageData['state'], ExperimentState.Complete);
});
// return Promise.resolve(null).then(result => {
// assert.equal(storageData['state'], ExperimentState.Complete);
// });
});
// });
test('Show experimental prompt if experiment should be run. Choosing negative option should mark experiment as complete', () => {
@@ -115,20 +115,16 @@ export function onExtensionChanged(accessor: ServicesAccessor): Event<IExtension
});
}
export function getInstalledExtensions(accessor: ServicesAccessor): Promise<IExtensionStatus[]> {
export async function getInstalledExtensions(accessor: ServicesAccessor): Promise<IExtensionStatus[]> {
const extensionService = accessor.get(IExtensionManagementService);
const extensionEnablementService = accessor.get(IExtensionEnablementService);
return extensionService.getInstalled().then(extensions => {
return extensionEnablementService.getDisabledExtensions()
.then(disabledExtensions => {
return extensions.map(extension => {
return {
identifier: extension.identifier,
local: extension,
globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.identifier))
};
});
});
const extensions = await extensionService.getInstalled();
return extensions.map(extension => {
return {
identifier: extension.identifier,
local: extension,
globallyEnabled: extensionEnablementService.isEnabled(extension)
};
});
}
@@ -33,7 +33,6 @@ import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/we
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@@ -200,7 +199,6 @@ export class ExtensionEditor extends BaseEditor {
@IKeybindingService private readonly keybindingService: IKeybindingService,
@INotificationService private readonly notificationService: INotificationService,
@IOpenerService private readonly openerService: IOpenerService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
@IStorageService storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@@ -546,7 +544,6 @@ export class ExtensionEditor extends BaseEditor {
.then(removeEmbeddedSVGs)
.then(body => {
const wbeviewElement = this.instantiationService.createInstance(WebviewElement,
this.layoutService.getContainer(Parts.EDITOR_PART),
{
enableFindWidget: true,
},
@@ -6,7 +6,7 @@
import 'vs/css!./media/extensionActions';
import { localize } from 'vs/nls';
import { IAction, Action } from 'vs/base/common/actions';
import { Throttler, Delayer } from 'vs/base/common/async';
import { Delayer } from 'vs/base/common/async';
import * as DOM from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
@@ -1044,9 +1044,10 @@ export class ReloadAction extends ExtensionAction {
private static readonly EnabledClass = 'extension-action reload';
private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
// Use delayer to wait for more updates
private throttler: Throttler;
private disposables: IDisposable[] = [];
private _runningExtensions: IExtensionDescription[] = [];
private get runningExtensions(): IExtensionDescription[] { return this._runningExtensions; }
private set runningExtensions(runningExtensions: IExtensionDescription[]) { this._runningExtensions = runningExtensions; this.update(); }
constructor(
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@@ -1055,39 +1056,38 @@ export class ReloadAction extends ExtensionAction {
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
) {
super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
this.throttler = new Throttler();
this.extensionService.onDidChangeExtensions(this.update, this, this.disposables);
this.update();
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
this.updateRunningExtensions();
}
update(): Promise<void> {
return this.throttler.queue(() => {
this.enabled = false;
this.tooltip = '';
if (!this.extension) {
return Promise.resolve(undefined);
}
const state = this.extension.state;
if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
return Promise.resolve(undefined);
}
const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
const local = this.extension.local || (installed && installed.local);
if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) {
return Promise.resolve(undefined);
}
return this.extensionService.getExtensions()
.then(runningExtensions => this.computeReloadState(runningExtensions, installed));
}).then(() => {
this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
});
private updateRunningExtensions(): void {
this.extensionService.getExtensions().then(runningExtensions => this.runningExtensions = runningExtensions);
}
private computeReloadState(runningExtensions: IExtensionDescription[], installed: IExtension): void {
update(): void {
this.enabled = false;
this.tooltip = '';
if (!this.extension) {
return;
}
const state = this.extension.state;
if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
return;
}
const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
const local = this.extension.local || (installed && installed.local);
if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) {
return;
}
this.computeReloadState(installed);
this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
}
private computeReloadState(installed: IExtension): void {
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const isDisabled = this.extension.local ? !this.extensionEnablementService.isEnabled(this.extension.local) : false;
const isEnabled = this.extension.local ? this.extensionEnablementService.isEnabled(this.extension.local) : false;
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const runningExtension = this.runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
if (installed && installed.local) {
if (runningExtension) {
@@ -11,12 +11,12 @@ import * as ExtensionsActions from 'vs/workbench/contrib/extensions/electron-bro
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@@ -74,7 +74,7 @@ suite('ExtensionsActions Test', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService, <IExtensionManagementServer>{ authority: 'vscode-local', extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local' }));
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
@@ -1202,7 +1202,7 @@ suite('ExtensionsActions Test', () => {
const gallery = aGalleryExtension('a', { identifier: local.identifier, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, gallery });
didInstallEvent.fire({ identifier: gallery.identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) });
didInstallEvent.fire({ identifier: gallery.identifier, gallery, operation: InstallOperation.Update, local: aLocalExtension('a', gallery, gallery) });
assert.ok(!testObject.enabled);
});
@@ -34,7 +34,7 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { IURLService } from 'vs/platform/url/common/url';
import product from 'vs/platform/product/node/product';
import { ITextModel } from 'vs/editor/common/model';
@@ -12,12 +12,12 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason, SortBy
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, EnablementState, ExtensionRecommendationReason, SortBy
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';
import { Emitter } from 'vs/base/common/event';
@@ -90,7 +90,7 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService, <IExtensionManagementServer>{ authority: 'vscode-local', extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local' }));
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
@@ -12,12 +12,12 @@ import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurat
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@@ -77,7 +77,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
});
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService, <IExtensionManagementServer>{ authority: 'vscode-local', extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local' }));
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
@@ -7,7 +7,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor';
import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
@@ -94,7 +94,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
distinct(
coalesce(this.editorService.visibleEditors
.map(editorInput => {
const resource = toResource(editorInput, { supportSideBySide: true });
const resource = toResource(editorInput, { supportSideBySide: SideBySideEditorChoice.MASTER });
return resource ? this.textFileService.models.get(resource) : undefined;
}))
.filter(model => !model.isDirty()),
@@ -325,7 +325,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
private handleUpdatesToVisibleBinaryEditors(e: FileChangesEvent): void {
const editors = this.editorService.visibleControls;
editors.forEach(editor => {
const resource = editor.input ? toResource(editor.input, { supportSideBySide: true }) : undefined;
const resource = editor.input ? toResource(editor.input, { supportSideBySide: SideBySideEditorChoice.MASTER }) : undefined;
// Support side-by-side binary editors too
let isBinaryEditor = false;
@@ -346,7 +346,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
private handleOutOfWorkspaceWatchers(): void {
const visibleOutOfWorkspacePaths = new ResourceMap<URI>();
coalesce(this.editorService.visibleEditors.map(editorInput => {
return toResource(editorInput, { supportSideBySide: true });
return toResource(editorInput, { supportSideBySide: SideBySideEditorChoice.MASTER });
})).filter(resource => {
return this.fileService.canHandleResource(resource) && !this.contextService.isInsideWorkspace(resource);
}).forEach(resource => {
@@ -17,7 +17,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { VIEWLET_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService, AutoSaveConfiguration } from 'vs/platform/files/common/files';
import { toResource, ITextEditor } from 'vs/workbench/common/editor';
import { toResource, ITextEditor, SideBySideEditor } from 'vs/workbench/common/editor';
import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
@@ -723,7 +723,7 @@ export class ShowActiveFileInExplorer extends Action {
}
public run(): Promise<any> {
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource);
} else {
@@ -795,7 +795,7 @@ export class ShowOpenedFileInNewWindow extends Action {
}
public run(): Promise<any> {
const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (fileResource) {
if (this.fileService.canHandleResource(fileResource)) {
this.windowService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true });
@@ -898,7 +898,7 @@ export class CompareWithClipboardAction extends Action {
}
public run(): Promise<any> {
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) {
if (!this.registrationDisposal) {
const provider = this.instantiationService.createInstance(ClipboardContentProvider);
@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
// {{SQL CARBON EDIT}} - Import EditorInput
import { toResource, IEditorCommandsContext, EditorInput } from 'vs/workbench/common/editor';
import { toResource, IEditorCommandsContext, EditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowOptions } from 'vs/platform/windows/common/windows';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -41,6 +41,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { ILabelService } from 'vs/platform/label/common/label';
import { onUnexpectedError } from 'vs/base/common/errors';
import { basename } from 'vs/base/common/resources';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
@@ -136,7 +137,7 @@ function save(
let viewStateOfSource: IEditorViewState | null;
const activeTextEditorWidget = getCodeEditor(editorService.activeTextEditorWidget);
if (activeTextEditorWidget) {
const activeResource = toResource(editorService.activeEditor, { supportSideBySide: true });
const activeResource = toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (activeResource && (fileService.canHandleResource(activeResource) || resource.scheme === Schemas.untitled) && activeResource.toString() === resource.toString()) {
viewStateOfSource = activeTextEditorWidget.saveViewState();
}
@@ -314,28 +315,46 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
});
const COMPARE_WITH_SAVED_SCHEMA = 'showModifications';
let provider: FileOnDiskContentProvider;
let providerDisposables: IDisposable[] = [];
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: COMPARE_WITH_SAVED_COMMAND_ID,
when: undefined,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_D),
handler: (accessor, resource: URI | object) => {
if (!provider) {
const instantiationService = accessor.get(IInstantiationService);
const textModelService = accessor.get(ITextModelService);
provider = instantiationService.createInstance(FileOnDiskContentProvider);
textModelService.registerTextModelContentProvider(COMPARE_WITH_SAVED_SCHEMA, provider);
const instantiationService = accessor.get(IInstantiationService);
const textModelService = accessor.get(ITextModelService);
const editorService = accessor.get(IEditorService);
// Register provider at first as needed
let registerEditorListener = false;
if (providerDisposables.length === 0) {
registerEditorListener = true;
const provider = instantiationService.createInstance(FileOnDiskContentProvider);
providerDisposables.push(provider);
providerDisposables.push(textModelService.registerTextModelContentProvider(COMPARE_WITH_SAVED_SCHEMA, provider));
}
const editorService = accessor.get(IEditorService);
// Open editor (only files supported)
const uri = getResourceForCommand(resource, accessor.get(IListService), editorService);
if (uri && uri.scheme === Schemas.file /* only files on disk supported for now */) {
const name = basename(uri);
const editorLabel = nls.localize('modifiedLabel', "{0} (on disk) ↔ {1}", name, name);
return editorService.openEditor({ leftResource: uri.with({ scheme: COMPARE_WITH_SAVED_SCHEMA }), rightResource: uri, label: editorLabel }).then(() => undefined);
editorService.openEditor({ leftResource: uri.with({ scheme: COMPARE_WITH_SAVED_SCHEMA }), rightResource: uri, label: editorLabel }).then(() => {
// Dispose once no more diff editor is opened with the scheme
if (registerEditorListener) {
providerDisposables.push(editorService.onDidVisibleEditorsChange(() => {
if (!editorService.editors.some(editor => !!toResource(editor, { supportSideBySide: SideBySideEditor.DETAILS, filterByScheme: COMPARE_WITH_SAVED_SCHEMA }))) {
providerDisposables = dispose(providerDisposables);
}
}));
}
}, error => {
providerDisposables = dispose(providerDisposables);
});
}
return Promise.resolve(true);
@@ -554,7 +573,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: accessor => {
const editorService = accessor.get(IEditorService);
const resource = toResource(editorService.activeEditor, { supportSideBySide: true });
const resource = toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
// {{SQL CARBON EDIT}}
return save(resource, false, { skipSaveParticipants: true }, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService));
@@ -586,7 +605,7 @@ CommandsRegistry.registerCommand({
const editorGroup = editorGroupService.getGroup(context.groupId);
if (editorGroup) {
editorGroup.editors.forEach(editor => {
const resource = toResource(editor, { supportSideBySide: true });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource && (resource.scheme === Schemas.untitled || fileService.canHandleResource(resource))) {
saveAllArg.push(resource);
}
@@ -6,7 +6,7 @@
import { URI } from 'vs/base/common/uri';
import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { OpenEditor } from 'vs/workbench/contrib/files/common/files';
import { toResource } from 'vs/workbench/common/editor';
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
@@ -41,7 +41,7 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe
}
}
return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: true }) : null;
return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
}
export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): Array<URI> {
@@ -25,9 +25,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider {
this.toDispose.push(contextService.onDidChangeWorkspaceFolders(e => {
this._onDidChange.fire(e.changed.concat(e.added).map(wf => wf.uri));
}));
this.toDispose.push(explorerService.onDidChangeItem(item => {
if (item) {
this._onDidChange.fire([item.resource]);
this.toDispose.push(explorerService.onDidChangeItem(change => {
if (change.item) {
this._onDidChange.fire([change.item.resource]);
}
}));
}
@@ -10,7 +10,7 @@ import { Action, IAction } from 'vs/base/common/actions';
import { memoize } from 'vs/base/common/decorators';
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash } from 'vs/workbench/contrib/files/common/files';
import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView } from 'vs/workbench/contrib/files/browser/fileActions';
import { toResource } from 'vs/workbench/common/editor';
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import * as DOM from 'vs/base/browser/dom';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
@@ -167,11 +167,11 @@ export class ExplorerView extends ViewletPanel {
this.disposables.push(this.labelService.onDidChangeFormatters(() => {
this._onDidChangeTitleArea.fire();
this.refresh();
this.refresh(true);
}));
this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput()));
this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e)));
this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e.recursive, e.item)));
this.disposables.push(this.explorerService.onDidChangeEditable(async e => {
const isEditing = !!this.explorerService.getEditableData(e);
@@ -181,7 +181,7 @@ export class ExplorerView extends ViewletPanel {
DOM.removeClass(treeContainer, 'highlight');
}
await this.refresh(e.parent);
await this.refresh(false, e.parent);
if (isEditing) {
DOM.addClass(treeContainer, 'highlight');
@@ -365,7 +365,7 @@ export class ExplorerView extends ViewletPanel {
// Refresh viewer as needed if this originates from a config event
if (event && needsRefresh) {
this.refresh();
this.refresh(true);
}
}
@@ -421,7 +421,7 @@ export class ExplorerView extends ViewletPanel {
* Refresh the contents of the explorer to get up to date data from the disk about the file structure.
* If the item is passed we refresh only that level of the tree, otherwise we do a full refresh.
*/
private refresh(item?: ExplorerItem): Promise<void> {
private refresh(recursive: boolean, item?: ExplorerItem): Promise<void> {
if (!this.tree || !this.isBodyVisible()) {
this.shouldRefresh = true;
return Promise.resolve(undefined);
@@ -432,7 +432,6 @@ export class ExplorerView extends ViewletPanel {
return Promise.resolve(undefined);
}
const recursive = !item;
const toRefresh = item || this.tree.getInput();
return this.tree.updateChildren(toRefresh, recursive);
@@ -504,7 +503,7 @@ export class ExplorerView extends ViewletPanel {
}
// check for files
return withNullAsUndefined(toResource(input, { supportSideBySide: true }));
return withNullAsUndefined(toResource(input, { supportSideBySide: SideBySideEditor.MASTER }));
}
private async onSelectResource(resource: URI | undefined, reveal = this.autoReveal): Promise<void> {
@@ -325,8 +325,7 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
// Hide those that match Hidden Patterns
const cached = this.hiddenExpressionPerRoot.get(stat.root.resource.toString());
if (cached && cached.parsed(path.normalize(path.relative(stat.root.resource.path, stat.resource.path)), stat.name, name => !!(stat.parent && stat.parent.getChild(name)))) {
// review (isidor): is path.normalize necessary? path.relative already returns an os path
if (cached && cached.parsed(path.relative(stat.root.resource.path, stat.resource.path), stat.name, name => !!(stat.parent && stat.parent.getChild(name)))) {
return false; // hidden through pattern
}
@@ -32,7 +32,7 @@ export class ExplorerService implements IExplorerService {
private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first
private _onDidChangeRoots = new Emitter<void>();
private _onDidChangeItem = new Emitter<ExplorerItem | undefined>();
private _onDidChangeItem = new Emitter<{ item?: ExplorerItem, recursive: boolean }>();
private _onDidChangeEditable = new Emitter<ExplorerItem>();
private _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>();
private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>();
@@ -40,6 +40,7 @@ export class ExplorerService implements IExplorerService {
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
private _sortOrder: SortOrder;
private cutItems: ExplorerItem[] | undefined;
private fileSystemProviderSchemes = new Set<string>();
constructor(
@IFileService private fileService: IFileService,
@@ -60,7 +61,7 @@ export class ExplorerService implements IExplorerService {
return this._onDidChangeRoots.event;
}
get onDidChangeItem(): Event<ExplorerItem | undefined> {
get onDidChangeItem(): Event<{ item?: ExplorerItem, recursive: boolean }> {
return this._onDidChangeItem.event;
}
@@ -98,7 +99,14 @@ export class ExplorerService implements IExplorerService {
this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(undefined)));
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(e => {
if (e.added && this.fileSystemProviderSchemes.has(e.scheme)) {
// A file system provider got re-registered, we should update all file stats since they might change (got read-only)
this._onDidChangeItem.fire({ recursive: true });
} else {
this.fileSystemProviderSchemes.add(e.scheme);
}
}));
this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
return model;
@@ -158,19 +166,19 @@ export class ExplorerService implements IExplorerService {
// Update Input with disk Stat
ExplorerItem.mergeLocalWithDisk(modelStat, root);
const item = root.find(resource);
this._onDidChangeItem.fire(item ? item.parent : undefined);
this._onDidChangeItem.fire({ item: root, recursive: true });
// Select and Reveal
this._onDidSelectResource.fire({ resource: item ? item.resource : undefined, reveal });
}, () => {
root.isError = true;
this._onDidChangeItem.fire(root);
this._onDidChangeItem.fire({ item: root, recursive: false });
});
}
refresh(): void {
this.model.roots.forEach(r => r.forgetChildren());
this._onDidChangeItem.fire(undefined);
this._onDidChangeItem.fire({ recursive: true });
const resource = this.editorService.activeEditor ? this.editorService.activeEditor.getResource() : undefined;
if (resource) {
// We did a top level refresh, reveal the active file #67118
@@ -205,7 +213,7 @@ export class ExplorerService implements IExplorerService {
p.removeChild(childElement);
p.addChild(childElement);
// Refresh the Parent (View)
this._onDidChangeItem.fire(p);
this._onDidChangeItem.fire({ item: p, recursive: false });
});
});
}
@@ -224,7 +232,7 @@ export class ExplorerService implements IExplorerService {
modelElements.forEach(modelElement => {
// Rename File (Model)
modelElement.rename(newElement);
this._onDidChangeItem.fire(modelElement.parent);
this._onDidChangeItem.fire({ item: modelElement.parent, recursive: false });
});
}
@@ -238,8 +246,8 @@ export class ExplorerService implements IExplorerService {
modelElements.forEach((modelElement, index) => {
const oldParent = modelElement.parent;
modelElement.move(newParents[index]);
this._onDidChangeItem.fire(oldParent);
this._onDidChangeItem.fire(newParents[index]);
this._onDidChangeItem.fire({ item: oldParent, recursive: false });
this._onDidChangeItem.fire({ item: newParents[index], recursive: false });
});
}
}
@@ -254,7 +262,7 @@ export class ExplorerService implements IExplorerService {
// Remove Element from Parent (Model)
parent.removeChild(element);
// Refresh Parent (View)
this._onDidChangeItem.fire(parent);
this._onDidChangeItem.fire({ item: parent, recursive: false });
}
});
}
@@ -332,7 +340,7 @@ export class ExplorerService implements IExplorerService {
if (shouldRefresh()) {
this.roots.forEach(r => r.forgetChildren());
this._onDidChangeItem.fire(undefined);
this._onDidChangeItem.fire({ recursive: true });
}
}, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY);
}
@@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorInput, toResource } from 'vs/workbench/common/editor';
import { IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { IFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
@@ -43,7 +43,7 @@ export interface IExplorerService {
readonly roots: ExplorerItem[];
readonly sortOrder: SortOrder;
readonly onDidChangeRoots: Event<void>;
readonly onDidChangeItem: Event<ExplorerItem | undefined>;
readonly onDidChangeItem: Event<{ item?: ExplorerItem, recursive: boolean }>;
readonly onDidChangeEditable: Event<ExplorerItem>;
readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
@@ -231,7 +231,7 @@ export class OpenEditor implements IEditorIdentifier {
}
public isUntitled(): boolean {
return !!toResource(this.editor, { supportSideBySide: true, filter: Schemas.untitled });
return !!toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.untitled });
}
public isDirty(): boolean {
@@ -239,6 +239,6 @@ export class OpenEditor implements IEditorIdentifier {
}
public getResource(): URI | null {
return toResource(this.editor, { supportSideBySide: true });
return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
}
}
@@ -6,7 +6,7 @@
import { URI } from 'vs/base/common/uri';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IModelService } from 'vs/editor/common/services/modelService';
import { LinkProviderRegistry, ILink } from 'vs/editor/common/modes';
import { LinkProviderRegistry, ILink, ILinksList } from 'vs/editor/common/modes';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { OUTPUT_MODE_ID, LOG_MODE_ID } from 'vs/workbench/contrib/output/common/output';
import { MonacoWebWorker, createWebWorker } from 'vs/editor/common/services/webWorker';
@@ -42,8 +42,8 @@ export class OutputLinkProvider {
if (folders.length > 0) {
if (!this.linkProviderRegistration) {
this.linkProviderRegistration = LinkProviderRegistry.register([{ language: OUTPUT_MODE_ID, scheme: '*' }, { language: LOG_MODE_ID, scheme: '*' }], {
provideLinks: (model, token): Promise<ILink[]> => {
return this.provideLinks(model.uri);
provideLinks: (model): Promise<ILinksList> => {
return this.provideLinks(model.uri).then(links => links && { links });
}
});
}
@@ -9,7 +9,7 @@ import { ISearchConfiguration, ISearchConfigurationProperties } from 'vs/workben
import { SymbolKind, Location, ProviderResult } from 'vs/editor/common/modes';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { toResource } from 'vs/workbench/common/editor';
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -85,7 +85,7 @@ export function getOutOfWorkspaceEditorResources(editorService: IEditorService,
const resources: URI[] = [];
editorService.editors.forEach(editor => {
const resource = toResource(editor, { supportSideBySide: true });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource && !contextService.isInsideWorkspace(resource)) {
resources.push(resource);
}
@@ -241,7 +241,7 @@ CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (acc
if (!resource) {
const editorService = accessor.get(IEditorService);
const file = editorService.activeEditor ? toResource(editorService.activeEditor, { filter: 'file' }) : null;
const file = editorService.activeEditor ? toResource(editorService.activeEditor, { filterByScheme: 'file' }) : null;
if (file) {
process(file).then(result => {
console.log(result);
@@ -41,9 +41,9 @@ const defaultCssRules = `
body {
background-color: var(--vscode-editor-background);
color: var(--vscode-editor-foreground);
font-family: var(--vscode-editor-font-family);
font-weight: var(--vscode-editor-font-weight);
font-size: var(--vscode-editor-font-size);
font-family: var(--vscode-font-family);
font-weight: var(--vscode-font-weight);
font-size: var(--vscode-font-size);
margin: 0;
padding: 0 20px;
}
@@ -112,16 +112,18 @@ module.exports = function createWebviewManager(host) {
};
/**
* @param {HTMLDocument} document
* @param {HTMLElement} body
* @param {HTMLDocument?} document
* @param {HTMLElement?} body
*/
const applyStyles = (document, body) => {
if (!body) {
if (!document) {
return;
}
body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
body.classList.add(initData.activeTheme);
if (body) {
body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
body.classList.add(initData.activeTheme);
}
if (initData.styles) {
for (const variable of Object.keys(initData.styles)) {
@@ -323,7 +325,16 @@ module.exports = function createWebviewManager(host) {
// write new content onto iframe
newFrame.contentDocument.open('text/html', 'replace');
newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown);
newFrame.contentWindow.addEventListener('DOMContentLoaded', e => {
const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined;
if (contentDocument) {
applyStyles(contentDocument, contentDocument.body);
}
});
newFrame.contentWindow.onbeforeunload = () => {
if (isInDevelopmentMode) { // Allow reloads while developing a webview
host.postMessage('do-reload');
@@ -22,6 +22,8 @@ import { WebviewFindWidget } from '../browser/webviewFindWidget';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { WebviewContentOptions, WebviewPortMapping, WebviewOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorOptions, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
interface IKeydownEvent {
@@ -306,7 +308,6 @@ export class WebviewElement extends Disposable implements Webview {
public get onDidFocus(): Event<void> { return this._onDidFocus.event; }
constructor(
private readonly _styleElement: Element,
private readonly _options: WebviewOptions,
private _contentOptions: WebviewContentOptions,
@IInstantiationService instantiationService: IInstantiationService,
@@ -314,6 +315,7 @@ export class WebviewElement extends Disposable implements Webview {
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
) {
super();
this._webview = document.createElement('webview');
@@ -540,9 +542,12 @@ export class WebviewElement extends Disposable implements Webview {
});
}
// {{SQL CARBON EDIT}}
// {{SQL CARBON EDIT}} - make public
public style(theme: ITheme): void {
const { fontFamily, fontWeight, fontSize } = window.getComputedStyle(this._styleElement); // TODO@theme avoid styleElement
const configuration = this._configurationService.getValue<IEditorOptions>('editor');
const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight;
const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize;
const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => {
const color = theme.getColor(entry.id);
@@ -554,9 +559,12 @@ export class WebviewElement extends Disposable implements Webview {
const styles = {
'vscode-editor-font-family': fontFamily,
'vscode-editor-font-weight': fontWeight,
'vscode-editor-font-size': fontSize,
'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif',
'vscode-font-weight': 'normal',
'vscode-font-size': '13px',
'vscode-editor-font-family': editorFontFamily,
'vscode-editor-font-weight': editorFontWeight,
'vscode-editor-font-size': editorFontSize,
...exportedColors
};
@@ -4,15 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWebviewService, Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IWebviewService, WebviewOptions, WebviewContentOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
export class WebviewService implements IWebviewService {
_serviceBrand: any;
constructor(
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) { }
@@ -21,7 +19,6 @@ export class WebviewService implements IWebviewService {
contentOptions: WebviewContentOptions
): Webview {
const element = this._instantiationService.createInstance(WebviewElement,
this._layoutService.getContainer(Parts.EDITOR_PART),
options,
contentOptions);
+11 -3
View File
@@ -168,6 +168,11 @@ class CodeRendererMain extends Disposable {
private initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> {
const serviceCollection = new ServiceCollection();
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE.
// CONTRIBUTE IT VIA WORKBENCH.MAIN.TS AND registerSingleton().
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Main Process
const mainProcessService = this._register(new MainProcessService(this.configuration.windowId));
serviceCollection.set(IMainProcessService, mainProcessService);
@@ -205,7 +210,7 @@ class CodeRendererMain extends Disposable {
}
return this.resolveWorkspaceInitializationPayload(environmentService).then(payload => Promise.all([
this.createWorkspaceService(payload, environmentService, remoteAgentService, logService).then(service => {
this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => {
// Workspace
serviceCollection.set(IWorkspaceContextService, service);
@@ -296,8 +301,11 @@ class CodeRendererMain extends Disposable {
}, error => onUnexpectedError(error));
}
private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise<WorkspaceService> {
const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), remoteAgentService);
private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, fileService: FileService2, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise<WorkspaceService> {
const configurationFileService = new ConfigurationFileService();
fileService.whenReady.then(() => configurationFileService.fileService = fileService);
const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService);
return workspaceService.initialize(payload).then(() => workspaceService, error => {
onUnexpectedError(error);
+2 -2
View File
@@ -11,7 +11,7 @@ import * as DOM from 'vs/base/browser/dom';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction, Action } from 'vs/base/common/actions';
import { IFileService } from 'vs/platform/files/common/files';
import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor';
import { toResource, IUntitledResourceInput, SideBySideEditor } from 'vs/workbench/common/editor';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows';
@@ -122,7 +122,7 @@ export class ElectronWindow extends Disposable {
if (request.from === 'touchbar') {
const activeEditor = this.editorService.activeEditor;
if (activeEditor) {
const resource = toResource(activeEditor, { supportSideBySide: true });
const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
args.push(resource);
}
@@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as errors from 'vs/base/common/errors';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
@@ -22,67 +22,36 @@ import { equals } from 'vs/base/common/objects';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration';
import { createSHA1 } from 'vs/base/browser/hash';
export class LocalUserConfiguration extends Disposable {
private readonly userConfigurationResource: URI;
private userConfiguration: NodeBasedUserConfiguration | FileServiceBasedUserConfiguration;
private changeDisposable: IDisposable = Disposable.None;
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
constructor(
userConfigurationResource: URI,
configurationFileService: IConfigurationFileService
) {
super();
this.userConfigurationResource = userConfigurationResource;
this.userConfiguration = this._register(new NodeBasedUserConfiguration(this.userConfigurationResource, configurationFileService));
}
initialize(): Promise<ConfigurationModel> {
return this.userConfiguration.initialize();
}
reload(): Promise<ConfigurationModel> {
return this.userConfiguration.reload();
}
async adopt(fileService: IFileService): Promise<ConfigurationModel | null> {
if (this.userConfiguration instanceof NodeBasedUserConfiguration) {
const oldConfigurationModel = this.userConfiguration.getConfigurationModel();
this.userConfiguration.dispose();
dispose(this.changeDisposable);
let newConfigurationModel = new ConfigurationModel();
this.userConfiguration = this._register(new FileServiceBasedUserConfiguration(this.userConfigurationResource, fileService));
this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)));
newConfigurationModel = await this.userConfiguration.initialize();
const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel);
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
return newConfigurationModel;
}
}
return null;
}
}
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class RemoteUserConfiguration extends Disposable {
private readonly _cachedConfiguration: CachedUserConfiguration;
private _userConfiguration: FileServiceBasedUserConfiguration | CachedUserConfiguration;
private readonly _configurationFileService: IConfigurationFileService;
private _userConfiguration: UserConfiguration | CachedUserConfiguration;
private _userConfigurationDisposable: IDisposable = Disposable.None;
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
constructor(
remoteAuthority: string,
configurationCache: IConfigurationCache
configurationCache: IConfigurationCache,
configurationFileService: IConfigurationFileService,
remoteAgentService: IRemoteAgentService
) {
super();
this._configurationFileService = configurationFileService;
this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, configurationCache);
remoteAgentService.getEnvironment().then(environment => {
if (environment) {
this._userConfiguration.dispose();
this._userConfigurationDisposable.dispose();
this._userConfiguration = this._register(new UserConfiguration(environment.appSettingsPath, this._configurationFileService));
this._userConfigurationDisposable = this._register(this._userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
this._userConfiguration.initialize().then(configurationModel => this.onDidUserConfigurationChange(configurationModel));
}
});
}
initialize(): Promise<ConfigurationModel> {
@@ -93,24 +62,6 @@ export class RemoteUserConfiguration extends Disposable {
return this._userConfiguration.reload();
}
async adopt(configurationResource: URI | null, fileService: IFileService): Promise<ConfigurationModel | null> {
if (this._userConfiguration instanceof CachedUserConfiguration) {
const oldConfigurationModel = this._userConfiguration.getConfigurationModel();
let newConfigurationModel = new ConfigurationModel();
if (configurationResource) {
this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, fileService);
this._register(this._userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
newConfigurationModel = await this._userConfiguration.initialize();
}
const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel);
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
this.updateCache(newConfigurationModel);
return newConfigurationModel;
}
}
return null;
}
private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void {
this.updateCache(configurationModel);
this._onDidChangeConfiguration.fire(configurationModel);
@@ -121,51 +72,7 @@ export class RemoteUserConfiguration extends Disposable {
}
}
class NodeBasedUserConfiguration extends Disposable {
private configuraitonModel: ConfigurationModel = new ConfigurationModel();
constructor(
private readonly userConfigurationResource: URI,
private readonly configurationFileService: IConfigurationFileService
) {
super();
}
initialize(): Promise<ConfigurationModel> {
return this._load();
}
reload(): Promise<ConfigurationModel> {
return this._load();
}
getConfigurationModel(): ConfigurationModel {
return this.configuraitonModel;
}
async _load(): Promise<ConfigurationModel> {
const exists = await this.configurationFileService.exists(this.userConfigurationResource);
if (exists) {
try {
const content = await this.configurationFileService.resolveContent(this.userConfigurationResource);
const parser = new ConfigurationModelParser(this.userConfigurationResource.toString());
parser.parse(content);
this.configuraitonModel = parser.configurationModel;
} catch (e) {
// ignore error
errors.onUnexpectedError(e);
this.configuraitonModel = new ConfigurationModel();
}
} else {
this.configuraitonModel = new ConfigurationModel();
}
return this.configuraitonModel;
}
}
export class FileServiceBasedUserConfiguration extends Disposable {
export class UserConfiguration extends Disposable {
private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
@@ -176,11 +83,11 @@ export class FileServiceBasedUserConfiguration extends Disposable {
constructor(
private readonly configurationResource: URI,
private readonly fileService: IFileService
private readonly configurationFileService: IConfigurationFileService
) {
super();
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
this._register(configurationFileService.onFileChanges(e => this.handleFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
this._register(toDisposable(() => {
this.stopWatchingResource();
@@ -189,7 +96,7 @@ export class FileServiceBasedUserConfiguration extends Disposable {
}
private watchResource(): void {
this.fileWatcherDisposable = this.fileService.watch(this.configurationResource);
this.fileWatcherDisposable = this.configurationFileService.watch(this.configurationResource);
}
private stopWatchingResource(): void {
@@ -199,7 +106,7 @@ export class FileServiceBasedUserConfiguration extends Disposable {
private watchDirectory(): void {
const directory = resources.dirname(this.configurationResource);
this.directoryWatcherDisposable = this.fileService.watch(directory);
this.directoryWatcherDisposable = this.configurationFileService.watch(directory);
}
private stopWatchingDirectory(): void {
@@ -208,22 +115,34 @@ export class FileServiceBasedUserConfiguration extends Disposable {
}
async initialize(): Promise<ConfigurationModel> {
const exists = await this.fileService.exists(this.configurationResource);
const exists = await this.configurationFileService.exists(this.configurationResource);
this.onResourceExists(exists);
return this.reload();
const configuraitonModel = await this.reload();
if (!this.configurationFileService.isWatching) {
this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted(configuraitonModel));
}
return configuraitonModel;
}
async reload(): Promise<ConfigurationModel> {
try {
const content = await this.fileService.resolveContent(this.configurationResource);
const content = await this.configurationFileService.resolveContent(this.configurationResource);
const parser = new ConfigurationModelParser(this.configurationResource.toString());
parser.parse(content.value);
parser.parse(content);
return parser.configurationModel;
} catch (e) {
return new ConfigurationModel();
}
}
private async onWatchStarted(currentModel: ConfigurationModel): Promise<void> {
const configuraitonModel = await this.reload();
const { added, removed, updated } = compare(currentModel, configuraitonModel);
if (added.length || removed.length || updated.length) {
this._onDidChangeConfiguration.fire(configuraitonModel);
}
}
private async handleFileEvents(event: FileChangesEvent): Promise<void> {
const events = event.changes;
@@ -304,41 +223,38 @@ class CachedUserConfiguration extends Disposable {
export class WorkspaceConfiguration extends Disposable {
private readonly _cachedConfiguration: CachedWorkspaceConfiguration;
private readonly _configurationFileService: IConfigurationFileService;
private readonly _cachedConfiguration: CachedWorkspaceConfiguration;
private _workspaceConfiguration: IWorkspaceConfiguration;
private _workspaceConfigurationChangeDisposable: IDisposable = Disposable.None;
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
private _fileService: IFileService | null = null;
private readonly _onDidUpdateConfiguration: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidUpdateConfiguration: Event<void> = this._onDidUpdateConfiguration.event;
private _loaded: boolean = false;
get loaded(): boolean { return this._loaded; }
constructor(
configurationCache: IConfigurationCache,
configurationFileService: IConfigurationFileService
) {
super();
this._cachedConfiguration = new CachedWorkspaceConfiguration(configurationCache);
this._configurationFileService = configurationFileService;
this._workspaceConfiguration = this._cachedConfiguration;
this._workspaceConfiguration = this._cachedConfiguration = new CachedWorkspaceConfiguration(configurationCache);
}
adopt(fileService: IFileService): Promise<boolean> {
if (!this._fileService) {
this._fileService = fileService;
if (this.adoptWorkspaceConfiguration()) {
if (this._workspaceIdentifier) {
return this._workspaceConfiguration.load(this._workspaceIdentifier).then(() => true);
}
async load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
this._workspaceIdentifier = workspaceIdentifier;
if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) {
if (this._workspaceIdentifier.configPath.scheme === Schemas.file) {
this.switch();
} else {
this.waitAndSwitch(this._workspaceIdentifier);
}
}
return Promise.resolve(false);
}
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
this._workspaceIdentifier = workspaceIdentifier;
this.adoptWorkspaceConfiguration();
return this._workspaceConfiguration.load(this._workspaceIdentifier);
await this._workspaceConfiguration.load(this._workspaceIdentifier);
this._loaded = this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration;
}
reload(): Promise<void> {
@@ -366,38 +282,26 @@ export class WorkspaceConfiguration extends Disposable {
return this.getConfiguration();
}
private adoptWorkspaceConfiguration(): boolean {
if (this._workspaceIdentifier) {
if (this._fileService) {
if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) {
const oldWorkspaceConfiguration = this._workspaceConfiguration;
this._workspaceConfiguration = new FileServiceBasedWorkspaceConfiguration(this._fileService, oldWorkspaceConfiguration);
this._register(this._workspaceConfiguration.onDidChange(e => this.onDidWorkspaceConfigurationChange()));
if (oldWorkspaceConfiguration instanceof CachedWorkspaceConfiguration) {
this.updateCache();
return true;
} else {
dispose(oldWorkspaceConfiguration);
return false;
}
}
return false;
}
if (this._workspaceIdentifier.configPath.scheme === Schemas.file) {
if (!(this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration)) {
dispose(this._workspaceConfiguration);
this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration(this._configurationFileService);
return true;
}
return false;
}
private async waitAndSwitch(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
await this._configurationFileService.whenProviderRegistered(workspaceIdentifier.configPath.scheme);
if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) {
this.switch();
this._loaded = true;
this.onDidWorkspaceConfigurationChange();
}
return false;
}
private onDidWorkspaceConfigurationChange(): void {
private switch(): void {
this._workspaceConfiguration.dispose();
this._workspaceConfigurationChangeDisposable.dispose();
this._workspaceConfiguration = this._register(new FileServiceBasedWorkspaceConfiguration(this._configurationFileService));
this._workspaceConfigurationChangeDisposable = this._register(this._workspaceConfiguration.onDidChange(e => this.onDidWorkspaceConfigurationChange()));
}
private async onDidWorkspaceConfigurationChange(): Promise<void> {
await this.reload();
this.updateCache();
this.reload().then(() => this._onDidUpdateConfiguration.fire());
this._onDidUpdateConfiguration.fire();
}
private updateCache(): Promise<void> {
@@ -421,20 +325,30 @@ interface IWorkspaceConfiguration extends IDisposable {
reprocessWorkspaceSettings(): ConfigurationModel;
}
abstract class AbstractWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
workspaceSettings: ConfigurationModel;
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
private workspaceConfigWatcher: IDisposable;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(from?: IWorkspaceConfiguration) {
constructor(private configurationFileService: IConfigurationFileService) {
super();
this.workspaceConfigurationModelParser = from ? from.workspaceConfigurationModelParser : new WorkspaceConfigurationModelParser('');
this.workspaceSettings = from ? from.workspaceSettings : new ConfigurationModel();
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser('');
this.workspaceSettings = new ConfigurationModel();
this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile());
if (!this.configurationFileService.isWatching) {
this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted());
}
}
get workspaceIdentifier(): IWorkspaceIdentifier | null {
@@ -442,13 +356,20 @@ abstract class AbstractWorkspaceConfiguration extends Disposable implements IWor
}
async load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
this._workspaceIdentifier = workspaceIdentifier;
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(workspaceIdentifier.id);
if (!this._workspaceIdentifier || this._workspaceIdentifier.id !== workspaceIdentifier.id) {
this._workspaceIdentifier = workspaceIdentifier;
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceIdentifier.id);
dispose(this.workspaceConfigWatcher);
this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile());
}
let contents = '';
try {
contents = (await this.loadWorkspaceConfigurationContents(workspaceIdentifier.configPath)) || '';
} catch (e) {
errors.onUnexpectedError(e);
contents = await this.configurationFileService.resolveContent(this._workspaceIdentifier.configPath);
} catch (error) {
const exists = await this.configurationFileService.exists(this._workspaceIdentifier.configPath);
if (exists) {
errors.onUnexpectedError(error);
}
}
this.workspaceConfigurationModelParser.parse(contents);
this.consolidate();
@@ -472,69 +393,34 @@ abstract class AbstractWorkspaceConfiguration extends Disposable implements IWor
return this.getWorkspaceSettings();
}
private async onWatchStarted(): Promise<void> {
if (this.workspaceIdentifier) {
const currentModel = this.getConfigurationModel();
await this.load(this.workspaceIdentifier);
const newModel = this.getConfigurationModel();
const { added, removed, updated } = compare(currentModel, newModel);
if (added.length || removed.length || updated.length) {
this._onDidChange.fire();
}
}
}
private consolidate(): void {
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel);
}
protected abstract loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string | undefined>;
}
class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
constructor(private readonly configurationFileService: IConfigurationFileService) {
super();
}
protected async loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string | undefined> {
const exists = await this.configurationFileService.exists(workspaceConfigurationResource);
if (exists) {
return this.configurationFileService.resolveContent(workspaceConfigurationResource);
}
return undefined;
}
}
class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
private workspaceConfig: URI | null = null;
private workspaceConfigWatcher: IDisposable;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
constructor(private fileService: IFileService, from?: IWorkspaceConfiguration) {
super(from);
this.workspaceConfig = from && from.workspaceIdentifier ? from.workspaceIdentifier.configPath : null;
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
}
private watchWorkspaceConfigurationFile(): IDisposable {
if (this.workspaceConfig) {
return this.fileService.watch(this.workspaceConfig);
}
return Disposable.None;
}
protected loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string> {
if (!(this.workspaceConfig && resources.isEqual(this.workspaceConfig, workspaceConfigurationResource))) {
dispose(this.workspaceConfigWatcher);
this.workspaceConfig = workspaceConfigurationResource;
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
}
return this.fileService.resolveContent(this.workspaceConfig).then(content => content.value);
return this._workspaceIdentifier ? this.configurationFileService.watch(this._workspaceIdentifier.configPath) : Disposable.None;
}
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
if (this.workspaceConfig) {
if (this._workspaceIdentifier) {
const events = event.changes;
let affectedByChanges = false;
// Find changes that affect workspace file
for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) {
affectedByChanges = resources.isEqual(this.workspaceConfig, events[i].resource);
affectedByChanges = resources.isEqual(this._workspaceIdentifier.configPath, events[i].resource);
}
if (affectedByChanges) {
@@ -542,12 +428,6 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
}
}
}
dispose(): void {
super.dispose();
dispose(this.workspaceConfigWatcher);
}
}
class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
@@ -617,45 +497,50 @@ class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfi
export interface IFolderConfiguration extends IDisposable {
readonly onDidChange: Event<void>;
readonly loaded: boolean;
loadConfiguration(): Promise<ConfigurationModel>;
reprocess(): ConfigurationModel;
}
export abstract class AbstractFolderConfiguration extends Disposable implements IFolderConfiguration {
class FileServiceBasedFolderConfiguration extends Disposable implements IFolderConfiguration {
private _folderSettingsModelParser: FolderSettingsModelParser;
private _standAloneConfigurations: ConfigurationModel[];
private _cache: ConfigurationModel;
private _loaded: boolean = false;
private readonly configurationNames: string[];
protected readonly configurationResources: URI[];
private changeEventTriggerScheduler: RunOnceScheduler;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, from?: AbstractFolderConfiguration) {
constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, private configurationFileService: IConfigurationFileService) {
super();
this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY];
this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY];
this.configurationResources = this.configurationNames.map(name => resources.joinPath(this.configurationFolder, `${name}.json`));
this._folderSettingsModelParser = from ? from._folderSettingsModelParser : new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? [ConfigurationScope.RESOURCE] : [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._standAloneConfigurations = from ? from._standAloneConfigurations : [];
this._cache = from ? from._cache : new ConfigurationModel();
}
this._folderSettingsModelParser = new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? [ConfigurationScope.RESOURCE] : [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._standAloneConfigurations = [];
this._cache = new ConfigurationModel();
get loaded(): boolean {
return this._loaded;
this.changeEventTriggerScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
if (!this.configurationFileService.isWatching) {
this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted());
}
}
async loadConfiguration(): Promise<ConfigurationModel> {
const configurationContents = await Promise.all(this.configurationResources.map(resource =>
this.loadConfigurationResourceContents(resource)
.then(undefined, error => {
/* never fail */
const configurationContents = await Promise.all(this.configurationResources.map(async resource => {
try {
return await this.configurationFileService.resolveContent(resource);
} catch (error) {
const exists = await this.configurationFileService.exists(resource);
if (exists) {
errors.onUnexpectedError(error);
return undefined;
})));
}
}
return undefined;
}));
// reset
this._standAloneConfigurations = [];
@@ -677,7 +562,6 @@ export abstract class AbstractFolderConfiguration extends Disposable implements
// Consolidate (support *.json files in the workspace settings folder)
this.consolidate();
this._loaded = true;
return this._cache;
}
@@ -694,41 +578,13 @@ export abstract class AbstractFolderConfiguration extends Disposable implements
this._cache = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations);
}
protected abstract loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined>;
}
export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration {
constructor(private readonly configurationFileService: IConfigurationFileService, configurationFolder: URI, workbenchState: WorkbenchState) {
super(configurationFolder, workbenchState);
}
protected async loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined> {
const exists = await this.configurationFileService.exists(configurationResource);
if (exists) {
return this.configurationFileService.resolveContent(configurationResource);
private async onWatchStarted(): Promise<void> {
const currentModel = this._cache;
const newModel = await this.loadConfiguration();
const { added, removed, updated } = compare(currentModel, newModel);
if (added.length || removed.length || updated.length) {
this._onDidChange.fire();
}
return undefined;
}
}
export class FileServiceBasedFolderConfiguration extends AbstractFolderConfiguration {
private changeEventTriggerScheduler: RunOnceScheduler;
constructor(configurationFolder: URI, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) {
super(configurationFolder, workbenchState, from);
this.changeEventTriggerScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
}
protected async loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined> {
const exists = await this.fileService.exists(configurationResource);
if (exists) {
const contents = await this.fileService.resolveContent(configurationResource);
return contents.value;
}
return undefined;
}
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
@@ -784,7 +640,6 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati
private configurationModel: ConfigurationModel;
private readonly key: Thenable<ConfigurationKey>;
loaded: boolean = false;
constructor(
folder: URI,
@@ -802,7 +657,6 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati
const contents = await this.configurationCache.read(key);
const parsed: IConfigurationModel = JSON.parse(contents.toString());
this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides);
this.loaded = true;
} catch (e) {
}
return this.configurationModel;
@@ -832,79 +686,44 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
readonly onDidChange: Event<void> = this._onDidChange.event;
private folderConfiguration: IFolderConfiguration;
private folderConfigurationDisposable: IDisposable = Disposable.None;
private readonly configurationFolder: URI;
private cachedFolderConfiguration: CachedFolderConfiguration;
private _loaded: boolean = false;
constructor(
readonly workspaceFolder: IWorkspaceFolder,
configFolderRelativePath: string,
private readonly workbenchState: WorkbenchState,
configurationFileService: IConfigurationFileService,
configurationCache: IConfigurationCache,
fileService?: IFileService
configurationCache: IConfigurationCache
) {
super();
this.configurationFolder = resources.joinPath(workspaceFolder.uri, configFolderRelativePath);
this.cachedFolderConfiguration = new CachedFolderConfiguration(workspaceFolder.uri, configFolderRelativePath, configurationCache);
this.folderConfiguration = this.cachedFolderConfiguration;
if (fileService) {
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
} else if (workspaceFolder.uri.scheme === Schemas.file) {
this.folderConfiguration = new NodeBasedFolderConfiguration(configurationFileService, this.configurationFolder, this.workbenchState);
this.folderConfiguration = this.cachedFolderConfiguration = new CachedFolderConfiguration(workspaceFolder.uri, configFolderRelativePath, configurationCache);
if (workspaceFolder.uri.scheme === Schemas.file) {
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, configurationFileService);
} else {
configurationFileService.whenProviderRegistered(workspaceFolder.uri.scheme)
.then(() => {
this.folderConfiguration.dispose();
this.folderConfigurationDisposable.dispose();
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, configurationFileService);
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
this.onDidFolderConfigurationChange();
});
}
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
this.folderConfigurationDisposable = this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
}
loadConfiguration(): Promise<ConfigurationModel> {
return this.folderConfiguration.loadConfiguration()
.then(model => {
this._loaded = this.folderConfiguration.loaded;
return model;
});
return this.folderConfiguration.loadConfiguration();
}
reprocess(): ConfigurationModel {
return this.folderConfiguration.reprocess();
}
get loaded(): boolean {
return this._loaded;
}
adopt(fileService: IFileService): Promise<boolean> {
if (fileService) {
if (this.folderConfiguration instanceof CachedFolderConfiguration) {
return this.adoptFromCachedConfiguration(fileService);
}
if (this.folderConfiguration instanceof NodeBasedFolderConfiguration) {
return this.adoptFromNodeBasedConfiguration(fileService);
}
}
return Promise.resolve(false);
}
private adoptFromCachedConfiguration(fileService: IFileService): Promise<boolean> {
const folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
return folderConfiguration.loadConfiguration()
.then(() => {
this.folderConfiguration = folderConfiguration;
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
this.updateCache();
return true;
});
}
private adoptFromNodeBasedConfiguration(fileService: IFileService): Promise<boolean> {
const oldFolderConfiguration = this.folderConfiguration;
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService, <AbstractFolderConfiguration>oldFolderConfiguration);
oldFolderConfiguration.dispose();
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
return Promise.resolve(false);
}
private onDidFolderConfigurationChange(): void {
this.updateCache();
this._onDidChange.fire();
@@ -13,7 +13,6 @@ import { Queue, Barrier } from 'vs/base/common/async';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { isLinux } from 'vs/base/common/platform';
import { IFileService } from 'vs/platform/files/common/files';
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
@@ -23,13 +22,12 @@ import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resour
import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService';
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, LocalUserConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
import { isEqual, dirname } from 'vs/base/common/resources';
import { mark } from 'vs/base/common/performance';
import { Schemas } from 'vs/base/common/network';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService {
@@ -41,7 +39,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
private readonly configurationCache: IConfigurationCache;
private _configuration: Configuration;
private defaultConfiguration: DefaultConfigurationModel;
private localUserConfiguration: LocalUserConfiguration | null = null;
private localUserConfiguration: UserConfiguration | null = null;
private remoteUserConfiguration: RemoteUserConfiguration | null = null;
private workspaceConfiguration: WorkspaceConfiguration;
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
@@ -60,14 +58,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
protected readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState> = this._register(new Emitter<WorkbenchState>());
public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;
private fileService: IFileService;
private configurationEditingService: ConfigurationEditingService;
private jsonEditingService: JSONEditingService;
constructor(
{ userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache },
private readonly configurationFileService: IConfigurationFileService,
private readonly remoteAgentService: IRemoteAgentService,
remoteAgentService: IRemoteAgentService,
) {
super();
@@ -75,11 +72,11 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.defaultConfiguration = new DefaultConfigurationModel();
this.configurationCache = configurationCache;
if (userSettingsResource) {
this.localUserConfiguration = this._register(new LocalUserConfiguration(userSettingsResource, configurationFileService));
this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, configurationFileService));
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
}
if (remoteAuthority) {
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache));
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, configurationFileService, remoteAgentService));
this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration)));
}
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, this.configurationFileService));
@@ -229,9 +226,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
// Workspace Configuration Service Impl
getConfigurationData(): IConfigurationData {
const configurationData = this._configuration.toData();
configurationData.isComplete = this.cachedFolderConfigs.values().every(c => c.loaded);
return configurationData;
return this._configuration.toData();
}
getValue<T>(): T;
@@ -294,45 +289,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
});
}
acquireFileService(fileService: IFileService): void {
this.fileService = fileService;
const changedWorkspaceFolders: IWorkspaceFolder[] = [];
if (this.localUserConfiguration) {
this.localUserConfiguration.adopt(fileService)
.then(changedModel => {
if (changedModel) {
this.onLocalUserConfigurationChanged(changedModel);
}
});
}
Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values()
.map(folderConfiguration => folderConfiguration.adopt(fileService)
.then(result => {
if (result) {
changedWorkspaceFolders.push(folderConfiguration.workspaceFolder);
}
return result;
}))])
.then(([workspaceChanged]) => {
if (workspaceChanged) {
this.onWorkspaceConfigurationChanged();
}
for (const workspaceFolder of changedWorkspaceFolders) {
this.onWorkspaceFolderConfigurationChanged(workspaceFolder);
}
this.releaseWorkspaceBarrier();
});
if (this.remoteUserConfiguration) {
this.remoteAgentService.getEnvironment()
.then(environment => this.remoteUserConfiguration!.adopt(environment ? environment.appSettingsPath : null, fileService)
.then(changedModel => {
if (changedModel) {
this.onRemoteUserConfigurationChanged(changedModel);
}
}));
}
}
acquireInstantiationService(instantiationService: IInstantiationService): void {
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
@@ -357,8 +313,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath));
const workspaceId = workspaceIdentifier.id;
const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath);
if (workspace.configuration!.scheme === Schemas.file) {
this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is from disk.
if (this.workspaceConfiguration.loaded) {
this.releaseWorkspaceBarrier();
}
return workspace;
});
@@ -583,6 +539,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.triggerConfigurationChange(workspaceConfigurationChangeEvent, ConfigurationTarget.WORKSPACE);
}
}
if (this.workspaceConfiguration.loaded) {
this.releaseWorkspaceBarrier();
}
return Promise.resolve(undefined);
}
@@ -629,7 +588,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
return Promise.all([...folders.map(folder => {
let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
if (!folderConfiguration) {
folderConfiguration = new FolderConfiguration(folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.configurationFileService, this.configurationCache, this.fileService);
folderConfiguration = new FolderConfiguration(folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.configurationFileService, this.configurationCache);
this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));
this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
}
@@ -4,6 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
export const FOLDER_CONFIG_FOLDER_NAME = '.azuredatastudio';
export const FOLDER_SETTINGS_NAME = 'settings';
@@ -34,6 +37,48 @@ export interface IConfigurationCache {
}
export interface IConfigurationFileService {
fileService: IFileService | null;
readonly onFileChanges: Event<FileChangesEvent>;
readonly isWatching: boolean;
readonly whenWatchingStarted: Promise<void>;
whenProviderRegistered(scheme: string): Promise<void>;
watch(resource: URI): IDisposable;
exists(resource: URI): Promise<boolean>;
resolveContent(resource: URI): Promise<string>;
}
export class ConfigurationFileService implements IConfigurationFileService {
constructor(public fileService: IFileService) { }
get onFileChanges() { return this.fileService.onFileChanges; }
readonly whenWatchingStarted: Promise<void> = Promise.resolve();
readonly isWatching: boolean = true;
whenProviderRegistered(scheme: string): Promise<void> {
if (this.fileService.canHandleResource(URI.from({ scheme }))) {
return Promise.resolve();
}
return new Promise((c, e) => {
const disposable = this.fileService.onDidChangeFileSystemProviderRegistrations(e => {
if (e.scheme === scheme && e.added) {
disposable.dispose();
c();
}
});
});
}
watch(resource: URI): IDisposable {
return this.fileService.watch(resource);
}
exists(resource: URI): Promise<boolean> {
return this.fileService.exists(resource);
}
resolveContent(resource: URI): Promise<string> {
return this.fileService.resolveContent(resource, { encoding: 'utf8' }).then(content => content.value);
}
}
@@ -4,18 +4,76 @@
*--------------------------------------------------------------------------------------------*/
import * as pfs from 'vs/base/node/pfs';
import { IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
import { IConfigurationFileService, ConfigurationFileService as FileServiceBasedConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
export class ConfigurationFileService implements IConfigurationFileService {
export class ConfigurationFileService extends Disposable implements IConfigurationFileService {
private _fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService | null = null;
private _fileServiceBasedConfigurationFileServiceCallback: (fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService) => void;
private _whenFileServiceBasedConfigurationFileServiceAvailable: Promise<FileServiceBasedConfigurationFileService> = new Promise((c) => this._fileServiceBasedConfigurationFileServiceCallback = c);
private _watchResources: { resource: URI, disposable: { disposable: IDisposable | null } }[] = [];
readonly whenWatchingStarted: Promise<void> = this._whenFileServiceBasedConfigurationFileServiceAvailable.then(() => undefined);
private readonly _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>());
readonly onFileChanges: Event<FileChangesEvent> = this._onFileChanges.event;
get isWatching(): boolean {
return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.isWatching : false;
}
watch(resource: URI): IDisposable {
if (this._fileServiceBasedConfigurationFileService) {
return this._fileServiceBasedConfigurationFileService.watch(resource);
}
const disposable: { disposable: IDisposable | null } = { disposable: null };
this._watchResources.push({ resource, disposable });
return toDisposable(() => {
if (disposable.disposable) {
disposable.disposable.dispose();
}
});
}
whenProviderRegistered(scheme: string): Promise<void> {
if (scheme === Schemas.file) {
return Promise.resolve();
}
return this._whenFileServiceBasedConfigurationFileServiceAvailable
.then(fileServiceBasedConfigurationFileService => fileServiceBasedConfigurationFileService.whenProviderRegistered(scheme));
}
exists(resource: URI): Promise<boolean> {
return pfs.exists(resource.fsPath);
return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.exists(resource) : pfs.exists(resource.fsPath);
}
async resolveContent(resource: URI): Promise<string> {
const contents = await pfs.readFile(resource.fsPath);
return contents.toString();
if (this._fileServiceBasedConfigurationFileService) {
return this._fileServiceBasedConfigurationFileService.resolveContent(resource);
} else {
const contents = await pfs.readFile(resource.fsPath);
return contents.toString();
}
}
private _fileService: IFileService | null;
get fileService(): IFileService | null {
return this._fileService;
}
set fileService(fileService: IFileService | null) {
if (fileService && !this._fileServiceBasedConfigurationFileService) {
this._fileServiceBasedConfigurationFileService = new FileServiceBasedConfigurationFileService(fileService);
this._fileService = fileService;
this._register(this._fileServiceBasedConfigurationFileService.onFileChanges(e => this._onFileChanges.fire(e)));
for (const { resource, disposable } of this._watchResources) {
disposable.disposable = this._fileServiceBasedConfigurationFileService.watch(resource);
}
this._fileServiceBasedConfigurationFileServiceCallback(this._fileServiceBasedConfigurationFileService);
}
}
}
@@ -55,7 +55,7 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic
if (activeEditor instanceof DiffEditorInput) {
activeEditor = activeEditor.modifiedInput;
}
const fileResource = toResource(activeEditor, { filter: Schemas.file });
const fileResource = toResource(activeEditor, { filterByScheme: Schemas.file });
if (!fileResource) {
return undefined;
}
@@ -106,7 +106,7 @@ export class FileDialogService implements IFileDialogService {
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return (this.fileService.resolve(uri)).then(stat => {
const toOpen: IURIToOpen = stat.isDirectory ? { fileUri: uri } : { folderUri: uri };
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
});
}
@@ -210,7 +210,7 @@ export class FileDialogService implements IFileDialogService {
showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
const schema = this.getFileSystemSchema(options);
if (schema !== Schemas.file) {
if (this.shouldUseSimplified(schema)) {
if (!options.availableFileSystems) {
options.availableFileSystems = [schema]; // by default only allow loading in the own file system
}
@@ -110,10 +110,13 @@ export class RemoteFileDialog {
let defaultUri = options.defaultUri;
const filename = (defaultUri && isSave && (resources.dirname(defaultUri).path === '/')) ? resources.basename(defaultUri) : undefined;
if (!defaultUri || filename) {
const env = await this.remoteAgentService.getEnvironment();
if (env) {
defaultUri = env.userHome;
} else {
if (this.scheme !== Schemas.file) {
const env = await this.remoteAgentService.getEnvironment();
if (env) {
defaultUri = env.userHome;
}
}
if (!defaultUri) {
defaultUri = URI.from({ scheme: this.scheme, path: this.environmentService.userHome });
}
if (filename) {
@@ -192,7 +195,7 @@ export class RemoteFileDialog {
if (validated) {
isResolving = true;
this.filePickBox.hide();
resolve(resolveValue);
doResolve(this, resolveValue);
}
});
});
@@ -200,6 +203,13 @@ export class RemoteFileDialog {
this.filePickBox.title = this.options.title;
this.filePickBox.value = this.pathFromUri(this.currentFolder);
this.filePickBox.items = [];
function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) {
resolve(uri);
dialog.contextKey.set(false);
dialog.filePickBox.dispose();
}
this.filePickBox.onDidAccept(_ => {
if (isAcceptHandled || this.filePickBox.busy) {
return;
@@ -210,9 +220,9 @@ export class RemoteFileDialog {
this.onDidAccept().then(resolveValue => {
if (resolveValue) {
this.filePickBox.hide();
resolve(resolveValue);
doResolve(this, resolveValue);
} else if (this.hidden) {
resolve(undefined);
doResolve(this, undefined);
} else {
isResolving = false;
isAcceptHandled = false;
@@ -221,6 +231,10 @@ export class RemoteFileDialog {
});
this.filePickBox.onDidChangeActive(i => {
isAcceptHandled = false;
// update input box to match the first selected item
if (i.length === 1) {
this.setAutoComplete(this.userValue, resources.basename(this.remoteUriFrom(this.userValue)), i[0], true);
}
});
this.filePickBox.onDidChangeValue(async value => {
@@ -229,13 +243,11 @@ export class RemoteFileDialog {
if (value !== this.userValue) {
this.filePickBox.validationMessage = undefined;
this.shouldOverwriteFile = false;
const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value;
const valueUri = this.remoteUriFrom(trimmedPickBoxValue);
if (!resources.isEqual(this.currentFolder, valueUri, true)) {
const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value));
if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) {
await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value));
}
this.setActiveItems(value);
this.userValue = value;
} else {
this.filePickBox.activeItems = [];
}
@@ -244,10 +256,8 @@ export class RemoteFileDialog {
this.filePickBox.onDidHide(() => {
this.hidden = true;
if (!isResolving) {
resolve(undefined);
doResolve(this, undefined);
}
this.contextKey.set(false);
this.filePickBox.dispose();
});
this.filePickBox.show();
@@ -361,15 +371,8 @@ export class RemoteFileDialog {
let hasMatch = false;
for (let i = 0; i < this.filePickBox.items.length; i++) {
const item = <FileQuickPickItem>this.filePickBox.items[i];
const itemBasename = (item.label === '..') ? item.label : resources.basename(item.uri);
if ((itemBasename.length >= inputBasename.length) && (itemBasename.substr(0, inputBasename.length).toLowerCase() === inputBasename.toLowerCase())) {
if (this.setAutoComplete(value, inputBasename, item)) {
this.filePickBox.activeItems = [item];
const insertValue = itemBasename.substr(inputBasename.length);
this.autoComplete = value + insertValue;
if (this.filePickBox.inputHasFocus()) {
document.execCommand('insertText', false, insertValue);
this.filePickBox.valueSelection = [value.length, this.filePickBox.value.length];
}
hasMatch = true;
break;
}
@@ -377,6 +380,44 @@ export class RemoteFileDialog {
if (!hasMatch) {
this.filePickBox.activeItems = [];
}
} else {
this.userValue = value;
this.autoComplete = '';
}
}
private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem, force: boolean = false): boolean {
const itemBasename = (quickPickItem.label === '..') ? quickPickItem.label : resources.basename(quickPickItem.uri);
const itemPathLabel = (itemBasename === '..') ? this.pathAppend(this.currentFolder, itemBasename) : this.pathFromUri(quickPickItem.uri);
if (this.trimTrailingSlash(this.filePickBox.value) !== itemPathLabel) {
// Either foce the autocomplete, or the old value should be one smaller than the new value and match the new value.
if (!force && (itemBasename.length >= startingBasename.length) && (itemBasename.substr(0, startingBasename.length).toLowerCase() === startingBasename.toLowerCase())) {
this.userValue = startingValue;
const autoCompleteValue = itemBasename.substr(startingBasename.length);
this.autoComplete = startingValue + autoCompleteValue;
this.insertText(this.autoComplete, autoCompleteValue);
this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length];
return true;
} else if (force) {
this.userValue = this.pathFromUri(this.currentFolder, true);
this.autoComplete = this.pathAppend(this.currentFolder, itemBasename);
this.filePickBox.valueSelection = [this.userValue.length, this.filePickBox.value.length];
// use insert text to preserve undo buffer
this.insertText(this.autoComplete, itemBasename);
this.filePickBox.valueSelection = [this.filePickBox.value.length - itemBasename.length, this.filePickBox.value.length];
return true;
}
}
this.userValue = startingValue;
this.autoComplete = '';
return false;
}
private insertText(wholeValue: string, insertText: string) {
if (this.filePickBox.inputHasFocus()) {
document.execCommand('insertText', false, insertText);
} else {
this.filePickBox.value = wholeValue;
}
}
@@ -406,6 +447,39 @@ export class RemoteFileDialog {
return result;
}
private trimTrailingSlash(path: string): string {
return ((path.length > 1) && this.endsWithSlash(path)) ? path.substr(0, path.length - 1) : path;
}
private yesNoPrompt(message: string): Promise<boolean> {
interface YesNoItem extends IQuickPickItem {
value: boolean;
}
const prompt = this.quickInputService.createQuickPick<YesNoItem>();
const no = nls.localize('remoteFileDialog.no', 'No');
prompt.items = [{ label: no, value: false }, { label: nls.localize('remoteFileDialog.yes', 'Yes'), value: true }];
prompt.title = message;
prompt.placeholder = no;
let isResolving = false;
return new Promise<boolean>(resolve => {
prompt.onDidAccept(() => {
isResolving = true;
prompt.hide();
resolve(prompt.selectedItems ? prompt.selectedItems[0].value : false);
});
prompt.onDidHide(() => {
if (!isResolving) {
resolve(false);
}
this.filePickBox.show();
this.hidden = false;
this.filePickBox.items = this.filePickBox.items;
prompt.dispose();
});
prompt.show();
});
}
private async validate(uri: URI): Promise<boolean> {
let stat: IFileStat | undefined;
let statDirname: IFileStat | undefined;
@@ -424,8 +498,9 @@ export class RemoteFileDialog {
} else if (stat && !this.shouldOverwriteFile) {
// Replacing a file.
this.shouldOverwriteFile = true;
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri));
return Promise.resolve(false);
// Show a yes/no prompt
const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri));
return this.yesNoPrompt(message);
} else if (!this.isValidBaseName(resources.basename(uri))) {
// Filename not allowed
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.');
@@ -455,7 +530,11 @@ export class RemoteFileDialog {
private updateItems(newFolder: URI, trailing?: string) {
this.currentFolder = newFolder;
this.filePickBox.value = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true);
this.userValue = this.pathFromUri(newFolder);
this.autoComplete = '';
this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true);
this.insertText(newValue, newValue);
this.filePickBox.busy = true;
this.createItems(this.currentFolder).then(items => {
this.filePickBox.items = items;
@@ -480,6 +559,14 @@ export class RemoteFileDialog {
return result;
}
private pathAppend(uri: URI, additional: string): string {
if (additional === '..') {
return this.pathFromUri(uri) + this.labelService.getSeparator(uri.scheme, uri.authority) + additional;
} else {
return this.pathFromUri(resources.joinPath(uri, additional));
}
}
private isValidBaseName(name: string): boolean {
if (!name || name.length === 0 || /^\s+$/.test(name)) {
return false; // require a name that is not just whitespace
@@ -5,7 +5,7 @@
import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IResourceInput, ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource } from 'vs/workbench/common/editor';
import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -419,7 +419,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
// Resource editor
else {
for (const editorInGroup of group.editors) {
const resource = toResource(editorInGroup, { supportSideBySide: true });
const resource = toResource(editorInGroup, { supportSideBySide: SideBySideEditor.MASTER });
if (!resource) {
continue; // need a resource to compare with
}

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