Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/vs/workbench/browser/parts/editor/editorPlaceholder.ts')
-rw-r--r--src/vs/workbench/browser/parts/editor/editorPlaceholder.ts245
1 files changed, 161 insertions, 84 deletions
diff --git a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts
index af524fb057f..0464f14265b 100644
--- a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts
+++ b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts
@@ -5,14 +5,15 @@
import 'vs/css!./media/editorplaceholder';
import { localize } from 'vs/nls';
+import Severity from 'vs/base/common/severity';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
-import { IThemeService } from 'vs/platform/theme/common/themeService';
-import { Dimension, size, clearNode } from 'vs/base/browser/dom';
+import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
+import { Dimension, size, clearNode, $ } from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -23,8 +24,29 @@ import { EditorOpenSource, IEditorOptions } from 'vs/platform/editor/common/edit
import { computeEditorAriaLabel, EditorPaneDescriptor } from 'vs/workbench/browser/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Link } from 'vs/platform/opener/browser/link';
+import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel';
+import { editorErrorForeground, editorInfoForeground, editorWarningForeground } from 'vs/platform/theme/common/colorRegistry';
+import { Codicon } from 'vs/base/common/codicons';
+import { FileChangeType, FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
+import { isErrorWithActions, toErrorMessage } from 'vs/base/common/errorMessage';
+import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
+
+export interface IEditorPlaceholderContents {
+ icon: string;
+ label: string;
+ actions: IEditorPlaceholderContentsAction[];
+}
+
+export interface IEditorPlaceholderContentsAction {
+ label: string;
+ run: () => unknown;
+}
-abstract class EditorPlaceholderPane extends EditorPane {
+export interface IErrorEditorPlaceholderOptions extends IEditorOptions {
+ error?: Error;
+}
+
+export abstract class EditorPlaceholder extends EditorPane {
private container: HTMLElement | undefined;
private scrollbar: DomScrollableElement | undefined;
@@ -32,18 +54,14 @@ abstract class EditorPlaceholderPane extends EditorPane {
constructor(
id: string,
- private readonly title: string,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
- @IStorageService storageService: IStorageService
+ @IStorageService storageService: IStorageService,
+ @IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(id, telemetryService, themeService, storageService);
}
- override getTitle(): string {
- return this.title;
- }
-
protected createEditor(parent: HTMLElement): void {
// Container
@@ -66,21 +84,43 @@ abstract class EditorPlaceholderPane extends EditorPane {
}
// Render Input
- this.inputDisposable.value = this.renderInput(input);
+ this.inputDisposable.value = await this.renderInput(input, options);
}
- private renderInput(input: EditorInput): IDisposable {
+ private async renderInput(input: EditorInput, options: IEditorOptions | undefined): Promise<IDisposable> {
const [container, scrollbar] = assertAllDefined(this.container, this.scrollbar);
// Reset any previous contents
clearNode(container);
- // Update ARIA label
- container.setAttribute('aria-label', computeEditorAriaLabel(input, undefined, this.group, undefined));
-
- // Delegate to implementation
+ // Delegate to implementation for contents
const disposables = new DisposableStore();
- this.renderBody(container, disposables);
+ const { icon, label, actions } = await this.getContents(input, options, disposables);
+
+ // Icon
+ const iconContainer = container.appendChild($('.editor-placeholder-icon-container'));
+ const iconWidget = new SimpleIconLabel(iconContainer);
+ iconWidget.text = icon;
+
+ // Label
+ const labelContainer = container.appendChild($('.editor-placeholder-label-container'));
+ const labelWidget = document.createElement('span');
+ labelWidget.textContent = label;
+ labelContainer.appendChild(labelWidget);
+
+ // ARIA label
+ container.setAttribute('aria-label', `${computeEditorAriaLabel(input, undefined, this.group, undefined)}, ${label}`);
+
+ // Actions
+ const actionsContainer = container.appendChild($('.editor-placeholder-actions-container'));
+ for (const action of actions) {
+ disposables.add(this.instantiationService.createInstance(Link, actionsContainer, {
+ label: action.label,
+ href: ''
+ }, {
+ opener: () => action.run()
+ }));
+ }
// Adjust scrollbar
scrollbar.scanDomNode();
@@ -88,7 +128,7 @@ abstract class EditorPlaceholderPane extends EditorPane {
return disposables;
}
- protected abstract renderBody(container: HTMLElement, disposables: DisposableStore): void;
+ protected abstract getContents(input: EditorInput, options: IEditorOptions | undefined, disposables: DisposableStore): Promise<IEditorPlaceholderContents>;
override clearInput(): void {
if (this.container) {
@@ -108,6 +148,9 @@ abstract class EditorPlaceholderPane extends EditorPane {
// Adjust scrollbar
scrollbar.scanDomNode();
+
+ // Toggle responsive class
+ container.classList.toggle('max-height-200px', dimension.height <= 200);
}
override focus(): void {
@@ -123,11 +166,12 @@ abstract class EditorPlaceholderPane extends EditorPane {
}
}
-export class WorkspaceTrustRequiredEditor extends EditorPlaceholderPane {
+export class WorkspaceTrustRequiredPlaceholderEditor extends EditorPlaceholder {
static readonly ID = 'workbench.editors.workspaceTrustRequiredEditor';
- static readonly LABEL = localize('trustRequiredEditor', "Workspace Trust Required");
- static readonly DESCRIPTOR = EditorPaneDescriptor.create(WorkspaceTrustRequiredEditor, WorkspaceTrustRequiredEditor.ID, WorkspaceTrustRequiredEditor.LABEL);
+ private static readonly LABEL = localize('trustRequiredEditor', "Workspace Trust Required");
+
+ static readonly DESCRIPTOR = EditorPaneDescriptor.create(WorkspaceTrustRequiredPlaceholderEditor, WorkspaceTrustRequiredPlaceholderEditor.ID, WorkspaceTrustRequiredPlaceholderEditor.LABEL);
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -135,95 +179,128 @@ export class WorkspaceTrustRequiredEditor extends EditorPlaceholderPane {
@ICommandService private readonly commandService: ICommandService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
- @IInstantiationService private readonly instantiationService: IInstantiationService
+ @IInstantiationService instantiationService: IInstantiationService
) {
- super(WorkspaceTrustRequiredEditor.ID, WorkspaceTrustRequiredEditor.LABEL, telemetryService, themeService, storageService);
+ super(WorkspaceTrustRequiredPlaceholderEditor.ID, telemetryService, themeService, storageService, instantiationService);
}
- protected renderBody(container: HTMLElement, disposables: DisposableStore): void {
- const label = container.appendChild(document.createElement('p'));
- label.textContent = isSingleFolderWorkspaceIdentifier(toWorkspaceIdentifier(this.workspaceService.getWorkspace())) ?
- localize('requiresFolderTrustText', "The file is not displayed in the editor because trust has not been granted to the folder.") :
- localize('requiresWorkspaceTrustText', "The file is not displayed in the editor because trust has not been granted to the workspace.");
+ override getTitle(): string {
+ return WorkspaceTrustRequiredPlaceholderEditor.LABEL;
+ }
- disposables.add(this.instantiationService.createInstance(Link, label, {
- label: localize('manageTrust', "Manage Workspace Trust"),
- href: ''
- }, {
- opener: () => this.commandService.executeCommand('workbench.trust.manage')
- }));
+ protected async getContents(): Promise<IEditorPlaceholderContents> {
+ return {
+ icon: '$(workspace-untrusted)',
+ label: isSingleFolderWorkspaceIdentifier(toWorkspaceIdentifier(this.workspaceService.getWorkspace())) ?
+ localize('requiresFolderTrustText', "The file is not displayed in the editor because trust has not been granted to the folder.") :
+ localize('requiresWorkspaceTrustText', "The file is not displayed in the editor because trust has not been granted to the workspace."),
+ actions: [
+ {
+ label: localize('manageTrust', "Manage Workspace Trust"),
+ run: () => this.commandService.executeCommand('workbench.trust.manage')
+ }
+ ]
+ };
}
}
-abstract class AbstractErrorEditor extends EditorPlaceholderPane {
+export class ErrorPlaceholderEditor extends EditorPlaceholder {
+
+ private static readonly ID = 'workbench.editors.errorEditor';
+ private static readonly LABEL = localize('errorEditor', "Error Editor");
+
+ static readonly DESCRIPTOR = EditorPaneDescriptor.create(ErrorPlaceholderEditor, ErrorPlaceholderEditor.ID, ErrorPlaceholderEditor.LABEL);
constructor(
- id: string,
- label: string,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
- @IInstantiationService private readonly instantiationService: IInstantiationService
+ @IInstantiationService instantiationService: IInstantiationService,
+ @IFileService private readonly fileService: IFileService,
+ @IDialogService private readonly dialogService: IDialogService
) {
- super(id, label, telemetryService, themeService, storageService);
+ super(ErrorPlaceholderEditor.ID, telemetryService, themeService, storageService, instantiationService);
}
- protected abstract getErrorMessage(): string;
-
- protected renderBody(container: HTMLElement, disposables: DisposableStore): void {
- const label = container.appendChild(document.createElement('p'));
- label.textContent = this.getErrorMessage();
-
- // Offer to re-open
+ protected async getContents(input: EditorInput, options: IErrorEditorPlaceholderOptions, disposables: DisposableStore): Promise<IEditorPlaceholderContents> {
+ const resource = input.resource;
const group = this.group;
- const input = this.input;
- if (group && input) {
- disposables.add(this.instantiationService.createInstance(Link, label, {
- label: localize('retry', "Try Again"),
- href: ''
- }, {
- opener: () => group.openEditor(input, { ...this.options, source: EditorOpenSource.USER /* explicit user gesture */ })
- }));
+ const error = options.error;
+ const isFileNotFound = (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND;
+
+ // Error Label
+ let label: string;
+ if (isFileNotFound) {
+ label = localize('unavailableResourceErrorEditorText', "The editor could not be opened because the file was not found.");
+ } else if (error) {
+ label = localize('unknownErrorEditorTextWithError', "The editor could not be opened due to an unexpected error: {0}", toErrorMessage(error));
+ } else {
+ label = localize('unknownErrorEditorTextWithoutError', "The editor could not be opened due to an unexpected error.");
}
- }
-}
-
-export class UnknownErrorEditor extends AbstractErrorEditor {
- static readonly ID = 'workbench.editors.unknownErrorEditor';
- static readonly LABEL = localize('unknownErrorEditor', "Unknown Error Editor");
- static readonly DESCRIPTOR = EditorPaneDescriptor.create(UnknownErrorEditor, UnknownErrorEditor.ID, UnknownErrorEditor.LABEL);
+ // Actions
+ let actions: IEditorPlaceholderContentsAction[] | undefined = undefined;
+ if (isErrorWithActions(error) && error.actions.length > 0) {
+ actions = error.actions.map(action => {
+ return {
+ label: action.label,
+ run: () => {
+ const result = action.run();
+ if (result instanceof Promise) {
+ result.catch(error => this.dialogService.show(Severity.Error, toErrorMessage(error)));
+ }
+ }
+ };
+ });
+ } else if (group) {
+ actions = [
+ {
+ label: localize('retry', "Try Again"),
+ run: () => group.openEditor(input, { ...options, source: EditorOpenSource.USER /* explicit user gesture */ })
+ }
+ ];
+ }
- constructor(
- @ITelemetryService telemetryService: ITelemetryService,
- @IThemeService themeService: IThemeService,
- @IStorageService storageService: IStorageService,
- @IInstantiationService instantiationService: IInstantiationService
- ) {
- super(UnknownErrorEditor.ID, UnknownErrorEditor.LABEL, telemetryService, themeService, storageService, instantiationService);
- }
+ // Auto-reload when file is added
+ if (group && isFileNotFound && resource && this.fileService.hasProvider(resource)) {
+ disposables.add(this.fileService.onDidFilesChange(e => {
+ if (e.contains(resource, FileChangeType.ADDED, FileChangeType.UPDATED)) {
+ group.openEditor(input, options);
+ }
+ }));
+ }
- protected override getErrorMessage(): string {
- return localize('unknownErrorEditorText', "The editor could not be opened due to an unexpected error.");
+ return { icon: '$(error)', label, actions: actions ?? [] };
}
}
-export class UnavailableResourceErrorEditor extends AbstractErrorEditor {
+registerThemingParticipant((theme, collector) => {
- static readonly ID = 'workbench.editors.unavailableResourceErrorEditor';
- static readonly LABEL = localize('unavailableResourceErrorEditor', "Unavailable Resource Error Editor");
- static readonly DESCRIPTOR = EditorPaneDescriptor.create(UnavailableResourceErrorEditor, UnavailableResourceErrorEditor.ID, UnavailableResourceErrorEditor.LABEL);
+ // Editor Placeholder Error Icon
+ const editorErrorIconForegroundColor = theme.getColor(editorErrorForeground);
+ if (editorErrorIconForegroundColor) {
+ collector.addRule(`
+ .monaco-editor-pane-placeholder .editor-placeholder-icon-container ${Codicon.error.cssSelector} {
+ color: ${editorErrorIconForegroundColor};
+ }`);
+ }
- constructor(
- @ITelemetryService telemetryService: ITelemetryService,
- @IThemeService themeService: IThemeService,
- @IStorageService storageService: IStorageService,
- @IInstantiationService instantiationService: IInstantiationService
- ) {
- super(UnavailableResourceErrorEditor.ID, UnavailableResourceErrorEditor.LABEL, telemetryService, themeService, storageService, instantiationService);
+ // Editor Placeholder Warning Icon
+ const editorWarningIconForegroundColor = theme.getColor(editorWarningForeground);
+ if (editorWarningIconForegroundColor) {
+ collector.addRule(`
+ .monaco-editor-pane-placeholder .editor-placeholder-icon-container ${Codicon.warning.cssSelector} {
+ color: ${editorWarningIconForegroundColor};
+ }`);
}
- protected override getErrorMessage(): string {
- return localize('unavailableResourceErrorEditorText', "The editor could not be opened because the file was not found.");
+ // Editor Placeholder Info/Trust Icon
+ const editorInfoIconForegroundColor = theme.getColor(editorInfoForeground);
+ if (editorInfoIconForegroundColor) {
+ collector.addRule(`
+ .monaco-editor-pane-placeholder .editor-placeholder-icon-container ${Codicon.info.cssSelector},
+ .monaco-editor-pane-placeholder .editor-placeholder-icon-container ${Codicon.workspaceUntrusted.cssSelector} {
+ color: ${editorInfoIconForegroundColor};
+ }`);
}
-}
+});