mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-04-10 13:46:46 -04:00
Merge from vscode 1ce89e2cb720d69c496c2815c4696ee4fd4429a6 (#6779)
* Merge from vscode 1ce89e2cb720d69c496c2815c4696ee4fd4429a6 * redisable accounts because of issues
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 |
@@ -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]) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
2
src/vs/monaco.d.ts
vendored
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
192
src/vs/platform/driver/browser/baseDriver.ts
Normal file
192
src/vs/platform/driver/browser/baseDriver.ts
Normal 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>;
|
||||
}
|
||||
27
src/vs/platform/driver/browser/driver.ts
Normal file
27
src/vs/platform/driver/browser/driver.ts
Normal 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: () => { } };
|
||||
});
|
||||
}
|
||||
56
src/vs/platform/driver/common/driver.ts
Normal file
56
src/vs/platform/driver/common/driver.ts
Normal 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>;
|
||||
}
|
||||
@@ -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' });
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
29
src/vs/vscode.proposed.d.ts
vendored
29
src/vs/vscode.proposed.d.ts
vendored
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 })));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -310,6 +310,8 @@ export interface ITreeView extends IDisposable {
|
||||
|
||||
showCollapseAllAction: boolean;
|
||||
|
||||
canSelectMany: boolean;
|
||||
|
||||
message?: string;
|
||||
|
||||
readonly visible: boolean;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'] })));
|
||||
}
|
||||
@@ -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 });
|
||||
|
||||
@@ -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);
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user