Merge from vscode 1ce89e2cb720d69c496c2815c4696ee4fd4429a6 (#6779)

* Merge from vscode 1ce89e2cb720d69c496c2815c4696ee4fd4429a6

* redisable accounts because of issues
This commit is contained in:
Anthony Dresser
2019-08-15 23:56:46 -07:00
committed by GitHub
parent fb4e3c6a05
commit 6f297efb88
166 changed files with 1941 additions and 1279 deletions

View File

@@ -107,6 +107,16 @@ steps:
displayName: Run integration tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
cd test/smoke
yarn compile
cd -
yarn smoketest --web --headless
continueOnError: true
displayName: Run smoke tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd

View File

@@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 3.76345L5.80687 11.9351L5.08584 11.8927L1 7.29614L1.76345 6.61752L5.50997 10.8324L14.3214 3L15 3.76345Z" fill="#C5C5C5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4315 3.32315L5.96154 13.3232L5.17083 13.2874L1.82083 8.51736L2.63918 7.94263L5.617 12.1827L13.6685 2.67683L14.4315 3.32315Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 298 B

View File

@@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 3.76345L5.80687 11.9351L5.08584 11.8927L1 7.29614L1.76345 6.61752L5.50997 10.8324L14.3214 3L15 3.76345Z" fill="#424242"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4315 3.32317L5.96154 13.3232L5.17083 13.2874L1.82083 8.51737L2.63918 7.94264L5.617 12.1827L13.6685 2.67685L14.4315 3.32317Z" fill="#424242"/>
</svg>

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 298 B

View File

@@ -209,7 +209,7 @@ export class MarkdownContentProvider {
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' ${rule} https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' ${rule} https: data: http://localhost:* http://127.0.0.1:*;">`;
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
return '';
return '<meta http-equiv="Content-Security-Policy" content="">';
case MarkdownPreviewSecurityLevel.Strict:
default:

View File

@@ -131,7 +131,7 @@
"gulp-shell": "^0.6.5",
"gulp-tsb": "2.0.7",
"gulp-tslint": "^8.1.3",
"gulp-uglify": "^3.0.2",
"gulp-uglify": "^3.0.0",
"gulp-untar": "^0.0.7",
"gulp-vinyl-zip": "^2.1.2",
"http-server": "^0.11.1",
@@ -155,6 +155,7 @@
"optimist": "0.3.5",
"p-all": "^1.0.0",
"pump": "^1.0.1",
"puppeteer": "^1.19.0",
"queue": "3.0.6",
"rcedit": "^1.1.0",
"rimraf": "^2.2.8",
@@ -168,7 +169,7 @@
"typemoq": "^0.3.2",
"typescript": "3.5.2",
"typescript-formatter": "7.1.0",
"uglify-es": "^3.3.9",
"uglify-es": "^3.0.18",
"vinyl": "^2.0.0",
"vinyl-fs": "^3.0.0",
"vsce": "1.48.0",

View File

@@ -7,10 +7,10 @@ import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { INodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import * as azdata from 'azdata';
import * as sqlops from 'sqlops';
import { IAzdataExtensionApiFactory, ISqlopsExtensionApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl';
import { INodeModuleFactory } from 'vs/workbench/api/common/extHostRequireInterceptor';
export class AzdataNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'azdata';
@@ -24,10 +24,10 @@ export class AzdataNodeModuleFactory implements INodeModuleFactory {
) {
}
public load(request: string, parent: { filename: string; }): any {
public load(request: string, parent: URI): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const ext = this._extensionPaths.findSubstr(parent.fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
@@ -41,7 +41,7 @@ export class AzdataNodeModuleFactory implements INodeModuleFactory {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'azdata' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
console.warn(`Could not identify extension for 'azdata' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription);
}
return this._defaultApiImpl;
@@ -60,10 +60,10 @@ export class SqlopsNodeModuleFactory implements INodeModuleFactory {
) {
}
public load(request: string, parent: { filename: string; }): any {
public load(request: string, parent: URI): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const ext = this._extensionPaths.findSubstr(parent.fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
@@ -77,7 +77,7 @@ export class SqlopsNodeModuleFactory implements INodeModuleFactory {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'sqlops' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
console.warn(`Could not identify extension for 'sqlops' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription);
}
return this._defaultApiImpl;

View File

@@ -34,6 +34,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionApiFactory as vsIApiFactory, createApiFactoryAndRegisterActors as vsApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
export interface ISqlopsExtensionApiFactory {
(extension: IExtensionDescription): typeof sqlops;
@@ -43,11 +44,34 @@ export interface IAzdataExtensionApiFactory {
(extension: IExtensionDescription): typeof azdata;
}
export interface IExtensionApiFactory {
azdata: IAzdataExtensionApiFactory;
sqlops: ISqlopsExtensionApiFactory;
vscode: vsIApiFactory;
}
export interface IAdsExtensionApiFactory {
azdata: IAzdataExtensionApiFactory;
sqlops: ISqlopsExtensionApiFactory;
}
/**
* This method instantiates and returns the extension API surface
*/
export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): IExtensionApiFactory {
const { azdata, sqlops } = createAdsApiFactory(accessor);
return {
azdata,
sqlops,
vscode: vsApiFactory(accessor)
};
}
export interface IAdsExtensionApiFactory {
azdata: IAzdataExtensionApiFactory;
sqlops: ISqlopsExtensionApiFactory;
}
/**
* This method instantiates and returns the extension API surface

View File

@@ -67,9 +67,7 @@ export default class WebViewComponent extends ComponentBase implements IComponen
private _createWebview(): void {
this._webview = this.webviewService.createWebview(this.id,
{
allowSvgs: true
},
{},
{
allowScripts: true
});

View File

@@ -171,9 +171,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
private domNode: HTMLElement;
private treeContainer: HTMLElement;
private _messageValue: string | IMarkdownString | undefined;
private _canSelectMany: boolean = false;
private messageElement: HTMLDivElement;
private tree: WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>;
private treeLabels: ResourceLabels;
private root: ITreeItem;
private elementsToRefresh: ITreeItem[] = [];
private menus: TitleMenus;
@@ -273,6 +275,14 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.updateMessage();
}
get canSelectMany(): boolean {
return this._canSelectMany;
}
set canSelectMany(canSelectMany: boolean) {
this._canSelectMany = canSelectMany;
}
get hasIconForParentNode(): boolean {
return this._hasIconForParentNode;
}
@@ -391,12 +401,15 @@ export class CustomTreeView extends Disposable implements ITreeView {
expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command,
collapseByDefault: (e: ITreeItem): boolean => {
return e.collapsibleState !== TreeItemCollapsibleState.Expanded;
}
},
multipleSelectionSupport: this.canSelectMany,
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
aligner.tree = this.tree;
const actionRunner = new MultipleSelectionActionRunner(() => this.tree!.getSelection());
renderer.actionRunner = actionRunner;
this.tree.contextKeyService.createKey<boolean>(this.id, true);
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e)));
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner)));
this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements)));
this._register(this.tree.onDidChangeCollapseState(e => {
const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element;
@@ -425,7 +438,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}));
}
private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent<ITreeItem>): void {
private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent<ITreeItem>, actionRunner: MultipleSelectionActionRunner): void {
const node: ITreeItem | null = treeEvent.element;
if (node === null) {
return;
@@ -461,7 +474,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
getActionsContext: () => (<TreeViewItemHandleArg>{ $treeViewId: this.id, $treeItemHandle: node.handle, $treeItem: node, $treeContainerId: this.treeContainer.id }),
actionRunner: new MultipleSelectionActionRunner(() => this.tree.getSelection())
actionRunner
});
}
@@ -724,6 +737,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
static readonly ITEM_HEIGHT = 22;
static readonly TREE_TEMPLATE_ID = 'treeExplorer';
private _actionRunner: MultipleSelectionActionRunner | undefined;
constructor(
private treeViewId: string,
private menus: TreeMenus,
@@ -741,6 +756,10 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
return TreeRenderer.TREE_TEMPLATE_ID;
}
set actionRunner(actionRunner: MultipleSelectionActionRunner) {
this._actionRunner = actionRunner;
}
renderTemplate(container: HTMLElement): ITreeExplorerTemplateData {
DOM.addClass(container, 'custom-view-tree-node-item');
@@ -782,8 +801,11 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
DOM.toggleClass(templateData.icon, sqlIcon, !!sqlIcon);
DOM.toggleClass(templateData.icon, 'icon', !!sqlIcon);
DOM.toggleClass(templateData.icon, 'custom-view-tree-node-item-icon', !!iconUrl || !!sqlIcon);
templateData.actionBar.context = (<TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle });
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
if (this._actionRunner) {
templateData.actionBar.actionRunner = this._actionRunner;
}
this.setAlignment(templateData.container, node);
templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node)));
}
@@ -860,23 +882,23 @@ class Aligner extends Disposable {
class MultipleSelectionActionRunner extends ActionRunner {
constructor(private getSelectedResources: (() => any[])) {
constructor(private getSelectedResources: (() => ITreeItem[])) {
super();
}
runAction(action: IAction, context: any): Promise<any> {
if (action instanceof MenuItemAction) {
const selection = this.getSelectedResources();
const filteredSelection = selection.filter(s => s !== context);
if (selection.length === filteredSelection.length || selection.length === 1) {
return action.run(context);
}
return action.run(context, ...filteredSelection);
runAction(action: IAction, context: TreeViewItemHandleArg): Promise<any> {
const selection = this.getSelectedResources();
let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined;
if (selection.length > 1) {
selectionHandleArgs = [];
selection.forEach(selected => {
if (selected.handle !== context.$treeItemHandle) {
selectionHandleArgs!.push({ $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle });
}
});
}
return super.runAction(action, context);
return action.run(...[context, selectionHandleArgs]);
}
}

View File

@@ -5,7 +5,7 @@
"strictNullChecks": true,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"skipLibCheck": true
"skipLibCheck": true,
},
"include": [
"./typings",
@@ -18,7 +18,7 @@
"./sql/base/**/*.ts",
"./sql/editor/**/*.ts",
"./sql/platform/angularEventing/**/*.ts",
"./sql/platform/accounts/**/*.ts",
// "./sql/platform/accounts/**/*.ts",
"./sql/platform/clipboard/**/*.ts",
"./sql/platform/credentials/**/*.ts",
"./sql/platform/theme/**/*.ts",

View File

@@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 3.76345L5.80687 11.9351L5.08584 11.8927L1 7.29614L1.76345 6.61752L5.50997 10.8324L14.3214 3L15 3.76345Z" fill="#C5C5C5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4315 3.3232L5.96151 13.3232L5.1708 13.2874L1.8208 8.5174L2.63915 7.94268L5.61697 12.1827L13.6684 2.67688L14.4315 3.3232Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 295 B

View File

@@ -293,12 +293,9 @@ function topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, re
}
/**
* @returns a new array with all falsy values removed. The original array IS NOT modified.
* @returns New array with all falsy values removed. The original array IS NOT modified.
*/
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
if (!array) {
return array as T[]; // {{SQL CARBON EDIT}} @anthonydresser strict-null-checks
}
return <T[]>array.filter(e => !!e);
}
@@ -306,9 +303,6 @@ export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
* Remove all falsey values from `array`. The original array IS modified.
*/
export function coalesceInPlace<T>(array: Array<T | undefined | null>): void {
if (!array) {
return;
}
let to = 0;
for (let i = 0; i < array.length; i++) {
if (!!array[i]) {

View File

@@ -127,8 +127,7 @@ export class DisposableStore implements IDisposable {
markTracked(t);
if (this._isDisposed) {
console.warn(new Error('Registering disposable on object that has already been disposed of').stack);
t.dispose();
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
} else {
this._toDispose.add(t);
}

View File

@@ -49,3 +49,26 @@ export namespace Schemas {
export const userData: string = 'vscode-userdata';
}
class RemoteAuthoritiesImpl {
private readonly _hosts: { [authority: string]: string; };
private readonly _ports: { [authority: string]: number; };
private readonly _connectionTokens: { [authority: string]: string; };
constructor() {
this._hosts = Object.create(null);
this._ports = Object.create(null);
this._connectionTokens = Object.create(null);
}
public set(authority: string, host: string, port: number): void {
this._hosts[authority] = host;
this._ports[authority] = port;
}
public setConnectionToken(authority: string, connectionToken: string): void {
this._connectionTokens[authority] = connectionToken;
}
}
export const RemoteAuthorities = new RemoteAuthoritiesImpl();

View File

@@ -3,11 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isWindows, isMacintosh, setImmediate } from 'vs/base/common/platform';
import { isWindows, isMacintosh, setImmediate, IProcessEnvironment } from 'vs/base/common/platform';
interface IProcess {
platform: string;
env: object;
env: IProcessEnvironment;
cwd(): string;
nextTick(callback: (...args: any[]) => void): number;

View File

@@ -8,8 +8,7 @@ import { IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc
import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import * as platform from 'vs/base/common/platform';
declare var process: any;
import * as process from 'vs/base/common/process';
export interface ISocket extends IDisposable {
onData(listener: (e: VSBuffer) => void): IDisposable;
@@ -434,11 +433,7 @@ export function createBufferedEvent<T>(source: Event<T>): Event<T> {
// it is important to deliver these messages after this call, but before
// other messages have a chance to be received (to guarantee in order delivery)
// that's why we're using here nextTick and not other types of timeouts
if (typeof process !== 'undefined') {
process.nextTick(deliverMessages);
} else {
platform.setImmediate(deliverMessages);
}
process.nextTick(deliverMessages);
},
onLastListenerRemove: () => {
hasListeners = false;

View File

@@ -86,7 +86,7 @@ export interface IIPCOptions {
export class Client implements IChannelClient, IDisposable {
private disposeDelayer: Delayer<void>;
private disposeDelayer: Delayer<void> | undefined;
private activeRequests = new Set<IDisposable>();
private child: ChildProcess | null;
private _client: IPCClient | null;
@@ -137,7 +137,7 @@ export class Client implements IChannelClient, IDisposable {
cancellationTokenListener.dispose();
this.activeRequests.delete(disposable);
if (this.activeRequests.size === 0) {
if (this.activeRequests.size === 0 && this.disposeDelayer) {
this.disposeDelayer.trigger(() => this.disposeClient());
}
});
@@ -271,8 +271,10 @@ export class Client implements IChannelClient, IDisposable {
dispose() {
this._onDidProcessExit.dispose();
this.disposeDelayer.cancel();
this.disposeDelayer = null!; // StrictNullOverride: nulling out ok in dispose
if (this.disposeDelayer) {
this.disposeDelayer.cancel();
this.disposeDelayer = undefined;
}
this.disposeClient();
this.activeRequests.clear();
}

View File

@@ -206,7 +206,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return this.doUpdateItems(recoveryConnection, { insert: recovery() }).then(() => closeRecoveryConnection(), error => {
// In case of an error updating items, still ensure to close the connection
// to prevent SQLITE_BUSY errors when the connection is restablished
// to prevent SQLITE_BUSY errors when the connection is reestablished
closeRecoveryConnection();
return Promise.reject(error);

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { IDisposable, dispose, ReferenceCollection, Disposable as DisposableBase, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle';
class Disposable implements IDisposable {
isDisposed = false;
@@ -50,38 +50,6 @@ suite('Lifecycle', () => {
});
});
suite('DisposableBase', () => {
test('register should not leak if object has already been disposed', () => {
let aCount = 0;
let bCount = 0;
const disposable = new class extends DisposableBase {
register(other: IDisposable) {
this._register(other);
}
};
disposable.register(toDisposable(() => ++aCount));
assert.strictEqual(aCount, 0);
assert.strictEqual(bCount, 0);
disposable.dispose();
assert.strictEqual(aCount, 1);
assert.strictEqual(bCount, 0);
// Any newly added disposables should be disposed of immediately
disposable.register(toDisposable(() => ++bCount));
assert.strictEqual(aCount, 1);
assert.strictEqual(bCount, 1);
// Further dispose calls should have no effect
disposable.dispose();
assert.strictEqual(aCount, 1);
assert.strictEqual(bCount, 1);
});
});
suite('Reference Collection', () => {
class Collection extends ReferenceCollection<number> {
private _count = 0;

View File

@@ -30,6 +30,7 @@
import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import * as process from 'vs/base/common/process';
suite('Paths (Node Implementation)', () => {
test('join', () => {

View File

@@ -31,7 +31,7 @@ import { WindowsService } from 'vs/platform/windows/electron-browser/windowsServ
import { MainProcessService, IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel';
import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue';
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
@@ -39,7 +39,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil';
import { Button } from 'vs/base/browser/ui/button/button';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
const MAX_URL_LENGTH = 2045;

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { assign } from 'vs/base/common/objects';
import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/node/issue';
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
export interface IssueReporterData {
issueType: IssueType;

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel';
import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil';
import { IssueType } from 'vs/platform/issue/common/issue';
import { IssueType } from 'vs/platform/issue/node/issue';
suite('IssueReporter', () => {

View File

@@ -9,7 +9,7 @@ import { repeat } from 'vs/base/common/strings';
import { totalmem } from 'os';
import product from 'vs/platform/product/node/product';
import { localize } from 'vs/nls';
import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import * as browser from 'vs/base/browser/browser';
import * as platform from 'vs/base/common/platform';
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
@@ -17,7 +17,7 @@ import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu';
import { ProcessItem } from 'vs/base/common/processes';
import { addDisposableListener } from 'vs/base/browser/dom';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
let mapPidToWindowTitle = new Map<number, string>();

View File

@@ -48,15 +48,13 @@ import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService';
import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/product';
import { ProductService } from 'vs/platform/product/node/productService';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@@ -112,10 +110,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
await configurationService.initialize();
services.set(IEnvironmentService, environmentService);
services.set(IProductService, { _serviceBrand: undefined, ...product });
services.set(ILogService, logService);
services.set(IConfigurationService, configurationService);
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IProductService, new SyncDescriptor(ProductService));
const mainProcessService = new MainProcessService(server, mainRouter);
services.set(IMainProcessService, mainProcessService);

View File

@@ -47,7 +47,7 @@ import { getMachineId } from 'vs/base/node/id';
import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32';
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { IIssueService } from 'vs/platform/issue/node/issue';
import { IssueChannel } from 'vs/platform/issue/node/issueIpc';
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
@@ -81,8 +81,8 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory';
import { VSBuffer } from 'vs/base/common/buffer';
import { statSync } from 'fs';
import { ISignService } from 'vs/platform/sign/common/sign';
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';

View File

@@ -47,7 +47,6 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IProductService } from 'vs/platform/product/common/product';
import { ProductService } from 'vs/platform/product/node/productService';
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
@@ -325,7 +324,7 @@ export async function main(argv: ParsedArgs): Promise<void> {
services.set(ILogService, logService);
services.set(IConfigurationService, configurationService);
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IProductService, new SyncDescriptor(ProductService));
services.set(IProductService, { _serviceBrand: undefined, ...product });
// Files
const fileService = new FileService(logService);

View File

@@ -13,7 +13,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
@@ -612,7 +612,7 @@ export interface ICodeEditor extends editorCommon.IEditor {
* @param edits The edits to execute.
* @param endCursorState Cursor state after the edits were applied.
*/
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean;
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean;
/**
* Execute multiple (concomitant) commands on the editor.

View File

@@ -33,7 +33,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { InternalEditorAction } from 'vs/editor/common/editorAction';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
@@ -980,7 +980,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return true;
}
public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean {
public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean {
if (!this._modelData) {
return false;
}
@@ -989,14 +989,16 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return false;
}
this._modelData.model.pushEditOperations(this._modelData.cursor.getSelections(), edits, () => {
return endCursorState ? endCursorState : null;
});
if (endCursorState) {
this._modelData.cursor.setSelections(source, endCursorState);
let cursorStateComputer: ICursorStateComputer;
if (!endCursorState) {
cursorStateComputer = () => null;
} else if (Array.isArray(endCursorState)) {
cursorStateComputer = () => endCursorState;
} else {
cursorStateComputer = endCursorState;
}
this._modelData.cursor.executeEdits(source, edits, cursorStateComputer);
return true;
}

View File

@@ -15,7 +15,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model';
import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer } from 'vs/editor/common/model';
import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
@@ -429,6 +429,31 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// ------ auxiliary handling logic
private _pushAutoClosedAction(autoClosedCharactersRanges: Range[], autoClosedEnclosingRanges: Range[]): void {
let autoClosedCharactersDeltaDecorations: IModelDeltaDecoration[] = [];
let autoClosedEnclosingDeltaDecorations: IModelDeltaDecoration[] = [];
for (let i = 0, len = autoClosedCharactersRanges.length; i < len; i++) {
autoClosedCharactersDeltaDecorations.push({
range: autoClosedCharactersRanges[i],
options: {
inlineClassName: 'auto-closed-character',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
autoClosedEnclosingDeltaDecorations.push({
range: autoClosedEnclosingRanges[i],
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
}
const autoClosedCharactersDecorations = this._model.deltaDecorations([], autoClosedCharactersDeltaDecorations);
const autoClosedEnclosingDecorations = this._model.deltaDecorations([], autoClosedEnclosingDeltaDecorations);
this._autoClosedActions.push(new AutoClosedAction(this._model, autoClosedCharactersDecorations, autoClosedEnclosingDecorations));
}
private _executeEditOperation(opResult: EditOperationResult | null): void {
if (!opResult) {
@@ -446,32 +471,19 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._interpretCommandResult(result);
// Check for auto-closing closed characters
let autoClosedCharactersRanges: IModelDeltaDecoration[] = [];
let autoClosedEnclosingRanges: IModelDeltaDecoration[] = [];
let autoClosedCharactersRanges: Range[] = [];
let autoClosedEnclosingRanges: Range[] = [];
for (let i = 0; i < opResult.commands.length; i++) {
const command = opResult.commands[i];
if (command instanceof TypeWithAutoClosingCommand && command.enclosingRange && command.closeCharacterRange) {
autoClosedCharactersRanges.push({
range: command.closeCharacterRange,
options: {
inlineClassName: 'auto-closed-character',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
autoClosedEnclosingRanges.push({
range: command.enclosingRange,
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});
autoClosedCharactersRanges.push(command.closeCharacterRange);
autoClosedEnclosingRanges.push(command.enclosingRange);
}
}
if (autoClosedCharactersRanges.length > 0) {
const autoClosedCharactersDecorations = this._model.deltaDecorations([], autoClosedCharactersRanges);
const autoClosedEnclosingDecorations = this._model.deltaDecorations([], autoClosedEnclosingRanges);
this._autoClosedActions.push(new AutoClosedAction(this._model, autoClosedCharactersDecorations, autoClosedEnclosingDecorations));
this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges);
}
this._prevEditOperationType = opResult.type;
@@ -563,6 +575,75 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// -----------------------------------------------------------------------------------------------------------
// ----- handlers beyond this point
private _findAutoClosingPairs(edits: IIdentifiedSingleEditOperation[]): [number, number][] | null {
if (!edits.length) {
return null;
}
let indices: [number, number][] = [];
for (let i = 0, len = edits.length; i < len; i++) {
const edit = edits[i];
if (!edit.text || edit.text.indexOf('\n') >= 0) {
return null;
}
const m = edit.text.match(/([)\]}>'"`])([^)\]}>'"`]*)$/);
if (!m) {
return null;
}
const closeChar = m[1];
const openChar = this.context.config.autoClosingPairsClose[closeChar];
if (!openChar) {
return null;
}
const closeCharIndex = edit.text.length - m[2].length - 1;
const openCharIndex = edit.text.lastIndexOf(openChar, closeCharIndex - 1);
if (openCharIndex === -1) {
return null;
}
indices.push([openCharIndex, closeCharIndex]);
}
return indices;
}
public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
let autoClosingIndices: [number, number][] | null = null;
if (source === 'snippet') {
autoClosingIndices = this._findAutoClosingPairs(edits);
}
if (autoClosingIndices) {
edits[0]._isTracked = true;
}
let autoClosedCharactersRanges: Range[] = [];
let autoClosedEnclosingRanges: Range[] = [];
const selections = this._model.pushEditOperations(this.getSelections(), edits, (undoEdits) => {
if (autoClosingIndices) {
for (let i = 0, len = autoClosingIndices.length; i < len; i++) {
const [openCharInnerIndex, closeCharInnerIndex] = autoClosingIndices[i];
const undoEdit = undoEdits[i];
const lineNumber = undoEdit.range.startLineNumber;
const openCharIndex = undoEdit.range.startColumn - 1 + openCharInnerIndex;
const closeCharIndex = undoEdit.range.startColumn - 1 + closeCharInnerIndex;
autoClosedCharactersRanges.push(new Range(lineNumber, closeCharIndex + 1, lineNumber, closeCharIndex + 2));
autoClosedEnclosingRanges.push(new Range(lineNumber, openCharIndex + 1, lineNumber, closeCharIndex + 2));
}
}
return cursorStateComputer(undoEdits);
});
if (selections) {
this.setSelections(source, selections);
}
if (autoClosedCharactersRanges.length > 0) {
this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges);
}
}
public trigger(source: string, handlerId: string, payload: any): void {
const H = editorCommon.Handler;

View File

@@ -489,21 +489,18 @@ export class SnippetSession {
return;
}
const model = this._editor.getModel();
// make insert edit and start with first selections
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText);
this._snippets = snippets;
const selections = model.pushEditOperations(this._editor.getSelections(), edits, undoEdits => {
this._editor.executeEdits('snippet', edits, undoEdits => {
if (this._snippets[0].hasPlaceholder) {
return this._move(true);
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
}
})!;
this._editor.setSelections(selections);
this._editor.revealRange(selections[0]);
});
this._editor.revealRange(this._editor.getSelections()[0]);
}
merge(template: string, options: ISnippetSessionInsertOptions = _defaultOptions): void {
@@ -513,8 +510,7 @@ export class SnippetSession {
this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]);
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText);
this._editor.setSelections(this._editor.getModel().pushEditOperations(this._editor.getSelections(), edits, undoEdits => {
this._editor.executeEdits('snippet', edits, undoEdits => {
for (const snippet of this._snippets) {
snippet.merge(snippets);
}
@@ -525,7 +521,7 @@ export class SnippetSession {
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
}
})!);
});
}
next(): void {

View File

@@ -4692,6 +4692,28 @@ suite('autoClosingPairs', () => {
mode.dispose();
});
test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'<div id'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
cursor.setSelections('test', [new Selection(1, 8, 1, 8)]);
cursor.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)]);
assert.strictEqual(model.getLineContent(1), '<div id=""');
cursorCommand(cursor, H.Type, { text: 'a' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), '<div id="a"');
cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard');
assert.strictEqual(model.getLineContent(1), '<div id="a"');
});
mode.dispose();
});
test('issue #15825: accents on mac US intl keyboard', () => {
let mode = new AutoClosingMode();
usingCursor({

2
src/vs/monaco.d.ts vendored
View File

@@ -4058,7 +4058,7 @@ declare namespace monaco.editor {
* @param edits The edits to execute.
* @param endCursorState Cursor state after the edits were applied.
*/
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean;
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean;
/**
* Execute multiple (concomitant) commands on the editor.
* @param source The source of the call.

View File

@@ -15,10 +15,18 @@ export class BrowserClipboardService implements IClipboardService {
private _internalResourcesClipboard: URI[] | undefined;
async writeText(text: string, type?: string): Promise<void> {
if (type) {
return; // TODO@sbatten
}
return navigator.clipboard.writeText(text);
}
async readText(type?: string): Promise<string> {
if (type) {
return ''; // TODO@sbatten
}
return navigator.clipboard.readText();
}

View File

@@ -5,8 +5,6 @@
import { UriComponents } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IStringDictionary } from 'vs/base/common/collections';
@@ -67,18 +65,6 @@ export interface IWorkspaceInformation extends IWorkspace {
telemetryId: string | undefined;
}
export const ID = 'diagnosticsService';
export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
export interface IDiagnosticsService {
_serviceBrand: any;
getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<PerformanceInfo>;
getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<SystemInfo>;
getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<string>;
reportWorkspaceStats(workspace: IWorkspaceInformation): Promise<void>;
}
export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError {
return !!x.hostName && !!x.errorMessage;
}

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IDiagnosticsService, IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsService } from './diagnosticsService';
import { Event } from 'vs/base/common/event';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as osLib from 'os';
import { virtualMachineHint } from 'vs/base/node/id';
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, IDiagnosticsService, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
import { readdir, stat, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError } from 'vs/base/common/json';
@@ -17,6 +17,19 @@ import { URI } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ID = 'diagnosticsService';
export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
export interface IDiagnosticsService {
_serviceBrand: any;
getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<PerformanceInfo>;
getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<SystemInfo>;
getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<string>;
reportWorkspaceStats(workspace: IWorkspaceInformation): Promise<void>;
}
export interface VersionInfo {
vscodeVersion: string;

View File

@@ -0,0 +1,192 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom';
import { coalesce } from 'vs/base/common/arrays';
import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
function serializeElement(element: Element, recursive: boolean): IElement {
const attributes = Object.create(null);
for (let j = 0; j < element.attributes.length; j++) {
const attr = element.attributes.item(j);
if (attr) {
attributes[attr.name] = attr.value;
}
}
const children: IElement[] = [];
if (recursive) {
for (let i = 0; i < element.children.length; i++) {
const child = element.children.item(i);
if (child) {
children.push(serializeElement(child, true));
}
}
}
const { left, top } = getTopLeftOffset(element as HTMLElement);
return {
tagName: element.tagName,
className: element.className,
textContent: element.textContent || '',
attributes,
children,
left,
top
};
}
export abstract class BaseWindowDriver implements IWindowDriver {
constructor() { }
abstract click(selector: string, xoffset?: number, yoffset?: number): Promise<void>;
abstract doubleClick(selector: string): Promise<void>;
async setValue(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
return Promise.reject(new Error(`Element not found: ${selector}`));
}
const inputElement = element as HTMLInputElement;
inputElement.value = text;
const event = new Event('input', { bubbles: true, cancelable: true });
inputElement.dispatchEvent(event);
}
async getTitle(): Promise<string> {
return document.title;
}
async isActiveElement(selector: string): Promise<boolean> {
const element = document.querySelector(selector);
if (element !== document.activeElement) {
const chain: string[] = [];
let el = document.activeElement;
while (el) {
const tagName = el.tagName;
const id = el.id ? `#${el.id}` : '';
const classes = coalesce(el.className.split(/\s+/g).map(c => c.trim())).map(c => `.${c}`).join('');
chain.unshift(`${tagName}${id}${classes}`);
el = el.parentElement;
}
throw new Error(`Active element not found. Current active element is '${chain.join(' > ')}'. Looking for ${selector}`);
}
return true;
}
async getElements(selector: string, recursive: boolean): Promise<IElement[]> {
const query = document.querySelectorAll(selector);
const result: IElement[] = [];
for (let i = 0; i < query.length; i++) {
const element = query.item(i);
result.push(serializeElement(element, recursive));
}
return result;
}
async getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> {
const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined;
return this._getElementXY(selector, offset);
}
async typeInEditor(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Editor not found: ${selector}`);
}
const textarea = element as HTMLTextAreaElement;
const start = textarea.selectionStart;
const newStart = start + text.length;
const value = textarea.value;
const newValue = value.substr(0, start) + text + value.substr(start);
textarea.value = newValue;
textarea.setSelectionRange(newStart, newStart);
const event = new Event('input', { 'bubbles': true, 'cancelable': true });
textarea.dispatchEvent(event);
}
async getTerminalBuffer(selector: string): Promise<string[]> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Terminal not found: ${selector}`);
}
const xterm = (element as any).xterm;
if (!xterm) {
throw new Error(`Xterm not found: ${selector}`);
}
const lines: string[] = [];
for (let i = 0; i < xterm.buffer.length; i++) {
lines.push(xterm.buffer.getLine(i)!.translateToString(true));
}
return lines;
}
async writeInTerminal(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Element not found: ${selector}`);
}
const xterm = (element as any).xterm;
if (!xterm) {
throw new Error(`Xterm not found: ${selector}`);
}
xterm._core._coreService.triggerDataEvent(text);
}
protected async _getElementXY(selector: string, offset?: { x: number, y: number }): Promise<{ x: number; y: number; }> {
const element = document.querySelector(selector);
if (!element) {
return Promise.reject(new Error(`Element not found: ${selector}`));
}
const { left, top } = getTopLeftOffset(element as HTMLElement);
const { width, height } = getClientArea(element as HTMLElement);
let x: number, y: number;
if (offset) {
x = left + offset.x;
y = top + offset.y;
} else {
x = left + (width / 2);
y = top + (height / 2);
}
x = Math.round(x);
y = Math.round(y);
return { x, y };
}
abstract async openDevTools(): Promise<void>;
}

View File

@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver';
class BrowserWindowDriver extends BaseWindowDriver {
click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void> {
throw new Error('Method not implemented.');
}
doubleClick(selector: string): Promise<void> {
throw new Error('Method not implemented.');
}
openDevTools(): Promise<void> {
throw new Error('Method not implemented.');
}
}
export async function registerWindowDriver(): Promise<IDisposable> {
(<any>window).driver = new BrowserWindowDriver();
return toDisposable(() => {
return { dispose: () => { } };
});
}

View File

@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
// !! Do not remove the following START and END markers, they are parsed by the smoketest build
//*START
export interface IElement {
tagName: string;
className: string;
textContent: string;
attributes: { [name: string]: string; };
children: IElement[];
top: number;
left: number;
}
export interface IDriver {
_serviceBrand: any;
getWindowIds(): Promise<number[]>;
capturePage(windowId: number): Promise<string>;
reloadWindow(windowId: number): Promise<void>;
exitApplication(): Promise<void>;
dispatchKeybinding(windowId: number, keybinding: string): Promise<void>;
click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
doubleClick(windowId: number, selector: string): Promise<void>;
setValue(windowId: number, selector: string, text: string): Promise<void>;
getTitle(windowId: number): Promise<string>;
isActiveElement(windowId: number, selector: string): Promise<boolean>;
getElements(windowId: number, selector: string, recursive?: boolean): Promise<IElement[]>;
getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>;
typeInEditor(windowId: number, selector: string, text: string): Promise<void>;
getTerminalBuffer(windowId: number, selector: string): Promise<string[]>;
writeInTerminal(windowId: number, selector: string, text: string): Promise<void>;
}
//*END
export const ID = 'driverService';
export const IDriver = createDecorator<IDriver>(ID);
export interface IWindowDriver {
click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
doubleClick(selector: string): Promise<void>;
setValue(selector: string, text: string): Promise<void>;
getTitle(): Promise<string>;
isActiveElement(selector: string): Promise<boolean>;
getElements(selector: string, recursive: boolean): Promise<IElement[]>;
getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>;
typeInEditor(selector: string, text: string): Promise<void>;
getTerminalBuffer(selector: string): Promise<string[]>;
writeInTerminal(selector: string, text: string): Promise<void>;
}

View File

@@ -4,55 +4,21 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom';
import * as electron from 'electron';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Terminal } from 'xterm';
import { timeout } from 'vs/base/common/async';
import { coalesce } from 'vs/base/common/arrays';
import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver';
function serializeElement(element: Element, recursive: boolean): IElement {
const attributes = Object.create(null);
for (let j = 0; j < element.attributes.length; j++) {
const attr = element.attributes.item(j);
if (attr) {
attributes[attr.name] = attr.value;
}
}
const children: IElement[] = [];
if (recursive) {
for (let i = 0; i < element.children.length; i++) {
const child = element.children.item(i);
if (child) {
children.push(serializeElement(child, true));
}
}
}
const { left, top } = getTopLeftOffset(element as HTMLElement);
return {
tagName: element.tagName,
className: element.className,
textContent: element.textContent || '',
attributes,
children,
left,
top
};
}
class WindowDriver implements IWindowDriver {
class WindowDriver extends BaseWindowDriver {
constructor(
@IWindowService private readonly windowService: IWindowService
) { }
) {
super();
}
click(selector: string, xoffset?: number, yoffset?: number): Promise<void> {
const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined;
@@ -63,31 +29,6 @@ class WindowDriver implements IWindowDriver {
return this._click(selector, 2);
}
private async _getElementXY(selector: string, offset?: { x: number, y: number }): Promise<{ x: number; y: number; }> {
const element = document.querySelector(selector);
if (!element) {
return Promise.reject(new Error(`Element not found: ${selector}`));
}
const { left, top } = getTopLeftOffset(element as HTMLElement);
const { width, height } = getClientArea(element as HTMLElement);
let x: number, y: number;
if (offset) {
x = left + offset.x;
y = top + offset.y;
} else {
x = left + (width / 2);
y = top + (height / 2);
}
x = Math.round(x);
y = Math.round(y);
return { x, y };
}
private async _click(selector: string, clickCount: number, offset?: { x: number, y: number }): Promise<void> {
const { x, y } = await this._getElementXY(selector, offset);
@@ -99,116 +40,6 @@ class WindowDriver implements IWindowDriver {
await timeout(100);
}
async setValue(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
return Promise.reject(new Error(`Element not found: ${selector}`));
}
const inputElement = element as HTMLInputElement;
inputElement.value = text;
const event = new Event('input', { bubbles: true, cancelable: true });
inputElement.dispatchEvent(event);
}
async getTitle(): Promise<string> {
return document.title;
}
async isActiveElement(selector: string): Promise<boolean> {
const element = document.querySelector(selector);
if (element !== document.activeElement) {
const chain: string[] = [];
let el = document.activeElement;
while (el) {
const tagName = el.tagName;
const id = el.id ? `#${el.id}` : '';
const classes = coalesce(el.className.split(/\s+/g).map(c => c.trim())).map(c => `.${c}`).join('');
chain.unshift(`${tagName}${id}${classes}`);
el = el.parentElement;
}
throw new Error(`Active element not found. Current active element is '${chain.join(' > ')}'. Looking for ${selector}`);
}
return true;
}
async getElements(selector: string, recursive: boolean): Promise<IElement[]> {
const query = document.querySelectorAll(selector);
const result: IElement[] = [];
for (let i = 0; i < query.length; i++) {
const element = query.item(i);
result.push(serializeElement(element, recursive));
}
return result;
}
async typeInEditor(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Editor not found: ${selector}`);
}
const textarea = element as HTMLTextAreaElement;
const start = textarea.selectionStart;
const newStart = start + text.length;
const value = textarea.value;
const newValue = value.substr(0, start) + text + value.substr(start);
textarea.value = newValue;
textarea.setSelectionRange(newStart, newStart);
const event = new Event('input', { 'bubbles': true, 'cancelable': true });
textarea.dispatchEvent(event);
}
async getTerminalBuffer(selector: string): Promise<string[]> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Terminal not found: ${selector}`);
}
const xterm: Terminal = (element as any).xterm;
if (!xterm) {
throw new Error(`Xterm not found: ${selector}`);
}
const lines: string[] = [];
for (let i = 0; i < xterm.buffer.length; i++) {
lines.push(xterm.buffer.getLine(i)!.translateToString(true));
}
return lines;
}
async writeInTerminal(selector: string, text: string): Promise<void> {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`Element not found: ${selector}`);
}
const xterm: Terminal = (element as any).xterm;
if (!xterm) {
throw new Error(`Xterm not found: ${selector}`);
}
xterm._core._coreService.triggerDataEvent(text);
}
async openDevTools(): Promise<void> {
await this.windowService.openDevTools({ mode: 'detach' });
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDriver, DriverChannel, IElement, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver';
import { DriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IDriverOptions } from 'vs/platform/driver/node/driver';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
@@ -17,6 +17,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
import { timeout } from 'vs/base/common/async';
import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
function isSilentKeyCode(keyCode: KeyCode) {
return keyCode < KeyCode.KEY_0;
@@ -163,6 +164,11 @@ export class Driver implements IDriver, IWindowDriverRegistry {
return await windowDriver.getElements(selector, recursive);
}
async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> {
const windowDriver = await this.getWindowDriver(windowId);
return await windowDriver.getElementXY(selector, xoffset, yoffset);
}
async typeInEditor(windowId: number, selector: string, text: string): Promise<void> {
const windowDriver = await this.getWindowDriver(windowId);
await windowDriver.typeInEditor(selector, text);

View File

@@ -5,45 +5,9 @@
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
export const ID = 'driverService';
export const IDriver = createDecorator<IDriver>(ID);
// !! Do not remove the following START and END markers, they are parsed by the smoketest build
//*START
export interface IElement {
tagName: string;
className: string;
textContent: string;
attributes: { [name: string]: string; };
children: IElement[];
top: number;
left: number;
}
export interface IDriver {
_serviceBrand: any;
getWindowIds(): Promise<number[]>;
capturePage(windowId: number): Promise<string>;
reloadWindow(windowId: number): Promise<void>;
exitApplication(): Promise<void>;
dispatchKeybinding(windowId: number, keybinding: string): Promise<void>;
click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
doubleClick(windowId: number, selector: string): Promise<void>;
setValue(windowId: number, selector: string, text: string): Promise<void>;
getTitle(windowId: number): Promise<string>;
isActiveElement(windowId: number, selector: string): Promise<boolean>;
getElements(windowId: number, selector: string, recursive?: boolean): Promise<IElement[]>;
typeInEditor(windowId: number, selector: string, text: string): Promise<void>;
getTerminalBuffer(windowId: number, selector: string): Promise<string[]>;
writeInTerminal(windowId: number, selector: string, text: string): Promise<void>;
}
//*END
import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
export class DriverChannel implements IServerChannel {
@@ -66,6 +30,7 @@ export class DriverChannel implements IServerChannel {
case 'getTitle': return this.driver.getTitle(arg[0]);
case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]);
case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]);
case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]);
@@ -125,6 +90,10 @@ export class DriverChannelClient implements IDriver {
return this.channel.call('getElements', [windowId, selector, recursive]);
}
getElementXY(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise<{ x: number, y: number }> {
return this.channel.call('getElementXY', [windowId, selector, xoffset, yoffset]);
}
typeInEditor(windowId: number, selector: string, text: string): Promise<void> {
return this.channel.call('typeInEditor', [windowId, selector, text]);
}
@@ -180,18 +149,6 @@ export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry
}
}
export interface IWindowDriver {
click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
doubleClick(selector: string): Promise<void>;
setValue(selector: string, text: string): Promise<void>;
getTitle(): Promise<string>;
isActiveElement(selector: string): Promise<boolean>;
getElements(selector: string, recursive: boolean): Promise<IElement[]>;
typeInEditor(selector: string, text: string): Promise<void>;
getTerminalBuffer(selector: string): Promise<string[]>;
writeInTerminal(selector: string, text: string): Promise<void>;
}
export class WindowDriverChannel implements IServerChannel {
constructor(private driver: IWindowDriver) { }
@@ -208,6 +165,7 @@ export class WindowDriverChannel implements IServerChannel {
case 'getTitle': return this.driver.getTitle();
case 'isActiveElement': return this.driver.isActiveElement(arg);
case 'getElements': return this.driver.getElements(arg[0], arg[1]);
case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]);
@@ -247,6 +205,10 @@ export class WindowDriverChannelClient implements IWindowDriver {
return this.channel.call('getElements', [selector, recursive]);
}
getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number, y: number }> {
return this.channel.call('getElementXY', [selector, xoffset, yoffset]);
}
typeInEditor(selector: string, text: string): Promise<void> {
return this.channel.call('typeInEditor', [selector, text]);
}

View File

@@ -392,10 +392,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IProductService private readonly productService: IProductService,
@optional(IStorageService) private readonly storageService: IStorageService,
) {
const config = productService.productConfiguration.extensionsGallery;
const config = productService.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
this.extensionsControlUrl = config && config.controlUrl;
this.commonHeadersPromise = resolveMarketplaceHeaders(productService.productConfiguration.version, this.environmentService, this.fileService, this.storageService);
this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService, this.storageService);
}
private api(path = ''): string {
@@ -440,7 +440,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0];
if (versionAsset) {
const extension = toExtension(rawExtension, versionAsset, 0, query);
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) {
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) {
return extension;
}
}
@@ -788,7 +788,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
if (galleryExtensions.length) {
if (compatible) {
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.productConfiguration.version) ? v : null)))
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null)))
.then(versions => versions
.filter(v => !!v)
.map(v => ({ version: v!.version, date: v!.lastUpdated })));
@@ -874,7 +874,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (!engine) {
return null;
}
if (isEngineValid(engine, this.productService.productConfiguration.version)) {
if (isEngineValid(engine, this.productService.version)) {
return Promise.resolve(version);
}
}
@@ -906,7 +906,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const version = versions[0];
return this.getEngine(version)
.then(engine => {
if (!isEngineValid(engine, this.productService.productConfiguration.version)) {
if (!isEngineValid(engine, this.productService.version)) {
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}

View File

@@ -620,26 +620,26 @@ export interface IReadFileOptions {
* that have been read already with the same etag.
* It is the task of the caller to makes sure to handle this error case from the promise.
*/
etag?: string;
readonly etag?: string;
/**
* Is an integer specifying where to begin reading from in the file. If position is null,
* data will be read from the current file position.
*/
position?: number;
readonly position?: number;
/**
* Is an integer specifying how many bytes to read from the file. By default, all bytes
* will be read.
*/
length?: number;
readonly length?: number;
/**
* If provided, the size of the file will be checked against the limits.
*/
limits?: {
size?: number;
memory?: number;
readonly size?: number;
readonly memory?: number;
};
}
@@ -648,12 +648,12 @@ export interface IWriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
mtime?: number;
readonly mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
etag?: string;
readonly etag?: string;
}
export interface IResolveFileOptions {
@@ -662,22 +662,22 @@ export interface IResolveFileOptions {
* Automatically continue resolving children of a directory until the provided resources
* are found.
*/
resolveTo?: URI[];
readonly resolveTo?: readonly URI[];
/**
* Automatically continue resolving children of a directory if the number of children is 1.
*/
resolveSingleChildDescendants?: boolean;
readonly resolveSingleChildDescendants?: boolean;
/**
* Will resolve mtime, size and etag of files if enabled. This can have a negative impact
* on performance and thus should only be used when these values are required.
*/
resolveMetadata?: boolean;
readonly resolveMetadata?: boolean;
}
export interface IResolveMetadataFileOptions extends IResolveFileOptions {
resolveMetadata: true;
readonly resolveMetadata: true;
}
export interface ICreateFileOptions {
@@ -686,7 +686,7 @@ export interface ICreateFileOptions {
* Overwrite the file to create if it already exists on disk. Otherwise
* an error will be thrown (FILE_MODIFIED_SINCE).
*/
overwrite?: boolean;
readonly overwrite?: boolean;
}
export class FileOperationError extends Error {

View File

@@ -134,7 +134,7 @@ export class OutOfProcessWin32FolderWatcher {
public dispose(): void {
if (this.handle) {
this.handle.kill();
this.handle = null!; // StrictNullOverride: nulling out ok in dispose
this.handle = undefined;
}
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -6,10 +6,11 @@
import { localize } from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { BrowserWindow, ipcMain, screen, Event, dialog } from 'electron';
import { ILaunchService } from 'vs/platform/launch/electron-main/launchService';
import { PerformanceInfo, IDiagnosticsService, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';

View File

@@ -5,7 +5,7 @@
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { IIssueService } from 'vs/platform/issue/node/issue';
export class IssueChannel implements IServerChannel {

View File

@@ -19,7 +19,7 @@ import { BrowserWindow, ipcMain, Event as IpcEvent, app } 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, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launchService';
export const ID = 'launchService';

View File

@@ -1,27 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
export class ProductService implements IProductService {
_serviceBrand!: ServiceIdentifier<IProductService>;
readonly productConfiguration: IProductConfiguration;
constructor() {
const element = document.getElementById('vscode-remote-product-configuration');
this.productConfiguration = {
...element ? JSON.parse(element.getAttribute('data-settings')!) : {
version: '1.38.0-unknown',
nameLong: 'Unknown',
extensionAllowedProposedApi: [],
}, ...{ urlProtocol: '', enableTelemetry: false },
...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion
};
}
}

View File

@@ -3,19 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IProductService = createDecorator<IProductService>('productService');
export interface IProductService {
export interface IProductService extends Readonly<IProductConfiguration> {
_serviceBrand: ServiceIdentifier<any>;
_serviceBrand: undefined;
readonly productConfiguration: IProductConfiguration;
}
export interface IProductConfiguration {
readonly version: string;
version: string;
nameShort: string;
nameLong: string;
readonly applicationName: string;
@@ -51,7 +50,6 @@ export interface IProductConfiguration {
readonly extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly exeBasedExtensionTips: { [id: string]: IExeBasedExtensionTip; };
readonly extensionKeywords: { [extension: string]: readonly string[]; };
readonly extensionAllowedBadgeProviders: readonly string[];
readonly extensionAllowedProposedApi: readonly string[];
readonly keymapExtensionTips: readonly string[];
readonly crashReporter: {

View File

@@ -6,6 +6,7 @@
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { IProductConfiguration } from 'vs/platform/product/common/product';
import pkg from 'vs/platform/product/node/package';
const rootPath = path.dirname(getPathFromAmdModule(require, ''));
const productJsonPath = path.join(rootPath, 'product.json');
@@ -17,4 +18,6 @@ if (process.env['VSCODE_DEV']) {
product.dataFolderName += '-dev';
}
product.version = pkg.version;
export default product;

View File

@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
export class ProductService implements IProductService {
_serviceBrand!: ServiceIdentifier<IProductService>;
readonly productConfiguration: IProductConfiguration;
constructor() {
this.productConfiguration = {
...product, ...{ version: pkg.version }, ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion}
};
}
}

View File

@@ -9,6 +9,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
export interface IRemoteAgentEnvironment {
pid: number;
connectionToken: string;
appRoot: URI;
appSettingsHome: URI;
settingsPath: URI;

View File

@@ -6,6 +6,7 @@
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ipcRenderer as ipc } from 'electron';
import * as errors from 'vs/base/common/errors';
import { RemoteAuthorities } from 'vs/base/common/network';
class PendingResolveAuthorityRequest {
constructor(
@@ -50,6 +51,7 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
if (this._resolveAuthorityRequests[resolvedAuthority.authority]) {
let request = this._resolveAuthorityRequests[resolvedAuthority.authority];
ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority);
RemoteAuthorities.set(resolvedAuthority.authority, resolvedAuthority.host, resolvedAuthority.port);
request.resolve({ authority: resolvedAuthority, options });
}
}

View File

@@ -96,12 +96,12 @@ export interface IWindowsService {
_serviceBrand: any;
onWindowOpen: Event<number>;
onWindowFocus: Event<number>;
onWindowBlur: Event<number>;
onWindowMaximize: Event<number>;
onWindowUnmaximize: Event<number>;
onRecentlyOpenedChange: Event<void>;
readonly onWindowOpen: Event<number>;
readonly onWindowFocus: Event<number>;
readonly onWindowBlur: Event<number>;
readonly onWindowMaximize: Event<number>;
readonly onWindowUnmaximize: Event<number>;
readonly onRecentlyOpenedChange: Event<void>;
// Dialogs
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;

View File

@@ -12,14 +12,14 @@ import { IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/commo
export class WindowsChannel implements IServerChannel {
private onWindowOpen: Event<number>;
private onWindowFocus: Event<number>;
private onWindowBlur: Event<number>;
private onWindowMaximize: Event<number>;
private onWindowUnmaximize: Event<number>;
private onRecentlyOpenedChange: Event<void>;
private readonly onWindowOpen: Event<number>;
private readonly onWindowFocus: Event<number>;
private readonly onWindowBlur: Event<number>;
private readonly onWindowMaximize: Event<number>;
private readonly onWindowUnmaximize: Event<number>;
private readonly onRecentlyOpenedChange: Event<void>;
constructor(private service: IWindowsService) {
constructor(private readonly service: IWindowsService) {
this.onWindowOpen = Event.buffer(service.onWindowOpen, true);
this.onWindowFocus = Event.buffer(service.onWindowFocus, true);
this.onWindowBlur = Event.buffer(service.onWindowBlur, true);

View File

@@ -17,6 +17,7 @@ import { URI } from 'vs/base/common/uri';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { isWindows } from 'vs/base/common/platform';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { dirname, joinPath } from 'vs/base/common/resources';
suite('WorkspacesMainService', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice');
@@ -51,13 +52,13 @@ suite('WorkspacesMainService', () => {
let service: TestWorkspacesMainService;
setup(() => {
setup(async () => {
service = new TestWorkspacesMainService(environmentService, logService);
// Delete any existing backups completely and then re-create it.
return pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE).then(() => {
return pfs.mkdirp(untitledWorkspacesHomePath);
});
await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE);
return pfs.mkdirp(untitledWorkspacesHomePath);
});
teardown(() => {
@@ -77,57 +78,50 @@ suite('WorkspacesMainService', () => {
assert.equal(u1.toString(), u2.toString());
}
test('createWorkspace (folders)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
test('createWorkspace (folders)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
assert.equal(ws.folders.length, 2); //
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
});
const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
assert.equal(ws.folders.length, 2);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
});
test('createWorkspace (folders with name)', () => {
return createWorkspace([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']).then(workspace => {
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
test('createWorkspace (folders with name)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']);
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
assert.equal(ws.folders.length, 2); //
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
assert.equal((<IRawFileWorkspaceFolder>ws.folders[0]).name, 'currentworkingdirectory');
assert.equal((<IRawFileWorkspaceFolder>ws.folders[1]).name, 'tempdir');
});
const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
assert.equal(ws.folders.length, 2);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
assert.equal((<IRawFileWorkspaceFolder>ws.folders[0]).name, 'currentworkingdirectory');
assert.equal((<IRawFileWorkspaceFolder>ws.folders[1]).name, 'tempdir');
});
test('createUntitledWorkspace (folders as other resource URIs)', () => {
test('createUntitledWorkspace (folders as other resource URIs)', async () => {
const folder1URI = URI.parse('myscheme://server/work/p/f1');
const folder2URI = URI.parse('myscheme://server/work/o/f3');
return service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server').then(workspace => {
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
const workspace = await service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server');
assert.ok(workspace);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
assert.ok(service.isUntitledWorkspace(workspace));
const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
assert.equal(ws.folders.length, 2);
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, folder1URI.toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, folder2URI.toString(true));
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
assert.equal(ws.remoteAuthority, 'server');
});
const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
assert.equal(ws.folders.length, 2);
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, folder1URI.toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, folder2URI.toString(true));
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
assert.equal(ws.remoteAuthority, 'server');
});
test('createWorkspaceSync (folders)', () => {
@@ -178,145 +172,130 @@ suite('WorkspacesMainService', () => {
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
});
test('resolveWorkspaceSync', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
assert.ok(service.resolveLocalWorkspaceSync(workspace.configPath));
test('resolveWorkspaceSync', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
assert.ok(service.resolveLocalWorkspaceSync(workspace.configPath));
// make it a valid workspace path
const newPath = path.join(path.dirname(workspace.configPath.fsPath), `workspace.${WORKSPACE_EXTENSION}`);
fs.renameSync(workspace.configPath.fsPath, newPath);
workspace.configPath = URI.file(newPath);
// make it a valid workspace path
const newPath = path.join(path.dirname(workspace.configPath.fsPath), `workspace.${WORKSPACE_EXTENSION}`);
fs.renameSync(workspace.configPath.fsPath, newPath);
workspace.configPath = URI.file(newPath);
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assert.equal(2, resolved!.folders.length);
assertEqualURI(resolved!.configPath, workspace.configPath);
assert.ok(resolved!.id);
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assert.equal(2, resolved!.folders.length);
assertEqualURI(resolved!.configPath, workspace.configPath);
assert.ok(resolved!.id);
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ something: 'something' })); // invalid workspace
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ something: 'something' })); // invalid workspace
const resolvedInvalid = service.resolveLocalWorkspaceSync(workspace.configPath);
assert.ok(!resolvedInvalid);
});
const resolvedInvalid = service.resolveLocalWorkspaceSync(workspace.configPath);
assert.ok(!resolvedInvalid);
});
test('resolveWorkspaceSync (support relative paths)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] }));
test('resolveWorkspaceSync (support relative paths)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] }));
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
test('resolveWorkspaceSync (support relative paths #2)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] }));
test('resolveWorkspaceSync (support relative paths #2)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] }));
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')));
});
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')));
});
test('resolveWorkspaceSync (support relative paths #3)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] }));
test('resolveWorkspaceSync (support relative paths #3)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] }));
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
test('resolveWorkspaceSync (support invalid JSON via fault tolerant parsing)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
fs.writeFileSync(workspace.configPath.fsPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma
test('resolveWorkspaceSync (support invalid JSON via fault tolerant parsing)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
fs.writeFileSync(workspace.configPath.fsPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
test('rewriteWorkspaceFileForNewLocation', () => {
test('rewriteWorkspaceFileForNewLocation', async () => {
const folder1 = process.cwd(); // absolute path because outside of tmpDir
const tmpDir = os.tmpdir();
const tmpInsideDir = path.join(os.tmpdir(), 'inside');
return createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]).then(workspace => {
const origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
const workspace = await createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]);
const origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
let origConfigPath = workspace.configPath;
let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace'));
let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath);
let origConfigPath = workspace.configPath;
let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace'));
let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath);
let ws = (JSON.parse(newContent) as IStoredWorkspace);
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1); // absolute path because outside of tmpdir
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, '.');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'somefolder');
let ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1); // absolute path because outside of tmpdir
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, '.');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'somefolder');
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = (JSON.parse(newContent) as IStoredWorkspace);
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, 'inside');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder');
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = (JSON.parse(newContent) as IStoredWorkspace);
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, tmpInsideDir);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder'));
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, 'inside');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder');
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace');
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = (JSON.parse(newContent) as IStoredWorkspace);
assert.equal(ws.folders.length, 3);
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, URI.file(folder1).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true));
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, tmpInsideDir);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder'));
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace');
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, URI.file(folder1).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true));
service.deleteUntitledWorkspaceSync(workspace);
});
service.deleteUntitledWorkspaceSync(workspace);
});
test('rewriteWorkspaceFileForNewLocation (preserves comments)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
test('rewriteWorkspaceFileForNewLocation (preserves comments)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]);
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = `// this is a comment\n${origContent}`;
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = `// this is a comment\n${origContent}`;
let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
assert.equal(0, newContent.indexOf('// this is a comment'));
service.deleteUntitledWorkspaceSync(workspace);
});
let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
assert.equal(0, newContent.indexOf('// this is a comment'));
service.deleteUntitledWorkspaceSync(workspace);
});
test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash
test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]);
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash
const ws = JSON.parse(newContent) as IStoredWorkspace;
assert.ok(ws.folders.every(f => (<IRawFileWorkspaceFolder>f).path.indexOf('\\') < 0));
service.deleteUntitledWorkspaceSync(workspace);
});
const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
const ws = (JSON.parse(newContent) as IStoredWorkspace);
assert.ok(ws.folders.every(f => (<IRawFileWorkspaceFolder>f).path.indexOf('\\') < 0));
service.deleteUntitledWorkspaceSync(workspace);
});
test('rewriteWorkspaceFileForNewLocation (unc paths)', () => {
test('rewriteWorkspaceFileForNewLocation (unc paths)', async () => {
if (!isWindows) {
return Promise.resolve();
}
@@ -326,68 +305,58 @@ suite('WorkspacesMainService', () => {
const folder2Location = '\\\\server\\share2\\some\\path';
const folder3Location = path.join(os.tmpdir(), 'wsloc', 'inner', 'more');
return createWorkspace([folder1Location, folder2Location, folder3Location]).then(workspace => {
const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
const workspace = await createWorkspace([folder1Location, folder2Location, folder3Location]);
const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
const ws = (JSON.parse(newContent) as IStoredWorkspace);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, folder2Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'inner\\more');
const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
const ws = JSON.parse(newContent) as IStoredWorkspace;
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, folder2Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'inner\\more');
service.deleteUntitledWorkspaceSync(workspace);
});
service.deleteUntitledWorkspaceSync(workspace);
});
test('deleteUntitledWorkspaceSync (untitled)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
assert.ok(fs.existsSync(workspace.configPath.fsPath));
service.deleteUntitledWorkspaceSync(workspace);
assert.ok(!fs.existsSync(workspace.configPath.fsPath));
});
test('deleteUntitledWorkspaceSync (untitled)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
assert.ok(fs.existsSync(workspace.configPath.fsPath));
service.deleteUntitledWorkspaceSync(workspace);
assert.ok(!fs.existsSync(workspace.configPath.fsPath));
});
test('deleteUntitledWorkspaceSync (saved)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
service.deleteUntitledWorkspaceSync(workspace);
});
test('deleteUntitledWorkspaceSync (saved)', async () => {
const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
service.deleteUntitledWorkspaceSync(workspace);
});
test('getUntitledWorkspaceSync', () => {
test('getUntitledWorkspaceSync', async () => {
let untitled = service.getUntitledWorkspacesSync();
assert.equal(untitled.length, 0);
return createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => {
assert.ok(fs.existsSync(untitledOne.configPath.fsPath));
const untitledOne = await createWorkspace([process.cwd(), os.tmpdir()]);
assert.ok(fs.existsSync(untitledOne.configPath.fsPath));
untitled = service.getUntitledWorkspacesSync();
untitled = service.getUntitledWorkspacesSync();
assert.equal(1, untitled.length);
assert.equal(untitledOne.id, untitled[0].workspace.id);
assert.equal(1, untitled.length);
assert.equal(untitledOne.id, untitled[0].workspace.id);
const untitledTwo = await createWorkspace([os.tmpdir(), process.cwd()]);
assert.ok(fs.existsSync(untitledTwo.configPath.fsPath));
assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`);
untitled = service.getUntitledWorkspacesSync();
assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`);
if (untitled.length === 1) {
const untitledHome = dirname(dirname(untitledTwo.configPath));
assert.fail(`Unexpected workspaces count of 1 (expected 2), all workspaces:\n ${fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8'))}`);
}
assert.equal(2, untitled.length);
return createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => {
assert.ok(fs.existsSync(untitledTwo.configPath.fsPath));
service.deleteUntitledWorkspaceSync(untitledOne);
untitled = service.getUntitledWorkspacesSync();
assert.equal(1, untitled.length);
untitled = service.getUntitledWorkspacesSync();
if (untitled.length === 1) {
assert.fail('Unexpected workspaces count, contents:\n' + fs.readFileSync(untitledTwo.configPath.fsPath, 'utf8'));
}
assert.equal(2, untitled.length);
service.deleteUntitledWorkspaceSync(untitledOne);
untitled = service.getUntitledWorkspacesSync();
assert.equal(1, untitled.length);
service.deleteUntitledWorkspaceSync(untitledTwo);
untitled = service.getUntitledWorkspacesSync();
assert.equal(0, untitled.length);
});
});
service.deleteUntitledWorkspaceSync(untitledTwo);
untitled = service.getUntitledWorkspacesSync();
assert.equal(0, untitled.length);
});
});

View File

@@ -565,22 +565,6 @@ declare module 'vscode' {
//#endregion
//#region Joh: onDidExecuteCommand
export interface CommandExecutionEvent {
command: string;
arguments: any[];
}
export namespace commands {
/**
* An event that is emitted when a [command](#Command) is executed.
*/
export const onDidExecuteCommand: Event<CommandExecutionEvent>;
}
//#endregion
//#region Joh: decorations
//todo@joh -> make class
@@ -1046,6 +1030,15 @@ declare module 'vscode' {
*/
constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState);
}
export interface TreeViewOptions2<T> extends TreeViewOptions<T> {
/**
* Whether the tree supports multi-select. When the tree supports multi-select and a command is executed from the tree,
* the first argument to the command is the tree item that the command was executed on and the second argument is an
* array containing the other selected tree items.
*/
canSelectMany?: boolean;
}
//#endregion
//#region CustomExecution
@@ -1168,11 +1161,11 @@ declare module 'vscode' {
/**
* Content security policy source for webview resources.
*
* This is origin used in a content security policy rule:
* This is the origin that should be used in a content security policy rule:
*
* ```
* img-src https: ${webview.cspSource} ...;
* ````
* ```
*/
readonly cspSource: string;
}

View File

@@ -90,7 +90,6 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
const webview = this._webviewService.createWebview('' + handle, {
enableFindWidget: false,
allowSvgs: false,
extension: { id: extensionId, location: URI.revive(extensionLocation) }
}, {
allowScripts: options.enableScripts,

View File

@@ -15,7 +15,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _commandRegistrations = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
private _onDidExecuteCommandListener?: IDisposable;
constructor(
extHostContext: IExtHostContext,
@@ -78,19 +77,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
return this._commandService.executeCommand<T>(id, ...args);
}
$registerCommandListener() {
if (!this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener = this._commandService.onDidExecuteCommand(command => this._proxy.$handleDidExecuteCommand(command));
}
}
$unregisterCommandListener() {
if (this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener.dispose();
this._onDidExecuteCommandListener = undefined;
}
}
$getCommands(): Promise<string[]> {
return Promise.resolve([...CommandsRegistry.getCommands().keys()]);
}

View File

@@ -5,49 +5,33 @@
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
private readonly _credentialsService?: ICredentialsService;
constructor(
_extHostContext: IExtHostContext,
@optional(ICredentialsService) credentialsService: ICredentialsService,
) {
this._credentialsService = credentialsService;
@ICredentialsService private readonly _credentialsService: ICredentialsService,
) { }
async $getPassword(service: string, account: string): Promise<string | null> {
return this._credentialsService.getPassword(service, account);
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
return this._credentialsService.setPassword(service, account, password);
}
async $deletePassword(service: string, account: string): Promise<boolean> {
return this._credentialsService.deletePassword(service, account);
}
async $findPassword(service: string): Promise<string | null> {
return this._credentialsService.findPassword(service);
}
dispose(): void {
//
}
async $getPassword(service: string, account: string): Promise<string | null> {
if (this._credentialsService) {
return this._credentialsService.getPassword(service, account);
}
return null;
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
if (this._credentialsService) {
return this._credentialsService.setPassword(service, account, password);
}
}
async $deletePassword(service: string, account: string): Promise<boolean> {
if (this._credentialsService) {
return this._credentialsService.deletePassword(service, account);
}
return false;
}
async $findPassword(service: string): Promise<string | null> {
if (this._credentialsService) {
return this._credentialsService.findPassword(service);
}
return null;
}
}

View File

@@ -27,13 +27,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
viewer.canSelectMany = !!options.canSelectMany;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {

View File

@@ -327,7 +327,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
if (this._productService.productConfiguration.urlProtocol === link.scheme) {
if (this._productService.urlProtocol === link.scheme) {
return true;
}
return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command';

View File

@@ -151,7 +151,7 @@ const viewsContribution: IJSONSchema = {
default: []
},
'remote': {
description: localize('views.remote', "Contributes views to Remote container in the Activity bar"),
description: localize('views.remote', "Contributes views to Remote container in the Activity bar. To contribute to this container, enableProposedApi needs to be turned on"),
type: 'array',
items: nestableViewDescriptor,
default: []
@@ -387,6 +387,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return;
}
if (entry.key === 'remote' && !extension.description.enableProposedApi) {
collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key));
return;
}
const viewContainer = this.getViewContainer(entry.key);
if (!viewContainer) {
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));

View File

@@ -225,11 +225,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
},
onDidExecuteCommand: proposedApiFunction(extension, (listener, thisArgs?, disposables?) => {
checkProposedApiEnabled(extension);
return extHostCommands.onDidExecuteCommand(listener, thisArgs, disposables);
}),
}
};
// namespace: env

View File

@@ -21,7 +21,7 @@ import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model'
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -119,8 +119,6 @@ export interface MainThreadClipboardShape extends IDisposable {
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
$registerCommandListener(): void;
$unregisterCommandListener(): void;
$unregisterCommand(id: string): void;
$executeCommand<T>(id: string, args: any[]): Promise<T | undefined>;
$getCommands(): Promise<string[]>;
@@ -246,7 +244,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void;
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void>;
$setMessage(treeViewId: string, message: string): void;
@@ -739,7 +737,6 @@ export interface MainThreadWindowShape extends IDisposable {
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
$handleDidExecuteCommand(command: ICommandEvent): void;
}
export interface ExtHostConfigurationShape {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { validateConstraint } from 'vs/base/common/types';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
@@ -17,7 +17,6 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
@@ -36,9 +35,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
readonly _serviceBrand: any;
private readonly _onDidExecuteCommand: Emitter<vscode.CommandExecutionEvent>;
readonly onDidExecuteCommand: Event<vscode.CommandExecutionEvent>;
private readonly _commands = new Map<string, CommandHandler>();
private readonly _proxy: MainThreadCommandsShape;
private readonly _converter: CommandsConverter;
@@ -50,11 +46,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
@ILogService logService: ILogService
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands);
this._onDidExecuteCommand = new Emitter<vscode.CommandExecutionEvent>({
onFirstListenerDidAdd: () => this._proxy.$registerCommandListener(),
onLastListenerRemove: () => this._proxy.$unregisterCommandListener(),
});
this.onDidExecuteCommand = Event.filter(this._onDidExecuteCommand.event, e => e.command[0] !== '_'); // filter 'private' commands
this._logService = logService;
this._converter = new CommandsConverter(this);
this._argumentProcessors = [
@@ -119,22 +110,13 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}
$handleDidExecuteCommand(command: ICommandEvent): void {
this._onDidExecuteCommand.fire({
command: command.commandId,
arguments: command.args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg))
});
}
executeCommand<T>(id: string, ...args: any[]): Promise<T> {
this._logService.trace('ExtHostCommands#executeCommand', id);
if (this._commands.has(id)) {
// we stay inside the extension host and support
// to pass any kind of parameters around
const res = this._executeContributedCommand<T>(id, args);
this._onDidExecuteCommand.fire({ command: id, arguments: args });
return res;
return this._executeContributedCommand<T>(id, args);
} else {
// automagically convert some argument types

View File

@@ -5,14 +5,22 @@
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { MainThreadKeytarShape, IEnvironment, MainThreadWindowShape, MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { endsWith } from 'vs/base/common/strings';
import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { platform } from 'vs/base/common/process';
import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/common/extHostRequireInterceptor'; // {{SQL CARBON EDIT}}
import { IExtensionApiFactory as sqlIApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}}
interface LoadFunction {
@@ -21,40 +29,45 @@ interface LoadFunction {
export interface INodeModuleFactory { //{{SQL CARBON EDIT}} export interface
readonly nodeModuleName: string | string[];
load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any;
alternaiveModuleName?(name: string): string | undefined;
load(request: string, parent: URI, isMain: any, original: LoadFunction): any;
alternativeModuleName?(name: string): string | undefined;
}
export class NodeModuleRequireInterceptor {
public static INSTANCE = new NodeModuleRequireInterceptor();
export abstract class RequireInterceptor {
private readonly _factories: Map<string, INodeModuleFactory>;
private readonly _alternatives: ((moduleName: string) => string | undefined)[];
protected readonly _factories: Map<string, INodeModuleFactory>;
protected readonly _alternatives: ((moduleName: string) => string | undefined)[];
constructor() {
constructor(
private _apiFactory: sqlIApiFactory, // {{SQL CARBON EDIT}} replace with ours
private _extensionRegistry: ExtensionDescriptionRegistry,
@IInstantiationService private readonly _instaService: IInstantiationService,
@IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration,
@IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService,
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService
) {
this._factories = new Map<string, INodeModuleFactory>();
this._alternatives = [];
this._installInterceptor(this._factories, this._alternatives);
}
private _installInterceptor(factories: Map<string, INodeModuleFactory>, alternatives: ((moduleName: string) => string | undefined)[]): void {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (!factories.has(request)) {
return original.apply(this, arguments);
}
return factories.get(request)!.load(request, parent, isMain, original);
};
async install(): Promise<void> {
this._installInterceptor();
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
this.register(new VSCodeNodeModuleFactory(this._apiFactory.vscode, extensionPaths, this._extensionRegistry, configProvider)); // {{SQL CARBON EDIT}} // add node module
this.register(new AzdataNodeModuleFactory(this._apiFactory.azdata, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
this.register(new SqlopsNodeModuleFactory(this._apiFactory.sqlops, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
this.register(this._instaService.createInstance(KeytarNodeModuleFactory));
if (this._initData.remote.isRemote) {
this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths));
}
}
protected abstract _installInterceptor(): void;
public register(interceptor: INodeModuleFactory): void {
if (Array.isArray(interceptor.nodeModuleName)) {
for (let moduleName of interceptor.nodeModuleName) {
@@ -63,15 +76,17 @@ export class NodeModuleRequireInterceptor {
} else {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
if (typeof interceptor.alternaiveModuleName === 'function') {
if (typeof interceptor.alternativeModuleName === 'function') {
this._alternatives.push((moduleName) => {
return interceptor.alternaiveModuleName!(moduleName);
return interceptor.alternativeModuleName!(moduleName);
});
}
}
}
export class VSCodeNodeModuleFactory implements INodeModuleFactory {
//#region --- vscode-module
class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map<string, typeof vscode>();
@@ -85,10 +100,10 @@ export class VSCodeNodeModuleFactory implements INodeModuleFactory {
) {
}
public load(request: string, parent: { filename: string; }): any {
public load(_request: string, parent: URI): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const ext = this._extensionPaths.findSubstr(parent.fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
@@ -102,13 +117,18 @@ export class VSCodeNodeModuleFactory implements INodeModuleFactory {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return this._defaultApiImpl;
}
}
//#endregion
//#region --- keytar-module
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
@@ -116,16 +136,23 @@ interface IKeytarModule {
findPassword(service: string): Promise<string | null>;
}
export class KeytarNodeModuleFactory implements INodeModuleFactory {
class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private alternativeNames: Set<string> | undefined;
private _impl: IKeytarModule;
constructor(mainThreadKeytar: MainThreadKeytarShape, environment: IEnvironment) {
constructor(
@IExtHostRpcService rpcService: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
const { environment } = initData;
const mainThreadKeytar = rpcService.getProxy(MainContext.MainThreadKeytar);
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
if (process.platform === 'win32') {
if (platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
@@ -151,11 +178,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
};
}
public load(request: string, parent: { filename: string; }): any {
public load(_request: string, _parent: URI): any {
return this._impl;
}
public alternaiveModuleName(name: string): string | undefined {
public alternativeModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
@@ -173,6 +200,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
}
}
//#endregion
//#region --- opn/open-module
interface OpenOptions {
wait: boolean;
app: string | string[];
@@ -186,15 +218,23 @@ interface IOpenModule {
(target: string, options?: OpenOptions): Thenable<void>;
}
export class OpenNodeModuleFactory implements INodeModuleFactory {
class OpenNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string[] = ['open', 'opn'];
private _extensionId: string | undefined;
private _original?: IOriginalOpen;
private _impl: IOpenModule;
private _mainThreadTelemetry: MainThreadTelemetryShape;
constructor(
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
@IExtHostRpcService rpcService: IExtHostRpcService,
) {
this._mainThreadTelemetry = rpcService.getProxy(MainContext.MainThreadTelemetry);
const mainThreadWindow = rpcService.getProxy(MainContext.MainThreadWindow);
constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>) {
this._impl = (target, options) => {
const uri: URI = URI.parse(target);
// If we have options use the original method.
@@ -210,15 +250,15 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
};
}
public load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any {
public load(request: string, parent: URI, isMain: any, original: LoadFunction): any {
// get extension id from filename and api for extension
const extension = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const extension = this._extensionPaths.findSubstr(parent.fsPath);
if (extension) {
this._extensionId = extension.identifier.value;
this.sendShimmingTelemetry();
}
this._original = original(request, parent, isMain);
this._original = original(request, { filename: parent.fsPath }, isMain);
return this._impl;
}
@@ -234,7 +274,7 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
}
private sendNoForwardTelemetry(): void {
@@ -244,6 +284,8 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenCallNoForwardClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
}
}
//#endregion

View File

@@ -55,10 +55,21 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
private commands: ExtHostCommands,
private logService: ILogService
) {
function isTreeViewItemHandleArg(arg: any): boolean {
return arg && arg.$treeViewId && arg.$treeItemHandle;
}
commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$treeViewId && arg.$treeItemHandle) {
if (isTreeViewItemHandleArg(arg)) {
return this.convertArgument(arg);
} else if (Array.isArray(arg) && (arg.length > 0)) {
return arg.map(item => {
if (isTreeViewItemHandleArg(item)) {
return this.convertArgument(item);
}
return item;
});
}
return arg;
}
@@ -187,13 +198,10 @@ export class ExtHostTreeView<T> extends Disposable {
private refreshPromise: Promise<void> = Promise.resolve();
private refreshQueue: Promise<void> = Promise.resolve();
constructor(private viewId: string, options: vscode.TreeViewOptions<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
constructor(private viewId: string, options: vscode.TreeViewOptions2<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
super();
this.dataProvider = options.treeDataProvider;
// {{SQL CARBON EDIT}}
if (this.proxy) {
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll });
}
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
if (this.dataProvider.onDidChangeTreeData) {
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
}

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import { createApiFactoryAndRegisterActors } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} replace with ours
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
import { MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
@@ -14,15 +14,33 @@ import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { createAdsApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} use our extension initalizer
import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/node/extHostRequireInterceptor'; // {{SQL CARBON EDIT}} use our extension initalizer
class NodeModuleRequireInterceptor extends RequireInterceptor {
protected _installInterceptor(): void {
const that = this;
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of that._alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (!that._factories.has(request)) {
return original.apply(this, arguments);
}
return that._factories.get(request)!.load(request, URI.file(parent.filename), isMain, original);
};
}
}
export class ExtHostExtensionService extends AbstractExtHostExtensionService {
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
// initialize API and register actors
const extensionApiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
const adsExtensionApiFactory = this._instaService.invokeFunction(createAdsApiFactory); // {{SQL CARBON EDIT}} // add factory
// Register Download command
this._instaService.createInstance(ExtHostDownloadService);
@@ -34,21 +52,11 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
}
// Module loading tricks
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPaths = await this.getExtensionPathIndex();
NodeModuleRequireInterceptor.INSTANCE.register(new AzdataNodeModuleFactory(adsExtensionApiFactory.azdata, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
NodeModuleRequireInterceptor.INSTANCE.register(new SqlopsNodeModuleFactory(adsExtensionApiFactory.sqlops, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(extensionApiFactory, extensionPaths, this._registry, configProvider));
NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._initData.environment));
if (this._initData.remote.isRemote) {
NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory(
this._extHostContext.getProxy(MainContext.MainThreadWindow),
this._extHostContext.getProxy(MainContext.MainThreadTelemetry),
extensionPaths
));
}
const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._registry);
await interceptor.install();
// Do this when extension service exists, but extensions are not being activated yet.
const configProvider = await this._extHostConfiguration.getConfigProvider();
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy);
// Use IPC messages to forward console-calls, note that the console is

View File

@@ -444,6 +444,14 @@ export class ExtHostTask implements ExtHostTaskShape {
if (dto === undefined) {
return Promise.reject(new Error('Task is not valid'));
}
// If this task is a custom execution, then we need to save it away
// in the provided custom execution map that is cleaned up after the
// task is executed.
if (CustomExecution2DTO.is(dto.execution)) {
await this.addCustomExecution2(dto, <vscode.Task2>task);
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
}
}
@@ -529,11 +537,6 @@ export class ExtHostTask implements ExtHostTaskShape {
return Promise.reject(new Error('no handler found'));
}
// For custom execution tasks, we need to store the execution objects locally
// since we obviously cannot send callback functions through the proxy.
// So, clear out any existing ones.
this._providedCustomExecutions2.clear();
// Set up a list of task ID promises that we can wait on
// before returning the provided tasks. The ensures that
// our task IDs are calculated for any custom execution tasks.
@@ -695,5 +698,17 @@ export class ExtHostTask implements ExtHostTaskShape {
if (extensionCallback2) {
this._activeCustomExecutions2.delete(execution.id);
}
const lastCustomExecution = this._providedCustomExecutions2.get(execution.id);
// Technically we don't really need to do this, however, if an extension
// is executing a task through "executeTask" over and over again
// with different properties in the task definition, then this list
// could grow indefinitely, something we don't want.
this._providedCustomExecutions2.clear();
// We do still need to hang on to the last custom execution so that the
// Rerun Task command doesn't choke when it tries to rerun a custom execution
if (lastCustomExecution) {
this._providedCustomExecutions2.set(execution.id, lastCustomExecution);
}
}
}

View File

@@ -3,118 +3,152 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
import { createApiFactoryAndRegisterActors } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} replace with ours
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import * as vscode from 'vscode';
import { TernarySearchTree } from 'vs/base/common/map';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
class ApiInstances {
class ExportsTrap {
private readonly _apiInstances = new Map<string, typeof vscode>();
static readonly Instance = new ExportsTrap();
constructor(
private readonly _apiFactory: IExtensionApiFactory,
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
private readonly _configProvider: ExtHostConfigProvider,
) {
//
private readonly _names: string[] = [];
private readonly _exports = new Map<string, any>();
private constructor() {
const exportsProxy = new Proxy({}, {
set: (target: any, p: PropertyKey, value: any) => {
// store in target
target[p] = value;
// store in named-bucket
const name = this._names[this._names.length - 1];
this._exports.get(name)![p] = value;
return true;
}
});
const moduleProxy = new Proxy({}, {
get: (target: any, p: PropertyKey) => {
if (p === 'exports') {
return exportsProxy;
}
return target[p];
},
set: (target: any, p: PropertyKey, value: any) => {
// store in target
target[p] = value;
// override bucket
if (p === 'exports') {
const name = this._names[this._names.length - 1];
this._exports.set(name, value);
}
return true;
}
});
(<any>self).exports = exportsProxy;
(<any>self).module = moduleProxy;
}
get(modulePath: string): typeof vscode {
const extension = this._extensionPaths.findSubstr(modulePath) || nullExtensionDescription;
const id = ExtensionIdentifier.toKey(extension.identifier);
add(name: string) {
this._exports.set(name, Object.create(null));
this._names.push(name);
let apiInstance = this._apiInstances.get(id);
if (!apiInstance) {
apiInstance = this._apiFactory(extension, this._extensionRegistry, this._configProvider);
this._apiInstances.set(id, apiInstance);
return {
claim: () => {
const result = this._exports.get(name);
this._exports.delete(name);
this._names.pop();
return result;
}
};
}
}
class WorkerRequireInterceptor extends RequireInterceptor {
_installInterceptor() { }
getModule(request: string, parent: URI): undefined | any {
for (let alternativeModuleName of this._alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
return apiInstance;
if (this._factories.has(request)) {
return this._factories.get(request)!.load(request, parent, false, () => { throw new Error(); });
}
return undefined;
}
}
export class ExtHostExtensionService extends AbstractExtHostExtensionService {
private _apiInstances?: ApiInstances;
private _fakeModules: WorkerRequireInterceptor;
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
// initialize API and register actors
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPath = await this.getExtensionPathIndex();
this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider);
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry);
await this._fakeModules.install();
}
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
interface FakeCommonJSSelf {
module?: object;
exports?: object;
require?: (module: string) => any;
window?: object;
__dirname: never;
__filename: never;
}
// FAKE commonjs world that only collects exports
const patchSelf: FakeCommonJSSelf = <any>self;
patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly
(<any>self).window = self; // <- that's improper but might help extensions that aren't authored correctly
// FAKE require function that only works for the vscode-module
const moduleStack: URI[] = [];
patchSelf.require = (mod: string) => {
(<any>self).require = (mod: string) => {
const parent = moduleStack[moduleStack.length - 1];
if (mod === 'vscode') {
return this._apiInstances!.get(parent.fsPath);
const result = this._fakeModules.getModule(mod, parent);
if (result !== undefined) {
return result;
}
if (!startsWith(mod, '.')) {
throw new Error(`Cannot load module '${mod}'`);
}
const exports = Object.create(null);
patchSelf.module = { exports };
patchSelf.exports = exports;
const next = joinPath(parent, '..', ensureSuffix(mod, '.js'));
moduleStack.push(next);
const trap = ExportsTrap.Instance.add(next.toString());
importScripts(asDomUri(next).toString(true));
moduleStack.pop();
return exports;
return trap.claim();
};
try {
activationTimesBuilder.codeLoadingStart();
const exports = Object.create(null);
patchSelf.module = { exports };
patchSelf.exports = exports;
module = module.with({ path: ensureSuffix(module.path, '.js') });
moduleStack.push(module);
const trap = ExportsTrap.Instance.add(module.toString());
importScripts(asDomUri(module).toString(true));
moduleStack.pop();
return Promise.resolve<T>(trap.claim());
} finally {
activationTimesBuilder.codeLoadingStop();
}
return Promise.resolve<T>(exports);
}
async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
async $setRemoteEnvironment(_env: { [key: string]: string | null }): Promise<void> {
throw new Error('Not supported');
}
}

View File

@@ -164,9 +164,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
private domNode!: HTMLElement;
private treeContainer!: HTMLElement;
private _messageValue: string | undefined;
private _canSelectMany: boolean = false;
private messageElement!: HTMLDivElement;
private tree: WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore> | undefined;
private treeLabels: ResourceLabels | undefined;
private root: ITreeItem;
private elementsToRefresh: ITreeItem[] = [];
private menus: TitleMenus;
@@ -253,6 +255,14 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.updateMessage();
}
get canSelectMany(): boolean {
return this._canSelectMany;
}
set canSelectMany(canSelectMany: boolean) {
this._canSelectMany = canSelectMany;
}
get hasIconForParentNode(): boolean {
return this._hasIconForParentNode;
}
@@ -372,12 +382,14 @@ export class CustomTreeView extends Disposable implements ITreeView {
collapseByDefault: (e: ITreeItem): boolean => {
return e.collapsibleState !== TreeItemCollapsibleState.Expanded;
},
multipleSelectionSupport: false
}));
multipleSelectionSupport: this.canSelectMany,
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
aligner.tree = this.tree;
const actionRunner = new MultipleSelectionActionRunner(() => this.tree!.getSelection());
renderer.actionRunner = actionRunner;
this.tree.contextKeyService.createKey<boolean>(this.id, true);
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e)));
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner)));
this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements)));
this._register(this.tree.onDidChangeCollapseState(e => {
if (!e.node.element) {
@@ -406,7 +418,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}));
}
private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent<ITreeItem>): void {
private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent<ITreeItem>, actionRunner: MultipleSelectionActionRunner): void {
const node: ITreeItem | null = treeEvent.element;
if (node === null) {
return;
@@ -442,7 +454,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
getActionsContext: () => (<TreeViewItemHandleArg>{ $treeViewId: this.id, $treeItemHandle: node.handle }),
actionRunner: new MultipleSelectionActionRunner(() => this.tree!.getSelection())
actionRunner
});
}
@@ -686,6 +698,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
static readonly ITEM_HEIGHT = 22;
static readonly TREE_TEMPLATE_ID = 'treeExplorer';
private _actionRunner: MultipleSelectionActionRunner | undefined;
constructor(
private treeViewId: string,
private menus: TreeMenus,
@@ -703,6 +717,10 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
return TreeRenderer.TREE_TEMPLATE_ID;
}
set actionRunner(actionRunner: MultipleSelectionActionRunner) {
this._actionRunner = actionRunner;
}
renderTemplate(container: HTMLElement): ITreeExplorerTemplateData {
DOM.addClass(container, 'custom-view-tree-node-item');
@@ -740,8 +758,11 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
templateData.icon.style.backgroundImage = iconUrl ? `url('${DOM.asDomUri(iconUrl).toString(true)}')` : '';
DOM.toggleClass(templateData.icon, 'custom-view-tree-node-item-icon', !!iconUrl);
templateData.actionBar.context = (<TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle });
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
if (this._actionRunner) {
templateData.actionBar.actionRunner = this._actionRunner;
}
this.setAlignment(templateData.container, node);
templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node)));
}
@@ -822,23 +843,23 @@ class Aligner extends Disposable {
class MultipleSelectionActionRunner extends ActionRunner {
constructor(private getSelectedResources: (() => any[])) {
constructor(private getSelectedResources: (() => ITreeItem[])) {
super();
}
runAction(action: IAction, context: any): Promise<any> {
if (action instanceof MenuItemAction) {
const selection = this.getSelectedResources();
const filteredSelection = selection.filter(s => s !== context);
if (selection.length === filteredSelection.length || selection.length === 1) {
return action.run(context);
}
return action.run(context, ...filteredSelection);
runAction(action: IAction, context: TreeViewItemHandleArg): Promise<any> {
const selection = this.getSelectedResources();
let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined;
if (selection.length > 1) {
selectionHandleArgs = [];
selection.forEach(selected => {
if (selected.handle !== context.$treeItemHandle) {
selectionHandleArgs!.push({ $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle });
}
});
}
return super.runAction(action, context);
return action.run(...[context, selectionHandleArgs]);
}
}

View File

@@ -14,7 +14,7 @@ import { Workbench } from 'vs/workbench/browser/workbench';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IProductService } from 'vs/platform/product/common/product';
import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
@@ -33,7 +33,6 @@ import { ISignService } from 'vs/platform/sign/common/sign';
import { SignService } from 'vs/platform/sign/browser/signService';
import { hash } from 'vs/base/common/hash';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { ProductService } from 'vs/platform/product/browser/productService';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { BACKUPS } from 'vs/platform/environment/common/environment';
import { joinPath } from 'vs/base/common/resources';
@@ -41,6 +40,7 @@ import { BrowserStorageService } from 'vs/platform/storage/browser/storageServic
import { IStorageService } from 'vs/platform/storage/common/storage';
import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService';
import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
class CodeRendererMain extends Disposable {
@@ -83,6 +83,11 @@ class CodeRendererMain extends Disposable {
}));
this._register(workbench.onShutdown(() => this.dispose()));
// Driver
if (this.configuration.driver) {
registerWindowDriver().then(d => this._register(d));
}
// Startup
workbench.startup();
}
@@ -117,16 +122,11 @@ class CodeRendererMain extends Disposable {
const payload = await this.resolveWorkspaceInitializationPayload();
// Environment
const environmentService = new BrowserWorkbenchEnvironmentService({
workspaceId: payload.id,
remoteAuthority: this.configuration.remoteAuthority,
webviewEndpoint: this.configuration.webviewEndpoint,
connectionToken: this.configuration.connectionToken
});
const environmentService = new BrowserWorkbenchEnvironmentService(payload.id, this.configuration);
serviceCollection.set(IWorkbenchEnvironmentService, environmentService);
// Product
const productService = new ProductService();
const productService = this.createProductService();
serviceCollection.set(IProductService, productService);
// Remote
@@ -192,6 +192,18 @@ class CodeRendererMain extends Disposable {
return { serviceCollection, logService, storageService: services[1] };
}
private createProductService(): IProductService {
const element = document.getElementById('vscode-remote-product-configuration');
const productConfiguration: IProductConfiguration = {
...element ? JSON.parse(element.getAttribute('data-settings')!) : {
version: '1.38.0-unknown',
nameLong: 'Unknown',
extensionAllowedProposedApi: [],
}, ...{ urlProtocol: '', enableTelemetry: false }
};
return { _serviceBrand: undefined, ...productConfiguration };
}
private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService): Promise<BrowserStorageService> {
const storageService = new BrowserStorageService(environmentService, fileService);

View File

@@ -422,6 +422,8 @@ export class SimpleWindowService extends Disposable implements IWindowService {
}
closeWindow(): Promise<void> {
window.close();
return Promise.resolve();
}
@@ -751,13 +753,13 @@ export class SimpleWindowsService implements IWindowsService {
async openAboutDialog(): Promise<void> {
const detail = localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
this.productService.productConfiguration.version || 'Unknown',
this.productService.productConfiguration.commit || 'Unknown',
this.productService.productConfiguration.date || 'Unknown',
this.productService.version || 'Unknown',
this.productService.commit || 'Unknown',
this.productService.date || 'Unknown',
navigator.userAgent
);
const result = await this.dialogService.show(Severity.Info, this.productService.productConfiguration.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail });
const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail });
if (result === 0) {
this.clipboardService.writeText(detail);
@@ -857,7 +859,7 @@ registerSingleton(ITunnelService, SimpleTunnelService);
//#region workspace stats
class WorkspaceStatsService implements IWorkspaceStatsService {
class SimpleWorkspaceStatsService implements IWorkspaceStatsService {
_serviceBrand: any;
@@ -875,6 +877,6 @@ class WorkspaceStatsService implements IWorkspaceStatsService {
}
registerSingleton(IWorkspaceStatsService, WorkspaceStatsService);
registerSingleton(IWorkspaceStatsService, SimpleWorkspaceStatsService);
//#endregion

View File

@@ -310,6 +310,8 @@ export interface ITreeView extends IDisposable {
showCollapseAllAction: boolean;
canSelectMany: boolean;
message?: string;
readonly visible: boolean;

View File

@@ -29,13 +29,14 @@ export abstract class SimpleFindWidget extends Widget {
private readonly _findInput: FindInput;
private readonly _domNode: HTMLElement;
private readonly _innerDomNode: HTMLElement;
private _isVisible: boolean = false;
private readonly _focusTracker: dom.IFocusTracker;
private readonly _findInputFocusTracker: dom.IFocusTracker;
private readonly _updateHistoryDelayer: Delayer<void>;
private prevBtn: SimpleButton;
private nextBtn: SimpleButton;
private foundMatch: boolean;
private readonly prevBtn: SimpleButton;
private readonly nextBtn: SimpleButton;
private _isVisible: boolean = false;
private foundMatch: boolean = false;
constructor(
@IContextViewService private readonly _contextViewService: IContextViewService,

View File

@@ -144,7 +144,8 @@ export class CallStackView extends ViewletPanel {
return nls.localize('showMoreStackFrames2', "Show More Stack Frames");
}
}
},
expandOnlyOnTwistieClick: true
});
this.tree.setInput(this.debugService.getModel()).then(undefined, onUnexpectedError);
@@ -576,7 +577,7 @@ function isDebugModel(obj: any): obj is IDebugModel {
}
function isDebugSession(obj: any): obj is IDebugSession {
return typeof obj.getAllThreads === 'function';
return obj && typeof obj.getAllThreads === 'function';
}
function isDeemphasized(frame: IStackFrame): boolean {
@@ -608,6 +609,10 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
} else if (isDebugSession(element)) {
const childSessions = this.debugService.getModel().getSessions().filter(s => s.parentSession === element);
const threads: CallStackItem[] = element.getAllThreads();
if (threads.length === 1 && childSessions.length === 0) {
// Do not show thread when there is only one to be compact.
return this.getThreadChildren(<Thread>threads[0]);
}
return Promise.resolve(threads.concat(childSessions));
} else {

View File

@@ -35,19 +35,23 @@ class ToggleBreakpointAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
const debugService = accessor.get(IDebugService);
const position = editor.getPosition();
if (editor.hasModel() && position) {
if (editor.hasModel()) {
const debugService = accessor.get(IDebugService);
const modelUri = editor.getModel().uri;
const bps = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri });
const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel());
// Does not account for multi line selections, Set to remove multiple cursor on the same line
const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))];
if (bps.length) {
return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId())));
}
if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber }], 'debugEditorActions.toggleBreakpointAction');
}
return Promise.all(lineNumbers.map(line => {
const bps = debugService.getModel().getBreakpoints({ lineNumber: line, uri: modelUri });
if (bps.length) {
return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId())));
} else if (canSet) {
return (debugService.addBreakpoints(modelUri, [{ lineNumber: line }], 'debugEditorActions.toggleBreakpointAction'));
} else {
return Promise.resolve([]);
}
}));
}
return Promise.resolve();

