Не подтверждена Коммит c72d7612 создал по автору Logan Ramos's avatar Logan Ramos Зафиксировано автором GitHub
Просмотр файлов

Add an untitled document data property (#115790)

* Add an untitled document data property

* Dispose of the model passed back

* Fix layer problem

* Add untitledDocumentData to untitled notebooks

* Fix compilation errors

* Switch to using VS Buffer

* Fix threading the untitled document data back through

* Prevent save dialog on untitled reopen with

* Change ignore save setting

* Change setting name and abide by preserve focus

* Add unit test

* Switch to js doc comments

* Consolidate calculating the untitled value, and added test
владелец 371562f6
......@@ -1074,6 +1074,20 @@ declare module 'vscode' {
//#endregion
//#region Provide a way for custom editors to process untitled files without relying on textDocument https://github.com/microsoft/vscode/issues/115631
/**
* Additional information about the opening custom document.
*/
interface CustomDocumentOpenContext {
/**
* If the URI is an untitled file, this will be populated with the byte data of that file
*
* If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in
*/
readonly untitledDocumentData?: Uint8Array;
}
//#endregion
//#region https://github.com/microsoft/vscode/issues/106744, Notebooks (misc)
export enum NotebookCellKind {
......@@ -1543,6 +1557,7 @@ declare module 'vscode' {
interface NotebookDocumentOpenContext {
readonly backupId?: string;
readonly untitledDocumentData?: Uint8Array;
}
// todo@API use openNotebookDOCUMENT to align with openCustomDocument etc?
......
......@@ -5,6 +5,7 @@
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
......@@ -303,8 +304,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
getEditors: () => CustomEditorInput[],
cancellation: CancellationToken,
_backupFileService: IBackupFileService,
) {
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation);
): Promise<MainThreadCustomEditorModel> {
const editors = getEditors();
let untitledDocumentData: VSBuffer | undefined;
if (editors.length !== 0) {
untitledDocumentData = editors[0].untitledDocumentData;
}
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, untitledDocumentData, cancellation);
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors);
}
......
......@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { diffMaps, diffSets } from 'vs/base/common/collections';
import { Emitter } from 'vs/base/common/event';
......@@ -402,8 +403,8 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
contentOptions.transientOutputs = newOptions.transientOutputs;
},
viewOptions: options.viewOptions,
openNotebook: async (viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken) => {
const data = await this._proxy.$openNotebook(viewType, uri, backupId, token);
openNotebook: async (viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer) => {
const data = await this._proxy.$openNotebook(viewType, uri, backupId, token, untitledDocumentData);
return {
data,
transientOptions: contentOptions
......
......@@ -755,7 +755,7 @@ export interface ExtHostCustomEditorsShape {
position: EditorGroupColumn,
cancellation: CancellationToken
): Promise<void>;
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
$undo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
......@@ -1862,7 +1862,7 @@ export interface ExtHostNotebookShape {
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken): Promise<NotebookDataDto>;
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<NotebookDataDto>;
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
......
......@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { hash } from 'vs/base/common/hash';
import { DisposableStore } from 'vs/base/common/lifecycle';
......@@ -208,7 +209,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}));
}
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken) {
const entry = this._editorProviders.get(viewType);
if (!entry) {
throw new Error(`No provider found for '${viewType}'`);
......@@ -219,7 +220,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}
const revivedResource = URI.revive(resource);
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
const document = await entry.provider.openCustomDocument(revivedResource, { backupId, untitledDocumentData: untitledDocumentData?.buffer }, cancellation);
let storageRoot: URI | undefined;
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
......
......@@ -25,6 +25,7 @@ import { ExtHostNotebookEditor } from './extHostNotebookEditor';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { IRelativePattern } from 'vs/base/common/glob';
import { assertIsDefined } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { hash } from 'vs/base/common/hash';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
......@@ -497,9 +498,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
// --- open, save, saveAs, backup
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken): Promise<NotebookDataDto> {
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<NotebookDataDto> {
const { provider } = this._getProviderData(viewType);
const data = await provider.openNotebook(URI.revive(uri), { backupId }, token);
const data = await provider.openNotebook(URI.revive(uri), { backupId, untitledDocumentData: untitledDocumentData?.buffer }, token);
return {
metadata: {
...notebookDocumentMetadataDefaults,
......
......@@ -1621,7 +1621,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Extract active vs. inactive replacements
let activeReplacement: EditorReplacement | undefined;
const inactiveReplacements: EditorReplacement[] = [];
for (let { editor, replacement, options } of editors) {
for (let { editor, replacement, forceReplaceDirty, options } of editors) {
const index = this.getIndexOfEditor(editor);
if (index >= 0) {
const isActiveEditor = this.isActive(editor);
......@@ -1636,7 +1636,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
options.inactive = !isActiveEditor;
options.pinned = options.pinned ?? true; // unless specified, prefer to pin upon replace
const editorToReplace = { editor, replacement, options };
const editorToReplace = { editor, replacement, forceReplaceDirty, options };
if (isActiveEditor) {
activeReplacement = editorToReplace;
} else {
......@@ -1646,14 +1646,20 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Handle inactive first
for (const { editor, replacement, options } of inactiveReplacements) {
for (const { editor, replacement, forceReplaceDirty, options } of inactiveReplacements) {
// Open inactive editor
await this.doOpenEditor(replacement, options);
// Close replaced inactive editor unless they match
if (!editor.matches(replacement)) {
const closed = await this.doCloseEditorWithDirtyHandling(editor, { preserveFocus: true });
let closed = false;
if (forceReplaceDirty) {
this.doCloseEditor(editor, false);
closed = true;
} else {
closed = await this.doCloseEditorWithDirtyHandling(editor, { preserveFocus: true });
}
if (!closed) {
return; // canceled
}
......@@ -1668,7 +1674,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close replaced active editor unless they match
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
await this.doCloseEditorWithDirtyHandling(activeReplacement.editor, { preserveFocus: true });
if (activeReplacement.forceReplaceDirty) {
this.doCloseEditor(activeReplacement.editor, false);
} else {
await this.doCloseEditorWithDirtyHandling(activeReplacement.editor, { preserveFocus: true });
}
}
await openEditorResult;
......@@ -1787,6 +1797,8 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
export interface EditorReplacement {
readonly editor: EditorInput;
readonly replacement: EditorInput;
/** Skips asking the user for confirmation and doesn't save the document. Only use this if you really need to! */
readonly forceReplaceDirty?: boolean;
readonly options?: EditorOptions;
}
......
......@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { memoize } from 'vs/base/common/decorators';
import { IReference } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
......@@ -30,6 +31,8 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
private readonly _backupId: string | undefined;
private readonly _untitledDocumentData: VSBuffer | undefined;
get resource() { return this._editorResource; }
private _modelRef?: IReference<ICustomEditorModel>;
......@@ -39,7 +42,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
viewType: string,
id: string,
webview: WebviewOverlay,
options: { startsDirty?: boolean, backupId?: string },
options: { startsDirty?: boolean, backupId?: string, untitledDocumentData?: VSBuffer },
@IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILabelService private readonly labelService: ILabelService,
......@@ -52,6 +55,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
this._editorResource = resource;
this._defaultDirtyState = options.startsDirty;
this._backupId = options.backupId;
this._untitledDocumentData = options.untitledDocumentData;
}
public getTypeId(): string {
......@@ -251,4 +255,8 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
}
return this._backupId;
}
get untitledDocumentData(): VSBuffer | undefined {
return this._untitledDocumentData;
}
}
......@@ -4,8 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays';
import { VSBuffer } from 'vs/base/common/buffer';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { extname, isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
......@@ -28,6 +30,7 @@ import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/comm
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { ContributedCustomEditors, defaultCustomEditor } from '../common/contributedCustomEditors';
import { CustomEditorInput } from './customEditorInput';
......@@ -58,6 +61,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWebviewService private readonly webviewService: IWebviewService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService
) {
super();
......@@ -155,7 +159,11 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
}
}
const input = this.createInput(resource, viewType, group?.id);
// If it's an untitled file we must populate the untitledDocumentData
const untitledString = this.untitledTextEditorService.getValue(resource);
let untitledDocumentData = untitledString ? VSBuffer.fromString(untitledString) : undefined;
const input = this.createInput(resource, viewType, group?.id, { untitledDocumentData });
return this.openEditorForResource(resource, input, options, group);
}
......@@ -163,7 +171,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
resource: URI,
viewType: string,
group: GroupIdentifier | undefined,
options?: { readonly customClasses: string; },
options?: { readonly customClasses?: string, readonly untitledDocumentData?: VSBuffer },
): IEditorInput {
if (viewType === defaultCustomEditor.id) {
return this.editorService.createEditorInput({ resource, forceFile: true });
......@@ -171,7 +179,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
const id = generateUuid();
const webview = this.webviewService.createWebviewOverlay(id, { customClasses: options?.customClasses }, {}, undefined);
const input = this.instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview, {});
const input = this.instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview, { untitledDocumentData: options?.untitledDocumentData });
if (typeof group !== 'undefined') {
input.updateGroup(group);
}
......@@ -197,6 +205,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
await this.editorService.replaceEditors([{
editor: existing,
replacement: input,
forceReplaceDirty: existing.resource?.scheme === Schemas.untitled,
options: options ? EditorOptions.create(options) : undefined,
}], targetGroup);
......
......@@ -6,6 +6,7 @@
import { localize } from 'vs/nls';
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
import { flatten } from 'vs/base/common/arrays';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Iterable } from 'vs/base/common/iterator';
......@@ -512,12 +513,12 @@ export class NotebookService extends Disposable implements INotebookService, IEd
return result;
}
async fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> {
async fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> {
if (!await this.canResolve(viewType)) {
throw new Error(`CANNOT fetch notebook data, there is NO provider for '${viewType}'`);
}
const provider = this._withProvider(viewType)!;
return await provider.controller.openNotebook(viewType, uri, backupId, token);
return await provider.controller.openNotebook(viewType, uri, backupId, token, untitledDocumentData);
}
async save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean> {
......
......@@ -19,7 +19,9 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
import { TaskSequentializer } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { assertType } from 'vs/base/common/types';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
export class NotebookEditorModel extends EditorModel implements INotebookEditorModel {
......@@ -46,6 +48,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
@IFileService private readonly _fileService: IFileService,
@INotificationService private readonly _notificationService: INotificationService,
@ILogService private readonly _logService: ILogService,
@IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService,
@ILabelService labelService: ILabelService,
) {
super();
......@@ -168,9 +171,21 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
return this;
}
/**
* @description Uses the textmodel resolver service to acquire the untitled file's content
* @param resource The resource that is the untitled file
* @returns The bytes
*/
private async getUntitledDocumentData(resource: URI): Promise<VSBuffer | undefined> {
// If it's an untitled file we must populate the untitledDocumentData
const untitledString = this.untitledTextEditorService.getValue(resource);
let untitledDocumentData = untitledString ? VSBuffer.fromString(untitledString) : undefined;
return untitledDocumentData;
}
private async _loadFromProvider(backupId: string | undefined): Promise<void> {
const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId, CancellationToken.None);
const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId, CancellationToken.None, (await this.getUntitledDocumentData(this.resource)));
this._lastResolvedFileStat = await this._resolveStats(this.resource);
if (this.isDisposed()) {
......
......@@ -15,6 +15,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode
import { IDisposable } from 'vs/base/common/lifecycle';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { IRelativePattern } from 'vs/base/common/glob';
import { VSBuffer } from 'vs/base/common/buffer';
export const INotebookService = createDecorator<INotebookService>('notebookService');
......@@ -25,7 +26,7 @@ export interface IMainNotebookController {
resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise<void>;
onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void;
openNotebook(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
openNotebook(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
save(uri: URI, token: CancellationToken): Promise<boolean>;
saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean>;
backup(uri: URI, token: CancellationToken): Promise<string>;
......@@ -67,7 +68,7 @@ export interface INotebookService {
getNotebookProviderResourceRoots(): URI[];
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void;
fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<INotebookRawData>;
fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<INotebookRawData>;
save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean>;
saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise<boolean>;
backup(viewType: string, uri: URI, token: CancellationToken): Promise<string | undefined>;
......
......@@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IWorkingCopy, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
suite('NotebookEditorModel', function () {
......@@ -23,6 +24,7 @@ suite('NotebookEditorModel', function () {
const notebokService = new class extends mock<INotebookService>() { };
const backupService = new class extends mock<IBackupFileService>() { };
const notificationService = new class extends mock<INotificationService>() { };
const untitledTextEditorService = new class extends mock<IUntitledTextEditorService>() { };
const fileService = new class extends mock<IFileService>() {
onDidFilesChange = Event.None;
};
......@@ -45,8 +47,8 @@ suite('NotebookEditorModel', function () {
}
};
new NotebookEditorModel(r1, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), labelService);
new NotebookEditorModel(r2, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), labelService);
new NotebookEditorModel(r1, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
new NotebookEditorModel(r2, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
assert.strictEqual(copies.length, 2);
assert.strictEqual(!isEqual(copies[0].resource, copies[1].resource), true);
......
......@@ -540,6 +540,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
await this.replaceEditors([{
editor: existingEditor,
replacement: fileEditorInput,
forceReplaceDirty: existingEditor.resource?.scheme === Schemas.untitled,
options: options ? EditorOptions.create(options) : undefined,
}], group);
}
......@@ -995,6 +996,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
typedEditors.push({
editor: replacementArg.editor,
replacement: replacementArg.replacement,
forceReplaceDirty: replacementArg.forceReplaceDirty,
options: this.toOptions(replacementArg.options)
});
} else {
......
......@@ -108,6 +108,8 @@ export interface ICloseAllEditorsOptions {
export interface IEditorReplacement {
editor: IEditorInput;
replacement: IEditorInput;
/** Skips asking the user for confirmation and doesn't save the document. Only use this if you really need to! */
forceReplaceDirty?: boolean;
options?: IEditorOptions | ITextEditorOptions;
}
......
......@@ -201,6 +201,7 @@ export interface IEditorService {
* Replaces editors in an editor group with the provided replacement.
*
* @param editors the editors to replace
* @param group the editor group
*
* @returns a promise that is resolved when the replaced active
* editor (if any) has finished loading.
......
......@@ -978,6 +978,33 @@ suite('EditorGroupsService', () => {
assert.ok(input1.gotDisposed);
});
test('replaceEditors - forceReplaceDirty flag', async () => {
const [part, instantiationService] = createPart();
const accessor = instantiationService.createInstance(TestServiceAccessor);
accessor.fileDialogService.setConfirmResult(ConfirmResult.DONT_SAVE);
const group = part.activeGroup;
const input1 = new TestFileEditorInput(URI.file('foo/bar1'), TEST_EDITOR_INPUT_ID);
input1.dirty = true;
const input2 = new TestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID);
await group.openEditor(input1);
assert.strictEqual(group.activeEditor, input1);
accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL);
await group.replaceEditors([{ editor: input1, replacement: input2, forceReplaceDirty: false }]);
assert.strictEqual(group.activeEditor, input1);
assert.ok(!input1.gotDisposed);
await group.replaceEditors([{ editor: input1, replacement: input2, forceReplaceDirty: true }]);
assert.strictEqual(group.activeEditor, input2);
assert.ok(input1.gotDisposed);
});
test('replaceEditors - proper index handling', async () => {
const [part] = createPart();
const group = part.activeGroup;
......
......@@ -98,6 +98,13 @@ export interface IUntitledTextEditorModelManager {
*/
get(resource: URI): IUntitledTextEditorModel | undefined;
/**
* Returns the value of the untitled editor, undefined if none exists
* @param resource The URI of the untitled file
* @returns The content, or undefined
*/
getValue(resource: URI): string | undefined;
/**
* Resolves an untitled editor model from the provided options. If the `untitledResource`
* property is provided and the untitled editor exists, it will return that existing
......@@ -142,6 +149,10 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
return this.mapResourceToModel.get(resource);
}
getValue(resource: URI): string | undefined {
return this.get(resource)?.textEditorModel?.getValue();
}
resolve(options?: IInternalUntitledTextEditorOptions): Promise<UntitledTextEditorModel & IResolvedTextEditorModel> {
return this.doCreateOrGet(options).load();
}
......
......@@ -368,6 +368,20 @@ suite('Untitled text editors', () => {
assert.strictEqual(counter, 1);
});
test('service#getValue', async () => {
// This function is used for the untitledocumentData API
const service = accessor.untitledTextEditorService;
const model1 = await instantiationService.createInstance(UntitledTextEditorInput, service.create()).resolve();
model1.textEditorModel!.setValue('foo bar');
assert.strictEqual(service.getValue(model1.resource), 'foo bar');
model1.dispose();
// When a model doesn't exist, it should return undefined
assert.strictEqual(service.getValue(URI.parse('https://www.microsoft.com')), undefined);
});
test('model#onDidChangeContent', async function () {
const service = accessor.untitledTextEditorService;
const input = instantiationService.createInstance(UntitledTextEditorInput, service.create());
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать