diff --git a/package-lock.json b/package-lock.json index d77cffa34..2db196954 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", - "@github1s/vscode-web": "^0.28.1", + "@github1s/vscode-web": "^0.29.0", "chokidar": "^4.0.3", "clean-css": "^5.3.3", "copy-webpack-plugin": "^14.0.0", @@ -251,9 +251,9 @@ } }, "node_modules/@github1s/vscode-web": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.28.1.tgz", - "integrity": "sha512-dJ2mXQ+/xdBrOcLuqYBKTjmA04Rr8Fg2au+Xu5mL1JgsE+FWe5AA0STgPTvuy8Zu6zdzlRJ7z9LOLzTh8DI6Dw==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.29.0.tgz", + "integrity": "sha512-e0d+8B8F1rdDjoCSCw8F08R22MEXpmnQ5iN8H/d/WzUDDf6igiMWOnVhKOlF3IO0rbMHs5cZEsSCbkRv08v2OA==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index b5a1e6605..bf9256fc6 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", - "@github1s/vscode-web": "^0.28.1", + "@github1s/vscode-web": "^0.29.0", "chokidar": "^4.0.3", "clean-css": "^5.3.3", "copy-webpack-plugin": "^14.0.0", diff --git a/vscode-web/.VERSION b/vscode-web/.VERSION index 21f81fe80..12f5b64c8 100644 --- a/vscode-web/.VERSION +++ b/vscode-web/.VERSION @@ -1 +1 @@ -1.131.0 \ No newline at end of file +1.132.0 \ No newline at end of file diff --git a/vscode-web/package-lock.json b/vscode-web/package-lock.json index ea17e2ff8..da933de2c 100644 --- a/vscode-web/package-lock.json +++ b/vscode-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github1s/vscode-web", - "version": "0.28.1", + "version": "0.29.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github1s/vscode-web", - "version": "0.28.1", + "version": "0.29.0", "license": "MIT", "devDependencies": { "chokidar": "^4.0.3", diff --git a/vscode-web/package.json b/vscode-web/package.json index 84004815b..970031796 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.28.1", + "version": "0.29.0", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", diff --git a/vscode-web/scripts/.patch b/vscode-web/scripts/.patch index 5a85a9d63..6771478ab 100644 --- a/vscode-web/scripts/.patch +++ b/vscode-web/scripts/.patch @@ -1,8 +1,7 @@ { - "src/vs/workbench/browser/parts/activitybar/activitybarPart.ts": "1c5075a0d54829eadbb19a214f9913558ddab03c3e088b2ec3590b49b2b8c460", + "src/vs/workbench/browser/parts/activitybar/activitybarPart.ts": "b1608b71d4ff7392a42d80a8a406a97913717bb6af7be703c90f73c3f4520253", "src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css": "8ffe6921a1c36709db7ed7f5eb705782f55e90acdc780c7722030b0f9e6d6fc5", "src/vs/workbench/browser/web.main.ts": "180c4439bcb518402e34c9b8b154bcd204b4b2e47a3edf2b1c77c612a8ff2bf5", - "src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts": "e986095a30dbea54af56c25fa1b184b55f34f8914129b27e2f20d8c4ea9fd16b", "src/vs/workbench/contrib/webview/browser/pre/index.html": "9f4a216e3c7abfa81d3afa9e215a36e935eb92b9193ae244cf80b1904d3fae0c", "src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts": "16fc1f8830432097a2de87ba04f9f11e930408df8f672bb7a4bbbe3c1a7c509d", "src/vs/workbench/services/label/common/labelService.ts": "9e4e2aafeabc04ccf183b8f2f4ce8a4d39f240831e4005b06149684458caa8d4", diff --git a/vscode-web/src/setup.d.ts b/vscode-web/src/setup.d.ts index 821362da9..c20ff11f8 100644 --- a/vscode-web/src/setup.d.ts +++ b/vscode-web/src/setup.d.ts @@ -4,7 +4,6 @@ declare var _VSCODE_WEB: { workspaceId?: string; // the identifier to distinguish workspace workspaceLabel?: string; // the label shown on explorer hideTextFileLabelDecorations?: boolean; // whether hide the readonly icon for readonly files - allowEditorLabelOverride?: boolean; // whether allow override editor label // custom builtin extensions, types see IBundledExtension[] builtinExtensions?: any[] | ((builtinExtensions: any[]) => any[]); logo?: { diff --git a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index e349efa8e..e9cf047c5 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -134,6 +134,8 @@ export class ActivitybarPart extends Part { private updateCompactStyle(): void { if (this.element) { this.element.classList.toggle('compact', this._isCompact); + // Mirrored on the workbench root for floatingPanels.css + this.layoutService.mainContainer.classList.toggle('activitybar-compact', this._isCompact); this.element.style.setProperty('--activity-bar-width', `${this.baseWidth}px`); this.element.style.setProperty('--activity-bar-action-height', `${this.actionHeight}px`); this.element.style.setProperty('--activity-bar-icon-size', `${this._isCompact ? ActivitybarPart.COMPACT_ICON_SIZE : ActivitybarPart.ICON_SIZE}px`); @@ -394,7 +396,7 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { /* above codes are changed by github1s */ // Menubar: install a custom menu bar depending on configuration - this.menuBar.value = this._register(this.instantiationService.createInstance(CustomMenubarControl)); + this.menuBar.value = this.instantiationService.createInstance(CustomMenubarControl); this.menuBar.value.create(this.menuBarContainer); } diff --git a/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts b/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts deleted file mode 100644 index 8ccc31234..000000000 --- a/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts +++ /dev/null @@ -1,487 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { URI } from '../../../../../base/common/uri.js'; -import { IFileEditorInput, Verbosity, GroupIdentifier, IMoveResult, EditorInputCapabilities, IEditorDescriptor, IEditorPane, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, IUntypedFileEditorInput, findViewStateForEditor, isResourceEditorInput, IFileEditorInputOptions } from '../../../../common/editor.js'; -import { EditorInput, IUntypedEditorOptions } from '../../../../common/editor/editorInput.js'; -import { AbstractTextResourceEditorInput } from '../../../../common/editor/textResourceEditorInput.js'; -import { ITextResourceEditorInput } from '../../../../../platform/editor/common/editor.js'; -import { BinaryEditorModel } from '../../../../common/editor/binaryEditorModel.js'; -import { IFileService } from '../../../../../platform/files/common/files.js'; -import { ITextFileService, TextFileEditorModelState, TextFileResolveReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel, EncodingMode } from '../../../../services/textfile/common/textfiles.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { IReference, dispose, DisposableStore } from '../../../../../base/common/lifecycle.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { FILE_EDITOR_INPUT_ID, TEXT_FILE_EDITOR_ID, BINARY_FILE_EDITOR_ID } from '../../common/files.js'; -import { ILabelService } from '../../../../../platform/label/common/label.js'; -import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; -import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { isEqual } from '../../../../../base/common/resources.js'; -import { Event } from '../../../../../base/common/event.js'; -import { Schemas } from '../../../../../base/common/network.js'; -import { createTextBufferFactory } from '../../../../../editor/common/model/textModel.js'; -import { IPathService } from '../../../../services/path/common/pathService.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; -import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; -import { ICustomEditorLabelService } from '../../../../services/editor/common/customEditorLabelService.js'; - -const enum ForceOpenAs { - None, - Text, - Binary -} - -/** - * A file editor input is the input type for the file editor of file system resources. - */ -export class FileEditorInput extends AbstractTextResourceEditorInput implements IFileEditorInput { - - override get typeId(): string { - return FILE_EDITOR_INPUT_ID; - } - - override get editorId(): string | undefined { - return DEFAULT_EDITOR_ASSOCIATION.id; - } - - override get capabilities(): EditorInputCapabilities { - let capabilities = EditorInputCapabilities.CanSplitInGroup; - - if (this.model) { - if (this.model.isReadonly()) { - capabilities |= EditorInputCapabilities.Readonly; - } - } else { - if (this.fileService.hasProvider(this.resource)) { - if (this.filesConfigurationService.isReadonly(this.resource)) { - capabilities |= EditorInputCapabilities.Readonly; - } - } else { - capabilities |= EditorInputCapabilities.Untitled; - } - } - - if (!(capabilities & EditorInputCapabilities.Readonly)) { - capabilities |= EditorInputCapabilities.CanDropIntoEditor; - } - - return capabilities; - } - - private preferredName: string | undefined; - private preferredDescription: string | undefined; - private preferredEncoding: string | undefined; - private preferredLanguageId: string | undefined; - private preferredContents: string | undefined; - - private forceOpenAs: ForceOpenAs = ForceOpenAs.None; - - private model: ITextFileEditorModel | undefined = undefined; - private cachedTextFileModelReference: IReference | undefined = undefined; - - private readonly modelListeners = this._register(new DisposableStore()); - - constructor( - resource: URI, - preferredResource: URI | undefined, - preferredName: string | undefined, - preferredDescription: string | undefined, - preferredEncoding: string | undefined, - preferredLanguageId: string | undefined, - preferredContents: string | undefined, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextFileService textFileService: ITextFileService, - @ITextModelService private readonly textModelService: ITextModelService, - @ILabelService labelService: ILabelService, - @IFileService fileService: IFileService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, - @IEditorService editorService: IEditorService, - @IPathService private readonly pathService: IPathService, - @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, - @ICustomEditorLabelService customEditorLabelService: ICustomEditorLabelService - ) { - super(resource, preferredResource, editorService, textFileService, labelService, fileService, filesConfigurationService, textResourceConfigurationService, customEditorLabelService); - - this.model = this.textFileService.files.get(resource); - - if (preferredName) { - this.setPreferredName(preferredName); - } - - if (preferredDescription) { - this.setPreferredDescription(preferredDescription); - } - - if (preferredEncoding) { - this.setPreferredEncoding(preferredEncoding); - } - - if (preferredLanguageId) { - this.setPreferredLanguageId(preferredLanguageId); - } - - if (typeof preferredContents === 'string') { - this.setPreferredContents(preferredContents); - } - - // Attach to model that matches our resource once created - this._register(this.textFileService.files.onDidCreate(model => this.onDidCreateTextFileModel(model))); - - // If a file model already exists, make sure to wire it in - if (this.model) { - this.registerModelListeners(this.model); - } - } - - private onDidCreateTextFileModel(model: ITextFileEditorModel): void { - - // Once the text file model is created, we keep it inside - // the input to be able to implement some methods properly - if (isEqual(model.resource, this.resource)) { - this.model = model; - - this.registerModelListeners(model); - } - } - - private registerModelListeners(model: ITextFileEditorModel): void { - - // Clear any old - this.modelListeners.clear(); - - // re-emit some events from the model - this.modelListeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); - this.modelListeners.add(model.onDidChangeReadonly(() => this._onDidChangeCapabilities.fire())); - - // important: treat save errors as potential dirty change because - // a file that is in save conflict or error will report dirty even - // if auto save is turned on. - this.modelListeners.add(model.onDidSaveError(() => this._onDidChangeDirty.fire())); - - // remove model association once it gets disposed - this.modelListeners.add(Event.once(model.onWillDispose)(() => { - this.modelListeners.clear(); - this.model = undefined; - })); - } - - override getName(): string { - return this.preferredName || super.getName(); - } - - setPreferredName(name: string): void { - if (!this.allowLabelOverride()) { - return; // block for specific schemes we consider to be owning - } - - if (this.preferredName !== name) { - this.preferredName = name; - - this._onDidChangeLabel.fire(); - } - } - - private allowLabelOverride(): boolean { - /* below codes are changed by github1s */ - if (globalThis._VSCODE_WEB?.allowEditorLabelOverride) return true; - /* above codes are changed by github1s */ - return this.resource.scheme !== this.pathService.defaultUriScheme && - this.resource.scheme !== Schemas.vscodeUserData && - this.resource.scheme !== Schemas.file && - this.resource.scheme !== Schemas.vscodeRemote; - } - - getPreferredName(): string | undefined { - return this.preferredName; - } - - override isReadonly(): boolean | IMarkdownString { - return this.model ? this.model.isReadonly() : this.filesConfigurationService.isReadonly(this.resource); - } - - override getDescription(verbosity?: Verbosity): string | undefined { - return this.preferredDescription || super.getDescription(verbosity); - } - - setPreferredDescription(description: string): void { - if (!this.allowLabelOverride()) { - return; // block for specific schemes we consider to be owning - } - - if (this.preferredDescription !== description) { - this.preferredDescription = description; - - this._onDidChangeLabel.fire(); - } - } - - getPreferredDescription(): string | undefined { - return this.preferredDescription; - } - - override getTitle(verbosity?: Verbosity): string { - let title = super.getTitle(verbosity); - - const preferredTitle = this.getPreferredTitle(); - if (preferredTitle) { - title = `${preferredTitle} (${title})`; - } - - return title; - } - - protected getPreferredTitle(): string | undefined { - if (this.preferredName && this.preferredDescription) { - return `${this.preferredName} ${this.preferredDescription}`; - } - - if (this.preferredName || this.preferredDescription) { - return this.preferredName ?? this.preferredDescription; - } - - return undefined; - } - - getEncoding(): string | undefined { - if (this.model) { - return this.model.getEncoding(); - } - - return this.preferredEncoding; - } - - getPreferredEncoding(): string | undefined { - return this.preferredEncoding; - } - - async setEncoding(encoding: string, mode: EncodingMode): Promise { - this.setPreferredEncoding(encoding); - - return this.model?.setEncoding(encoding, mode); - } - - setPreferredEncoding(encoding: string): void { - this.preferredEncoding = encoding; - - // encoding is a good hint to open the file as text - this.setForceOpenAsText(); - } - - getLanguageId(): string | undefined { - if (this.model) { - return this.model.getLanguageId(); - } - - return this.preferredLanguageId; - } - - getPreferredLanguageId(): string | undefined { - return this.preferredLanguageId; - } - - setLanguageId(languageId: string, source?: string): void { - this.setPreferredLanguageId(languageId); - - this.model?.setLanguageId(languageId, source); - } - - setPreferredLanguageId(languageId: string): void { - this.preferredLanguageId = languageId; - - // languages are a good hint to open the file as text - this.setForceOpenAsText(); - } - - setPreferredContents(contents: string): void { - this.preferredContents = contents; - - // contents is a good hint to open the file as text - this.setForceOpenAsText(); - } - - setForceOpenAsText(): void { - this.forceOpenAs = ForceOpenAs.Text; - } - - setForceOpenAsBinary(): void { - this.forceOpenAs = ForceOpenAs.Binary; - } - - override isDirty(): boolean { - return !!(this.model?.isDirty()); - } - - override isSaving(): boolean { - if (this.model?.hasState(TextFileEditorModelState.SAVED) || this.model?.hasState(TextFileEditorModelState.CONFLICT) || this.model?.hasState(TextFileEditorModelState.ERROR)) { - return false; // require the model to be dirty and not in conflict or error state - } - - // Note: currently not checking for ModelState.PENDING_SAVE for a reason - // because we currently miss an event for this state change on editors - // and it could result in bad UX where an editor can be closed even though - // it shows up as dirty and has not finished saving yet. - - if (this.filesConfigurationService.hasShortAutoSaveDelay(this)) { - return true; // a short auto save is configured, treat this as being saved - } - - return super.isSaving(); - } - - override prefersEditorPane>(editorPanes: T[]): T | undefined { - if (this.forceOpenAs === ForceOpenAs.Binary) { - return editorPanes.find(editorPane => editorPane.typeId === BINARY_FILE_EDITOR_ID); - } - - return editorPanes.find(editorPane => editorPane.typeId === TEXT_FILE_EDITOR_ID); - } - - override resolve(options?: IFileEditorInputOptions): Promise { - - // Resolve as binary - if (this.forceOpenAs === ForceOpenAs.Binary) { - return this.doResolveAsBinary(); - } - - // Resolve as text - return this.doResolveAsText(options); - } - - private async doResolveAsText(options?: IFileEditorInputOptions): Promise { - try { - - // Unset preferred contents after having applied it once - // to prevent this property to stick. We still want future - // `resolve` calls to fetch the contents from disk. - const preferredContents = this.preferredContents; - this.preferredContents = undefined; - - // Resolve resource via text file service and only allow - // to open binary files if we are instructed so - await this.textFileService.files.resolve(this.resource, { - languageId: this.preferredLanguageId, - encoding: this.preferredEncoding, - contents: typeof preferredContents === 'string' ? createTextBufferFactory(preferredContents) : undefined, - reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model - allowBinary: this.forceOpenAs === ForceOpenAs.Text, - reason: TextFileResolveReason.EDITOR, - limits: this.ensureLimits(options) - }); - - // This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary - // or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into - // resolve() ensures we are not creating model references for these kind of resources. - // In addition we have a bit of payload to take into account (encoding, reload) that the text resolver does not handle yet. - if (!this.cachedTextFileModelReference) { - this.cachedTextFileModelReference = await this.textModelService.createModelReference(this.resource) as IReference; - } - - const model = this.cachedTextFileModelReference.object; - - // It is possible that this input was disposed before the model - // finished resolving. As such, we need to make sure to dispose - // the model reference to not leak it. - if (this.isDisposed()) { - this.disposeModelReference(); - } - - return model; - } catch (error) { - - // Handle binary files with binary model - if ((error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY) { - return this.doResolveAsBinary(); - } - - // Bubble any other error up - throw error; - } - } - - private async doResolveAsBinary(): Promise { - const model = this.instantiationService.createInstance(BinaryEditorModel, this.preferredResource, this.getName()); - await model.resolve(); - - return model; - } - - isResolved(): boolean { - return !!this.model; - } - - override async rename(group: GroupIdentifier, target: URI): Promise { - return { - editor: { - resource: target, - encoding: this.getEncoding(), - options: { - viewState: findViewStateForEditor(this, group, this.editorService) - } - } - }; - } - - override toUntyped(options?: IUntypedEditorOptions): ITextResourceEditorInput { - const untypedInput: IUntypedFileEditorInput = { - resource: this.preferredResource, - forceFile: true, - options: { - override: this.editorId - } - }; - - if (typeof options?.preserveViewState === 'number') { - untypedInput.encoding = this.getEncoding(); - untypedInput.languageId = this.getLanguageId(); - untypedInput.contents = (() => { - const model = this.textFileService.files.get(this.resource); - if (model?.isDirty() && !model.textEditorModel.isTooLargeForHeapOperation()) { - return model.textEditorModel.getValue(); // only if dirty and not too large - } - - return undefined; - })(); - - untypedInput.options = { - ...untypedInput.options, - viewState: findViewStateForEditor(this, options.preserveViewState, this.editorService) - }; - } - - return untypedInput; - } - - override matches(otherInput: EditorInput | IUntypedEditorInput): boolean { - if (this === otherInput) { - return true; - } - - if (otherInput instanceof FileEditorInput) { - return isEqual(otherInput.resource, this.resource); - } - - if (isResourceEditorInput(otherInput)) { - return super.matches(otherInput); - } - - return false; - } - - override dispose(): void { - - // Model - this.model = undefined; - - // Model reference - this.disposeModelReference(); - - super.dispose(); - } - - private disposeModelReference(): void { - dispose(this.cachedTextFileModelReference); - this.cachedTextFileModelReference = undefined; - } -} diff --git a/vscode-web/src/vs/workbench/services/label/common/labelService.ts b/vscode-web/src/vs/workbench/services/label/common/labelService.ts index 2557d4b4f..7832cb933 100644 --- a/vscode-web/src/vs/workbench/services/label/common/labelService.ts +++ b/vscode-web/src/vs/workbench/services/label/common/labelService.ts @@ -77,7 +77,9 @@ const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoin const posixPathSeparatorRegexp = /\//g; // on Unix, backslash is a valid filename character const winPathSeparatorRegexp = /[\\\/]/g; // on Windows, neither slash nor backslash are valid filename characters -const labelMatchingRegexp = /\$\{(scheme|authoritySuffix|authority|path|(query)\.(.+?))\}/g; +// below codes are changed by github1s +const labelMatchingRegexp = /\$\{(scheme|authoritySuffix(?::\d+)?|authority|path|(query)\.(.+?))\}/g; +// above codes are changed by github1s function hasDriveLetterIgnorePlatform(path: string): boolean { return !!(path && path[2] === ':'); @@ -452,13 +454,19 @@ export class LabelService extends Disposable implements ILabelService { } private formatUri(resource: URI, formatting: ResourceLabelFormatting, forceNoTildify?: boolean): string { - let label = formatting.label.replace(labelMatchingRegexp, (match, token, qsToken, qsValue) => { + // below codes are changed by github1s + let label = formatting.label.replace(labelMatchingRegexp, (match, tokenWithArgument, qsToken, qsValue) => { + const [token, argument] = tokenWithArgument.split(':'); + // above codes are changed by github1s switch (token) { case 'scheme': return resource.scheme; case 'authority': return resource.authority; case 'authoritySuffix': { const i = resource.authority.indexOf('+'); - return i === -1 ? resource.authority : resource.authority.slice(i + 1); + // below codes are changed by github1s + const authoritySuffix = i === -1 ? resource.authority : resource.authority.slice(i + 1); + return argument === undefined ? authoritySuffix : authoritySuffix.slice(0, Number(argument)); + // above codes are changed by github1s } case 'path': { let pathValue = resource.path;