View File

@@ -175,7 +175,7 @@ export class DebugSession implements IDebugSession {
return this.raw!.initialize({
clientID: 'vscode',
clientName: this.productService.productConfiguration.nameLong,
clientName: this.productService.nameLong,
adapterID: this.configuration.type,
pathFormat: 'path',
linesStartAt1: true,

View File

@@ -17,7 +17,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
//@IWindowService windowService: IWindowService,
// @IWindowService windowService: IWindowService, // TODO@weinand TODO@isidorn cyclic dependency?
@IEnvironmentService environmentService: IEnvironmentService
) {
const connection = remoteAgentService.getConnection();
@@ -30,13 +30,11 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
this._register(this.onReload(event => {
if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {
//windowService.reloadWindow();
window.location.reload();
}
}));
this._register(this.onClose(event => {
if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {
//this._windowService.closeWindow();
window.close();
}
}));

View File

@@ -16,6 +16,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { env as processEnv } from 'vs/base/common/process';
/**
* This interface represents a single command line argument split into a "prefix" and a "path" half.
@@ -594,10 +595,7 @@ export class RawDebugSession {
let env: IProcessEnvironment = {};
if (vscodeArgs.env) {
// merge environment variables into a copy of the process.env
if (typeof process === 'object' && process.env) {
env = objects.mixin(env, process.env);
}
env = objects.mixin(env, vscodeArgs.env);
env = objects.mixin(processEnv, vscodeArgs.env);
// and delete some if necessary
Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]);
}

View File

@@ -227,14 +227,14 @@ export class Expression extends ExpressionContainer implements IExpression {
const response = await session.evaluate(this.name, stackFrame ? stackFrame.frameId : undefined, context);
this.available = !!(response && response.body);
if (response && response.body) {
this.value = response.body.result;
this.value = response.body.result || '';
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type || this.type;
}
} catch (e) {
this.value = e.message;
this.value = e.message || '';
this.available = false;
this.reference = 0;
}
@@ -256,7 +256,7 @@ export class Variable extends ExpressionContainer implements IExpression {
reference: number | undefined,
public name: string,
public evaluateName: string | undefined,
value: string,
value: string | undefined,
namedVariables: number | undefined,
indexedVariables: number | undefined,
public presentationHint: DebugProtocol.VariablePresentationHint | undefined,
@@ -265,7 +265,7 @@ export class Variable extends ExpressionContainer implements IExpression {
startOfVariables = 0
) {
super(session, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables);
this.value = value;
this.value = value || '';
}
async setVariable(value: string): Promise<any> {
@@ -276,7 +276,7 @@ export class Variable extends ExpressionContainer implements IExpression {
try {
const response = await this.session.setVariable((<ExpressionContainer>this.parent).reference, this.name, value);
if (response && response.body) {
this.value = response.body.value;
this.value = response.body.value || '';
this.type = response.body.type || this.type;
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;

View File

@@ -170,10 +170,10 @@ export class ExperimentService extends Disposable implements IExperimentService
}
protected getExperiments(): Promise<IRawExperiment[]> {
if (!this.productService.productConfiguration.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
return Promise.resolve([]);
}
return this.requestService.request({ type: 'GET', url: this.productService.productConfiguration.experimentsUrl }, CancellationToken.None).then(context => {
return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => {
if (context.res.statusCode !== 200) {
return Promise.resolve(null);
}

View File

@@ -54,6 +54,7 @@ import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/we
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { renderDashboardContributions } from 'sql/workbench/parts/extensions/browser/contributionRenders'; // {{SQL CARBON EDIT}}
import { generateUuid } from 'vs/base/common/uuid';
import { platform } from 'vs/base/common/process';
function removeEmbeddedSVGs(documentContent: string): string {
const newDocument = new DOMParser().parseFromString(documentContent, 'text/html');
@@ -582,9 +583,7 @@ export class ExtensionEditor extends BaseEditor {
{
enableFindWidget: true,
},
{
svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders,
});
{});
webviewElement.mountTo(template.content);
this.contentDisposables.add(webviewElement.onDidFocus(() => this.fireOnDidFocus()));
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, webviewElement);
@@ -1287,7 +1286,7 @@ export class ExtensionEditor extends BaseEditor {
private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null {
let key: string | undefined;
switch (process.platform) {
switch (platform) {
case 'win32': key = rawKeyBinding.win; break;
case 'linux': key = rawKeyBinding.linux; break;
case 'darwin': key = rawKeyBinding.mac; break;

View File

@@ -27,9 +27,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService, EXTENSIONS_CONFIG, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/workbench/contrib/extensions/common/extensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as os from 'os';
import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
@@ -49,6 +47,9 @@ import { timeout } from 'vs/base/common/async';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; // {{SQL CARBON EDIT}}
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}}
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats';
import { Platform, setImmediate } from 'vs/base/common/platform';
import { platform, env as processEnv } from 'vs/base/common/process';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
const milliSecondsInADay = 1000 * 60 * 60 * 24;
const choiceNever = localize('neverShowAgain', "Don't Show Again");
@@ -106,7 +107,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IExtensionService private readonly extensionService: IExtensionService,
@IRequestService private readonly requestService: IRequestService,
@IViewletService private readonly viewletService: IViewletService,
@@ -126,8 +127,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return;
}
if (this.productService.productConfiguration.extensionsGallery && this.productService.productConfiguration.extensionsGallery.recommendationsUrl) {
this._extensionsRecommendationsUrl = this.productService.productConfiguration.extensionsGallery.recommendationsUrl;
if (this.productService.extensionsGallery && this.productService.extensionsGallery.recommendationsUrl) {
this._extensionsRecommendationsUrl = this.productService.extensionsGallery.recommendationsUrl;
}
this.sessionSeed = +new Date();
@@ -256,7 +257,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
getKeymapRecommendations(): IExtensionRecommendation[] {
return (this.productService.productConfiguration.keymapExtensionTips || [])
return (this.productService.keymapExtensionTips || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] }));
}
@@ -613,10 +614,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return Object.keys(this._fileBasedRecommendations)
.sort((a, b) => {
if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) {
if (!this.productService.productConfiguration.extensionImportantTips || caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, a)) {
if (!this.productService.extensionImportantTips || caseInsensitiveGet(this.productService.extensionImportantTips, a)) {
return -1;
}
if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, b)) {
if (caseInsensitiveGet(this.productService.extensionImportantTips, b)) {
return 1;
}
}
@@ -627,13 +628,13 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
/**
* Parse all file based recommendations from this.productService.productConfiguration.extensionTips
* Retire existing recommendations if they are older than a week or are not part of this.productService.productConfiguration.extensionTips anymore
* Parse all file based recommendations from this.productService.extensionTips
* Retire existing recommendations if they are older than a week or are not part of this.productService.extensionTips anymore
*/
private fetchFileBasedRecommendations() {
const extensionTips = this.productService.productConfiguration.extensionTips;
const extensionTips = this.productService.extensionTips;
// {{SQL CARBON EDIT}}
this._recommendations = this.productService.productConfiguration.recommendedExtensions;
this._recommendations = this.productService.recommendedExtensions;
if (!extensionTips) {
return;
}
@@ -650,7 +651,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
});
forEach(this.productService.productConfiguration.extensionImportantTips, entry => {
forEach(this.productService.extensionImportantTips, entry => {
let { key: id, value } = entry;
const { pattern } = value;
let ids = this._availableRecommendations[pattern];
@@ -712,7 +713,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
let { key: pattern, value: ids } = entry;
if (match(pattern, model.uri.toString())) {
for (let id of ids) {
if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id)) {
if (caseInsensitiveGet(this.productService.extensionImportantTips, id)) {
recommendationsToSuggest.push(id);
}
const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] };
@@ -766,7 +767,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
const id = recommendationsToSuggest[0];
const entry = caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id);
const entry = caseInsensitiveGet(this.productService.extensionImportantTips, id);
if (!entry) {
return false;
}
@@ -992,13 +993,15 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
/**
* If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations
* If user has any of the tools listed in this.productService.exeBasedExtensionTips, fetch corresponding recommendations
*/
private fetchExecutableRecommendations(important: boolean): Promise<void> {
const homeDir = os.homedir();
let foundExecutables: Set<string> = new Set<string>();
private async fetchExecutableRecommendations(important: boolean): Promise<void> {
if (Platform.Web) {
return;
}
let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => {
const foundExecutables: Set<string> = new Set<string>();
const findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => {
return this.fileService.exists(URI.file(path)).then(exists => {
if (exists && !foundExecutables.has(exeName)) {
foundExecutables.add(exeName);
@@ -1014,9 +1017,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
});
};
let promises: Promise<void>[] = [];
const promises: Promise<void>[] = [];
// Loop through recommended extensions
forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => {
forEach(this.productService.exeBasedExtensionTips, entry => {
if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) {
return;
}
@@ -1024,24 +1027,24 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return;
}
const exeName = entry.key;
if (process.platform === 'win32') {
if (platform === 'win32') {
let windowsPath = entry.value['windowsPath'];
if (!windowsPath || typeof windowsPath !== 'string') {
return;
}
windowsPath = windowsPath.replace('%USERPROFILE%', process.env['USERPROFILE']!)
.replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)']!)
.replace('%ProgramFiles%', process.env['ProgramFiles']!)
.replace('%APPDATA%', process.env['APPDATA']!)
.replace('%WINDIR%', process.env['WINDIR']!);
windowsPath = windowsPath.replace('%USERPROFILE%', processEnv['USERPROFILE']!)
.replace('%ProgramFiles(x86)%', processEnv['ProgramFiles(x86)']!)
.replace('%ProgramFiles%', processEnv['ProgramFiles']!)
.replace('%APPDATA%', processEnv['APPDATA']!)
.replace('%WINDIR%', processEnv['WINDIR']!);
promises.push(findExecutable(exeName, entry.value, windowsPath));
} else {
promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName)));
promises.push(findExecutable(exeName, entry.value, join(homeDir, exeName)));
promises.push(findExecutable(exeName, entry.value, join(this.environmentService.userHome, exeName)));
}
});
return Promise.all(promises).then(() => undefined);
await Promise.all(promises);
}
/**
@@ -1155,7 +1158,6 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
private isExtensionAllowedToBeRecommended(id: string): boolean {
return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1;
}
// {{SQL CARBON EDIT}}
promptRecommendedExtensionsByScenario(scenarioType: string): void {
const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType;
@@ -1246,7 +1248,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
}
return Promise.resolve((this.productService.productConfiguration.recommendedExtensionsByScenario[scenarioType] || [])
return Promise.resolve((this.productService.recommendedExtensionsByScenario[scenarioType] || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })));
}

View File

@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output';
@@ -45,9 +45,11 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
// Singletons
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
registerSingleton(IExtensionTipsService, ExtensionTipsService);
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false });

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
// Singletons
registerSingleton(IExtensionTipsService, ExtensionTipsService);

View File

@@ -79,10 +79,10 @@ export function toExtensionDescription(local: ILocalExtension): IExtensionDescri
const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error,
instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService, productService: IProductService) => {
if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.productConfiguration.extensionsGallery) {
if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.extensionsGallery) {
return Promise.reject(error);
} else {
const downloadUrl = `${productService.productConfiguration.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`;
const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`;
notificationService.prompt(Severity.Error, message, [{
label: localize('download', "Download Manually"),
run: () => openerService.open(URI.parse(downloadUrl)).then(() => {

View File

@@ -435,7 +435,7 @@ export class ExtensionsListView extends ViewletPanel {
// {{SQL CARBON EDIT}}
if (this.productService) {
let promiseRecommendedExtensionsByScenario: Promise<IPagedModel<IExtension>> | undefined;
Object.keys(this.productService.productConfiguration.recommendedExtensionsByScenario).forEach(scenarioType => {
Object.keys(this.productService.recommendedExtensionsByScenario).forEach(scenarioType => {
let re = new RegExp('@' + scenarioType, 'i');
if (re.test(query.value)) {
promiseRecommendedExtensionsByScenario = this.getRecommendedExtensionsByScenario(token, scenarioType);

View File

@@ -118,16 +118,16 @@ class Extension implements IExtension {
}
get url(): string | undefined {
if (!this.productService.productConfiguration.extensionsGallery || !this.gallery) {
if (!this.productService.extensionsGallery || !this.gallery) {
return undefined;
}
return `${this.productService.productConfiguration.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`;
return `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`;
}
// {{SQL CARBON EDIT}}
get downloadPage(): string {
if (!this.productService.productConfiguration.extensionsGallery) {
if (!this.productService.extensionsGallery) {
return null;
}
@@ -499,7 +499,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
private readonly _onChange: Emitter<IExtension | undefined> = new Emitter<IExtension | undefined>();
get onChange(): Event<IExtension | undefined> { return this._onChange.event; }
private _extensionAllowedBadgeProviders: string[] | undefined;
private installing: IExtension[] = [];
constructor(
@@ -631,7 +630,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
text = text.replace(extensionRegex, (m, ext) => {
// Get curated keywords
const lookup = this.productService.productConfiguration.extensionKeywords || {};
const lookup = this.productService.extensionKeywords || {};
const keywords = lookup[ext] || [];
// Get mode name
@@ -831,9 +830,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
// This is the execution path for install/update extension from marketplace.
// Check both the vscode version and azure data studio version
// The check is added here because we want to fail fast instead of downloading the VSIX and then fail.
if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.productConfiguration.vscodeVersion)
|| (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.productConfiguration.version)))) {
return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.productConfiguration.version, gallery.version)));
if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.vscodeVersion)
|| (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.version)))) {
return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.version, gallery.version)));
}
return this.installWithProgress(async () => {
@@ -1074,13 +1073,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
return changed;
}
get allowedBadgeProviders(): string[] {
if (!this._extensionAllowedBadgeProviders) {
this._extensionAllowedBadgeProviders = (this.productService.productConfiguration.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase());
}
return this._extensionAllowedBadgeProviders;
}
private _activityCallBack: (() => void) | null = null;
private updateActivity(): void {
if ((this.localExtensions && this.localExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling))

View File

@@ -93,7 +93,6 @@ export interface IExtensionsWorkbenchService {
setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise<void>;
open(extension: IExtension, sideByside?: boolean): Promise<any>;
checkForUpdates(): Promise<void>;
allowedBadgeProviders: string[];
}
export const ConfigurationKey = 'extensions';

View File

@@ -7,9 +7,7 @@ import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -25,7 +23,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler';
// Singletons
registerSingleton(IExtensionTipsService, ExtensionTipsService);
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);

View File

@@ -16,7 +16,7 @@ import {
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService } from 'vs/workbench/services/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 { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';

View File

@@ -15,14 +15,14 @@ import {
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { Emitter } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestContextService, TestLifecycleService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices';
import { TestContextService, TestLifecycleService, TestSharedProcessService, productService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
@@ -36,7 +36,6 @@ import { ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensi
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
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';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';

View File

@@ -17,7 +17,7 @@ import {
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService, ExtensionRecommendationReason } from 'vs/workbench/services/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 { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';

View File

@@ -17,7 +17,7 @@ import {
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/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 { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';

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