Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)

* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f

* fix various icon issues

* fix preview features
This commit is contained in:
Anthony Dresser
2019-09-19 21:50:52 -07:00
committed by GitHub
parent 9d3d64eef3
commit db498db0a8
459 changed files with 10195 additions and 7528 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);
});

View File

@@ -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
};
};
}

View File

@@ -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 {

View File

@@ -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');

View File

@@ -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(

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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
);

View File

@@ -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({

View 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
});
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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 = {

View File

@@ -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"; }

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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');
}

View File

@@ -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;
}));
}

View File

@@ -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) {

View File

@@ -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 });

View File

@@ -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) => {
}
`);
}
});

View File

@@ -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' };

View File

@@ -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

View File

@@ -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': {

View File

@@ -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';

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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));

View File

@@ -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);

View File

@@ -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> {

View File

@@ -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.'));

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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.'));

View File

@@ -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) || [];

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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> {

View File

@@ -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> {

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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.');
}

View File

@@ -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 {

View File

@@ -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}}

View File

@@ -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);

View File

@@ -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';

View File

@@ -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';

View File

@@ -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}}

View File

@@ -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;

View File

@@ -197,7 +197,7 @@
}
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count > .octicon {
font-size: 100%;
font-size: 120%;
margin-right: 2px;
}

View File

@@ -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';

View File

@@ -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) => {

View File

@@ -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);
}
}
}
}

View File

@@ -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));

View File

@@ -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)}`;

View File

@@ -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';

View File

@@ -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', {

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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, {

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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"));
}
}

View File

@@ -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';

View File

@@ -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));
}
}],

View File

@@ -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."),

View File

@@ -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> {

View File

@@ -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';

View File

@@ -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);

View File

@@ -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);
}
}

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