diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 07e61155da0..dae50a4b361 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -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 diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 408a6bc955a..d6ebc977648 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -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 diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 0c38f6f266b..82c91b59496 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -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' - }, + } } } } diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.ts index c853b062403..919261dd687 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.ts @@ -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; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index ba8663dd407..4f23b0f46f6 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -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) { diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index 41ff4edf313..ac48868bd37 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -272,6 +272,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt } async revert(): Promise { + + // No longer dirty this.setDirty(false); // Emit as event diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 919d45b23d1..1122ba55f90 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -112,6 +112,11 @@ export interface IUntitledTextEditorModelManager { resolve(options?: INewUntitledTextEditorOptions): Promise; resolve(options?: INewUntitledTextEditorWithAssociatedResourceOptions): Promise; resolve(options?: IExistingUntitledTextEditorOptions): Promise; + + /** + * 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()); 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); diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 22cf7a0a177..b1e089dcc1c 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -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); diff --git a/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopy.ts index dfe2507b4c5..bbe882059c5 100644 --- a/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopy.ts +++ b/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopy.ts @@ -135,7 +135,7 @@ export class UntitledFileWorkingCopy ex this._register(workingCopyService.registerWorkingCopy(this)); } - //#region Dirty + //#region Dirty/Modified private modified = this.hasAssociatedFilePath || Boolean(this.initialContents && this.initialContents.markModified !== false); diff --git a/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopyManager.ts index 1b2269830c9..1f50142c3d6 100644 --- a/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopyManager.ts @@ -171,8 +171,10 @@ export class UntitledFileWorkingCopyManager