untitled - have a isUntitledWithAssociatedResource method from the service (#183420)

This commit is contained in:
Benjamin Pasero 2023-05-25 14:56:39 +02:00 committed by GitHub
parent 8cf85bdb16
commit 49f7ecfcb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 39 additions and 25 deletions

View file

@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } from 'vs/base/common/types';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IEditorIdentifier, IEditorCommandsContext, CloseDirection, IVisibleEditorPane, EditorsOrder, EditorInputCapabilities, isEditorIdentifier, isEditorInputWithOptionsAndGroup, IUntitledTextResourceEditorInput, isUntitledWithAssociatedResource } from 'vs/workbench/common/editor';
import { IEditorIdentifier, IEditorCommandsContext, CloseDirection, IVisibleEditorPane, EditorsOrder, EditorInputCapabilities, isEditorIdentifier, isEditorInputWithOptionsAndGroup, IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor';
import { TextCompareEditorVisibleContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, TextCompareEditorActiveContext, SideBySideEditorActiveContext } from 'vs/workbench/common/contextkeys';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { EditorGroupColumn, columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn';
@ -39,6 +39,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { extname } from 'vs/base/common/resources';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
@ -517,6 +518,7 @@ function registerOpenEditorAPICommands(): void {
const openerService = accessor.get(IOpenerService);
const pathService = accessor.get(IPathService);
const configurationService = accessor.get(IConfigurationService);
const untitledTextEditorService = accessor.get(IUntitledTextEditorService);
const resourceOrString = typeof resourceArg === 'string' ? resourceArg : URI.from(resourceArg, true);
const [columnArg, optionsArg] = columnAndOptions ?? [];
@ -528,7 +530,7 @@ function registerOpenEditorAPICommands(): void {
const resource = URI.isUri(resourceOrString) ? resourceOrString : URI.parse(resourceOrString);
let input: IResourceEditorInput | IUntitledTextResourceEditorInput;
if (isUntitledWithAssociatedResource(resource)) {
if (untitledTextEditorService.isUntitledWithAssociatedResource(resource)) {
// special case for untitled: we are getting a resource with meaningful
// path from an extension to use for the untitled editor. as such, we
// have to assume it as an associated resource to use when saving. we

View file

@ -567,12 +567,6 @@ export function isUntitledResourceEditorInput(editor: unknown): editor is IUntit
return candidate.resource === undefined || candidate.resource.scheme === Schemas.untitled || candidate.forceUntitled === true;
}
const UNTITLED_WITHOUT_ASSOCIATED_RESOURCE_REGEX = /Untitled-\d+/;
export function isUntitledWithAssociatedResource(resource: URI): boolean {
return resource.scheme === Schemas.untitled && resource.path.length > 1 && !UNTITLED_WITHOUT_ASSOCIATED_RESOURCE_REGEX.test(resource.path);
}
export function isResourceMergeEditorInput(editor: unknown): editor is IResourceMergeEditorInput {
if (isEditorInput(editor)) {
return false; // make sure to not accidentally match on typed editor inputs

View file

@ -663,8 +663,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
args: [
{
isOptional: true,
name: 'New Untitled Text File args',
description: 'The editor view type, language ID, or resource path if known',
name: 'New Untitled Text File arguments',
description: 'The editor view type or language ID if known',
schema: {
'type': 'object',
'properties': {
@ -673,7 +673,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
},
'languageId': {
'type': 'string'
},
}
}
}
}

View file

@ -6,7 +6,7 @@
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IEditorSerializer, isUntitledWithAssociatedResource } from 'vs/workbench/common/editor';
import { IEditorSerializer } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService';
import { isEqual, toLocalResource } from 'vs/base/common/resources';
@ -19,11 +19,12 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IWorkingCopyIdentifier, NO_TYPE_ID } from 'vs/workbench/services/workingCopy/common/workingCopy';
import { IWorkingCopyEditorHandler, IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
interface ISerializedUntitledTextEditorInput {
resourceJSON: UriComponents;
modeId: string | undefined; // should be `languageId` but is kept for backwards compatibility
encoding: string | undefined;
readonly resourceJSON: UriComponents;
readonly modeId: string | undefined; // should be `languageId` but is kept for backwards compatibility
readonly encoding: string | undefined;
}
export class UntitledTextEditorInputSerializer implements IEditorSerializer {
@ -89,7 +90,8 @@ export class UntitledTextEditorWorkingCopyEditorHandler extends Disposable imple
@IWorkingCopyEditorService workingCopyEditorService: IWorkingCopyEditorService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IPathService private readonly pathService: IPathService,
@ITextEditorService private readonly textEditorService: ITextEditorService
@ITextEditorService private readonly textEditorService: ITextEditorService,
@IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService
) {
super();
@ -113,7 +115,7 @@ export class UntitledTextEditorWorkingCopyEditorHandler extends Disposable imple
// If the untitled has an associated resource,
// ensure to restore the local resource it had
if (isUntitledWithAssociatedResource(workingCopy.resource)) {
if (this.untitledTextEditorService.isUntitledWithAssociatedResource(workingCopy.resource)) {
editorInputResource = toLocalResource(workingCopy.resource, this.environmentService.remoteAuthority, this.pathService.defaultUriScheme);
} else {
editorInputResource = workingCopy.resource;

View file

@ -140,7 +140,7 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
if (typeof options?.preserveViewState === 'number') {
untypedInput.encoding = this.getEncoding();
untypedInput.languageId = this.getLanguageId();
untypedInput.contents = this.model.isDirty() ? this.model.textEditorModel?.getValue() : undefined;
untypedInput.contents = this.model.isModified() ? this.model.textEditorModel?.getValue() : undefined;
untypedInput.options.viewState = findViewStateForEditor(this, options.preserveViewState, this.editorService);
if (typeof untypedInput.contents === 'string' && !this.model.hasAssociatedFilePath) {

View file

@ -272,6 +272,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt
}
async revert(): Promise<void> {
// No longer dirty
this.setDirty(false);
// Emit as event

View file

@ -112,6 +112,11 @@ export interface IUntitledTextEditorModelManager {
resolve(options?: INewUntitledTextEditorOptions): Promise<IUntitledTextEditorModel>;
resolve(options?: INewUntitledTextEditorWithAssociatedResourceOptions): Promise<IUntitledTextEditorModel>;
resolve(options?: IExistingUntitledTextEditorOptions): Promise<IUntitledTextEditorModel>;
/**
* Figures out if the given resource has an associated resource or not.
*/
isUntitledWithAssociatedResource(resource: URI): boolean;
}
export interface IUntitledTextEditorService extends IUntitledTextEditorModelManager {
@ -123,6 +128,8 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
declare readonly _serviceBrand: undefined;
private static readonly UNTITLED_WITHOUT_ASSOCIATED_RESOURCE_REGEX = /Untitled-\d+/;
private readonly _onDidChangeDirty = this._register(new Emitter<IUntitledTextEditorModel>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
@ -259,6 +266,10 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
this._onDidChangeDirty.fire(model);
}
}
isUntitledWithAssociatedResource(resource: URI): boolean {
return resource.scheme === Schemas.untitled && resource.path.length > 1 && !UntitledTextEditorService.UNTITLED_WITHOUT_ASSOCIATED_RESOURCE_REGEX.test(resource.path);
}
}
registerSingleton(IUntitledTextEditorService, UntitledTextEditorService, InstantiationType.Delayed);

View file

@ -16,7 +16,7 @@ import { Range } from 'vs/editor/common/core/range';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { EditorInputCapabilities, isUntitledWithAssociatedResource } from 'vs/workbench/common/editor';
import { EditorInputCapabilities } from 'vs/workbench/common/editor';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isReadable, isReadableStream } from 'vs/base/common/stream';
import { readableToBuffer, streamToBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer';
@ -46,7 +46,7 @@ suite('Untitled text editors', () => {
const input1 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
await input1.resolve();
assert.strictEqual(service.get(input1.resource), input1.model);
assert.ok(!isUntitledWithAssociatedResource(input1.resource));
assert.ok(!accessor.untitledTextEditorService.isUntitledWithAssociatedResource(input1.resource));
assert.ok(service.get(input1.resource));
assert.ok(!service.get(URI.file('testing')));
@ -55,6 +55,7 @@ suite('Untitled text editors', () => {
assert.ok(!input1.hasCapability(EditorInputCapabilities.Readonly));
assert.ok(!input1.hasCapability(EditorInputCapabilities.Singleton));
assert.ok(!input1.hasCapability(EditorInputCapabilities.RequiresTrust));
assert.ok(!input1.hasCapability(EditorInputCapabilities.Scratchpad));
const input2 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
assert.strictEqual(service.get(input2.resource), input2.model);
@ -138,7 +139,7 @@ suite('Untitled text editors', () => {
});
const model = service.create({ associatedResource: file });
assert.ok(isUntitledWithAssociatedResource(model.resource));
assert.ok(accessor.untitledTextEditorService.isUntitledWithAssociatedResource(model.resource));
const untitled = instantiationService.createInstance(UntitledTextEditorInput, model);
assert.ok(untitled.isDirty());
assert.strictEqual(model, onDidChangeDirtyModel);

View file

@ -135,7 +135,7 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex
this._register(workingCopyService.registerWorkingCopy(this));
}
//#region Dirty
//#region Dirty/Modified
private modified = this.hasAssociatedFilePath || Boolean(this.initialContents && this.initialContents.markModified !== false);

View file

@ -171,8 +171,10 @@ export class UntitledFileWorkingCopyManager<M extends IUntitledFileWorkingCopyMo
}
// Handle untitled resource
else if (options.untitledResource?.scheme === Schemas.untitled) {
massagedOptions.untitledResource = options.untitledResource;
else {
if (options.untitledResource?.scheme === Schemas.untitled) {
massagedOptions.untitledResource = options.untitledResource;
}
massagedOptions.isScratchpad = options.isScratchpad;
}
@ -191,7 +193,7 @@ export class UntitledFileWorkingCopyManager<M extends IUntitledFileWorkingCopyMo
do {
untitledResource = URI.from({
scheme: Schemas.untitled,
path: `Untitled-${counter}`,
path: options.isScratchpad ? `Scratchpad-${counter}` : `Untitled-${counter}`,
query: this.workingCopyTypeId ?
`typeId=${this.workingCopyTypeId}` : // distinguish untitled resources among others by encoding the `typeId` as query param
undefined // keep untitled resources for text files as they are (when `typeId === ''`)