diff --git a/extensions/git/src/diagnostics.ts b/extensions/git/src/diagnostics.ts index 64bf11076fe08..69ff71b3604cd 100644 --- a/extensions/git/src/diagnostics.ts +++ b/extensions/git/src/diagnostics.ts @@ -23,7 +23,8 @@ export class GitCommitInputBoxDiagnosticsManager { this.migrateInputValidationSettings() .then(() => { - mapEvent(filterEvent(workspace.onDidChangeTextDocument, e => e.document.uri.scheme === 'vscode-scm'), e => e.document)(this.onDidChangeTextDocument, this, this.disposables); + mapEvent(filterEvent(workspace.onDidChangeTextDocument, e => e.document.uri.scheme === 'vscode-scm' || e.document.languageId === 'git-commit'), e => e.document)(this.onDidChangeTextDocument, this, this.disposables); + filterEvent(workspace.onDidCloseTextDocument, e => e.languageId === 'git-commit')(doc => this.diagnostics.delete(doc.uri), this, this.disposables); filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.inputValidation') || e.affectsConfiguration('git.inputValidationLength') || e.affectsConfiguration('git.inputValidationSubjectLength'))(this.onDidChangeConfiguration, this, this.disposables); }); } @@ -57,6 +58,11 @@ export class GitCommitInputBoxDiagnosticsManager { for (const repository of this.model.repositories) { this.onDidChangeTextDocument(repository.inputBox.document); } + for (const document of workspace.textDocuments) { + if (document.languageId === 'git-commit') { + this.onDidChangeTextDocument(document); + } + } } private onDidChangeTextDocument(document: TextDocument): void { @@ -79,10 +85,19 @@ export class GitCommitInputBoxDiagnosticsManager { const diagnostics: Diagnostic[] = []; const inputValidationLength = config.get('inputValidationLength', 50); const inputValidationSubjectLength = config.get('inputValidationSubjectLength', undefined); + const isCommitEditor = document.languageId === 'git-commit'; + let isFirstMessageLine = true; for (let index = 0; index < document.lineCount; index++) { const line = document.lineAt(index); - const threshold = index === 0 ? inputValidationSubjectLength ?? inputValidationLength : inputValidationLength; + + // Skip git comment/metadata lines in the commit editor + if (isCommitEditor && line.text.startsWith('#')) { + continue; + } + + const threshold = isFirstMessageLine ? inputValidationSubjectLength ?? inputValidationLength : inputValidationLength; + isFirstMessageLine = false; if (line.text.length > threshold) { const charactersOver = line.text.length - threshold; @@ -110,6 +125,7 @@ export class GitCommitInputBoxCodeActionsProvider implements CodeActionProvider constructor(private readonly diagnosticsManager: GitCommitInputBoxDiagnosticsManager) { this.disposables.push(languages.registerCodeActionsProvider({ scheme: 'vscode-scm' }, this)); + this.disposables.push(languages.registerCodeActionsProvider({ language: 'git-commit' }, this)); } provideCodeActions(document: TextDocument, range: Range | Selection): CodeAction[] { diff --git a/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts b/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts index 37a03148780d4..79b306d2a5ad0 100644 --- a/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts +++ b/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts @@ -12,7 +12,7 @@ import { IExtHostRpcService } from './extHostRpcService.js'; import { IExtHostTerminalService } from './extHostTerminalService.js'; import { Emitter, type Event } from '../../../base/common/event.js'; import { URI } from '../../../base/common/uri.js'; -import { AsyncIterableObject, Barrier, type AsyncIterableEmitter } from '../../../base/common/async.js'; +import { AsyncIterableProducer, Barrier, DeferredPromise, type AsyncIterableEmitter } from '../../../base/common/async.js'; export interface IExtHostTerminalShellIntegration extends ExtHostTerminalShellIntegrationShape { readonly _serviceBrand: undefined; @@ -400,7 +400,7 @@ class InternalTerminalShellExecution { private _createDataStream(): AsyncIterable { if (!this._dataStream) { if (this._isEnded) { - return AsyncIterableObject.EMPTY; + return AsyncIterableProducer.EMPTY; } this._dataStream = new ShellExecutionDataStream(); } @@ -432,7 +432,7 @@ class InternalTerminalShellExecution { class ShellExecutionDataStream extends Disposable { private _barrier: Barrier | undefined; - private _iterables: AsyncIterableObject[] = []; + private _completionPromises: DeferredPromise[] = []; private _emitters: AsyncIterableEmitter[] = []; createIterable(): AsyncIterable { @@ -440,11 +440,13 @@ class ShellExecutionDataStream extends Disposable { this._barrier = new Barrier(); } const barrier = this._barrier; - const iterable = new AsyncIterableObject(async emitter => { + const completionPromise = new DeferredPromise(); + this._completionPromises.push(completionPromise); + const iterable = new AsyncIterableProducer(async emitter => { this._emitters.push(emitter); await barrier.wait(); + completionPromise.complete(undefined); }); - this._iterables.push(iterable); return iterable; } @@ -459,7 +461,7 @@ class ShellExecutionDataStream extends Disposable { } async flush(): Promise { - await Promise.all(this._iterables.map(e => e.toPromise())); + await Promise.all(this._completionPromises.map(p => p.p)); } } diff --git a/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts b/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts index 23e544f1568f6..7fb813a6f76ba 100644 --- a/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts +++ b/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts @@ -64,7 +64,7 @@ suite('InternalTerminalShellIntegration', () => { } async function emitData(data: string): Promise { - // AsyncIterableObjects are initialized in a microtask, this doesn't matter in practice + // AsyncIterableProducers are initialized in a microtask, this doesn't matter in practice // since the events will always come through in different events. await new Promise(r => queueMicrotask(r)); si.emitData(data);