mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 10:40:41 +00:00
editors - reload error placeholder when file is coming back
This commit is contained in:
parent
cffd590665
commit
897ab9567c
|
@ -84,12 +84,8 @@ export function toErrorMessage(error: any = null, verbose: boolean = false): str
|
|||
}
|
||||
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: readonly IAction[];
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: readonly IAction[];
|
||||
export interface IErrorWithActions extends Error {
|
||||
actions: IAction[];
|
||||
}
|
||||
|
||||
export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {
|
||||
|
@ -98,12 +94,15 @@ export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {
|
|||
return candidate instanceof Error && Array.isArray(candidate.actions);
|
||||
}
|
||||
|
||||
export function createErrorWithActions(message: string, options: IErrorOptions = Object.create(null)): Error & IErrorWithActions {
|
||||
const result = new Error(message);
|
||||
|
||||
if (options.actions) {
|
||||
(result as IErrorWithActions).actions = options.actions;
|
||||
export function createErrorWithActions(messageOrError: string | Error, actions: IAction[]): IErrorWithActions {
|
||||
let error: IErrorWithActions;
|
||||
if (typeof messageOrError === 'string') {
|
||||
error = new Error(messageOrError) as IErrorWithActions;
|
||||
} else {
|
||||
error = messageOrError as IErrorWithActions;
|
||||
}
|
||||
|
||||
return result;
|
||||
error.actions = actions;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ 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 { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { FileChangeType, FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
|
||||
import { isErrorWithActions, toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
|
||||
export interface IEditorPlaceholderContents {
|
||||
|
@ -96,7 +96,7 @@ export abstract class EditorPlaceholder extends EditorPane {
|
|||
|
||||
// Delegate to implementation for contents
|
||||
const disposables = new DisposableStore();
|
||||
const { icon, label, actions } = await this.getContents(input, options);
|
||||
const { icon, label, actions } = await this.getContents(input, options, disposables);
|
||||
|
||||
// Icon
|
||||
const iconContainer = container.appendChild($('.editor-placeholder-icon-container'));
|
||||
|
@ -126,7 +126,7 @@ export abstract class EditorPlaceholder extends EditorPane {
|
|||
return disposables;
|
||||
}
|
||||
|
||||
protected abstract getContents(input: EditorInput, options: IEditorOptions | undefined): Promise<IEditorPlaceholderContents>;
|
||||
protected abstract getContents(input: EditorInput, options: IEditorOptions | undefined, disposables: DisposableStore): Promise<IEditorPlaceholderContents>;
|
||||
|
||||
override clearInput(): void {
|
||||
if (this.container) {
|
||||
|
@ -213,28 +213,32 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder {
|
|||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super(ErrorPlaceholderEditor.ID, telemetryService, themeService, storageService, instantiationService);
|
||||
}
|
||||
|
||||
protected async getContents(input: EditorInput, options: IErrorEditorPlaceholderOptions): Promise<IEditorPlaceholderContents> {
|
||||
protected async getContents(input: EditorInput, options: IErrorEditorPlaceholderOptions, disposables: DisposableStore): Promise<IEditorPlaceholderContents> {
|
||||
const resource = input.resource;
|
||||
const group = this.group;
|
||||
const error = options.error;
|
||||
const isFileNotFound = (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND;
|
||||
|
||||
// Error Label
|
||||
let label: string;
|
||||
if ((<FileOperationError>options.error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
|
||||
if (isFileNotFound) {
|
||||
label = localize('unavailableResourceErrorEditorText', "The editor could not be opened because the file was not found.");
|
||||
} else if (options.error) {
|
||||
label = localize('unknownErrorEditorTextWithError', "The editor could not be opened due to an unexpected error: {0}", toErrorMessage(options.error));
|
||||
} 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.");
|
||||
}
|
||||
|
||||
// Actions
|
||||
let actions: IEditorPlaceholderContentsAction[] | undefined = undefined;
|
||||
if (isErrorWithActions(options.error)) {
|
||||
actions = options.error.actions?.map(action => {
|
||||
if (isErrorWithActions(error) && error.actions.length > 0) {
|
||||
actions = error.actions.map(action => {
|
||||
return {
|
||||
label: action.label,
|
||||
run: () => action.run()
|
||||
|
@ -244,11 +248,20 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder {
|
|||
actions = [
|
||||
{
|
||||
label: localize('retry', "Try Again"),
|
||||
run: () => group.openEditor(input, { ...this.options, source: EditorOpenSource.USER /* explicit user gesture */ })
|
||||
run: () => group.openEditor(input, { ...options, source: EditorOpenSource.USER /* explicit user gesture */ })
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return { icon: '$(error)', label, actions: actions ?? [] };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
|
||||
|
@ -158,7 +157,7 @@ export class DebugTaskRunner {
|
|||
const errorMessage = typeof taskId === 'string'
|
||||
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
|
||||
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
|
||||
return Promise.reject(createErrorWithActions(errorMessage));
|
||||
return Promise.reject(new Error(errorMessage));
|
||||
}
|
||||
|
||||
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { toAction } from 'vs/base/common/actions';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
|
@ -747,11 +747,7 @@ export class RawDebugSession implements IDisposable {
|
|||
const uri = URI.parse(url);
|
||||
// Use a suffixed id if uri invokes a command, so default 'Open launch.json' command is suppressed on dialog
|
||||
const actionId = uri.scheme === Schemas.command ? 'debug.moreInfo.command' : 'debug.moreInfo';
|
||||
return createErrorWithActions(userMessage, {
|
||||
actions: [new Action(actionId, label, undefined, true, async () => {
|
||||
this.openerService.open(uri, { allowCommands: true });
|
||||
})]
|
||||
});
|
||||
return createErrorWithActions(userMessage, [toAction({ id: actionId, label, run: () => this.openerService.open(uri, { allowCommands: true }) })]);
|
||||
}
|
||||
if (showErrors && error && error.format && error.showUser) {
|
||||
this.notificationService.error(userMessage);
|
||||
|
|
|
@ -719,11 +719,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
|
|||
const message = err && err.message || '';
|
||||
|
||||
if (/ECONNREFUSED/.test(message)) {
|
||||
const error = createErrorWithActions(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), {
|
||||
actions: [
|
||||
new Action('open user settings', localize('open user settings', "Open User Settings"), undefined, true, () => this.preferencesService.openUserSettings())
|
||||
]
|
||||
});
|
||||
const error = createErrorWithActions(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), [
|
||||
new Action('open user settings', localize('open user settings', "Open User Settings"), undefined, true, () => this.preferencesService.openUserSettings())
|
||||
]);
|
||||
|
||||
this.notificationService.error(error);
|
||||
return;
|
||||
|
|
|
@ -972,11 +972,9 @@ export class ExtensionsListView extends ViewPane {
|
|||
const message = err && err.message || '';
|
||||
|
||||
if (/ECONNREFUSED/.test(message)) {
|
||||
const error = createErrorWithActions(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), {
|
||||
actions: [
|
||||
new Action('open user settings', localize('open user settings', "Open User Settings"), undefined, true, () => this.preferencesService.openUserSettings())
|
||||
]
|
||||
});
|
||||
const error = createErrorWithActions(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), [
|
||||
new Action('open user settings', localize('open user settings', "Open User Settings"), undefined, true, () => this.preferencesService.openUserSettings())
|
||||
]);
|
||||
|
||||
this.notificationService.error(error);
|
||||
return;
|
||||
|
|
|
@ -26,7 +26,7 @@ import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'
|
|||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { IExplorerService } from 'vs/workbench/contrib/files/browser/files';
|
||||
|
@ -187,8 +187,7 @@ export class TextFileEditor extends BaseTextEditor<ICodeEditorViewState> {
|
|||
|
||||
// Offer to create a file from the error if we have a file not found and the name is valid
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && await this.pathService.hasValidBasename(input.preferredResource)) {
|
||||
const fileNotFoundError: FileOperationError & IErrorWithActions = new FileOperationError(localize('fileNotFoundError', "File not found"), FileOperationResult.FILE_NOT_FOUND);
|
||||
fileNotFoundError.actions = [
|
||||
const fileNotFoundError = createErrorWithActions(new FileOperationError(localize('fileNotFoundError', "File not found"), FileOperationResult.FILE_NOT_FOUND), [
|
||||
toAction({
|
||||
id: 'workbench.files.action.createMissingFile', label: localize('createFile', "Create File"), run: async () => {
|
||||
await this.textFileService.create([{ resource: input.preferredResource }]);
|
||||
|
@ -201,7 +200,7 @@ export class TextFileEditor extends BaseTextEditor<ICodeEditorViewState> {
|
|||
});
|
||||
}
|
||||
})
|
||||
];
|
||||
]);
|
||||
|
||||
throw fileNotFoundError;
|
||||
}
|
||||
|
|
|
@ -62,24 +62,22 @@ export class NativeTextFileEditor extends TextFileEditor {
|
|||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_EXCEEDS_MEMORY_LIMIT) {
|
||||
const memoryLimit = Math.max(MIN_MAX_MEMORY_SIZE_MB, +this.textResourceConfigurationService.getValue<number>(undefined, 'files.maxMemoryForLargeFilesMB') || FALLBACK_MAX_MEMORY_SIZE_MB);
|
||||
|
||||
throw createErrorWithActions(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow {0} to use more memory", this.productService.nameShort), {
|
||||
actions: [
|
||||
toAction({
|
||||
id: 'workbench.window.action.relaunchWithIncreasedMemoryLimit', label: localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), run: () => {
|
||||
return this.nativeHostService.relaunch({
|
||||
addArgs: [
|
||||
`--max-memory=${memoryLimit}`
|
||||
]
|
||||
});
|
||||
}
|
||||
}),
|
||||
toAction({
|
||||
id: 'workbench.window.action.configureMemoryLimit', label: localize('configureMemoryLimit', 'Configure Memory Limit'), run: () => {
|
||||
return this.preferencesService.openUserSettings({ query: 'files.maxMemoryForLargeFilesMB' });
|
||||
}
|
||||
}),
|
||||
]
|
||||
});
|
||||
throw createErrorWithActions(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow {0} to use more memory", this.productService.nameShort), [
|
||||
toAction({
|
||||
id: 'workbench.window.action.relaunchWithIncreasedMemoryLimit', label: localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), run: () => {
|
||||
return this.nativeHostService.relaunch({
|
||||
addArgs: [
|
||||
`--max-memory=${memoryLimit}`
|
||||
]
|
||||
});
|
||||
}
|
||||
}),
|
||||
toAction({
|
||||
id: 'workbench.window.action.configureMemoryLimit', label: localize('configureMemoryLimit', 'Configure Memory Limit'), run: () => {
|
||||
return this.preferencesService.openUserSettings({ query: 'files.maxMemoryForLargeFilesMB' });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
// Fallback to handling in super type
|
||||
|
|
|
@ -7,7 +7,7 @@ import * as DOM from 'vs/base/browser/dom';
|
|||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, toAction } from 'vs/base/common/actions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorMessage';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { extname, isEqual } from 'vs/base/common/resources';
|
||||
|
@ -289,8 +289,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
const error: Error & IErrorWithActions = e instanceof Error ? e : new Error(e.message);
|
||||
error.actions = [
|
||||
const error = createErrorWithActions(e instanceof Error ? e : new Error(e.message), [
|
||||
toAction({
|
||||
id: 'workbench.notebook.action.openInTextEditor', label: localize('notebookOpenInTextEditor', "Open in Text Editor"), run: async () => {
|
||||
const activeEditorPane = this._editorService.activeEditorPane;
|
||||
|
@ -317,7 +316,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
|
|||
return;
|
||||
}
|
||||
})
|
||||
];
|
||||
]);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ suite('Notifications', () => {
|
|||
assert.strictEqual(called, 1);
|
||||
|
||||
// Error with Action
|
||||
let item7 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', { actions: [new Action('id', 'label')] }) })!;
|
||||
let item7 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', [new Action('id', 'label')]) })!;
|
||||
assert.strictEqual(item7.actions!.primary!.length, 1);
|
||||
|
||||
// Filter
|
||||
|
|
Loading…
Reference in a new issue