Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)
* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f * fix various icon issues * fix preview features
@@ -6,9 +6,10 @@
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
|
||||
import { IRemoteConsoleLog, log } from 'vs/base/common/console';
|
||||
import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
|
||||
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadConsole)
|
||||
@@ -20,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
|
||||
) {
|
||||
const devOpts = parseExtensionDevOptions(this._environmentService);
|
||||
@@ -40,7 +41,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
|
||||
|
||||
// Log on main side if running tests from cli
|
||||
if (this._isExtensionDevTestFromCli) {
|
||||
this._windowsService.log(entry.severity, parse(entry).args);
|
||||
logRemoteEntry(this._logService, entry);
|
||||
}
|
||||
|
||||
// Broadcast to other windows if we are in development mode
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadKeytar)
|
||||
export class MainThreadKeytar implements MainThreadKeytarShape {
|
||||
|
||||
@@ -422,6 +422,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution()));
|
||||
}
|
||||
});
|
||||
this._taskService.setJsonTasksSupported(Promise.resolve(this._proxy.$jsonTasksSupported()));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
@@ -8,39 +8,42 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
|
||||
/**
|
||||
* Bi-directional map between webview handles and inputs.
|
||||
*/
|
||||
class WebviewHandleStore {
|
||||
private readonly _handlesToInputs = new Map<string, WebviewEditorInput>();
|
||||
private readonly _inputsToHandles = new Map<WebviewEditorInput, string>();
|
||||
private readonly _handlesToInputs = new Map<string, WebviewInput>();
|
||||
private readonly _inputsToHandles = new Map<WebviewInput, string>();
|
||||
|
||||
public add(handle: string, input: WebviewEditorInput): void {
|
||||
public add(handle: string, input: WebviewInput): void {
|
||||
this._handlesToInputs.set(handle, input);
|
||||
this._inputsToHandles.set(input, handle);
|
||||
}
|
||||
|
||||
public getHandleForInput(input: WebviewEditorInput): string | undefined {
|
||||
public getHandleForInput(input: WebviewInput): string | undefined {
|
||||
return this._inputsToHandles.get(input);
|
||||
}
|
||||
|
||||
public getInputForHandle(handle: string): WebviewEditorInput | undefined {
|
||||
public getInputForHandle(handle: string): WebviewInput | undefined {
|
||||
return this._handlesToInputs.get(handle);
|
||||
}
|
||||
|
||||
@@ -68,8 +71,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
'vscode-insider',
|
||||
]);
|
||||
|
||||
private static revivalPool = 0;
|
||||
|
||||
private readonly _proxy: ExtHostWebviewsShape;
|
||||
private readonly _webviewEditorInputs = new WebviewHandleStore();
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
@@ -94,8 +95,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
// This reviver's only job is to activate webview panel extensions
|
||||
// This should trigger the real reviver to be registered from the extension host side.
|
||||
this._register(_webviewEditorService.registerResolver({
|
||||
canResolve: (webview: WebviewEditorInput) => {
|
||||
if (!webview.webview.state && webview.getTypeId() === WebviewEditorInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case
|
||||
canResolve: (webview: WebviewInput) => {
|
||||
if (!webview.webview.state && webview.getTypeId() === WebviewInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return;
|
||||
}
|
||||
|
||||
const handle = `revival-${MainThreadWebviews.revivalPool++}`;
|
||||
const handle = generateUuid();
|
||||
this._webviewEditorInputs.add(handle, webviewEditorInput);
|
||||
this.hookupWebviewEventDelegate(handle, webviewEditorInput);
|
||||
|
||||
@@ -246,13 +247,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
|
||||
this._editorProviders.set(viewType, this._webviewEditorService.registerResolver({
|
||||
canResolve: (webviewEditorInput) => {
|
||||
return webviewEditorInput.getTypeId() !== WebviewEditorInput.typeId && webviewEditorInput.viewType === viewType;
|
||||
return webviewEditorInput.getTypeId() !== WebviewInput.typeId && webviewEditorInput.viewType === viewType;
|
||||
},
|
||||
resolveWebview: async (webview) => {
|
||||
const handle = `resolved-${MainThreadWebviews.revivalPool++}`;
|
||||
const handle = generateUuid();
|
||||
this._webviewEditorInputs.add(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview);
|
||||
|
||||
if (webview instanceof CustomFileEditorInput) {
|
||||
webview.onWillSave(e => {
|
||||
e.waitUntil(this._proxy.$save(handle));
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxy.$resolveWebviewEditor(
|
||||
webview.getResource(),
|
||||
@@ -292,7 +299,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return viewType.replace(/^mainThreadWebview-/, '');
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewEditorInput) {
|
||||
private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewInput) {
|
||||
input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri));
|
||||
input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message));
|
||||
input.onDispose(() => {
|
||||
@@ -317,21 +324,31 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
|
||||
const activeInput = this._editorService.activeControl && this._editorService.activeControl.input;
|
||||
const viewStates: WebviewPanelViewStateData = {};
|
||||
|
||||
const updateViewStatesForInput = (group: IEditorGroup, topLevelInput: IEditorInput, editorInput: IEditorInput) => {
|
||||
if (!(editorInput instanceof WebviewInput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
editorInput.updateGroup(group.id);
|
||||
|
||||
const handle = this._webviewEditorInputs.getHandleForInput(editorInput);
|
||||
if (handle) {
|
||||
viewStates[handle] = {
|
||||
visible: topLevelInput.matches(group.activeEditor),
|
||||
active: topLevelInput.matches(activeInput),
|
||||
position: editorGroupToViewColumn(this._editorGroupService, group.id),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
for (const group of this._editorGroupService.groups) {
|
||||
for (const input of group.editors) {
|
||||
if (!(input instanceof WebviewEditorInput)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
input.updateGroup(group.id);
|
||||
|
||||
const handle = this._webviewEditorInputs.getHandleForInput(input);
|
||||
if (handle) {
|
||||
viewStates[handle] = {
|
||||
visible: input === group.activeEditor,
|
||||
active: input === activeInput,
|
||||
position: editorGroupToViewColumn(this._editorGroupService, group.id),
|
||||
};
|
||||
if (input instanceof DiffEditorInput) {
|
||||
updateViewStatesForInput(group, input, input.master);
|
||||
updateViewStatesForInput(group, input, input.details);
|
||||
} else {
|
||||
updateViewStatesForInput(group, input, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +365,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
}
|
||||
}
|
||||
|
||||
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
|
||||
private isSupportedLink(webview: WebviewInput, link: URI): boolean {
|
||||
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
|
||||
return true;
|
||||
}
|
||||
@@ -358,7 +375,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command';
|
||||
}
|
||||
|
||||
private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput {
|
||||
private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput {
|
||||
const webview = this.tryGetWebviewEditorInput(handle);
|
||||
if (!webview) {
|
||||
throw new Error('Unknown webview handle:' + handle);
|
||||
@@ -366,7 +383,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return webview;
|
||||
}
|
||||
|
||||
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined {
|
||||
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput | undefined {
|
||||
return this._webviewEditorInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,14 @@ import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isNative } from 'vs/base/common/platform';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
|
||||
@@ -25,6 +23,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@@ -44,10 +43,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
// The workspace file is provided be a unknown file system provider. It might come
|
||||
// from the extension host. So initialize now knowing that `rootPath` is undefined.
|
||||
if (workspace.configuration && !isNative && !fileService.canHandleResource(workspace.configuration)) {
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace));
|
||||
} else {
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
}
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
}
|
||||
@@ -214,19 +221,3 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
return this._windowService.resolveProxy(url);
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) {
|
||||
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
|
||||
const extensionService = accessor.get(IExtensionService);
|
||||
const windowService = accessor.get(IWindowService);
|
||||
|
||||
if (disableExtensions && disableExtensions.length) {
|
||||
const runningExtensions = await extensionService.getExtensions();
|
||||
// If requested extension to disable is running, then reload window with given workspace
|
||||
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => ExtensionIdentifier.equals(runningExtension.identifier, id)))) {
|
||||
return windowService.openWindow([{ workspaceUri: workspace }], { args: { _: [], 'disable-extension': disableExtensions } });
|
||||
}
|
||||
}
|
||||
|
||||
return workspaceEditingService.enterWorkspace(workspace);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
@@ -252,6 +252,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
get remoteName() {
|
||||
return getRemoteName(initData.remote.authority);
|
||||
},
|
||||
get uiKind() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return initData.uiKind;
|
||||
}
|
||||
};
|
||||
if (!initData.environment.extensionTestsLocationURI) {
|
||||
@@ -905,6 +909,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
Decoration: extHostTypes.Decoration,
|
||||
WebviewEditorState: extHostTypes.WebviewEditorState,
|
||||
UIKind: UIKind
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ export interface IInitData {
|
||||
logsLocation: URI;
|
||||
autoStart: boolean;
|
||||
remote: { isRemote: boolean; authority: string | undefined; };
|
||||
uiKind: UIKind;
|
||||
}
|
||||
|
||||
export interface IConfigurationInitData extends IConfigurationData {
|
||||
@@ -109,6 +110,11 @@ export interface IExtHostContext extends IRPCProtocol {
|
||||
export interface IMainContext extends IRPCProtocol {
|
||||
}
|
||||
|
||||
export enum UIKind {
|
||||
Desktop = 1,
|
||||
Web = 2
|
||||
}
|
||||
|
||||
// --- main thread
|
||||
|
||||
export interface MainThreadClipboardShape extends IDisposable {
|
||||
@@ -571,6 +577,7 @@ export interface ExtHostWebviewsShape {
|
||||
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$save(handle: WebviewPanelHandle): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -1191,6 +1198,7 @@ export interface ExtHostTaskShape {
|
||||
$OnDidEndTask(execution: tasks.TaskExecutionDTO): void;
|
||||
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>;
|
||||
$getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined }>;
|
||||
$jsonTasksSupported(): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface IBreakpointDto {
|
||||
|
||||
@@ -3,12 +3,27 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import { TaskSystemInfoDTO } from '../common/shared/tasks';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
|
||||
export interface IExtHostTask extends ExtHostTaskShape {
|
||||
|
||||
@@ -21,10 +36,703 @@ export interface IExtHostTask extends ExtHostTaskShape {
|
||||
onDidEndTaskProcess: Event<vscode.TaskProcessEndEvent>;
|
||||
|
||||
registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable;
|
||||
registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
|
||||
registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void;
|
||||
fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]>;
|
||||
executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution>;
|
||||
terminateTask(execution: vscode.TaskExecution): Promise<void>;
|
||||
}
|
||||
|
||||
export namespace TaskDefinitionDTO {
|
||||
export function from(value: vscode.TaskDefinition): tasks.TaskDefinitionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.TaskDefinitionDTO): vscode.TaskDefinition | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: vscode.TaskPresentationOptions): tasks.TaskPresentationOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: vscode.ProcessExecutionOptions): tasks.ProcessExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ProcessExecutionDTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ProcessExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as tasks.ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ProcessExecution): tasks.ProcessExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: tasks.ProcessExecutionDTO = {
|
||||
process: value.process,
|
||||
args: value.args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: tasks.ProcessExecutionDTO): types.ProcessExecution | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return new types.ProcessExecution(value.process, value.args, value.options);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: vscode.ShellExecutionOptions): tasks.ShellExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ShellExecutionDTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ShellExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as tasks.ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ShellExecution): tasks.ShellExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: tasks.ShellExecutionDTO = {
|
||||
};
|
||||
if (value.commandLine !== undefined) {
|
||||
result.commandLine = value.commandLine;
|
||||
} else {
|
||||
result.command = value.command;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: tasks.ShellExecutionDTO): types.ShellExecution | undefined {
|
||||
if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
if (value.commandLine) {
|
||||
return new types.ShellExecution(value.commandLine, value.options);
|
||||
} else {
|
||||
return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CustomExecution2DTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.CustomExecution2DTO {
|
||||
if (value) {
|
||||
let candidate = value as tasks.CustomExecution2DTO;
|
||||
return candidate && candidate.customExecution === 'customExecution2';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function from(value: vscode.CustomExecution2): tasks.CustomExecution2DTO {
|
||||
return {
|
||||
customExecution: 'customExecution2'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TaskHandleDTO {
|
||||
export function from(value: types.Task): tasks.TaskHandleDTO {
|
||||
let folder: UriComponents | undefined;
|
||||
if (value.scope !== undefined && typeof value.scope !== 'number') {
|
||||
folder = value.scope.uri;
|
||||
}
|
||||
return {
|
||||
id: value._id!,
|
||||
workspaceFolder: folder!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskDTO {
|
||||
export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): tasks.TaskDTO[] {
|
||||
if (tasks === undefined || tasks === null) {
|
||||
return [];
|
||||
}
|
||||
const result: tasks.TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
const converted = from(task, extension);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function from(value: vscode.Task, extension: IExtensionDescription): tasks.TaskDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined;
|
||||
if (value.execution instanceof types.ProcessExecution) {
|
||||
execution = ProcessExecutionDTO.from(value.execution);
|
||||
} else if (value.execution instanceof types.ShellExecution) {
|
||||
execution = ShellExecutionDTO.from(value.execution);
|
||||
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
|
||||
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
|
||||
}
|
||||
|
||||
const definition: tasks.TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
|
||||
let scope: number | UriComponents;
|
||||
if (value.scope) {
|
||||
if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = value.scope.uri;
|
||||
}
|
||||
} else {
|
||||
// To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here:
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
|
||||
const result: tasks.TaskDTO = {
|
||||
_id: (value as types.Task)._id!,
|
||||
definition,
|
||||
name: value.name,
|
||||
source: {
|
||||
extensionId: extension.identifier.value,
|
||||
label: value.source,
|
||||
scope: scope
|
||||
},
|
||||
execution: execution!,
|
||||
isBackground: value.isBackground,
|
||||
group: group,
|
||||
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
|
||||
problemMatchers: value.problemMatchers,
|
||||
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
|
||||
runOptions: (<vscode.Task>value).runOptions ? (<vscode.Task>value).runOptions : { reevaluateOnRerun: true },
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: types.ShellExecution | types.ProcessExecution | undefined;
|
||||
if (ProcessExecutionDTO.is(value.execution)) {
|
||||
execution = ProcessExecutionDTO.to(value.execution);
|
||||
} else if (ShellExecutionDTO.is(value.execution)) {
|
||||
execution = ShellExecutionDTO.to(value.execution);
|
||||
}
|
||||
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
|
||||
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
if (value.source) {
|
||||
if (value.source.scope !== undefined) {
|
||||
if (typeof value.source.scope === 'number') {
|
||||
scope = value.source.scope;
|
||||
} else {
|
||||
scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
|
||||
}
|
||||
} else {
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers);
|
||||
if (value.isBackground !== undefined) {
|
||||
result.isBackground = value.isBackground;
|
||||
}
|
||||
if (value.group !== undefined) {
|
||||
result.group = types.TaskGroup.from(value.group);
|
||||
}
|
||||
if (value.presentationOptions) {
|
||||
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!;
|
||||
}
|
||||
if (value._id) {
|
||||
result._id = value._id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskFilterDTO {
|
||||
export function from(value: vscode.TaskFilter | undefined): tasks.TaskFilterDTO | undefined {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function to(value: tasks.TaskFilterDTO): vscode.TaskFilter | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
|
||||
constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
}
|
||||
|
||||
public get task(): vscode.Task {
|
||||
return this._task;
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this._tasks.terminateTask(this);
|
||||
}
|
||||
|
||||
public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void {
|
||||
}
|
||||
|
||||
public fireDidEndProcess(value: tasks.TaskProcessEndedDTO): void {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskExecutionDTO {
|
||||
export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
|
||||
const task = await TaskDTO.to(value.task, workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be created.');
|
||||
}
|
||||
return new TaskExecutionImpl(tasks, value.id, task);
|
||||
}
|
||||
export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO {
|
||||
return {
|
||||
id: (value as TaskExecutionImpl)._id,
|
||||
task: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface HandlerData {
|
||||
type: string;
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
protected readonly _proxy: MainThreadTaskShape;
|
||||
protected readonly _workspaceProvider: IExtHostWorkspaceProvider;
|
||||
protected readonly _editorService: IExtHostDocumentsAndEditors;
|
||||
protected readonly _configurationService: IExtHostConfiguration;
|
||||
protected readonly _terminalService: IExtHostTerminalService;
|
||||
protected _handleCounter: number;
|
||||
protected _handlers: Map<number, HandlerData>;
|
||||
protected _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
protected _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _notProvidedCustomExecutions: Set<string>; // Used for custom executions tasks that are created and run through executeTask.
|
||||
protected _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _lastStartedTask: string | undefined;
|
||||
protected readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
|
||||
protected readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
|
||||
|
||||
protected readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
|
||||
protected readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceProvider = workspaceService;
|
||||
this._editorService = editorService;
|
||||
this._configurationService = configurationService;
|
||||
this._terminalService = extHostTerminalService;
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
this._notProvidedCustomExecutions = new Set<string>();
|
||||
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
const handle = this.nextHandle();
|
||||
this._handlers.set(handle, { type, provider, extension });
|
||||
this._proxy.$registerTaskProvider(handle, type);
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._proxy.$unregisterTaskProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void {
|
||||
this._proxy.$registerTaskSystem(scheme, info);
|
||||
}
|
||||
|
||||
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
|
||||
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
|
||||
const result: vscode.Task[] = [];
|
||||
for (let value of values) {
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider);
|
||||
if (task) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public abstract async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution>;
|
||||
|
||||
public get taskExecutions(): vscode.TaskExecution[] {
|
||||
const result: vscode.TaskExecution[] = [];
|
||||
this._taskExecutions.forEach(value => result.push(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
public terminateTask(execution: vscode.TaskExecution): Promise<void> {
|
||||
if (!(execution instanceof TaskExecutionImpl)) {
|
||||
throw new Error('No valid task execution provided');
|
||||
}
|
||||
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
|
||||
}
|
||||
|
||||
public get onDidStartTask(): Event<vscode.TaskStartEvent> {
|
||||
return this._onDidExecuteTask.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise<void> {
|
||||
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
|
||||
if (execution2) {
|
||||
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
|
||||
throw new Error('We should not be trying to start the same custom task executions twice.');
|
||||
}
|
||||
|
||||
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
|
||||
this._activeCustomExecutions2.set(execution.id, execution2);
|
||||
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
|
||||
}
|
||||
this._lastStartedTask = execution.id;
|
||||
|
||||
this._onDidExecuteTask.fire({
|
||||
execution: await this.getTaskExecution(execution)
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidEndTask(): Event<vscode.TaskEndEvent> {
|
||||
return this._onDidTerminateTask.event;
|
||||
}
|
||||
|
||||
public async $OnDidEndTask(execution: tasks.TaskExecutionDTO): Promise<void> {
|
||||
const _execution = await this.getTaskExecution(execution);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.customExecutionComplete(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
execution: _execution
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidStartTaskProcess(): Event<vscode.TaskProcessStartEvent> {
|
||||
return this._onDidTaskProcessStarted.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessStarted.fire({
|
||||
execution: execution,
|
||||
processId: value.processId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
|
||||
return this._onDidTaskProcessEnded.event;
|
||||
}
|
||||
|
||||
public async $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessEnded.fire({
|
||||
execution: execution,
|
||||
exitCode: value.exitCode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription };
|
||||
|
||||
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<tasks.TaskSetDTO> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Knowing this ID ahead of time is needed because when a task
|
||||
// start event is fired this is when the custom execution is called.
|
||||
// The task start event is also the first time we see the ID from the main
|
||||
// thread, which is too late for us because we need to save an map
|
||||
// from an ID to the custom execution function. (Kind of a cart before the horse problem).
|
||||
const taskIdPromises: Promise<void>[] = [];
|
||||
const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
return this.provideTasksInternal(validTypes, taskIdPromises, handler, value);
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetchPromise.then((result) => {
|
||||
Promise.all(taskIdPromises).then(() => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined>;
|
||||
|
||||
public async $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
if (taskDTO.definition.type !== handler.type) {
|
||||
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
|
||||
}
|
||||
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None);
|
||||
if (!resolvedTask) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
const resolvedTaskDTO: tasks.TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
|
||||
if (!resolvedTaskDTO) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
if (resolvedTask.definition !== task.definition) {
|
||||
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
|
||||
}
|
||||
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask, true);
|
||||
}
|
||||
|
||||
return await this.resolveTaskInternal(resolvedTaskDTO);
|
||||
}
|
||||
|
||||
public abstract async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>;
|
||||
|
||||
public abstract $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>;
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
|
||||
protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise<void> {
|
||||
const taskId = await this._proxy.$createTaskId(taskDTO);
|
||||
if (!isProvided && !this._providedCustomExecutions2.has(taskId)) {
|
||||
this._notProvidedCustomExecutions.add(taskId);
|
||||
}
|
||||
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
|
||||
}
|
||||
|
||||
protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
|
||||
if (typeof execution === 'string') {
|
||||
const taskExecution = this._taskExecutions.get(execution);
|
||||
if (!taskExecution) {
|
||||
throw new Error('Unexpected: The specified task is missing an execution');
|
||||
}
|
||||
return taskExecution;
|
||||
}
|
||||
|
||||
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
throw new Error('Unexpected: Task does not exist.');
|
||||
}
|
||||
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
|
||||
this._taskExecutions.set(execution.id, createdResult);
|
||||
return createdResult;
|
||||
}
|
||||
|
||||
private customExecutionComplete(execution: tasks.TaskExecutionDTO): void {
|
||||
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
|
||||
if (extensionCallback2) {
|
||||
this._activeCustomExecutions2.delete(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 the map of executions
|
||||
// could grow indefinitely, something we don't want.
|
||||
if (this._notProvidedCustomExecutions.has(execution.id) && (this._lastStartedTask !== execution.id)) {
|
||||
this._providedCustomExecutions2.delete(execution.id);
|
||||
this._notProvidedCustomExecutions.delete(execution.id);
|
||||
}
|
||||
let iterator = this._notProvidedCustomExecutions.values();
|
||||
let iteratorResult = iterator.next();
|
||||
while (!iteratorResult.done) {
|
||||
if (!this._activeCustomExecutions2.has(iteratorResult.value) && (this._lastStartedTask !== iteratorResult.value)) {
|
||||
this._providedCustomExecutions2.delete(iteratorResult.value);
|
||||
this._notProvidedCustomExecutions.delete(iteratorResult.value);
|
||||
}
|
||||
iteratorResult = iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract async $jsonTasksSupported(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
authority: initData.remote.authority,
|
||||
platform: Platform.PlatformToString(Platform.Platform.Web)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
const dto = TaskDTO.from(task, extension);
|
||||
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, false);
|
||||
} else {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
|
||||
}
|
||||
|
||||
protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO && CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
taskDTOs.push(taskDTO);
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task, true));
|
||||
} else {
|
||||
console.warn('Only custom execution tasks supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
}
|
||||
|
||||
protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
return resolvedTaskDTO;
|
||||
} else {
|
||||
console.warn('Only custom execution tasks supported.');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
const result = {
|
||||
process: <unknown>undefined as string,
|
||||
variables: Object.create(null)
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async $jsonTasksSupported(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostTask = createDecorator<IExtHostTask>('IExtHostTask');
|
||||
|
||||
@@ -41,8 +41,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
public get cspSource(): string {
|
||||
return this._initData.webviewCspSource
|
||||
.replace('{{uuid}}', this._handle)
|
||||
.replace('{{commit}}', this._initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44');
|
||||
.replace('{{uuid}}', this._handle);
|
||||
}
|
||||
|
||||
public get html(): string {
|
||||
@@ -224,6 +223,18 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
|
||||
this._proxy.$setState(this._handle, typeConverters.WebviewEditorState.from(newState));
|
||||
}
|
||||
|
||||
private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable<boolean>) => void }>();
|
||||
public readonly onWillSave = this._onWillSave.event;
|
||||
|
||||
async _save(): Promise<boolean> {
|
||||
const waitingOn: Thenable<boolean>[] = [];
|
||||
this._onWillSave.fire({
|
||||
waitUntil: (thenable: Thenable<boolean>): void => { waitingOn.push(thenable); },
|
||||
});
|
||||
const result = await Promise.all(waitingOn);
|
||||
return result.every(x => x);
|
||||
}
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
@@ -422,6 +433,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel));
|
||||
}
|
||||
|
||||
async $save(handle: WebviewPanelHandle): Promise<boolean> {
|
||||
const panel = this.getWebviewPanel(handle);
|
||||
if (panel) {
|
||||
return panel._save();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function convertWebviewOptions(
|
||||
|
||||
@@ -7,7 +7,6 @@ import { URI } from 'vs/base/common/uri';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface WebviewInitData {
|
||||
readonly commit?: string;
|
||||
readonly webviewResourceRoot: string;
|
||||
readonly webviewCspSource: string;
|
||||
}
|
||||
@@ -18,7 +17,6 @@ export function asWebviewUri(
|
||||
resource: vscode.Uri,
|
||||
): vscode.Uri {
|
||||
const uri = initData.webviewResourceRoot
|
||||
.replace('{{commit}}', initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44')
|
||||
.replace('{{resource}}', resource.toString().replace(/^\S+?:/, ''))
|
||||
.replace('{{uuid}}', uuid);
|
||||
return URI.parse(uri);
|
||||
|
||||
@@ -6,375 +6,23 @@
|
||||
import * as path from 'vs/base/common/path';
|
||||
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { win32 } from 'vs/base/node/processes';
|
||||
|
||||
|
||||
import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
|
||||
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
|
||||
ShellExecutionOptionsDTO, ShellExecutionDTO,
|
||||
CustomExecution2DTO,
|
||||
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
|
||||
} from '../common/shared/tasks';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecution2DTO, HandlerData } from 'vs/workbench/api/common/extHostTask';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: ProcessExecutionDTO = {
|
||||
process: value.process,
|
||||
args: value.args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ProcessExecutionDTO): types.ProcessExecution | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return new types.ProcessExecution(value.process, value.args, value.options);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ShellExecution): ShellExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: ShellExecutionDTO = {
|
||||
};
|
||||
if (value.commandLine !== undefined) {
|
||||
result.commandLine = value.commandLine;
|
||||
} else {
|
||||
result.command = value.command;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionDTO): types.ShellExecution | undefined {
|
||||
if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
if (value.commandLine) {
|
||||
return new types.ShellExecution(value.commandLine, value.options);
|
||||
} else {
|
||||
return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace CustomExecution2DTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
|
||||
if (value) {
|
||||
let candidate = value as CustomExecution2DTO;
|
||||
return candidate && candidate.customExecution === 'customExecution2';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function from(value: vscode.CustomExecution2): CustomExecution2DTO {
|
||||
return {
|
||||
customExecution: 'customExecution2'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskHandleDTO {
|
||||
export function from(value: types.Task): TaskHandleDTO {
|
||||
let folder: UriComponents | undefined;
|
||||
if (value.scope !== undefined && typeof value.scope !== 'number') {
|
||||
folder = value.scope.uri;
|
||||
}
|
||||
return {
|
||||
id: value._id!,
|
||||
workspaceFolder: folder!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDTO {
|
||||
|
||||
export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): TaskDTO[] {
|
||||
if (tasks === undefined || tasks === null) {
|
||||
return [];
|
||||
}
|
||||
const result: TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
const converted = from(task, extension);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined;
|
||||
if (value.execution instanceof types.ProcessExecution) {
|
||||
execution = ProcessExecutionDTO.from(value.execution);
|
||||
} else if (value.execution instanceof types.ShellExecution) {
|
||||
execution = ShellExecutionDTO.from(value.execution);
|
||||
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
|
||||
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
|
||||
}
|
||||
|
||||
const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
|
||||
let scope: number | UriComponents;
|
||||
if (value.scope) {
|
||||
if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = value.scope.uri;
|
||||
}
|
||||
} else {
|
||||
// To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here:
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
|
||||
const result: TaskDTO = {
|
||||
_id: (value as types.Task)._id!,
|
||||
definition,
|
||||
name: value.name,
|
||||
source: {
|
||||
extensionId: extension.identifier.value,
|
||||
label: value.source,
|
||||
scope: scope
|
||||
},
|
||||
execution: execution!,
|
||||
isBackground: value.isBackground,
|
||||
group: group,
|
||||
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
|
||||
problemMatchers: value.problemMatchers,
|
||||
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
|
||||
runOptions: (<vscode.Task>value).runOptions ? (<vscode.Task>value).runOptions : { reevaluateOnRerun: true },
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export async function to(value: TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: types.ShellExecution | types.ProcessExecution | undefined;
|
||||
if (ProcessExecutionDTO.is(value.execution)) {
|
||||
execution = ProcessExecutionDTO.to(value.execution);
|
||||
} else if (ShellExecutionDTO.is(value.execution)) {
|
||||
execution = ShellExecutionDTO.to(value.execution);
|
||||
}
|
||||
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
|
||||
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
if (value.source) {
|
||||
if (value.source.scope !== undefined) {
|
||||
if (typeof value.source.scope === 'number') {
|
||||
scope = value.source.scope;
|
||||
} else {
|
||||
scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
|
||||
}
|
||||
} else {
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers);
|
||||
if (value.isBackground !== undefined) {
|
||||
result.isBackground = value.isBackground;
|
||||
}
|
||||
if (value.group !== undefined) {
|
||||
result.group = types.TaskGroup.from(value.group);
|
||||
}
|
||||
if (value.presentationOptions) {
|
||||
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!;
|
||||
}
|
||||
if (value._id) {
|
||||
result._id = value._id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskFilterDTO {
|
||||
export function from(value: vscode.TaskFilter | undefined): TaskFilterDTO | undefined {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function to(value: TaskFilterDTO): vscode.TaskFilter | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
|
||||
constructor(private readonly _tasks: ExtHostTask, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
}
|
||||
|
||||
public get task(): vscode.Task {
|
||||
return this._task;
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this._tasks.terminateTask(this);
|
||||
}
|
||||
|
||||
public fireDidStartProcess(value: TaskProcessStartedDTO): void {
|
||||
}
|
||||
|
||||
public fireDidEndProcess(value: TaskProcessEndedDTO): void {
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskExecutionDTO {
|
||||
export async function to(value: TaskExecutionDTO, tasks: ExtHostTask, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
|
||||
const task = await TaskDTO.to(value.task, workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be created.');
|
||||
}
|
||||
return new TaskExecutionImpl(tasks, value.id, task);
|
||||
}
|
||||
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
|
||||
return {
|
||||
id: (value as TaskExecutionImpl)._id,
|
||||
task: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface HandlerData {
|
||||
type: string;
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
export class ExtHostTask implements ExtHostTaskShape {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _proxy: MainThreadTaskShape;
|
||||
private readonly _workspaceProvider: IExtHostWorkspaceProvider;
|
||||
private readonly _editorService: IExtHostDocumentsAndEditors;
|
||||
private readonly _configurationService: IExtHostConfiguration;
|
||||
private readonly _terminalService: IExtHostTerminalService;
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, HandlerData>;
|
||||
private _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
|
||||
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
|
||||
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
|
||||
|
||||
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
|
||||
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -384,17 +32,7 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceProvider = workspaceService;
|
||||
this._editorService = editorService;
|
||||
this._configurationService = configurationService;
|
||||
this._terminalService = extHostTerminalService;
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
@@ -404,36 +42,6 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
}
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
const handle = this.nextHandle();
|
||||
this._handlers.set(handle, { type, provider, extension });
|
||||
this._proxy.$registerTaskProvider(handle, type);
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._proxy.$unregisterTaskProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void {
|
||||
this._proxy.$registerTaskSystem(scheme, info);
|
||||
}
|
||||
|
||||
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
|
||||
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
|
||||
const result: vscode.Task[] = [];
|
||||
for (let value of values) {
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider);
|
||||
if (task) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
const tTask = (task as types.Task);
|
||||
// We have a preserved ID. So the task didn't change.
|
||||
@@ -449,175 +57,42 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
// 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);
|
||||
await this.addCustomExecution2(dto, <vscode.Task2>task, false);
|
||||
}
|
||||
|
||||
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
|
||||
}
|
||||
}
|
||||
|
||||
public get taskExecutions(): vscode.TaskExecution[] {
|
||||
const result: vscode.TaskExecution[] = [];
|
||||
this._taskExecutions.forEach(value => result.push(value));
|
||||
return result;
|
||||
}
|
||||
protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
public terminateTask(execution: vscode.TaskExecution): Promise<void> {
|
||||
if (!(execution instanceof TaskExecutionImpl)) {
|
||||
throw new Error('No valid task execution provided');
|
||||
}
|
||||
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
|
||||
}
|
||||
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO) {
|
||||
taskDTOs.push(taskDTO);
|
||||
|
||||
public get onDidStartTask(): Event<vscode.TaskStartEvent> {
|
||||
return this._onDidExecuteTask.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise<void> {
|
||||
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
|
||||
if (execution2) {
|
||||
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
|
||||
throw new Error('We should not be trying to start the same custom task executions twice.');
|
||||
}
|
||||
|
||||
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
|
||||
this._activeCustomExecutions2.set(execution.id, execution2);
|
||||
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
|
||||
}
|
||||
|
||||
this._onDidExecuteTask.fire({
|
||||
execution: await this.getTaskExecution(execution)
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidEndTask(): Event<vscode.TaskEndEvent> {
|
||||
return this._onDidTerminateTask.event;
|
||||
}
|
||||
|
||||
public async $OnDidEndTask(execution: TaskExecutionDTO): Promise<void> {
|
||||
const _execution = await this.getTaskExecution(execution);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.customExecutionComplete(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
execution: _execution
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidStartTaskProcess(): Event<vscode.TaskProcessStartEvent> {
|
||||
return this._onDidTaskProcessStarted.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTaskProcess(value: TaskProcessStartedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessStarted.fire({
|
||||
execution: execution,
|
||||
processId: value.processId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
|
||||
return this._onDidTaskProcessEnded.event;
|
||||
}
|
||||
|
||||
public async $onDidEndTaskProcess(value: TaskProcessEndedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessEnded.fire({
|
||||
execution: execution,
|
||||
exitCode: value.exitCode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Knowing this ID ahead of time is needed because when a task
|
||||
// start event is fired this is when the custom execution is called.
|
||||
// The task start event is also the first time we see the ID from the main
|
||||
// thread, which is too late for us because we need to save an map
|
||||
// from an ID to the custom execution function. (Kind of a cart before the horse problem).
|
||||
const taskIdPromises: Promise<void>[] = [];
|
||||
const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
const taskDTOs: TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO) {
|
||||
taskDTOs.push(taskDTO);
|
||||
|
||||
if (CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
|
||||
}
|
||||
if (CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetchPromise.then((result) => {
|
||||
Promise.all(taskIdPromises).then(() => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} disable debug related method
|
||||
public async $resolveTask(handle: number, taskDTO: TaskDTO): Promise<TaskDTO | undefined> {
|
||||
/*const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
if (taskDTO.definition.type !== handler.type) {
|
||||
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
|
||||
}
|
||||
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None);
|
||||
if (!resolvedTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedTaskDTO: TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
|
||||
if (!resolvedTaskDTO) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
if (resolvedTask.definition !== task.definition) {
|
||||
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
|
||||
}
|
||||
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask);
|
||||
}
|
||||
|
||||
return resolvedTaskDTO;*/
|
||||
return undefined;
|
||||
protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
return resolvedTaskDTO;
|
||||
}
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
@@ -666,53 +141,7 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
return this._terminalService.$requestDefaultShellAndArgs(true);
|
||||
}
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
|
||||
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
|
||||
const taskId = await this._proxy.$createTaskId(taskDTO);
|
||||
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
|
||||
}
|
||||
|
||||
private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
|
||||
if (typeof execution === 'string') {
|
||||
const taskExecution = this._taskExecutions.get(execution);
|
||||
if (!taskExecution) {
|
||||
throw new Error('Unexpected: The specified task is missing an execution');
|
||||
}
|
||||
return taskExecution;
|
||||
}
|
||||
|
||||
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
throw new Error('Unexpected: Task does not exist.');
|
||||
}
|
||||
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
|
||||
this._taskExecutions.set(execution.id, createdResult);
|
||||
return createdResult;
|
||||
}
|
||||
|
||||
private customExecutionComplete(execution: TaskExecutionDTO): void {
|
||||
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
|
||||
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);
|
||||
}
|
||||
public async $jsonTasksSupported(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import * as os from 'os';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
@@ -181,7 +181,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
envFromConfig,
|
||||
this._variableResolver,
|
||||
isWorkspaceShellAllowed,
|
||||
pkg.version,
|
||||
product.version,
|
||||
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
|
||||
baseEnv
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ import { clamp } from 'vs/base/common/numbers';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
export class InspectContextKeysAction extends Action {
|
||||
class InspectContextKeysAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.inspectContextKeys';
|
||||
static LABEL = nls.localize('inspect context keys', "Inspect Context Keys");
|
||||
@@ -91,7 +91,7 @@ export class InspectContextKeysAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class ToggleScreencastModeAction extends Action {
|
||||
class ToggleScreencastModeAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleScreencastMode';
|
||||
static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode");
|
||||
@@ -195,7 +195,7 @@ export class ToggleScreencastModeAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class LogStorageAction extends Action {
|
||||
class LogStorageAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.logStorage';
|
||||
static LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents");
|
||||
@@ -224,8 +224,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysActi
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory);
|
||||
|
||||
// --- Menu Registration
|
||||
|
||||
// Screencast Mode
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
|
||||
388
src/vs/workbench/browser/actions/helpActions.ts
Normal file
@@ -0,0 +1,388 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { isMacintosh, isLinux, language } from 'vs/base/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
|
||||
class KeybindingsReferenceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.keybindingsReference';
|
||||
static readonly LABEL = nls.localize('keybindingsReference', "Keyboard Shortcuts Reference");
|
||||
static readonly AVAILABLE = !!(isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin);
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
const url = isLinux ? this.productService.keyboardShortcutsUrlLinux : isMacintosh ? this.productService.keyboardShortcutsUrlMac : this.productService.keyboardShortcutsUrlWin;
|
||||
if (url) {
|
||||
this.openerService.open(URI.parse(url));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenDocumentationUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openDocumentationUrl';
|
||||
static readonly LABEL = nls.localize('openDocumentationUrl', "Documentation");
|
||||
static readonly AVAILABLE = !!product.documentationUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.documentationUrl) {
|
||||
this.openerService.open(URI.parse(this.productService.documentationUrl));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenIntroductoryVideosUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openIntroductoryVideosUrl';
|
||||
static readonly LABEL = nls.localize('openIntroductoryVideosUrl', "Introductory Videos");
|
||||
static readonly AVAILABLE = !!product.introductoryVideosUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.introductoryVideosUrl) {
|
||||
this.openerService.open(URI.parse(this.productService.introductoryVideosUrl));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenTipsAndTricksUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openTipsAndTricksUrl';
|
||||
static readonly LABEL = nls.localize('openTipsAndTricksUrl', "Tips and Tricks");
|
||||
static readonly AVAILABLE = !!product.tipsAndTricksUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.tipsAndTricksUrl) {
|
||||
this.openerService.open(URI.parse(this.productService.tipsAndTricksUrl));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenNewsletterSignupUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openNewsletterSignupUrl';
|
||||
static readonly LABEL = nls.localize('newsletterSignup', "Signup for the VS Code Newsletter");
|
||||
static readonly AVAILABLE = !!product.newsletterSignupUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const info = await this.telemetryService.getTelemetryInfo();
|
||||
|
||||
this.openerService.open(URI.parse(`${this.productService.newsletterSignupUrl}?machineId=${encodeURIComponent(info.machineId)}`));
|
||||
}
|
||||
}
|
||||
|
||||
class OpenTwitterUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openTwitterUrl';
|
||||
static readonly LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter");
|
||||
static readonly AVAILABLE = !!product.twitterUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.twitterUrl) {
|
||||
this.openerService.open(URI.parse(this.productService.twitterUrl));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenRequestFeatureUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openRequestFeatureUrl';
|
||||
static readonly LABEL = nls.localize('openUserVoiceUrl', "Search Feature Requests");
|
||||
static readonly AVAILABLE = !!product.requestFeatureUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.requestFeatureUrl) {
|
||||
this.openerService.open(URI.parse(this.productService.requestFeatureUrl));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenLicenseUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openLicenseUrl';
|
||||
static readonly LABEL = nls.localize('openLicenseUrl', "View License");
|
||||
static readonly AVAILABLE = !!product.licenseUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.licenseUrl) {
|
||||
if (language) {
|
||||
const queryArgChar = this.productService.licenseUrl.indexOf('?') > 0 ? '&' : '?';
|
||||
this.openerService.open(URI.parse(`${this.productService.licenseUrl}${queryArgChar}lang=${language}`));
|
||||
} else {
|
||||
this.openerService.open(URI.parse(this.productService.licenseUrl));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class OpenPrivacyStatementUrlAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openPrivacyStatementUrl';
|
||||
static readonly LABEL = nls.localize('openPrivacyStatement', "Privacy Statement");
|
||||
static readonly AVAILABE = !!product.privacyStatementUrl;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.productService.privacyStatementUrl) {
|
||||
if (language) {
|
||||
const queryArgChar = this.productService.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?';
|
||||
this.openerService.open(URI.parse(`${this.productService.privacyStatementUrl}${queryArgChar}lang=${language}`));
|
||||
} else {
|
||||
this.openerService.open(URI.parse(this.productService.privacyStatementUrl));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Actions Registration
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
const helpCategory = nls.localize('help', "Help");
|
||||
|
||||
if (KeybindingsReferenceAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenDocumentationUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenIntroductoryVideosUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenTipsAndTricksUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenNewsletterSignupUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenTwitterUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenRequestFeatureUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenLicenseUrlAction.AVAILABLE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory);
|
||||
}
|
||||
|
||||
if (OpenPrivacyStatementUrlAction.AVAILABE) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory);
|
||||
}
|
||||
|
||||
// --- Menu Registration
|
||||
|
||||
// Help
|
||||
|
||||
if (OpenDocumentationUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: OpenDocumentationUrlAction.ID,
|
||||
title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
}
|
||||
/* // {{SQL CARBON EDIT}} - Disable unused menu item
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: 'update.showCurrentReleaseNotes',
|
||||
title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
// Reference
|
||||
if (KeybindingsReferenceAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: KeybindingsReferenceAction.ID,
|
||||
title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (OpenIntroductoryVideosUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: OpenIntroductoryVideosUrlAction.ID,
|
||||
title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
}
|
||||
|
||||
if (OpenTipsAndTricksUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: OpenTipsAndTricksUrlAction.ID,
|
||||
title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
}
|
||||
|
||||
// Feedback
|
||||
if (OpenTwitterUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: OpenTwitterUrlAction.ID,
|
||||
title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (OpenRequestFeatureUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: OpenRequestFeatureUrlAction.ID,
|
||||
title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
}*/
|
||||
|
||||
// Legal
|
||||
if (OpenLicenseUrlAction.AVAILABLE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: OpenLicenseUrlAction.ID,
|
||||
title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (OpenPrivacyStatementUrlAction.AVAILABE) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: OpenPrivacyStatementUrlAction.ID,
|
||||
title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
@@ -142,15 +141,6 @@ export class ToggleEditorLayoutAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', function (accessor: ServicesAccessor, args: [GroupOrientation]) {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const [orientation] = args;
|
||||
|
||||
editorGroupService.setGroupOrientation(orientation);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
const group = viewCategory;
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group);
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import 'vs/css!./media/actions';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IWindowService, IURIToOpen, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService, IURIToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
@@ -162,7 +163,7 @@ export class OpenRecentAction extends BaseOpenRecentAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenRecentAction extends BaseOpenRecentAction {
|
||||
class QuickOpenRecentAction extends BaseOpenRecentAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenRecent';
|
||||
static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent...");
|
||||
@@ -186,7 +187,7 @@ export class QuickOpenRecentAction extends BaseOpenRecentAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class ToggleFullScreenAction extends Action {
|
||||
class ToggleFullScreenAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleFullScreen';
|
||||
static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");
|
||||
@@ -226,7 +227,7 @@ export class ReloadWindowAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowAboutDialogAction extends Action {
|
||||
class ShowAboutDialogAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.showAboutDialog';
|
||||
static readonly LABEL = nls.localize('about', "About");
|
||||
@@ -234,13 +235,13 @@ export class ShowAboutDialogAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWindowsService private readonly windowsService: IWindowsService
|
||||
@IDialogService private readonly dialogService: IDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.windowsService.openAboutDialog();
|
||||
return this.dialogService.about();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,24 +5,18 @@
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { WorkbenchStateContext, SupportsWorkspacesContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
@@ -42,36 +36,6 @@ export class OpenFileAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace OpenLocalFileCommand {
|
||||
export const ID = 'workbench.action.files.openLocalFile';
|
||||
export const LABEL = nls.localize('openLocalFile', "Open Local File...");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return accessor => {
|
||||
const dialogService = accessor.get(IFileDialogService);
|
||||
return dialogService.pickFileAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SaveLocalFileCommand {
|
||||
export const ID = 'workbench.action.files.saveLocalFile';
|
||||
export const LABEL = nls.localize('saveLocalFile', "Save Local File...");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return accessor => {
|
||||
const textFileService = accessor.get(ITextFileService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
let resource: URI | undefined = toResource(editorService.activeEditor);
|
||||
const options: ISaveOptions = { force: true, availableFileSystems: [Schemas.file] };
|
||||
if (resource) {
|
||||
return textFileService.saveAs(resource, undefined, options);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFolder';
|
||||
@@ -90,19 +54,6 @@ export class OpenFolderAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace OpenLocalFolderCommand {
|
||||
export const ID = 'workbench.action.files.openLocalFolder';
|
||||
export const LABEL = nls.localize('openLocalFolder', "Open Local Folder...");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return accessor => {
|
||||
const dialogService = accessor.get(IFileDialogService);
|
||||
return dialogService.pickFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class OpenFileFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFileFolder';
|
||||
@@ -121,16 +72,46 @@ export class OpenFileFolderAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace OpenLocalFileFolderCommand {
|
||||
export class OpenWorkspaceAction extends Action {
|
||||
|
||||
export const ID = 'workbench.action.files.openLocalFileFolder';
|
||||
export const LABEL = nls.localize('openLocalFileFolder', "Open Local...");
|
||||
static readonly ID = 'workbench.action.openWorkspace';
|
||||
static LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return accessor => {
|
||||
const dialogService = accessor.get(IFileDialogService);
|
||||
return dialogService.pickFileFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] });
|
||||
};
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: any, data?: ITelemetryData): Promise<any> {
|
||||
return this.dialogService.pickWorkspaceAndOpen({ telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenWorkspaceConfigFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspaceConfigFile';
|
||||
static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.workspaceContextService.getWorkspace().configuration;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const configuration = this.workspaceContextService.getWorkspace().configuration;
|
||||
if (configuration) {
|
||||
return this.editorService.openEditor({ resource: configuration });
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,141 +163,30 @@ export class GlobalRemoveRootFolderAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class SaveWorkspaceAsAction extends Action {
|
||||
// --- Actions Registration
|
||||
|
||||
static readonly ID = 'workbench.action.saveWorkspaceAs';
|
||||
static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
const workspacesCategory = nls.localize('workspaces', "Workspaces");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService
|
||||
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<any> {
|
||||
const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath();
|
||||
if (configPathUri) {
|
||||
switch (this.contextService.getWorkbenchState()) {
|
||||
case WorkbenchState.EMPTY:
|
||||
case WorkbenchState.FOLDER:
|
||||
const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri }));
|
||||
return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri);
|
||||
case WorkbenchState.WORKSPACE:
|
||||
return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspace';
|
||||
static LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: any, data?: ITelemetryData): Promise<any> {
|
||||
return this.dialogService.pickWorkspaceAndOpen({ telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closeFolder';
|
||||
static LABEL = nls.localize('closeWorkspace', "Close Workspace");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IWindowService private readonly windowService: IWindowService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close."));
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this.windowService.closeWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenWorkspaceConfigFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspaceConfigFile';
|
||||
static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.workspaceContextService.getWorkspace().configuration;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const configuration = this.workspaceContextService.getWorkspace().configuration;
|
||||
if (configuration) {
|
||||
return this.editorService.openEditor({ resource: configuration });
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export class DuplicateWorkspaceInNewWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow';
|
||||
static readonly LABEL = nls.localize('duplicateWorkspaceInNewWindow', "Duplicate Workspace in New Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IWorkspacesService private readonly workspacesService: IWorkspacesService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<any> {
|
||||
const folders = this.workspaceContextService.getWorkspace().folders;
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
|
||||
const newWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority);
|
||||
await this.workspaceEditingService.copyWorkspaceSettings(newWorkspace);
|
||||
|
||||
return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true });
|
||||
}
|
||||
}
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory, SupportsWorkspacesContext);
|
||||
|
||||
// --- Menu Registration
|
||||
|
||||
const workspacesCategory = nls.localize('workspaces', "Workspaces");
|
||||
|
||||
CommandsRegistry.registerCommand(OpenWorkspaceConfigFileAction.ID, serviceAccessor => {
|
||||
serviceAccessor.get(IInstantiationService).createInstance(OpenWorkspaceConfigFileAction, OpenWorkspaceConfigFileAction.ID, OpenWorkspaceConfigFileAction.LABEL).run();
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '3_workspace',
|
||||
command: {
|
||||
id: ADD_ROOT_FOLDER_COMMAND_ID,
|
||||
title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...")
|
||||
},
|
||||
order: 1,
|
||||
when: SupportsWorkspacesContext
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: OpenWorkspaceConfigFileAction.ID,
|
||||
|
||||
@@ -141,8 +141,10 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
// Development
|
||||
IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment);
|
||||
|
||||
// File Pickers
|
||||
SupportsWorkspacesContext.bindTo(this.contextKeyService);
|
||||
// Workspaces Support
|
||||
// - web: only if already in workspace state
|
||||
// - desktop: always
|
||||
SupportsWorkspacesContext.bindTo(this.contextKeyService).set(isWeb ? this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE : true);
|
||||
|
||||
// Editors
|
||||
this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService);
|
||||
|
||||
@@ -43,20 +43,20 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa
|
||||
private static readonly sashYHeightSettingsKey = 'workbench.panel.height';
|
||||
private static readonly panelSizeBeforeMaximizedKey = 'workbench.panel.sizeBeforeMaximized';
|
||||
|
||||
private workbenchSize: Dimension;
|
||||
private workbenchSize!: Dimension;
|
||||
|
||||
private sashXOne: Sash;
|
||||
private sashXTwo: Sash;
|
||||
private sashY: Sash;
|
||||
|
||||
private _sidebarWidth: number;
|
||||
private sidebarHeight: number;
|
||||
private titlebarHeight: number;
|
||||
private statusbarHeight: number;
|
||||
private panelSizeBeforeMaximized: number;
|
||||
private panelMaximized: boolean;
|
||||
private _panelHeight: number;
|
||||
private _panelWidth: number;
|
||||
private _sidebarWidth!: number;
|
||||
private sidebarHeight!: number;
|
||||
private titlebarHeight!: number;
|
||||
private statusbarHeight!: number;
|
||||
private panelSizeBeforeMaximized!: number;
|
||||
private panelMaximized!: boolean;
|
||||
private _panelHeight!: number;
|
||||
private _panelWidth!: number;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
@@ -413,7 +413,7 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa
|
||||
this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (isMacintosh || !menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention
|
||||
|
||||
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
|
||||
let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight);
|
||||
let sidebarSize = { width: this.sidebarWidth, height: this.sidebarHeight };
|
||||
|
||||
// Activity Bar
|
||||
let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height);
|
||||
@@ -463,7 +463,7 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa
|
||||
|
||||
this.storageService.store(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL);
|
||||
|
||||
const panelDimension = new Dimension(panelWidth, panelHeight);
|
||||
const panelDimension = { width: panelWidth, height: panelHeight };
|
||||
|
||||
// Editor
|
||||
let editorSize = {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; }
|
||||
.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
|
||||
.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
|
||||
.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Meiryo", sans-serif; }
|
||||
.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; }
|
||||
.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
|
||||
|
||||
.linux { font-family: "Ubuntu", "Droid Sans", sans-serif; }
|
||||
@@ -23,7 +23,7 @@
|
||||
.linux:lang(ja) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
|
||||
.linux:lang(ko) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
|
||||
|
||||
.mac { --monaco-monospace-font: Monaco, Menlo, Inconsolata, "Courier New", monospace; }
|
||||
.mac { --monaco-monospace-font: "SF Mono", Monaco, Menlo, Inconsolata, "Courier New", monospace; }
|
||||
.windows { --monaco-monospace-font: Consolas, Inconsolata, "Courier New", monospace; }
|
||||
.linux { --monaco-monospace-font: "Droid Sans Mono", Inconsolata, "Courier New", monospace, "Droid Sans Fallback"; }
|
||||
|
||||
|
||||
@@ -156,13 +156,14 @@ class PartLayout {
|
||||
titleSize = new Dimension(0, 0);
|
||||
}
|
||||
|
||||
// Content Size: Width (Fill), Height (Variable)
|
||||
const contentSize = new Dimension(width, height - titleSize.height);
|
||||
|
||||
let contentWidth = width;
|
||||
if (this.options && typeof this.options.borderWidth === 'function') {
|
||||
contentSize.width -= this.options.borderWidth(); // adjust for border size
|
||||
contentWidth -= this.options.borderWidth(); // adjust for border size
|
||||
}
|
||||
|
||||
// Content Size: Width (Fill), Height (Variable)
|
||||
const contentSize = new Dimension(contentWidth, height - titleSize.height);
|
||||
|
||||
// Content
|
||||
if (this.contentArea) {
|
||||
size(this.contentArea, contentSize.width, contentSize.height);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M7.97603 10.0719L12.3333 5.7146L12.9521 6.33332L8.28539 11L7.66667 11L3 6.33332L3.61872 5.7146L7.97603 10.0719Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 282 B |
@@ -1,3 +0,0 @@
|
||||
<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="M7.97603 10.0719L12.3333 5.7146L12.9521 6.33332L8.28539 11L7.66667 11L3 6.33332L3.61872 5.7146L7.97603 10.0719Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 280 B |
@@ -1,3 +0,0 @@
|
||||
<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="M7.97603 10.0719L12.3333 5.7146L12.9521 6.33332L8.28539 11L7.66667 11L3 6.33332L3.61872 5.7146L7.97603 10.0719Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 282 B |
@@ -1,3 +0,0 @@
|
||||
<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="M5.92809 7.97603L10.2854 12.3333L9.66668 12.9521L5.00001 8.28539V7.66667L9.66668 3L10.2854 3.61872L5.92809 7.97603Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1,3 +0,0 @@
|
||||
<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="M5.92809 7.97603L10.2854 12.3333L9.66668 12.9521L5.00001 8.28539V7.66667L9.66668 3L10.2854 3.61872L5.92809 7.97603Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 284 B |
@@ -1,3 +0,0 @@
|
||||
<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="M5.92809 7.97603L10.2854 12.3333L9.66668 12.9521L5.00001 8.28539V7.66667L9.66668 3L10.2854 3.61872L5.92809 7.97603Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1,3 +0,0 @@
|
||||
<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="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69063L6.33332 3.02396L5.7146 3.64268L10.0719 7.99999Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 284 B |
@@ -1,3 +0,0 @@
|
||||
<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="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69063L6.33332 3.02396L5.7146 3.64268L10.0719 7.99999Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 282 B |
@@ -1,3 +0,0 @@
|
||||
<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="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69063L6.33332 3.02396L5.7146 3.64268L10.0719 7.99999Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 284 B |
@@ -1,3 +0,0 @@
|
||||
<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="M8 6.04042L3.02022 11.0202L2.31311 10.3131L7.64644 4.97976L8.35355 4.97976L13.6869 10.3131L12.9798 11.0202L8 6.04042Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 288 B |
@@ -1,3 +0,0 @@
|
||||
<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="M8 6.04042L3.02022 11.0202L2.31311 10.3131L7.64644 4.97976L8.35355 4.97976L13.6869 10.3131L12.9798 11.0202L8 6.04042Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1,3 +0,0 @@
|
||||
<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="M8 6.04042L3.02022 11.0202L2.31311 10.3131L7.64644 4.97976L8.35355 4.97976L13.6869 10.3131L12.9798 11.0202L8 6.04042Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 288 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4L12 12" stroke="#C5C5C5"/>
|
||||
<path d="M12 4L4 12" stroke="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 183 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4L12 12" stroke="white"/>
|
||||
<path d="M12 4L4 12" stroke="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 179 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4L12 12" stroke="#424242"/>
|
||||
<path d="M12 4L4 12" stroke="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 183 B |
@@ -123,69 +123,3 @@
|
||||
min-width: 110px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Close */
|
||||
.monaco-workbench .hide-panel-action {
|
||||
background: url('close-light.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .hide-panel-action {
|
||||
background: url('close-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .hide-panel-action {
|
||||
background: url('close-hc.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
||||
/* Up */
|
||||
.monaco-workbench .maximize-panel-action {
|
||||
background-image: url('chevron-up-light.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .maximize-panel-action {
|
||||
background-image: url('chevron-up-dark.svg');
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .maximize-panel-action {
|
||||
background-image: url('chevron-up-hc.svg');
|
||||
}
|
||||
|
||||
/* Down */
|
||||
.monaco-workbench .minimize-panel-action {
|
||||
background-image: url('chevron-down-light.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .minimize-panel-action {
|
||||
background-image: url('chevron-down-dark.svg');
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .minimize-panel-action {
|
||||
background-image: url('chevron-down-hc.svg');
|
||||
}
|
||||
|
||||
/* Left */
|
||||
.monaco-workbench .panel.right .maximize-panel-action {
|
||||
background-image: url('chevron-left-light.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .panel.right .maximize-panel-action {
|
||||
background-image: url('chevron-left-dark.svg');
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .panel.right .maximize-panel-action {
|
||||
background-image: url('chevron-left-hc.svg');
|
||||
}
|
||||
|
||||
/* Right */
|
||||
.monaco-workbench .panel.right .minimize-panel-action {
|
||||
background-image: url('chevron-right-light.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .panel.right .minimize-panel-action {
|
||||
background-image: url('chevron-right-dark.svg');
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .panel.right .minimize-panel-action {
|
||||
background-image: url('chevron-right-hc.svg');
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ClosePanelAction extends Action {
|
||||
name: string,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, name, 'hide-panel-action');
|
||||
super(id, name, 'codicon-close');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -141,11 +141,11 @@ export class ToggleMaximizedPanelAction extends Action {
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IEditorGroupsService editorGroupsService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, layoutService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
|
||||
super(id, label, layoutService.isPanelMaximized() ? 'codicon-chevron-down' : 'codicon-chevron-up');
|
||||
|
||||
this.toDispose.add(editorGroupsService.onDidLayout(() => {
|
||||
const maximized = this.layoutService.isPanelMaximized();
|
||||
this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action';
|
||||
this.class = maximized ? 'codicon-chevron-down' : 'codicon-chevron-up';
|
||||
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/titlebarpart';
|
||||
import { dirname, posix } from 'vs/base/common/path';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
|
||||
import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import { IWindowService, IWindowsService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import * as nls from 'vs/nls';
|
||||
import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -29,7 +28,7 @@ import { trim } from 'vs/base/common/strings';
|
||||
import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { template, getBaseLabel } from 'vs/base/common/labels';
|
||||
import { template } from 'vs/base/common/labels';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
@@ -37,6 +36,9 @@ import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/bro
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
@@ -71,7 +73,6 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
private lastLayoutDimensions: Dimension;
|
||||
|
||||
private pendingTitle: string;
|
||||
private representedFileName: string;
|
||||
|
||||
private isInactive: boolean;
|
||||
|
||||
@@ -80,11 +81,12 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
private titleUpdater: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.doUpdateTitle(), 0));
|
||||
|
||||
private contextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@@ -92,10 +94,14 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
@@ -181,9 +187,6 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
// Apply to window
|
||||
this.windowService.setRepresentedFilename(path);
|
||||
|
||||
// Keep for context menu
|
||||
this.representedFileName = path;
|
||||
}
|
||||
|
||||
private doUpdateTitle(): void {
|
||||
@@ -254,6 +257,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
* {folderName}: e.g. myFolder
|
||||
* {folderPath}: e.g. /Users/Development/myFolder
|
||||
* {appName}: e.g. VS Code
|
||||
* {remoteName}: e.g. SSH
|
||||
* {dirty}: indicator
|
||||
* {separator}: conditional separator
|
||||
*/
|
||||
@@ -294,6 +298,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : '';
|
||||
const dirty = editor && editor.isDirty() ? TitlebarPart.TITLE_DIRTY : '';
|
||||
const appName = this.environmentService.appNameLong;
|
||||
const remoteName = this.environmentService.configuration.remoteAuthority;
|
||||
const separator = TitlebarPart.TITLE_SEPARATOR;
|
||||
const titleTemplate = this.configurationService.getValue<string>('window.title');
|
||||
|
||||
@@ -310,6 +315,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
folderPath,
|
||||
dirty,
|
||||
appName,
|
||||
remoteName,
|
||||
separator: { label: separator }
|
||||
});
|
||||
}
|
||||
@@ -376,7 +382,6 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
if (!isMacintosh && !isWeb) {
|
||||
this.windowControls = append(this.element, $('div.window-controls-container'));
|
||||
|
||||
|
||||
// Minimize
|
||||
const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg'));
|
||||
const minimizeIcon = append(minimizeIconContainer, $('div.window-icon'));
|
||||
@@ -503,44 +508,16 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const anchor = { x: event.posx, y: event.posy };
|
||||
|
||||
// Show menu
|
||||
const actions = this.getContextMenuActions();
|
||||
if (actions.length) {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => actions,
|
||||
onHide: () => actions.forEach(a => a.dispose())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getContextMenuActions(): IAction[] {
|
||||
// Fill in contributed actions
|
||||
const actions: IAction[] = [];
|
||||
const actionsDisposable = createAndFillInContextMenuActions(this.contextMenu, undefined, actions, this.contextMenuService);
|
||||
|
||||
if (this.representedFileName) {
|
||||
const segments = this.representedFileName.split(posix.sep);
|
||||
for (let i = segments.length; i > 0; i--) {
|
||||
const isFile = (i === segments.length);
|
||||
|
||||
let pathOffset = i;
|
||||
if (!isFile) {
|
||||
pathOffset++; // for segments which are not the file name we want to open the folder
|
||||
}
|
||||
|
||||
const path = segments.slice(0, pathOffset).join(posix.sep);
|
||||
|
||||
let label: string;
|
||||
if (!isFile) {
|
||||
label = getBaseLabel(dirname(path));
|
||||
} else {
|
||||
label = getBaseLabel(path);
|
||||
}
|
||||
|
||||
actions.push(new ShowItemInFolderAction(path, label || posix.sep, this.windowsService));
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => actions,
|
||||
onHide: () => dispose(actionsDisposable)
|
||||
});
|
||||
}
|
||||
|
||||
private adjustTitleMarginToCenter(): void {
|
||||
@@ -605,17 +582,6 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
}
|
||||
}
|
||||
|
||||
class ShowItemInFolderAction extends Action {
|
||||
|
||||
constructor(private path: string, label: string, private windowsService: IWindowsService) {
|
||||
super('showItemInFolder.action.id', label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.windowsService.showItemInFolder(URI.file(this.path));
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND);
|
||||
if (titlebarActiveFg) {
|
||||
|
||||
@@ -768,6 +768,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
}
|
||||
|
||||
templateData.icon.style.backgroundImage = iconUrl ? DOM.asCSSUrl(iconUrl) : '';
|
||||
templateData.icon.title = title ? title : '';
|
||||
DOM.toggleClass(templateData.icon, 'custom-view-tree-node-item-icon', !!iconUrl);
|
||||
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };
|
||||
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
|
||||
|
||||
@@ -6,11 +6,17 @@
|
||||
import 'vs/css!./media/style';
|
||||
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Icon defaults
|
||||
const iconForegroundColor = theme.getColor(iconForeground);
|
||||
if (iconForegroundColor) {
|
||||
collector.addRule(`.monaco-workbench .codicon { color: ${iconForegroundColor}; }`);
|
||||
}
|
||||
|
||||
// Foreground
|
||||
const windowForeground = theme.getColor(foreground);
|
||||
if (windowForeground) {
|
||||
@@ -136,4 +142,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -13,8 +13,8 @@ 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 product from 'vs/platform/product/browser/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import product 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';
|
||||
@@ -39,7 +39,7 @@ import { joinPath } from 'vs/base/common/resources';
|
||||
import { BrowserStorageService } from 'vs/platform/storage/browser/storageService';
|
||||
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 { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
|
||||
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
|
||||
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
|
||||
import { FileLogService } from 'vs/platform/log/common/fileLogService';
|
||||
@@ -75,6 +75,11 @@ class CodeRendererMain extends Disposable {
|
||||
// Layout
|
||||
this._register(addDisposableListener(window, EventType.RESIZE, () => workbench.layout()));
|
||||
|
||||
// Prevent the back/forward gestures in macOS
|
||||
this._register(addDisposableListener(this.domElement, EventType.WHEEL, (e) => {
|
||||
e.preventDefault();
|
||||
}, { passive: false }));
|
||||
|
||||
// Workbench Lifecycle
|
||||
this._register(workbench.onBeforeShutdown(event => {
|
||||
if (services.storageService.hasPendingUpdate) {
|
||||
@@ -222,7 +227,7 @@ class CodeRendererMain extends Disposable {
|
||||
|
||||
// User data
|
||||
if (!this.configuration.userDataProvider) {
|
||||
this.configuration.userDataProvider = this._register(new InMemoryUserDataProvider());
|
||||
this.configuration.userDataProvider = this._register(new InMemoryFileSystemProvider());
|
||||
}
|
||||
fileService.registerProvider(Schemas.userData, this.configuration.userDataProvider);
|
||||
}
|
||||
@@ -261,12 +266,12 @@ class CodeRendererMain extends Disposable {
|
||||
|
||||
// Multi-root workspace
|
||||
if (this.configuration.workspaceUri) {
|
||||
return { id: hash(URI.revive(this.configuration.workspaceUri).toString()).toString(16), configPath: URI.revive(this.configuration.workspaceUri) };
|
||||
return { id: hash(this.configuration.workspaceUri.toString()).toString(16), configPath: this.configuration.workspaceUri };
|
||||
}
|
||||
|
||||
// Single-folder workspace
|
||||
if (this.configuration.folderUri) {
|
||||
return { id: hash(URI.revive(this.configuration.folderUri).toString()).toString(16), folder: URI.revive(this.configuration.folderUri) };
|
||||
return { id: hash(this.configuration.folderUri.toString()).toString(16), folder: this.configuration.folderUri };
|
||||
}
|
||||
|
||||
return { id: 'empty-window' };
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/common/history';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -23,11 +23,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
//#region Window
|
||||
|
||||
@@ -246,7 +241,7 @@ export class SimpleWindowService extends Disposable implements IWindowService {
|
||||
for (let i = 0; i < _uris.length; i++) {
|
||||
const uri = _uris[i];
|
||||
if ('folderUri' in uri) {
|
||||
const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}`;
|
||||
const newAddress = `${document.location.origin}${document.location.pathname}?folder=${uri.folderUri.path}`;
|
||||
if (openFolderInNewWindow) {
|
||||
window.open(newAddress);
|
||||
} else {
|
||||
@@ -254,7 +249,7 @@ export class SimpleWindowService extends Disposable implements IWindowService {
|
||||
}
|
||||
}
|
||||
if ('workspaceUri' in uri) {
|
||||
const newAddress = `${document.location.origin}/?workspace=${uri.workspaceUri.path}`;
|
||||
const newAddress = `${document.location.origin}${document.location.pathname}?workspace=${uri.workspaceUri.path}`;
|
||||
if (openFolderInNewWindow) {
|
||||
window.open(newAddress);
|
||||
} else {
|
||||
@@ -323,8 +318,6 @@ registerSingleton(IWindowService, SimpleWindowService);
|
||||
export class SimpleWindowsService implements IWindowsService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
windowCount = 1;
|
||||
|
||||
readonly onWindowOpen: Event<number> = Event.None;
|
||||
readonly onWindowFocus: Event<number> = Event.None;
|
||||
readonly onWindowBlur: Event<number> = Event.None;
|
||||
@@ -332,13 +325,6 @@ export class SimpleWindowsService implements IWindowsService {
|
||||
readonly onWindowUnmaximize: Event<number> = Event.None;
|
||||
readonly onRecentlyOpenedChange: Event<void> = Event.None;
|
||||
|
||||
constructor(
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IClipboardService private readonly clipboardService: IClipboardService
|
||||
) {
|
||||
}
|
||||
|
||||
isFocused(_windowId: number): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -466,49 +452,6 @@ export class SimpleWindowsService implements IWindowsService {
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
|
||||
// we pass the "ParsedArgs" as query parameters of the URL
|
||||
|
||||
let newAddress = `${document.location.origin}/?`;
|
||||
let gotFolder = false;
|
||||
|
||||
const addQueryParameter = (key: string, value: string) => {
|
||||
const lastChar = newAddress.charAt(newAddress.length - 1);
|
||||
if (lastChar !== '?' && lastChar !== '&') {
|
||||
newAddress += '&';
|
||||
}
|
||||
newAddress += `${key}=${encodeURIComponent(value)}`;
|
||||
};
|
||||
|
||||
const f = args['folder-uri'];
|
||||
if (f) {
|
||||
const u = URI.parse(f[0]);
|
||||
gotFolder = true;
|
||||
addQueryParameter('folder', u.path);
|
||||
}
|
||||
if (!gotFolder) {
|
||||
// request empty window
|
||||
addQueryParameter('ew', 'true');
|
||||
}
|
||||
|
||||
const ep = args['extensionDevelopmentPath'];
|
||||
if (ep) {
|
||||
let u = ep[0];
|
||||
addQueryParameter('edp', u);
|
||||
}
|
||||
|
||||
const di = args['debugId'];
|
||||
if (di) {
|
||||
addQueryParameter('di', di);
|
||||
}
|
||||
|
||||
const ibe = args['inspect-brk-extensions'];
|
||||
if (ibe) {
|
||||
addQueryParameter('ibe', ibe);
|
||||
}
|
||||
|
||||
window.open(newAddress);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -516,18 +459,6 @@ export class SimpleWindowsService implements IWindowsService {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
getWindowCount(): Promise<number> {
|
||||
return Promise.resolve(this.windowCount);
|
||||
}
|
||||
|
||||
log(_severity: string, _args: string[]): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
showItemInFolder(_path: URI): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
newWindowTab(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -585,22 +516,6 @@ export class SimpleWindowsService implements IWindowsService {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
async openAboutDialog(): Promise<void> {
|
||||
const detail = localize('aboutDetail',
|
||||
"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
|
||||
this.productService.version || 'Unknown',
|
||||
this.productService.commit || 'Unknown',
|
||||
this.productService.date || 'Unknown',
|
||||
navigator.userAgent
|
||||
);
|
||||
|
||||
const { choice } = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail });
|
||||
|
||||
if (choice === 0) {
|
||||
this.clipboardService.writeText(detail);
|
||||
}
|
||||
}
|
||||
|
||||
resolveProxy(windowId: number, url: string): Promise<string | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
@@ -609,28 +524,3 @@ export class SimpleWindowsService implements IWindowsService {
|
||||
registerSingleton(IWindowsService, SimpleWindowsService);
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Workspaces
|
||||
|
||||
export class SimpleWorkspacesService implements IWorkspacesService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IWorkspacesService, SimpleWorkspacesService);
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform';
|
||||
|
||||
// Configuration
|
||||
(function registerConfiguration(): void {
|
||||
@@ -263,6 +263,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'
|
||||
nls.localize('rootName', "`\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace)."),
|
||||
nls.localize('rootPath', "`\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace)."),
|
||||
nls.localize('appName', "`\${appName}`: e.g. VS Code."),
|
||||
nls.localize('remoteName', "`\${remoteName}`: e.g. SSH"),
|
||||
nls.localize('dirty', "`\${dirty}`: a dirty indicator if the active editor is dirty."),
|
||||
nls.localize('separator', "`\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.")
|
||||
].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations
|
||||
@@ -275,7 +276,18 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'
|
||||
'properties': {
|
||||
'window.title': {
|
||||
'type': 'string',
|
||||
'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}',
|
||||
'default': (() => {
|
||||
if (isMacintosh && isNative) {
|
||||
return '${activeEditorShort}${separator}${rootName}'; // macOS has native dirty indicator
|
||||
}
|
||||
|
||||
const base = '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}';
|
||||
if (isWeb) {
|
||||
return base + '${separator}${remoteName}'; // Web: always show remote indicator
|
||||
}
|
||||
|
||||
return base;
|
||||
})(),
|
||||
'markdownDescription': windowTitleDescription
|
||||
},
|
||||
'window.menuBarVisibility': {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
@@ -4,19 +4,22 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { UnownedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WebviewEditorState } from 'vs/editor/common/modes';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IEditorInput, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { ConfirmResult, IEditorInput, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { WebviewEditorState } from 'vs/editor/common/modes';
|
||||
import { promptSave } from 'vs/workbench/services/textfile/browser/textFileService';
|
||||
|
||||
export class CustomFileEditorInput extends WebviewEditorInput {
|
||||
export class CustomFileEditorInput extends WebviewInput {
|
||||
|
||||
public static typeId = 'workbench.editors.webviewEditor';
|
||||
|
||||
@@ -30,12 +33,10 @@ export class CustomFileEditorInput extends WebviewEditorInput {
|
||||
viewType: string,
|
||||
id: string,
|
||||
webview: UnownedDisposable<WebviewEditorOverlay>,
|
||||
@ILabelService
|
||||
private readonly labelService: ILabelService,
|
||||
@IWebviewEditorService
|
||||
private readonly _webviewEditorService: IWebviewEditorService,
|
||||
@IExtensionService
|
||||
private readonly _extensionService: IExtensionService
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
super(id, viewType, '', undefined, webview);
|
||||
this._editorResource = resource;
|
||||
@@ -77,7 +78,7 @@ export class CustomFileEditorInput extends WebviewEditorInput {
|
||||
return this.labelService.getUriLabel(this.getResource());
|
||||
}
|
||||
|
||||
getTitle(verbosity?: Verbosity): string {
|
||||
public getTitle(verbosity?: Verbosity): string {
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.shortTitle;
|
||||
@@ -106,4 +107,26 @@ export class CustomFileEditorInput extends WebviewEditorInput {
|
||||
public isDirty() {
|
||||
return this._state === WebviewEditorState.Dirty;
|
||||
}
|
||||
|
||||
public async confirmSave(): Promise<ConfirmResult> {
|
||||
if (!this.isDirty()) {
|
||||
return ConfirmResult.DONT_SAVE;
|
||||
}
|
||||
return promptSave(this.dialogService, [this.getResource()]);
|
||||
}
|
||||
|
||||
public async save(): Promise<boolean> {
|
||||
if (!this.isDirty) {
|
||||
return true;
|
||||
}
|
||||
const waitingOn: Promise<boolean>[] = [];
|
||||
this._onWillSave.fire({
|
||||
waitUntil: (thenable: Promise<boolean>): void => { waitingOn.push(thenable); },
|
||||
});
|
||||
const result = await Promise.all(waitingOn);
|
||||
return result.every(x => x);
|
||||
}
|
||||
|
||||
private readonly _onWillSave = this._register(new Emitter<{ waitUntil: (thenable: Thenable<boolean>) => void }>());
|
||||
public readonly onWillSave = this._onWillSave.event;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import { coalesce, distinct } from 'vs/base/common/arrays';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { UnownedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { basename, DataUri } from 'vs/base/common/resources';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
@@ -16,7 +17,8 @@ import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/ed
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { webviewEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/browser/extensionPoint';
|
||||
import { CustomEditorDiscretion, CustomEditorInfo, CustomEditorSelector, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
@@ -25,10 +27,48 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe
|
||||
import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CustomFileEditorInput } from './customEditorInput';
|
||||
|
||||
const defaultEditorId = 'default';
|
||||
|
||||
const defaultEditorInfo: CustomEditorInfo = {
|
||||
id: defaultEditorId,
|
||||
displayName: nls.localize('promptOpenWith.defaultEditor', "Default built-in editor"),
|
||||
selector: [
|
||||
{ filenamePattern: '*' }
|
||||
],
|
||||
discretion: CustomEditorDiscretion.default,
|
||||
};
|
||||
|
||||
export class CustomEditorStore {
|
||||
private readonly contributedEditors = new Map<string, CustomEditorInfo>();
|
||||
|
||||
public clear() {
|
||||
this.contributedEditors.clear();
|
||||
}
|
||||
|
||||
public get(viewType: string): CustomEditorInfo | undefined {
|
||||
return viewType === defaultEditorId
|
||||
? defaultEditorInfo
|
||||
: this.contributedEditors.get(viewType);
|
||||
}
|
||||
|
||||
public add(info: CustomEditorInfo): void {
|
||||
if (info.id === defaultEditorId || this.contributedEditors.has(info.id)) {
|
||||
console.log(`Custom editor with id '${info.id}' already registered`);
|
||||
return;
|
||||
}
|
||||
this.contributedEditors.set(info.id, info);
|
||||
}
|
||||
|
||||
public getContributedEditors(resource: URI): readonly CustomEditorInfo[] {
|
||||
return Array.from(this.contributedEditors.values()).filter(customEditor =>
|
||||
customEditor.selector.some(selector => matches(selector, resource)));
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomEditorService implements ICustomEditorService {
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly customEditors = new Map<string, CustomEditorInfo>();
|
||||
private readonly editors = new CustomEditorStore();
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@@ -38,9 +78,11 @@ export class CustomEditorService implements ICustomEditorService {
|
||||
@IWebviewService private readonly webviewService: IWebviewService,
|
||||
) {
|
||||
webviewEditorsExtensionPoint.setHandler(extensions => {
|
||||
this.editors.clear();
|
||||
|
||||
for (const extension of extensions) {
|
||||
for (const webviewEditorContribution of extension.value) {
|
||||
this.customEditors.set(webviewEditorContribution.viewType, {
|
||||
this.editors.add({
|
||||
id: webviewEditorContribution.viewType,
|
||||
displayName: webviewEditorContribution.displayName,
|
||||
selector: webviewEditorContribution.selector || [],
|
||||
@@ -52,15 +94,14 @@ export class CustomEditorService implements ICustomEditorService {
|
||||
}
|
||||
|
||||
public getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[] {
|
||||
return Array.from(this.customEditors.values()).filter(customEditor =>
|
||||
customEditor.selector.some(selector => matches(selector, resource)));
|
||||
return this.editors.getContributedEditors(resource);
|
||||
}
|
||||
|
||||
public getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[] {
|
||||
const rawAssociations = this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsKey) || [];
|
||||
return coalesce(rawAssociations
|
||||
.filter(association => matches(association, resource))
|
||||
.map(association => this.customEditors.get(association.viewType)));
|
||||
.map(association => this.editors.get(association.viewType)));
|
||||
}
|
||||
|
||||
public async promptOpenWith(
|
||||
@@ -69,34 +110,23 @@ export class CustomEditorService implements ICustomEditorService {
|
||||
group?: IEditorGroup,
|
||||
): Promise<IEditor | undefined> {
|
||||
const customEditors = distinct([
|
||||
defaultEditorInfo,
|
||||
...this.getUserConfiguredCustomEditors(resource),
|
||||
...this.getContributedCustomEditors(resource),
|
||||
], editor => editor.id);
|
||||
|
||||
const defaultEditorId = 'default';
|
||||
const pick = await this.quickInputService.pick([
|
||||
{
|
||||
label: nls.localize('promptOpenWith.defaultEditor', "Default built-in editor"),
|
||||
id: defaultEditorId,
|
||||
},
|
||||
...customEditors.map((editorDescriptor): IQuickPickItem => ({
|
||||
const pick = await this.quickInputService.pick(
|
||||
customEditors.map((editorDescriptor): IQuickPickItem => ({
|
||||
label: editorDescriptor.displayName,
|
||||
id: editorDescriptor.id,
|
||||
}))
|
||||
], {
|
||||
})), {
|
||||
placeHolder: nls.localize('promptOpenWith.placeHolder', "Select editor to use for '{0}'...", basename(resource)),
|
||||
});
|
||||
|
||||
if (!pick) {
|
||||
if (!pick || !pick.id) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
if (pick.id === defaultEditorId) {
|
||||
const fileInput = this.instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
|
||||
return this.openEditorForResource(resource, fileInput, { ...options, ignoreOverrides: true }, group);
|
||||
} else {
|
||||
return this.openWith(resource, pick.id!, options, group);
|
||||
}
|
||||
return this.openWith(resource, pick.id, options, group);
|
||||
}
|
||||
|
||||
public openWith(
|
||||
@@ -105,17 +135,31 @@ export class CustomEditorService implements ICustomEditorService {
|
||||
options?: ITextEditorOptions,
|
||||
group?: IEditorGroup,
|
||||
): Promise<IEditor | undefined> {
|
||||
if (!this.customEditors.has(viewType)) {
|
||||
if (viewType === defaultEditorId) {
|
||||
const fileInput = this.instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
|
||||
return this.openEditorForResource(resource, fileInput, { ...options, ignoreOverrides: true }, group);
|
||||
}
|
||||
|
||||
if (!this.editors.get(viewType)) {
|
||||
return this.promptOpenWith(resource, options, group);
|
||||
}
|
||||
|
||||
const input = this.createInput(resource, viewType, group);
|
||||
return this.openEditorForResource(resource, input, options, group);
|
||||
}
|
||||
|
||||
public createInput(
|
||||
resource: URI,
|
||||
viewType: string,
|
||||
group: IEditorGroup | undefined
|
||||
): CustomFileEditorInput {
|
||||
const id = generateUuid();
|
||||
const webview = this.webviewService.createWebviewEditorOverlay(id, {}, {});
|
||||
const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, new UnownedDisposable(webview));
|
||||
if (group) {
|
||||
input.updateGroup(group!.id);
|
||||
}
|
||||
return this.openEditorForResource(resource, input, options, group);
|
||||
return input;
|
||||
}
|
||||
|
||||
private async openEditorForResource(
|
||||
@@ -159,6 +203,42 @@ export class CustomEditorContribution implements IWorkbenchContribution {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
if (editor instanceof DiffEditorInput) {
|
||||
const getCustomEditorOverrideForSubInput = (subInput: IEditorInput): EditorInput | undefined => {
|
||||
if (subInput instanceof CustomFileEditorInput) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
const resource = subInput.getResource();
|
||||
if (!resource) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
const editors = distinct([
|
||||
...this.customEditorService.getUserConfiguredCustomEditors(resource),
|
||||
...this.customEditorService.getContributedCustomEditors(resource),
|
||||
], editor => editor.id);
|
||||
|
||||
// Always prefer the first editor in the diff editor case
|
||||
return editors.length
|
||||
? this.customEditorService.createInput(resource, editors[0].id, group)
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const modifiedOverride = getCustomEditorOverrideForSubInput(editor.modifiedInput);
|
||||
const originalOverride = getCustomEditorOverrideForSubInput(editor.originalInput);
|
||||
|
||||
if (modifiedOverride || originalOverride) {
|
||||
return {
|
||||
override: (async () => {
|
||||
const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput);
|
||||
return this.editorService.openEditor(input, { ...options, ignoreOverrides: true }, group);
|
||||
})(),
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resource = editor.getResource();
|
||||
if (!resource) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
@@ -214,6 +294,15 @@ export class CustomEditorContribution implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
function matches(selector: CustomEditorSelector, resource: URI): boolean {
|
||||
if (resource.scheme === Schemas.data) {
|
||||
const metadata = DataUri.parseMetaData(resource);
|
||||
const mime = metadata.get(DataUri.META_DATA_MIME);
|
||||
if (!selector.mime || !mime) {
|
||||
return false;
|
||||
}
|
||||
return glob.match(selector.mime, mime.toLowerCase());
|
||||
}
|
||||
|
||||
if (!selector.filenamePattern && !selector.scheme) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditor } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, IEditor } from 'vs/workbench/common/editor';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
||||
|
||||
export const ICustomEditorService = createDecorator<ICustomEditorService>('customEditorService');
|
||||
|
||||
export interface ICustomEditorService {
|
||||
@@ -18,6 +17,8 @@ export interface ICustomEditorService {
|
||||
getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[];
|
||||
getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[];
|
||||
|
||||
createInput(resource: URI, viewType: string, group: IEditorGroup | undefined): EditorInput;
|
||||
|
||||
openWith(resource: URI, customEditorViewType: string, options?: ITextEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined>;
|
||||
promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined>;
|
||||
}
|
||||
@@ -30,6 +31,7 @@ export const enum CustomEditorDiscretion {
|
||||
export interface CustomEditorSelector {
|
||||
readonly scheme?: string;
|
||||
readonly filenamePattern?: string;
|
||||
readonly mime?: string;
|
||||
}
|
||||
|
||||
export interface CustomEditorInfo {
|
||||
|
||||
@@ -0,0 +1,567 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
interface IBreakpointDecoration {
|
||||
decorationId: string;
|
||||
breakpoint: IBreakpoint;
|
||||
range: Range;
|
||||
inlineWidget?: InlineBreakpointWidget;
|
||||
}
|
||||
|
||||
const breakpointHelperDecoration: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>, debugService: IDebugService): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: getBreakpointDecorationOptions(model, breakpoint, debugService),
|
||||
range
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, debugService: IDebugService): IModelDecorationOptions {
|
||||
const { className, message } = getBreakpointMessageAndClassName(debugService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString | undefined;
|
||||
|
||||
if (message) {
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
const modeId = model.getLanguageIdentifier().language;
|
||||
glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message);
|
||||
} else {
|
||||
glyphMarginHoverMessage = new MarkdownString().appendText(message);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-placeholder` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
async function createCandidateDecorations(model: ITextModel, breakpointDecorations: IBreakpointDecoration[], debugService: IDebugService): Promise<{ range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[]> {
|
||||
const lineNumbers = distinct(breakpointDecorations.map(bpd => bpd.range.startLineNumber));
|
||||
const result: { range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[] = [];
|
||||
const session = debugService.getViewModel().focusedSession;
|
||||
if (session && session.capabilities.supportsBreakpointLocationsRequest) {
|
||||
await Promise.all(lineNumbers.map(async lineNumber => {
|
||||
const positions = await session.breakpointsLocations(model.uri, lineNumber);
|
||||
if (positions.length > 1) {
|
||||
// Do not render candidates if there is only one, since it is already covered by the line breakpoint
|
||||
positions.forEach(p => {
|
||||
const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1);
|
||||
const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop();
|
||||
if (breakpointAtPosition && breakpointAtPosition.inlineWidget) {
|
||||
// Space already occupied, do not render candidate.
|
||||
return;
|
||||
}
|
||||
result.push({
|
||||
range,
|
||||
options: {
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
beforeContentClassName: `debug-breakpoint-placeholder`
|
||||
},
|
||||
breakpoint: breakpointAtPosition ? breakpointAtPosition.breakpoint : undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
|
||||
private breakpointHintDecoration: string[] = [];
|
||||
private breakpointWidget: BreakpointWidget | undefined;
|
||||
private breakpointWidgetVisible: IContextKey<boolean>;
|
||||
private toDispose: IDisposable[] = [];
|
||||
private ignoreDecorationsChangedEvent = false;
|
||||
private ignoreBreakpointsChangeEvent = false;
|
||||
private breakpointDecorations: IBreakpointDecoration[] = [];
|
||||
private candidateDecorations: { decorationId: string, inlineWidget: InlineBreakpointWidget }[] = [];
|
||||
private setDecorationsScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
|
||||
this.registerListeners();
|
||||
this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return BREAKPOINT_EDITOR_CONTRIBUTION_ID;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
||||
const data = e.target.detail as IMarginData;
|
||||
const model = this.editor.getModel();
|
||||
if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model);
|
||||
const lineNumber = e.target.position.lineNumber;
|
||||
const uri = model.uri;
|
||||
|
||||
if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) {
|
||||
if (!canSetBreakpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = { x: e.event.posx, y: e.event.posy };
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri });
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber),
|
||||
getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined
|
||||
});
|
||||
} else {
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber });
|
||||
|
||||
if (breakpoints.length) {
|
||||
// Show the dialog if there is a potential condition to be accidently lost.
|
||||
// Do not show dialog on linux due to electron issue freezing the mouse #50026
|
||||
if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) {
|
||||
const logPoint = breakpoints.every(bp => !!bp.logMessage);
|
||||
const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
const disable = breakpoints.some(bp => bp.enabled);
|
||||
|
||||
const enabling = nls.localize('breakpointHasConditionDisabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
const disabling = nls.localize('breakpointHasConditionEnabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
|
||||
const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [
|
||||
nls.localize('removeLogPoint', "Remove {0}", breakpointType),
|
||||
nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType),
|
||||
nls.localize('cancel', "Cancel")
|
||||
], { cancelId: 2 });
|
||||
|
||||
if (choice === 0) {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
if (choice === 1) {
|
||||
breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp));
|
||||
}
|
||||
} else {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
} else if (canSetBreakpoints) {
|
||||
this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => {
|
||||
let showBreakpointHintAtLineNumber = -1;
|
||||
const model = this.editor.getModel();
|
||||
if (model && e.target.position && e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
showBreakpointHintAtLineNumber = e.target.position.lineNumber;
|
||||
}
|
||||
}
|
||||
this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber);
|
||||
}));
|
||||
this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => {
|
||||
this.ensureBreakpointHintDecoration(-1);
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.editor.onDidChangeModel(async () => {
|
||||
this.closeBreakpointWidget();
|
||||
await this.setDecorations();
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(async () => {
|
||||
if (!this.ignoreBreakpointsChangeEvent && !this.setDecorationsScheduler.isScheduled()) {
|
||||
this.setDecorationsScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => this.onModelDecorationsChanged()));
|
||||
}
|
||||
|
||||
private getContextMenuActions(breakpoints: ReadonlyArray<IBreakpoint>, uri: uri, lineNumber: number, column?: number): Array<IAction | ContextSubMenu> {
|
||||
const actions: Array<IAction | ContextSubMenu> = [];
|
||||
if (breakpoints.length === 1) {
|
||||
const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
|
||||
actions.push(new Action(
|
||||
'workbench.debug.action.editBreakpointAction',
|
||||
nls.localize('editBreakpoint', "Edit {0}...", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column))
|
||||
));
|
||||
|
||||
actions.push(new Action(
|
||||
`workbench.debug.viewlet.action.toggleBreakpoint`,
|
||||
breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0])
|
||||
));
|
||||
} else if (breakpoints.length > 1) {
|
||||
const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1);
|
||||
actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action(
|
||||
'removeInlineBreakpoint',
|
||||
bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.removeBreakpoints(bp.getId())
|
||||
))));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp =>
|
||||
new Action('editBreakpoint',
|
||||
bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(bp.lineNumber, bp.column))
|
||||
)
|
||||
)));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action(
|
||||
bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint',
|
||||
bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint"))
|
||||
: (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp)
|
||||
))));
|
||||
} else {
|
||||
actions.push(new Action(
|
||||
'addBreakpoint',
|
||||
nls.localize('addBreakpoint', "Add Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.addBreakpoints(uri, [{ lineNumber, column }], `debugEditorContextMenu`)
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addConditionalBreakpoint',
|
||||
nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column))
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addLogPoint',
|
||||
nls.localize('addLogPoint', "Add Logpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column, BreakpointWidgetContext.LOG_MESSAGE))
|
||||
));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private marginFreeFromNonDebugDecorations(line: number): boolean {
|
||||
const decorations = this.editor.getLineDecorations(line);
|
||||
if (decorations) {
|
||||
for (const { options } of decorations) {
|
||||
if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void {
|
||||
const newDecoration: IModelDeltaDecoration[] = [];
|
||||
if (showBreakpointHintAtLineNumber !== -1) {
|
||||
newDecoration.push({
|
||||
options: breakpointHelperDecoration,
|
||||
range: {
|
||||
startLineNumber: showBreakpointHintAtLineNumber,
|
||||
startColumn: 1,
|
||||
endLineNumber: showBreakpointHintAtLineNumber,
|
||||
endColumn: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration);
|
||||
}
|
||||
|
||||
private async setDecorations(): Promise<void> {
|
||||
if (!this.editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeCodeEditor = this.editor;
|
||||
const model = activeCodeEditor.getModel();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
|
||||
const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService);
|
||||
|
||||
try {
|
||||
this.ignoreDecorationsChangedEvent = true;
|
||||
|
||||
// Set breakpoint decorations
|
||||
const decorationIds = activeCodeEditor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), desiredBreakpointDecorations);
|
||||
this.breakpointDecorations.forEach(bpd => {
|
||||
if (bpd.inlineWidget) {
|
||||
bpd.inlineWidget.dispose();
|
||||
}
|
||||
});
|
||||
this.breakpointDecorations = decorationIds.map((decorationId, index) => {
|
||||
let inlineWidget: InlineBreakpointWidget | undefined = undefined;
|
||||
const breakpoint = breakpoints[index];
|
||||
if (breakpoint.column) {
|
||||
inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, desiredBreakpointDecorations[index].options.glyphMarginClassName, breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([breakpoint], activeCodeEditor.getModel().uri, breakpoint.lineNumber, breakpoint.column));
|
||||
}
|
||||
|
||||
return {
|
||||
decorationId,
|
||||
breakpoint,
|
||||
range: desiredBreakpointDecorations[index].range,
|
||||
inlineWidget
|
||||
};
|
||||
});
|
||||
|
||||
} finally {
|
||||
this.ignoreDecorationsChangedEvent = false;
|
||||
}
|
||||
|
||||
// Set breakpoint candidate decorations
|
||||
const desiredCandidateDecorations = await createCandidateDecorations(this.editor.getModel(), this.breakpointDecorations, this.debugService);
|
||||
const candidateDecorationIds = this.editor.deltaDecorations(this.candidateDecorations.map(c => c.decorationId), desiredCandidateDecorations);
|
||||
this.candidateDecorations.forEach(candidate => {
|
||||
candidate.inlineWidget.dispose();
|
||||
});
|
||||
this.candidateDecorations = candidateDecorationIds.map((decorationId, index) => {
|
||||
const candidate = desiredCandidateDecorations[index];
|
||||
const cssClass = candidate.breakpoint ? undefined : 'debug-breakpoint-disabled';
|
||||
const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn));
|
||||
|
||||
return {
|
||||
decorationId,
|
||||
inlineWidget
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async onModelDecorationsChanged(): Promise<void> {
|
||||
if (this.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent || !this.editor.hasModel()) {
|
||||
// I have no decorations
|
||||
return;
|
||||
}
|
||||
let somethingChanged = false;
|
||||
const model = this.editor.getModel();
|
||||
this.breakpointDecorations.forEach(breakpointDecoration => {
|
||||
if (somethingChanged) {
|
||||
return;
|
||||
}
|
||||
const newBreakpointRange = model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
if (!somethingChanged) {
|
||||
// nothing to do, my decorations did not change.
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new Map<string, IBreakpointUpdateData>();
|
||||
for (let i = 0, len = this.breakpointDecorations.length; i < len; i++) {
|
||||
const breakpointDecoration = this.breakpointDecorations[i];
|
||||
const decorationRange = model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
// check if the line got deleted.
|
||||
if (decorationRange) {
|
||||
// since we know it is collapsed, it cannot grow to multiple lines
|
||||
if (breakpointDecoration.breakpoint) {
|
||||
data.set(breakpointDecoration.breakpoint.getId(), {
|
||||
lineNumber: decorationRange.startLineNumber,
|
||||
column: breakpointDecoration.breakpoint.column ? decorationRange.startColumn : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.ignoreBreakpointsChangeEvent = true;
|
||||
await this.debugService.updateBreakpoints(model.uri, data, true);
|
||||
} finally {
|
||||
this.ignoreBreakpointsChangeEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
// breakpoint widget
|
||||
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
|
||||
this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, column, context);
|
||||
this.breakpointWidget.show({ lineNumber, column: 1 });
|
||||
this.breakpointWidgetVisible.set(true);
|
||||
}
|
||||
|
||||
closeBreakpointWidget(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
this.breakpointWidget = undefined;
|
||||
this.breakpointWidgetVisible.reset();
|
||||
this.editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
this.editor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), []);
|
||||
dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
class InlineBreakpointWidget implements IContentWidget, IDisposable {
|
||||
|
||||
// editor.IContentWidget.allowEditorOverflow
|
||||
allowEditorOverflow = false;
|
||||
suppressMouseDown = true;
|
||||
|
||||
private domNode!: HTMLElement;
|
||||
private range: Range | null;
|
||||
private toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly editor: IActiveCodeEditor,
|
||||
private readonly decorationId: string,
|
||||
cssClass: string | null | undefined,
|
||||
private readonly breakpoint: IBreakpoint | undefined,
|
||||
private readonly debugService: IDebugService,
|
||||
private readonly contextMenuService: IContextMenuService,
|
||||
private readonly getContextMenuActions: () => ReadonlyArray<IAction | ContextSubMenu>
|
||||
) {
|
||||
this.range = this.editor.getModel().getDecorationRange(decorationId);
|
||||
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => {
|
||||
const model = this.editor.getModel();
|
||||
const range = model.getDecorationRange(this.decorationId);
|
||||
if (this.range && !this.range.equalsRange(range)) {
|
||||
this.range = range;
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
}));
|
||||
this.create(cssClass);
|
||||
|
||||
this.editor.addContentWidget(this);
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
private create(cssClass: string | null | undefined): void {
|
||||
this.domNode = $('.inline-breakpoint-widget');
|
||||
if (cssClass) {
|
||||
this.domNode.classList.add(cssClass);
|
||||
}
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => {
|
||||
if (this.breakpoint) {
|
||||
await this.debugService.removeBreakpoints(this.breakpoint.getId());
|
||||
} else {
|
||||
await this.debugService.addBreakpoints(this.editor.getModel().uri, [{ lineNumber: this.range!.startLineNumber, column: this.range!.startColumn }], 'debugEditorInlineWidget');
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CONTEXT_MENU, async e => {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const anchor = { x: event.posx, y: event.posy };
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(),
|
||||
getActionsContext: () => this.breakpoint
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@memoize
|
||||
getId(): string {
|
||||
return generateUuid();
|
||||
}
|
||||
|
||||
getDomNode(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
getPosition(): IContentWidgetPosition | null {
|
||||
if (!this.range) {
|
||||
return null;
|
||||
}
|
||||
// Workaround: since the content widget can not be placed before the first column we need to force the left position
|
||||
dom.toggleClass(this.domNode, 'line-start', this.range.startColumn === 1);
|
||||
|
||||
return {
|
||||
position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn - 1 },
|
||||
preference: [ContentWidgetPositionPreference.EXACT]
|
||||
};
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.editor.removeContentWidget(this);
|
||||
dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(BreakpointEditorContribution);
|
||||
@@ -13,7 +13,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -37,7 +37,7 @@ import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
const $ = dom.$;
|
||||
const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakopintWidgetService');
|
||||
const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakpointWidgetService');
|
||||
export interface IPrivateBreakpointWidgetService {
|
||||
_serviceBrand: undefined;
|
||||
close(success: boolean): void;
|
||||
@@ -55,7 +55,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
private logMessageInput = '';
|
||||
private breakpoint: IBreakpoint | undefined;
|
||||
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private context: Context,
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, private context: Context,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@@ -70,7 +70,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
const uri = model.uri;
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, uri });
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, column: this.column, uri });
|
||||
this.breakpoint = breakpoints.length ? breakpoints[0] : undefined;
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
}
|
||||
}
|
||||
|
||||
show(rangeOrPos: IRange | IPosition, heightInLines: number) {
|
||||
show(rangeOrPos: IRange | IPosition): void {
|
||||
const lineNum = this.input.getModel().getLineCount();
|
||||
super.show(rangeOrPos, lineNum + 1);
|
||||
}
|
||||
|
||||
fitHeightToContent() {
|
||||
fitHeightToContent(): void {
|
||||
const lineNum = this.input.getModel().getLineCount();
|
||||
this._relayout(lineNum + 1);
|
||||
}
|
||||
@@ -293,6 +293,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
if (model) {
|
||||
this.debugService.addBreakpoints(model.uri, [{
|
||||
lineNumber: this.lineNumber,
|
||||
column: this.column,
|
||||
enabled: true,
|
||||
condition,
|
||||
hitCondition,
|
||||
@@ -348,7 +349,7 @@ class CloseBreakpointWidgetCommand extends EditorCommand {
|
||||
}
|
||||
|
||||
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
|
||||
const debugContribution = editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
|
||||
const debugContribution = editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID);
|
||||
if (debugContribution) {
|
||||
// if focus is in outer editor we need to use the debug contribution to close
|
||||
return debugContribution.closeBreakpointWidget();
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -167,7 +167,7 @@ export class BreakpointsView extends ViewletPanel {
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -180,7 +180,7 @@ export class BreakpointsView extends ViewletPanel {
|
||||
actions.push(new Separator());
|
||||
}
|
||||
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService));
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
|
||||
|
||||
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
|
||||
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { DebugEditorModelManager } from 'vs/workbench/contrib/debug/browser/debugEditorModelManager';
|
||||
import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
|
||||
import * as service from 'vs/workbench/contrib/debug/browser/debugService';
|
||||
@@ -49,6 +48,7 @@ import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'
|
||||
import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl';
|
||||
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
|
||||
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
|
||||
import { DebugCallStackContribution } from 'vs/workbench/contrib/debug/browser/debugCallStackContribution';
|
||||
|
||||
class OpenDebugViewletAction extends ShowViewletAction {
|
||||
public static readonly ID = VIEWLET_ID;
|
||||
@@ -121,7 +121,7 @@ const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryEx
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View"));
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View"));
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugCallStackContribution, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually);
|
||||
|
||||
@@ -181,12 +181,12 @@ export class SelectAndStartAction extends AbstractDebugAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveBreakpointAction extends AbstractDebugAction {
|
||||
export class RemoveBreakpointAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint';
|
||||
static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action remove', debugService, keybindingService);
|
||||
constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService) {
|
||||
super(id, label, 'debug-action remove');
|
||||
}
|
||||
|
||||
public run(breakpoint: IBreakpoint): Promise<any> {
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
|
||||
interface IDebugEditorModelData {
|
||||
model: ITextModel;
|
||||
currentStackDecorations: string[];
|
||||
topStackFrameRange: Range | undefined;
|
||||
}
|
||||
|
||||
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
|
||||
export class DebugCallStackContribution implements IWorkbenchContribution {
|
||||
private modelDataMap = new Map<string, IDebugEditorModelData>();
|
||||
private toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
) {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this));
|
||||
this.modelService.getModels().forEach(model => this.onModelAdded(model));
|
||||
this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame()));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
if (state === State.Inactive) {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.topStackFrameRange = undefined;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onModelAdded(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr));
|
||||
|
||||
this.modelDataMap.set(modelUriStr, {
|
||||
model: model,
|
||||
currentStackDecorations: currentStackDecorations,
|
||||
topStackFrameRange: undefined
|
||||
});
|
||||
}
|
||||
|
||||
private onModelRemoved(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const data = this.modelDataMap.get(modelUriStr);
|
||||
if (data) {
|
||||
this.modelDataMap.delete(modelUriStr);
|
||||
}
|
||||
}
|
||||
|
||||
private onFocusStackFrame(): void {
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri));
|
||||
});
|
||||
}
|
||||
|
||||
private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] {
|
||||
const result: IModelDeltaDecoration[] = [];
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// only show decorations for the currently focused thread.
|
||||
const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1);
|
||||
|
||||
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame,
|
||||
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
|
||||
const callStack = stackFrame.thread.getCallStack();
|
||||
if (callStack && callStack.length && stackFrame === callStack[0]) {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
|
||||
const modelData = this.modelDataMap.get(modelUriStr);
|
||||
if (modelData) {
|
||||
if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_INLINE_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
modelData.topStackFrameRange = columnUntilEOLRange;
|
||||
}
|
||||
} else {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.FOCUSED_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugCallStackContribution.FOCUSED_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// editor decorations
|
||||
|
||||
static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
|
||||
private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-top-stack-frame',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-focused-stack-frame',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-line',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = {
|
||||
beforeContentClassName: 'debug-top-stack-frame-column'
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-focused-stack-frame-line',
|
||||
stickiness
|
||||
};
|
||||
|
||||
dispose(): void {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.model.deltaDecorations(modelData.currentStackDecorations, []);
|
||||
});
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
|
||||
this.modelDataMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const topStackFrame = theme.getColor(topStackFrameColor);
|
||||
if (topStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
}
|
||||
|
||||
const focusedStackFrame = theme.getColor(focusedStackFrameColor);
|
||||
if (focusedStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
@@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -75,7 +75,7 @@ class ConditionalBreakpointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class LogPointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { visit } from 'vs/base/common/json';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
@@ -19,32 +15,25 @@ import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, BreakpointWidgetContext } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugEditorContribution, IDebugService, State, EDITOR_CONTRIBUTION_ID, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget';
|
||||
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { getHover } from 'vs/editor/contrib/hover/getHover';
|
||||
import { IEditorHoverOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget';
|
||||
import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { getHover } from 'vs/editor/contrib/hover/getHover';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
@@ -53,17 +42,14 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We
|
||||
const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added
|
||||
const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped
|
||||
|
||||
export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private toDispose: IDisposable[];
|
||||
private hoverWidget: DebugHoverWidget;
|
||||
private nonDebugHoverPosition: Position | undefined;
|
||||
private hoverRange: Range | null = null;
|
||||
private mouseDown = false;
|
||||
|
||||
private breakpointHintDecoration: string[];
|
||||
private breakpointWidget: BreakpointWidget | undefined;
|
||||
private breakpointWidgetVisible: IContextKey<boolean>;
|
||||
private wordToLineNumbersMap: Map<string, Position[]> | undefined;
|
||||
|
||||
private exceptionWidget: ExceptionWidget | undefined;
|
||||
@@ -73,182 +59,21 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
this.breakpointHintDecoration = [];
|
||||
this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor);
|
||||
this.toDispose = [];
|
||||
this.registerListeners();
|
||||
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
|
||||
this.updateConfigurationWidgetVisibility();
|
||||
this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {});
|
||||
this.toggleExceptionWidget();
|
||||
}
|
||||
|
||||
private getContextMenuActions(breakpoints: ReadonlyArray<IBreakpoint>, uri: uri, lineNumber: number): Array<IAction | ContextSubMenu> {
|
||||
const actions: Array<IAction | ContextSubMenu> = [];
|
||||
if (breakpoints.length === 1) {
|
||||
const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService));
|
||||
actions.push(new Action(
|
||||
'workbench.debug.action.editBreakpointAction',
|
||||
nls.localize('editBreakpoint', "Edit {0}...", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column))
|
||||
));
|
||||
|
||||
actions.push(new Action(
|
||||
`workbench.debug.viewlet.action.toggleBreakpoint`,
|
||||
breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0])
|
||||
));
|
||||
} else if (breakpoints.length > 1) {
|
||||
const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1);
|
||||
actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action(
|
||||
'removeInlineBreakpoint',
|
||||
bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.removeBreakpoints(bp.getId())
|
||||
))));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp =>
|
||||
new Action('editBreakpoint',
|
||||
bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(bp.lineNumber, bp.column))
|
||||
)
|
||||
)));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action(
|
||||
bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint',
|
||||
bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint"))
|
||||
: (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp)
|
||||
))));
|
||||
} else {
|
||||
actions.push(new Action(
|
||||
'addBreakpoint',
|
||||
nls.localize('addBreakpoint', "Add Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorContextMenu`)
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addConditionalBreakpoint',
|
||||
nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined))
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addLogPoint',
|
||||
nls.localize('addLogPoint', "Add Logpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined, BreakpointWidgetContext.LOG_MESSAGE))
|
||||
));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
||||
const data = e.target.detail as IMarginData;
|
||||
const model = this.editor.getModel();
|
||||
if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model);
|
||||
const lineNumber = e.target.position.lineNumber;
|
||||
const uri = model.uri;
|
||||
|
||||
if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) {
|
||||
if (!canSetBreakpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = { x: e.event.posx, y: e.event.posy };
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri });
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber),
|
||||
getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined
|
||||
});
|
||||
} else {
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber });
|
||||
|
||||
if (breakpoints.length) {
|
||||
// Show the dialog if there is a potential condition to be accidently lost.
|
||||
// Do not show dialog on linux due to electron issue freezing the mouse #50026
|
||||
if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) {
|
||||
const logPoint = breakpoints.every(bp => !!bp.logMessage);
|
||||
const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
const disable = breakpoints.some(bp => bp.enabled);
|
||||
|
||||
const enabling = nls.localize('breakpointHasConditionDisabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
const disabling = nls.localize('breakpointHasConditionEnabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
|
||||
const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [
|
||||
nls.localize('removeLogPoint', "Remove {0}", breakpointType),
|
||||
nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType),
|
||||
nls.localize('cancel', "Cancel")
|
||||
], { cancelId: 2 });
|
||||
|
||||
if (choice === 0) {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
if (choice === 1) {
|
||||
breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp));
|
||||
}
|
||||
} else {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
} else if (canSetBreakpoints) {
|
||||
this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => {
|
||||
let showBreakpointHintAtLineNumber = -1;
|
||||
const model = this.editor.getModel();
|
||||
if (model && e.target.position && e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
showBreakpointHintAtLineNumber = e.target.position.lineNumber;
|
||||
}
|
||||
}
|
||||
this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber);
|
||||
}));
|
||||
this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => {
|
||||
this.ensureBreakpointHintDecoration(-1);
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(e => this.onFocusStackFrame(e.stackFrame)));
|
||||
|
||||
// hover listeners & hover widget
|
||||
@@ -279,7 +104,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
if (model) {
|
||||
this._applyHoverConfiguration(model, stackFrame);
|
||||
}
|
||||
this.closeBreakpointWidget();
|
||||
this.toggleExceptionWidget();
|
||||
this.hideHoverWidget();
|
||||
this.updateConfigurationWidgetVisibility();
|
||||
@@ -317,11 +141,11 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
getId(): string {
|
||||
return EDITOR_CONTRIBUTION_ID;
|
||||
}
|
||||
|
||||
public showHover(range: Range, focus: boolean): Promise<void> {
|
||||
showHover(range: Range, focus: boolean): Promise<void> {
|
||||
const sf = this.debugService.getViewModel().focusedStackFrame;
|
||||
const model = this.editor.getModel();
|
||||
if (sf && model && sf.source.uri.toString() === model.uri.toString()) {
|
||||
@@ -331,36 +155,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private marginFreeFromNonDebugDecorations(line: number): boolean {
|
||||
const decorations = this.editor.getLineDecorations(line);
|
||||
if (decorations) {
|
||||
for (const { options } of decorations) {
|
||||
if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void {
|
||||
const newDecoration: IModelDeltaDecoration[] = [];
|
||||
if (showBreakpointHintAtLineNumber !== -1) {
|
||||
newDecoration.push({
|
||||
options: DebugEditorContribution.BREAKPOINT_HELPER_DECORATION,
|
||||
range: {
|
||||
startLineNumber: showBreakpointHintAtLineNumber,
|
||||
startColumn: 1,
|
||||
endLineNumber: showBreakpointHintAtLineNumber,
|
||||
endColumn: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration);
|
||||
}
|
||||
|
||||
private onFocusStackFrame(sf: IStackFrame | undefined): void {
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
@@ -464,29 +258,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.hideHoverWidget();
|
||||
}
|
||||
}
|
||||
|
||||
// end hover business
|
||||
|
||||
// breakpoint widget
|
||||
public showBreakpointWidget(lineNumber: number, column: number, context?: BreakpointWidgetContext): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
|
||||
this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, context);
|
||||
this.breakpointWidget.show({ lineNumber, column: 1 }, 2);
|
||||
this.breakpointWidgetVisible.set(true);
|
||||
}
|
||||
|
||||
public closeBreakpointWidget(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
this.breakpointWidget = undefined;
|
||||
this.breakpointWidgetVisible.reset();
|
||||
this.editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// exception widget
|
||||
private toggleExceptionWidget(): void {
|
||||
// Toggles exception widget based on the state of the current editor model and debug stack frame
|
||||
@@ -547,7 +320,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
public addLaunchConfiguration(): Promise<any> {
|
||||
addLaunchConfiguration(): Promise<any> {
|
||||
/* __GDPR__
|
||||
"debug/addLaunchConfiguration" : {}
|
||||
*/
|
||||
@@ -594,11 +367,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return insertLine(configurationsArrayPosition).then(() => this.commandService.executeCommand('editor.action.triggerSuggest'));
|
||||
}
|
||||
|
||||
private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
// Inline Decorations
|
||||
|
||||
@memoize
|
||||
@@ -768,17 +536,14 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return this.wordToLineNumbersMap;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
dispose(): void {
|
||||
if (this.hoverWidget) {
|
||||
this.hoverWidget.dispose();
|
||||
}
|
||||
if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugService, IBreakpoint, State, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
interface IBreakpointDecoration {
|
||||
decorationId: string;
|
||||
modelId: string;
|
||||
range: Range;
|
||||
}
|
||||
|
||||
interface IDebugEditorModelData {
|
||||
model: ITextModel;
|
||||
toDispose: lifecycle.IDisposable[];
|
||||
breakpointDecorations: IBreakpointDecoration[];
|
||||
currentStackDecorations: string[];
|
||||
topStackFrameRange: Range | undefined;
|
||||
}
|
||||
|
||||
export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
static readonly ID = 'breakpointManager';
|
||||
static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
private modelDataMap: Map<string, IDebugEditorModelData>;
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private ignoreDecorationsChangedEvent = false;
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
) {
|
||||
this.modelDataMap = new Map<string, IDebugEditorModelData>();
|
||||
this.toDispose = [];
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
lifecycle.dispose(modelData.toDispose);
|
||||
modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), []);
|
||||
modelData.model.deltaDecorations(modelData.currentStackDecorations, []);
|
||||
});
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
this.modelDataMap.clear();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this));
|
||||
this.modelService.getModels().forEach(model => this.onModelAdded(model));
|
||||
this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this));
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame()));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
if (state === State.Inactive) {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.topStackFrameRange = undefined;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onModelAdded(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
|
||||
|
||||
const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr));
|
||||
const desiredDecorations = this.createBreakpointDecorations(model, breakpoints);
|
||||
const breakpointDecorationIds = model.deltaDecorations([], desiredDecorations);
|
||||
const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUriStr))];
|
||||
|
||||
this.modelDataMap.set(modelUriStr, {
|
||||
model: model,
|
||||
toDispose: toDispose,
|
||||
breakpointDecorations: breakpointDecorationIds.map((decorationId, index) => ({ decorationId, modelId: breakpoints[index].getId(), range: desiredDecorations[index].range })),
|
||||
currentStackDecorations: currentStackDecorations,
|
||||
topStackFrameRange: undefined
|
||||
});
|
||||
}
|
||||
|
||||
private onModelRemoved(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const data = this.modelDataMap.get(modelUriStr);
|
||||
if (data) {
|
||||
lifecycle.dispose(data.toDispose);
|
||||
this.modelDataMap.delete(modelUriStr);
|
||||
}
|
||||
}
|
||||
|
||||
// call stack management. Represent data coming from the debug service.
|
||||
|
||||
private onFocusStackFrame(): void {
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri));
|
||||
});
|
||||
}
|
||||
|
||||
private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] {
|
||||
const result: IModelDeltaDecoration[] = [];
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// only show decorations for the currently focused thread.
|
||||
const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1);
|
||||
|
||||
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame,
|
||||
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
|
||||
const callStack = stackFrame.thread.getCallStack();
|
||||
if (callStack && callStack.length && stackFrame === callStack[0]) {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
|
||||
const modelData = this.modelDataMap.get(modelUriStr);
|
||||
if (modelData) {
|
||||
if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_INLINE_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
modelData.topStackFrameRange = columnUntilEOLRange;
|
||||
}
|
||||
} else {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.FOCUSED_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugEditorModelManager.FOCUSED_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// breakpoints management. Represent data coming from the debug service and also send data back.
|
||||
private onModelDecorationsChanged(modelUrlStr: string): void {
|
||||
const modelData = this.modelDataMap.get(modelUrlStr);
|
||||
if (!modelData || modelData.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent) {
|
||||
// I have no decorations
|
||||
return;
|
||||
}
|
||||
let somethingChanged = false;
|
||||
modelData.breakpointDecorations.forEach(breakpointDecoration => {
|
||||
if (somethingChanged) {
|
||||
return;
|
||||
}
|
||||
const newBreakpointRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
if (!somethingChanged) {
|
||||
// nothing to do, my decorations did not change.
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new Map<string, IBreakpointUpdateData>();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints();
|
||||
const modelUri = modelData.model.uri;
|
||||
for (let i = 0, len = modelData.breakpointDecorations.length; i < len; i++) {
|
||||
const breakpointDecoration = modelData.breakpointDecorations[i];
|
||||
const decorationRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
// check if the line got deleted.
|
||||
if (decorationRange) {
|
||||
const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.modelId).pop();
|
||||
// since we know it is collapsed, it cannot grow to multiple lines
|
||||
if (breakpoint) {
|
||||
data.set(breakpoint.getId(), {
|
||||
lineNumber: decorationRange.startLineNumber,
|
||||
column: breakpoint.column ? decorationRange.startColumn : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.debugService.updateBreakpoints(modelUri, data, true).then(undefined, onUnexpectedError);
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const breakpointsMap = new Map<string, IBreakpoint[]>();
|
||||
this.debugService.getModel().getBreakpoints().forEach(bp => {
|
||||
const uriStr = bp.uri.toString();
|
||||
const breakpoints = breakpointsMap.get(uriStr);
|
||||
if (breakpoints) {
|
||||
breakpoints.push(bp);
|
||||
} else {
|
||||
breakpointsMap.set(uriStr, [bp]);
|
||||
}
|
||||
});
|
||||
|
||||
breakpointsMap.forEach((bps, uri) => {
|
||||
const data = this.modelDataMap.get(uri);
|
||||
if (data) {
|
||||
this.updateBreakpoints(data, breakpointsMap.get(uri)!);
|
||||
}
|
||||
});
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
if (!breakpointsMap.has(uri)) {
|
||||
this.updateBreakpoints(modelData, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void {
|
||||
const desiredDecorations = this.createBreakpointDecorations(modelData.model, newBreakpoints);
|
||||
try {
|
||||
this.ignoreDecorationsChangedEvent = true;
|
||||
const breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), desiredDecorations);
|
||||
modelData.breakpointDecorations = breakpointDecorationIds.map((decorationId, index) => ({
|
||||
decorationId,
|
||||
modelId: newBreakpoints[index].getId(),
|
||||
range: desiredDecorations[index].range
|
||||
}));
|
||||
} finally {
|
||||
this.ignoreDecorationsChangedEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
private createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: this.getBreakpointDecorationOptions(breakpoint),
|
||||
range
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions {
|
||||
const { className, message } = getBreakpointMessageAndClassName(this.debugService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString | undefined;
|
||||
|
||||
if (message) {
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
const modelData = this.modelDataMap.get(breakpoint.uri.toString());
|
||||
const modeId = modelData ? modelData.model.getLanguageIdentifier().language : '';
|
||||
glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message);
|
||||
} else {
|
||||
glyphMarginHoverMessage = new MarkdownString().appendText(message);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness: DebugEditorModelManager.STICKINESS,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-column ${className}-column` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
// editor decorations
|
||||
|
||||
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
|
||||
private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-top-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-focused-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-line',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = {
|
||||
beforeContentClassName: 'debug-top-stack-frame-column'
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-focused-stack-frame-line',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const topStackFrame = theme.getColor(topStackFrameColor);
|
||||
if (topStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
}
|
||||
|
||||
const focusedStackFrame = theme.getColor(focusedStackFrameColor);
|
||||
if (focusedStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
@@ -10,19 +10,20 @@ import * as platform from 'vs/base/common/platform';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -34,6 +35,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
|
||||
export class DebugSession implements IDebugSession {
|
||||
|
||||
@@ -73,7 +75,7 @@ export class DebugSession implements IDebugSession {
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
|
||||
@IOpenerService private readonly openerService: IOpenerService
|
||||
) {
|
||||
this.id = generateUuid();
|
||||
@@ -185,7 +187,7 @@ export class DebugSession implements IDebugSession {
|
||||
|
||||
return dbgr.createDebugAdapter(this).then(debugAdapter => {
|
||||
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService);
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
|
||||
|
||||
return this.raw.start().then(() => {
|
||||
|
||||
@@ -284,15 +286,7 @@ export class DebugSession implements IDebugSession {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const source = this.getSourceForUri(modelUri);
|
||||
let rawSource: DebugProtocol.Source;
|
||||
if (source) {
|
||||
rawSource = source.raw;
|
||||
} else {
|
||||
const data = Source.getEncodedDebugData(modelUri);
|
||||
rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference };
|
||||
}
|
||||
|
||||
const rawSource = this.getRawSource(modelUri);
|
||||
if (breakpointsToSend.length && !rawSource.adapterData) {
|
||||
rawSource.adapterData = breakpointsToSend[0].adapterData;
|
||||
}
|
||||
@@ -376,6 +370,17 @@ export class DebugSession implements IDebugSession {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
async breakpointsLocations(uri: URI, lineNumber: number): Promise<IPosition[]> {
|
||||
if (this.raw) {
|
||||
const source = this.getRawSource(uri);
|
||||
const response = await this.raw.breakpointLocations({ source, line: lineNumber });
|
||||
const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 }));
|
||||
|
||||
return distinct(positions, p => `${p.lineNumber}:${p.column}`);
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
customRequest(request: string, args: any): Promise<DebugProtocol.Response> {
|
||||
if (this.raw) {
|
||||
return this.raw.custom(request, args);
|
||||
@@ -914,6 +919,16 @@ export class DebugSession implements IDebugSession {
|
||||
return source;
|
||||
}
|
||||
|
||||
private getRawSource(uri: URI): DebugProtocol.Source {
|
||||
const source = this.getSourceForUri(uri);
|
||||
if (source) {
|
||||
return source.raw;
|
||||
} else {
|
||||
const data = Source.getEncodedDebugData(uri);
|
||||
return { name: data.name, path: data.path, sourceReference: data.sourceReference };
|
||||
}
|
||||
}
|
||||
|
||||
private getNewCancellationToken(threadId: number): CancellationToken {
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
const tokens = this.cancellationMap.get(threadId) || [];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -13,8 +13,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {
|
||||
|
||||
constructor(
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
@@ -45,6 +47,52 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
// we pass the "ParsedArgs" as query parameters of the URL
|
||||
|
||||
let newAddress = `${document.location.origin}${document.location.pathname}?`;
|
||||
let gotFolder = false;
|
||||
|
||||
const addQueryParameter = (key: string, value: string) => {
|
||||
const lastChar = newAddress.charAt(newAddress.length - 1);
|
||||
if (lastChar !== '?' && lastChar !== '&') {
|
||||
newAddress += '&';
|
||||
}
|
||||
newAddress += `${key}=${encodeURIComponent(value)}`;
|
||||
};
|
||||
|
||||
const f = args['folder-uri'];
|
||||
if (f) {
|
||||
const u = URI.parse(f[0]);
|
||||
gotFolder = true;
|
||||
addQueryParameter('folder', u.path);
|
||||
}
|
||||
if (!gotFolder) {
|
||||
// request empty window
|
||||
addQueryParameter('ew', 'true');
|
||||
}
|
||||
|
||||
const ep = args['extensionDevelopmentPath'];
|
||||
if (ep) {
|
||||
let u = ep[0];
|
||||
addQueryParameter('edp', u);
|
||||
}
|
||||
|
||||
const di = args['debugId'];
|
||||
if (di) {
|
||||
addQueryParameter('di', di);
|
||||
}
|
||||
|
||||
const ibe = args['inspect-brk-extensions'];
|
||||
if (ibe) {
|
||||
addQueryParameter('ibe', ibe);
|
||||
}
|
||||
|
||||
window.open(newAddress);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#C5C5C5"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#C5C5C5"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#C5C5C5"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#C5C5C5"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 484 B |
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="white"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="white"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="white"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="white"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 474 B |
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#424242"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#424242"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#424242"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#424242"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 484 B |
@@ -17,12 +17,20 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint-disabled,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled {
|
||||
background: url('breakpoint-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled:hover {
|
||||
background: url('breakpoint-hint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget.line-start {
|
||||
left: -0.45em !important;
|
||||
}
|
||||
|
||||
.debug-breakpoint-unverified,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unverified {
|
||||
background: url('breakpoint-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -35,21 +43,31 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint,
|
||||
.monaco-editor .debug-breakpoint-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
background: url('breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-column::before,
|
||||
.monaco-editor .debug-breakpoint-placeholder::before,
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
content: " ";
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
margin-left: 0.61em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.debug-function-breakpoint {
|
||||
background: url('breakpoint-function.svg') center center no-repeat;
|
||||
}
|
||||
@@ -75,34 +93,34 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-conditional {
|
||||
background: url('breakpoint-conditional.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-log-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log {
|
||||
background: url('breakpoint-log.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log-disabled,
|
||||
.monaco-editor .debug-breakpoint-log-disabled-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-disabled {
|
||||
background: url('breakpoint-log-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log-unverified,
|
||||
.monaco-editor .debug-breakpoint-log-unverified-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-unverified {
|
||||
background: url('breakpoint-log-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-unsupported,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unsupported {
|
||||
background: url('breakpoint-unsupported.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint,
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint-log,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-column.debug-top-stack-frame-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-top-stack-frame-column {
|
||||
background: url('current-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,19 +88,6 @@
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.debug-action.clear-repl {
|
||||
background: url('clear-light.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .debug-action.clear-repl {
|
||||
background: url('clear-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .debug-action.clear-repl {
|
||||
background: url('clear-hc.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* Output coloring and styling */
|
||||
.repl .repl-tree .output.expression > .ignore {
|
||||
font-style: italic;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { env as processEnv } from 'vs/base/common/process';
|
||||
@@ -79,7 +79,7 @@ export class RawDebugSession implements IDisposable {
|
||||
dbgr: IDebugger,
|
||||
private readonly telemetryService: ITelemetryService,
|
||||
public readonly customTelemetryService: ITelemetryService | undefined,
|
||||
private readonly windowsService: IWindowsService,
|
||||
private readonly extensionHostDebugService: IExtensionHostDebugService,
|
||||
private readonly openerService: IOpenerService
|
||||
|
||||
) {
|
||||
@@ -381,6 +381,13 @@ export class RawDebugSession implements IDisposable {
|
||||
return this.send<DebugProtocol.SetExceptionBreakpointsResponse>('setExceptionBreakpoints', args);
|
||||
}
|
||||
|
||||
breakpointLocations(args: DebugProtocol.BreakpointLocationsArguments): Promise<DebugProtocol.BreakpointLocationsResponse> {
|
||||
if (this.capabilities.supportsBreakpointLocationsRequest) {
|
||||
return this.send('breakpointLocations', args);
|
||||
}
|
||||
return Promise.reject(new Error('breakpointLocations is not supported'));
|
||||
}
|
||||
|
||||
configurationDone(): Promise<DebugProtocol.ConfigurationDoneResponse> {
|
||||
if (this.capabilities.supportsConfigurationDoneRequest) {
|
||||
return this.send('configurationDone', null);
|
||||
@@ -594,7 +601,7 @@ export class RawDebugSession implements IDisposable {
|
||||
} else {
|
||||
args[key] = [value];
|
||||
}
|
||||
} else if (key === 'extensionDevelopmentPath') {
|
||||
} else if (key === 'extensionDevelopmentPath' || key === 'enable-proposed-api') {
|
||||
const v = args[key];
|
||||
if (v) {
|
||||
v.push(value);
|
||||
@@ -625,7 +632,7 @@ export class RawDebugSession implements IDisposable {
|
||||
Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]);
|
||||
}
|
||||
|
||||
return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
|
||||
return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env);
|
||||
}
|
||||
|
||||
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
|
||||
|
||||
@@ -1008,7 +1008,7 @@ export class ClearReplAction extends Action {
|
||||
constructor(id: string, label: string,
|
||||
@IPanelService private readonly panelService: IPanelService
|
||||
) {
|
||||
super(id, label, 'debug-action clear-repl');
|
||||
super(id, label, 'debug-action codicon-clear-all');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
|
||||
import { IEditor, ITextEditor } from 'vs/workbench/common/editor';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
@@ -58,6 +58,7 @@ export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey<boolean>('resta
|
||||
export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey<boolean>('jumpToCursorSupported', false);
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
|
||||
export const DEBUG_SCHEME = 'debug';
|
||||
export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {
|
||||
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
|
||||
@@ -207,6 +208,7 @@ export interface IDebugSession extends ITreeElement {
|
||||
dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }>;
|
||||
sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise<void>;
|
||||
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void>;
|
||||
breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]>;
|
||||
|
||||
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse>;
|
||||
exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>;
|
||||
@@ -850,9 +852,12 @@ export const enum BreakpointWidgetContext {
|
||||
|
||||
export interface IDebugEditorContribution extends IEditorContribution {
|
||||
showHover(range: Range, focus: boolean): Promise<void>;
|
||||
addLaunchConfiguration(): Promise<any>;
|
||||
}
|
||||
|
||||
export interface IBreakpointEditorContribution extends IEditorContribution {
|
||||
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void;
|
||||
closeBreakpointWidget(): void;
|
||||
addLaunchConfiguration(): Promise<any>;
|
||||
}
|
||||
|
||||
// temporary debug helper service
|
||||
|
||||
@@ -7,13 +7,22 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
|
||||
export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
|
||||
constructor(
|
||||
@IMainProcessService readonly windowService: IMainProcessService,
|
||||
@IMainProcessService readonly mainProcessService: IMainProcessService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService
|
||||
) {
|
||||
super(windowService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
|
||||
super(mainProcessService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
// TODO@Isidor use debug IPC channel
|
||||
return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
@@ -132,6 +132,11 @@ export class MockDebugService implements IDebugService {
|
||||
}
|
||||
|
||||
export class MockSession implements IDebugSession {
|
||||
|
||||
breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/co
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats';
|
||||
|
||||
export const enum ExperimentState {
|
||||
|
||||
@@ -42,7 +42,7 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { extname } from 'vs/base/common/resources';
|
||||
import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product';
|
||||
import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/productService';
|
||||
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}}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow
|
||||
import {
|
||||
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
|
||||
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, OpenExtensionsFolderAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
|
||||
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
|
||||
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
@@ -43,7 +43,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/browser/extensionsDependencyChecker';
|
||||
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';
|
||||
|
||||
@@ -351,7 +350,6 @@ const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(Workbench
|
||||
class ExtensionsContributions implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
|
||||
) {
|
||||
|
||||
@@ -369,14 +367,7 @@ class ExtensionsContributions implements IWorkbenchContribution {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (workbenchEnvironmentService.extensionsPath) {
|
||||
const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting);
|
||||
|
||||
@@ -26,7 +26,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
|
||||
import { IFileService, IFileContent } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -45,7 +45,6 @@ import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/w
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
|
||||
@@ -59,7 +58,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
@@ -2073,13 +2072,13 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);
|
||||
|
||||
return this.jsonEditingService.write(workspaceConfigurationFile,
|
||||
{
|
||||
[{
|
||||
key: 'extensions',
|
||||
value: {
|
||||
recommendations: shouldRecommend ? insertInto : removeFrom,
|
||||
unwantedRecommendations: shouldRecommend ? removeFrom : insertInto
|
||||
}
|
||||
},
|
||||
}],
|
||||
true);
|
||||
});
|
||||
}
|
||||
@@ -2102,19 +2101,19 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) {
|
||||
removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);
|
||||
removeFromPromise = this.jsonEditingService.write(extensionsFileResource,
|
||||
{
|
||||
[{
|
||||
key: shouldRecommend ? 'unwantedRecommendations' : 'recommendations',
|
||||
value: removeFrom
|
||||
},
|
||||
}],
|
||||
true);
|
||||
}
|
||||
|
||||
return removeFromPromise.then(() =>
|
||||
this.jsonEditingService.write(extensionsFileResource,
|
||||
{
|
||||
[{
|
||||
key: shouldRecommend ? 'recommendations' : 'unwantedRecommendations',
|
||||
value: insertInto
|
||||
},
|
||||
}],
|
||||
true)
|
||||
);
|
||||
});
|
||||
@@ -2139,7 +2138,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
.then(content => {
|
||||
const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];
|
||||
if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {
|
||||
return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true)
|
||||
return this.jsonEditingService.write(workspaceConfigurationFile, [{ key: 'extensions', value: { recommendations: [] } }], true)
|
||||
.then(() => this.fileService.readFile(workspaceConfigurationFile));
|
||||
}
|
||||
return content;
|
||||
@@ -2801,41 +2800,6 @@ export class EnableAllWorkpsaceAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
|
||||
static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
) {
|
||||
super(id, label, undefined, true);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
if (this.environmentService.extensionsPath) {
|
||||
|
||||
const extensionsHome = URI.file(this.environmentService.extensionsPath);
|
||||
|
||||
return Promise.resolve(this.fileService.resolve(extensionsHome)).then(file => {
|
||||
let itemToShow: URI;
|
||||
if (file.children && file.children.length > 0) {
|
||||
itemToShow = file.children[0].resource;
|
||||
} else {
|
||||
itemToShow = extensionsHome;
|
||||
}
|
||||
|
||||
return this.windowsService.showItemInFolder(itemToShow);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallVSIXAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.installVSIX';
|
||||
|
||||
@@ -44,7 +44,7 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { asDomUri } from 'vs/base/browser/dom';
|
||||
|
||||
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; // {{SQL CARBON EDIT}}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
margin: 0.15em;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .action-label.system-disable.icon {
|
||||
.monaco-action-bar .action-item .action-label.system-disable.codicon {
|
||||
opacity: 1;
|
||||
height: 18px;
|
||||
width: 10px;
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count > .octicon {
|
||||
font-size: 100%;
|
||||
font-size: 120%;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { randomPort } from 'vs/base/node/ports';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
|
||||
|
||||
@@ -8,7 +8,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 { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -22,6 +22,9 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler';
|
||||
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
|
||||
import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
// Singletons
|
||||
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true);
|
||||
@@ -57,6 +60,20 @@ const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExte
|
||||
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer"));
|
||||
|
||||
class ExtensionsContributions implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
if (workbenchEnvironmentService.extensionsPath) {
|
||||
const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting);
|
||||
|
||||
// Register Commands
|
||||
|
||||
CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor) => {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
|
||||
static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IElectronService private readonly electronService: IElectronService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
) {
|
||||
super(id, label, undefined, true);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (this.environmentService.extensionsPath) {
|
||||
const extensionsHome = URI.file(this.environmentService.extensionsPath);
|
||||
const file = await this.fileService.resolve(extensionsHome);
|
||||
|
||||
let itemToShow: URI;
|
||||
if (file.children && file.children.length > 0) {
|
||||
itemToShow = file.children[0].resource;
|
||||
} else {
|
||||
itemToShow = extensionsHome;
|
||||
}
|
||||
|
||||
if (itemToShow.scheme === Schemas.file) {
|
||||
return this.electronService.showItemInFolder(itemToShow.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -139,7 +139,7 @@ class ReportExtensionSlowAction extends Action {
|
||||
- Extension Name: \`${this.extension.name}\`
|
||||
- Extension Version: \`${this.extension.version}\`
|
||||
- OS Version: \`${osVersion}\`
|
||||
- VSCode version: \`${pkg.version}\`\n\n${message}`);
|
||||
- VSCode version: \`${product.version}\`\n\n${message}`);
|
||||
|
||||
const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`;
|
||||
this._openerService.open(URI.parse(url));
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
import 'vs/css!./media/runtimeExtensionsEditor';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as os from 'os';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -523,7 +522,7 @@ export class ReportExtensionIssueAction extends Action {
|
||||
- Extension Name: \`${extension.description.name}\`
|
||||
- Extension Version: \`${extension.description.version}\`
|
||||
- OS Version: \`${osVersion}\`
|
||||
- VSCode version: \`${pkg.version}\`\n\n${message}`
|
||||
- VSCode version: \`${product.version}\`\n\n${message}`
|
||||
);
|
||||
|
||||
return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`;
|
||||
|
||||
@@ -41,7 +41,7 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedPr
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
@@ -51,7 +51,7 @@ import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
|
||||
const mockExtensionGallery: IGalleryExtension[] = [
|
||||
aGalleryExtension('MockExtension1', {
|
||||
|
||||
@@ -41,7 +41,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browse
|
||||
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
|
||||
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
export interface IFeedback {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { FeedbackDropdown, IFeedback, IFeedbackDelegate } from 'vs/workbench/contrib/feedback/browser/feedback';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
@@ -10,11 +10,11 @@ import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTI
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { openWindowCommand, REVEAL_IN_OS_COMMAND_ID, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, REVEAL_IN_OS_LABEL, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands';
|
||||
import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands';
|
||||
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { isWindows, isMacintosh, isWeb } from 'vs/base/common/platform';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
@@ -23,9 +23,9 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { SupportsWorkspacesContext, IsWebContext, RemoteFileDialogContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { SupportsWorkspacesContext, IsWebContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { OpenFileFolderAction, OpenLocalFileFolderCommand, OpenFileAction, OpenFolderAction, OpenLocalFileCommand, OpenLocalFolderCommand, OpenWorkspaceAction, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { ActiveEditorIsSaveableContext } from 'vs/workbench/common/editor';
|
||||
import { SidebarFocusContext } from 'vs/workbench/common/viewlet';
|
||||
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
|
||||
@@ -48,57 +48,17 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithClipboardAc
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category.value);
|
||||
|
||||
|
||||
const fileCategory = nls.localize('file', "File");
|
||||
const workspacesCategory = nls.localize('workspaces', "Workspaces");
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory);
|
||||
|
||||
const fileCategory = nls.localize('file', "File");
|
||||
if (isMacintosh) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory);
|
||||
if (!isWeb) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: OpenLocalFileFolderCommand.ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_O,
|
||||
when: RemoteFileDialogContext,
|
||||
description: { description: OpenLocalFileFolderCommand.LABEL, args: [] },
|
||||
handler: OpenLocalFileFolderCommand.handler()
|
||||
});
|
||||
}
|
||||
} else {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory);
|
||||
if (!isWeb) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: OpenLocalFileCommand.ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_O,
|
||||
when: RemoteFileDialogContext,
|
||||
description: { description: OpenLocalFileCommand.LABEL, args: [] },
|
||||
handler: OpenLocalFileCommand.handler()
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: OpenLocalFolderCommand.ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O),
|
||||
when: RemoteFileDialogContext,
|
||||
description: { description: OpenLocalFolderCommand.LABEL, args: [] },
|
||||
handler: OpenLocalFolderCommand.handler()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!isWeb) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: SaveLocalFileCommand.ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S,
|
||||
when: RemoteFileDialogContext,
|
||||
description: { description: SaveLocalFileCommand.LABEL, args: [] },
|
||||
handler: SaveLocalFileCommand.handler()
|
||||
});
|
||||
}
|
||||
|
||||
const workspacesCategory = nls.localize('workspaces', "Workspaces");
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, SupportsWorkspacesContext);
|
||||
|
||||
// Commands
|
||||
CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand);
|
||||
CommandsRegistry.registerCommand('_files.newWindow', newWindowCommand);
|
||||
@@ -212,11 +172,9 @@ const copyRelativePathCommand = {
|
||||
// Editor Title Context Menu
|
||||
appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
|
||||
appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
|
||||
appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file));
|
||||
appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)));
|
||||
appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource);
|
||||
|
||||
function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr | undefined, group?: string): void {
|
||||
export function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr | undefined, group?: string): void {
|
||||
|
||||
// Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, {
|
||||
@@ -252,7 +210,7 @@ function appendSaveConflictEditorTitleAction(id: string, title: string, iconLoca
|
||||
|
||||
// Menu registration - command palette
|
||||
|
||||
function appendToCommandPalette(id: string, title: ILocalizedString, category: ILocalizedString, when?: ContextKeyExpr): void {
|
||||
export function appendToCommandPalette(id: string, title: ILocalizedString, category: ILocalizedString, when?: ContextKeyExpr): void {
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id,
|
||||
@@ -272,7 +230,6 @@ appendToCommandPalette(SAVE_ALL_IN_GROUP_COMMAND_ID, { value: nls.localize('save
|
||||
appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', "Save All Files"), original: 'Save All Files' }, category);
|
||||
appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, category);
|
||||
appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, category);
|
||||
appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category);
|
||||
appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category);
|
||||
appendToCommandPalette(CLOSE_EDITOR_COMMAND_ID, { value: nls.localize('closeEditor', "Close Editor"), original: 'Close Editor' }, { value: nls.localize('view', "View"), original: 'View' });
|
||||
appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0'));
|
||||
@@ -292,17 +249,6 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
when: ResourceContextKey.IsFileSystemResource
|
||||
});
|
||||
|
||||
const revealInOsCommand = {
|
||||
id: REVEAL_IN_OS_COMMAND_ID,
|
||||
title: isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder")
|
||||
};
|
||||
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
group: 'navigation',
|
||||
order: 20,
|
||||
command: revealInOsCommand,
|
||||
when: ResourceContextKey.IsFileSystemResource
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
group: '1_cutcopypaste',
|
||||
order: 10,
|
||||
@@ -470,13 +416,6 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 20,
|
||||
command: revealInOsCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: '3_compare',
|
||||
order: 20,
|
||||
@@ -570,7 +509,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
id: REMOVE_ROOT_FOLDER_COMMAND_ID,
|
||||
title: REMOVE_ROOT_FOLDER_LABEL
|
||||
},
|
||||
when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext)
|
||||
when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext, SupportsWorkspacesContext)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
@@ -714,8 +653,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
id: OpenWorkspaceAction.ID,
|
||||
title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...")
|
||||
},
|
||||
order: 3,
|
||||
when: SupportsWorkspacesContext
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
|
||||
@@ -26,9 +26,8 @@ import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { getResourceForCommand, getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
@@ -50,8 +49,6 @@ import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/q
|
||||
|
||||
// Commands
|
||||
|
||||
export const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS';
|
||||
export const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder");
|
||||
export const REVEAL_IN_EXPLORER_COMMAND_ID = 'revealInExplorer';
|
||||
export const REVERT_FILE_COMMAND_ID = 'workbench.action.files.revert';
|
||||
export const OPEN_TO_SIDE_COMMAND_ID = 'explorer.openToSide';
|
||||
@@ -437,44 +434,6 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
function revealResourcesInOS(resources: URI[], windowsService: IWindowsService, notificationService: INotificationService, workspaceContextService: IWorkspaceContextService): void {
|
||||
if (resources.length) {
|
||||
sequence(resources.map(r => () => windowsService.showItemInFolder(r.scheme === Schemas.userData ? r.with({ scheme: Schemas.file }) : r)));
|
||||
} else if (workspaceContextService.getWorkspace().folders.length) {
|
||||
windowsService.showItemInFolder(workspaceContextService.getWorkspace().folders[0].uri);
|
||||
} else {
|
||||
notificationService.info(nls.localize('openFileToReveal', "Open a file first to reveal"));
|
||||
}
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: REVEAL_IN_OS_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.focus.toNegated(),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R,
|
||||
win: {
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_R
|
||||
},
|
||||
handler: (accessor: ServicesAccessor, resource: URI | object) => {
|
||||
const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService));
|
||||
revealResourcesInOS(resources, accessor.get(IWindowsService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R),
|
||||
id: 'workbench.action.files.revealActiveFileInWindows',
|
||||
handler: (accessor: ServicesAccessor) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const activeInput = editorService.activeEditor;
|
||||
const resource = activeInput ? activeInput.getResource() : null;
|
||||
const resources = resource ? [resource] : [];
|
||||
revealResourcesInOS(resources, accessor.get(IWindowsService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
|
||||
}
|
||||
});
|
||||
|
||||
async function resourcesToClipboard(resources: URI[], relative: boolean, clipboardService: IClipboardService, notificationService: INotificationService, labelService: ILabelService): Promise<void> {
|
||||
if (resources.length) {
|
||||
const lineDelimiter = isWindows ? '\r\n' : '\n';
|
||||
|
||||
@@ -381,13 +381,16 @@ export class OpenEditorsView extends ViewletPanel {
|
||||
private focusActiveEditor(): void {
|
||||
if (this.list.length && this.editorGroupService.activeGroup) {
|
||||
const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor);
|
||||
this.list.setFocus([index]);
|
||||
this.list.setSelection([index]);
|
||||
this.list.reveal(index);
|
||||
} else {
|
||||
this.list.setFocus([]);
|
||||
this.list.setSelection([]);
|
||||
if (index >= 0) {
|
||||
this.list.setFocus([index]);
|
||||
this.list.setSelection([index]);
|
||||
this.list.reveal(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.list.setFocus([]);
|
||||
this.list.setSelection([]);
|
||||
}
|
||||
|
||||
private onConfigurationChange(event: IConfigurationChangeEvent): void {
|
||||
|
||||
@@ -111,7 +111,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
|
||||
setPreferredEncoding(encoding: string): void {
|
||||
this.preferredEncoding = encoding;
|
||||
this.forceOpenAs = ForceOpenAs.Text; // encoding is a good hint to open the file as text
|
||||
this.setForceOpenAsText(); // encoding is a good hint to open the file as text
|
||||
}
|
||||
|
||||
getPreferredMode(): string | undefined {
|
||||
@@ -129,7 +129,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
|
||||
setPreferredMode(mode: string): void {
|
||||
this.preferredMode = mode;
|
||||
this.forceOpenAs = ForceOpenAs.Text; // mode is a good hint to open the file as text
|
||||
this.setForceOpenAsText(); // mode is a good hint to open the file as text
|
||||
}
|
||||
|
||||
setForceOpenAsText(): void {
|
||||
@@ -165,21 +165,15 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
}
|
||||
|
||||
getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string {
|
||||
let description: string;
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
description = this.shortDescription;
|
||||
break;
|
||||
return this.shortDescription;
|
||||
case Verbosity.LONG:
|
||||
description = this.longDescription;
|
||||
break;
|
||||
return this.longDescription;
|
||||
case Verbosity.MEDIUM:
|
||||
default:
|
||||
description = this.mediumDescription;
|
||||
break;
|
||||
return this.mediumDescription;
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
@memoize
|
||||
@@ -198,24 +192,16 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
}
|
||||
|
||||
getTitle(verbosity: Verbosity): string {
|
||||
let title: string;
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
title = this.shortTitle;
|
||||
// already decorated by getName()
|
||||
break;
|
||||
return this.shortTitle;
|
||||
default:
|
||||
case Verbosity.MEDIUM:
|
||||
title = this.mediumTitle;
|
||||
title = this.decorateLabel(title);
|
||||
break;
|
||||
return this.decorateLabel(this.mediumTitle);
|
||||
case Verbosity.LONG:
|
||||
title = this.longTitle;
|
||||
title = this.decorateLabel(title);
|
||||
break;
|
||||
return this.decorateLabel(this.longTitle);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private decorateLabel(label: string): string {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-browser/fileCommands';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution';
|
||||
|
||||
const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS';
|
||||
const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder");
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: REVEAL_IN_OS_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.focus.toNegated(),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R,
|
||||
win: {
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_R
|
||||
},
|
||||
handler: (accessor: ServicesAccessor, resource: URI | object) => {
|
||||
const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService));
|
||||
revealResourcesInOS(resources, accessor.get(IElectronService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R),
|
||||
id: 'workbench.action.files.revealActiveFileInWindows',
|
||||
handler: (accessor: ServicesAccessor) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const activeInput = editorService.activeEditor;
|
||||
const resource = activeInput ? activeInput.getResource() : null;
|
||||
const resources = resource ? [resource] : [];
|
||||
revealResourcesInOS(resources, accessor.get(IElectronService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
|
||||
}
|
||||
});
|
||||
|
||||
appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file));
|
||||
|
||||
// Menu registration - open editors
|
||||
|
||||
const revealInOsCommand = {
|
||||
id: REVEAL_IN_OS_COMMAND_ID,
|
||||
title: isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder")
|
||||
};
|
||||
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
group: 'navigation',
|
||||
order: 20,
|
||||
command: revealInOsCommand,
|
||||
when: ResourceContextKey.IsFileSystemResource
|
||||
});
|
||||
|
||||
// Menu registration - explorer
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 20,
|
||||
command: revealInOsCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
|
||||
// Command Palette
|
||||
|
||||
const category = { value: nls.localize('filesCategory', "File"), original: 'File' };
|
||||
appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category);
|
||||
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
|
||||
// Commands
|
||||
|
||||
export function revealResourcesInOS(resources: URI[], electronService: IElectronService, notificationService: INotificationService, workspaceContextService: IWorkspaceContextService): void {
|
||||
if (resources.length) {
|
||||
sequence(resources.map(r => async () => {
|
||||
if (r.scheme === Schemas.file) {
|
||||
electronService.showItemInFolder(r.fsPath);
|
||||
}
|
||||
}));
|
||||
} else if (workspaceContextService.getWorkspace().folders.length) {
|
||||
const uri = workspaceContextService.getWorkspace().folders[0].uri;
|
||||
if (uri.scheme === Schemas.file) {
|
||||
electronService.showItemInFolder(uri.fsPath);
|
||||
}
|
||||
} else {
|
||||
notificationService.info(nls.localize('openFileToReveal', "Open a file first to reveal"));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as nls from 'vs/nls';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions';
|
||||
|
||||
@@ -80,7 +80,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
|
||||
[{
|
||||
label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"),
|
||||
run: () => {
|
||||
const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: locale }, true) : Promise.resolve(undefined);
|
||||
const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined);
|
||||
updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e));
|
||||
}
|
||||
}],
|
||||
|
||||
@@ -65,7 +65,7 @@ export class ConfigureLocaleAction extends Action {
|
||||
}
|
||||
|
||||
if (selectedLanguage) {
|
||||
await this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: selectedLanguage.label }, true);
|
||||
await this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: selectedLanguage.label }], true);
|
||||
const restart = await this.dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."),
|
||||
|
||||
@@ -8,7 +8,7 @@ import { join } from 'vs/base/common/path';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { SetLogLevelAction, OpenLogsFolderAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
|
||||
import { SetLogLevelAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
|
||||
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -64,10 +64,6 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution {
|
||||
};
|
||||
registerTelemetryChannel(this.logService.getLevel());
|
||||
this.logService.onDidChangeLogLevel(registerTelemetryChannel);
|
||||
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
|
||||
const devCategory = nls.localize('developer', "Developer");
|
||||
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
|
||||
}
|
||||
|
||||
private async registerLogChannel(id: string, label: string, file: URI): Promise<void> {
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -16,23 +13,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
||||
import { dirname, basename, isEqual } from 'vs/base/common/resources';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class OpenLogsFolderAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.openLogsFolder';
|
||||
static LABEL = nls.localize('openLogsFolder', "Open Logs Folder");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.windowsService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')));
|
||||
}
|
||||
}
|
||||
|
||||
export class SetLogLevelAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.setLogLevel';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser/logsActions';
|
||||
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
|
||||
const devCategory = nls.localize('developer', "Developer");
|
||||
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
|
||||
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
|
||||
export class OpenLogsFolderAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.openLogsFolder';
|
||||
static LABEL = nls.localize('openLogsFolder', "Open Logs Folder");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IElectronService private readonly electronService: IElectronService,
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.electronService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')).fsPath);
|
||||
}
|
||||
}
|
||||