From 27f661572a803db5012d9c12dfa101253fdc53df Mon Sep 17 00:00:00 2001 From: Jonas Dellinger Date: Mon, 27 Sep 2021 23:23:22 +0200 Subject: [PATCH 1/8] Use full editor for commit messages --- extensions/git/extension.webpack.config.js | 3 +- extensions/git/package.json | 14 +++ extensions/git/package.nls.json | 2 + extensions/git/src/api/git.d.ts | 5 +- extensions/git/src/askpass.ts | 14 +-- extensions/git/src/commands.ts | 25 +++++- .../git/src/commitFileSystemProvider.ts | 87 +++++++++++++++++++ extensions/git/src/git-editor-main.ts | 21 +++++ extensions/git/src/git.ts | 41 ++++++--- extensions/git/src/gitEditor.ts | 73 ++++++++++++++++ extensions/git/src/ipc/ipcServer.ts | 2 + extensions/git/src/main.ts | 19 +++- .../git/src/scripts/git-editor-empty.bat | 1 + .../git/src/scripts/git-editor-empty.sh | 1 + extensions/git/src/scripts/git-editor.bat | 3 + extensions/git/src/scripts/git-editor.sh | 3 + extensions/git/yarn.lock | 10 +++ 17 files changed, 293 insertions(+), 31 deletions(-) create mode 100644 extensions/git/src/commitFileSystemProvider.ts create mode 100644 extensions/git/src/git-editor-main.ts create mode 100644 extensions/git/src/gitEditor.ts create mode 100644 extensions/git/src/scripts/git-editor-empty.bat create mode 100644 extensions/git/src/scripts/git-editor-empty.sh create mode 100644 extensions/git/src/scripts/git-editor.bat create mode 100644 extensions/git/src/scripts/git-editor.sh diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js index 5efa2052e88e5..3324b6c1d988b 100644 --- a/extensions/git/extension.webpack.config.js +++ b/extensions/git/extension.webpack.config.js @@ -13,6 +13,7 @@ module.exports = withDefaults({ context: __dirname, entry: { main: './src/main.ts', - ['askpass-main']: './src/askpass-main.ts' + ['askpass-main']: './src/askpass-main.ts', + ['git-editor-main']: './src/git-editor-main.ts' } }); diff --git a/extensions/git/package.json b/extensions/git/package.json index 5a222f7647125..b01616c2a0fc7 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1802,6 +1802,18 @@ "scope": "machine", "description": "%config.defaultCloneDirectory%" }, + "git.useEditorToCommit": { + "type": "boolean", + "scope": "resource", + "description": "%config.useEditorToCommit%", + "default": false + }, + "git.verboseCommit": { + "type": "boolean", + "scope": "resource", + "description": "%config.verboseCommit%", + "default": false + }, "git.enableSmartCommit": { "type": "boolean", "scope": "resource", @@ -2406,6 +2418,7 @@ "file-type": "^7.2.0", "iconv-lite-umd": "0.6.8", "jschardet": "3.0.0", + "uuid": "^8.3.2", "vscode-extension-telemetry": "0.4.2", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", @@ -2416,6 +2429,7 @@ "@types/file-type": "^5.2.1", "@types/mocha": "^8.2.0", "@types/node": "14.x", + "@types/uuid": "^8.3.1", "@types/which": "^1.0.28" }, "repository": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index ce652f94947d9..9d53291ebd412 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -119,6 +119,8 @@ "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.", "config.ignoreRebaseWarning": "Ignores the warning when it looks like the branch might have been rebased when pulling.", "config.defaultCloneDirectory": "The default location to clone a git repository.", + "config.useEditorToCommit": "Use a full sized editor to write commit messages.", + "config.verboseCommit": "Enable verbose output in the COMMIT_EDITMSG file. Only works when useEditorToCommit is enabled.", "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.", "config.smartCommitChanges.all": "Automatically stage all changes.", diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 19ac3aa708d73..8005bd90940e5 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, Event, Disposable, ProviderResult } from 'vscode'; +import { Disposable, Event, ProviderResult, Uri } from 'vscode'; export { ProviderResult } from 'vscode'; export interface Git { @@ -137,6 +137,8 @@ export interface CommitOptions { empty?: boolean; noVerify?: boolean; requireUserConfig?: boolean; + useEditor?: boolean; + verbose?: boolean; } export interface FetchOptions { @@ -324,4 +326,5 @@ export const enum GitErrorCodes { PatchDoesNotApply = 'PatchDoesNotApply', NoPathFound = 'NoPathFound', UnknownPath = 'UnknownPath', + EmptyCommitMessage = 'EmptyCommitMessage' } diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 7fc29a371ad91..9faa69d819acb 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode'; +import { window, InputBoxOptions, Uri, Disposable, workspace } from 'vscode'; import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; -import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer'; +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; export class Askpass implements IIPCHandler { @@ -15,16 +15,8 @@ export class Askpass implements IIPCHandler { private cache = new Map(); private credentialsProviders = new Set(); - static async create(outputChannel: OutputChannel, context?: string): Promise { - try { - return new Askpass(await createIPCServer(context)); - } catch (err) { - outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`); - return new Askpass(); - } - } - private constructor(private ipc?: IIPCServer) { + constructor(private ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index ff780988c8261..e7f332fba40d1 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1417,6 +1417,19 @@ export class CommandCenter { opts.signoff = true; } + if (config.get('useEditorToCommit')) { + opts.useEditor = true; + } + + if (config.get('verboseCommit')) { + opts.verbose = true; + if (!opts.useEditor) { + window.showInformationMessage( + localize('useless verbose commit', "Verbose committing has no effect. The setting 'git.useEditorToCommit' is not enabled.") + ); + } + } + const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges'); if ( @@ -1464,7 +1477,7 @@ export class CommandCenter { let message = await getCommitMessage(); - if (!message && !opts.amend) { + if (!message && !opts.amend && !opts.useEditor) { return false; } @@ -1494,10 +1507,13 @@ export class CommandCenter { private async commitWithAnyInput(repository: Repository, opts?: CommitOptions): Promise { const message = repository.inputBox.value; + const root = Uri.file(repository.root); + const config = workspace.getConfiguration('git', root); + const getCommitMessage = async () => { let _message: string | undefined = message; - if (!_message) { + if (!_message && !config.get('useEditorToCommit')) { let value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { @@ -2827,6 +2843,11 @@ export class CommandCenter { message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git."); choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup'))); break; + case GitErrorCodes.EmptyCommitMessage: + message = localize('empty commit', "Aborting commit due to empty commit message."); + type = 'warning'; + options.modal = false; + break; default: const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') diff --git a/extensions/git/src/commitFileSystemProvider.ts b/extensions/git/src/commitFileSystemProvider.ts new file mode 100644 index 0000000000000..853ab84c1f1c5 --- /dev/null +++ b/extensions/git/src/commitFileSystemProvider.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { FileSystemProvider, FileChangeEvent, EventEmitter, Event, Disposable, FileStat, Uri, FileType, workspace, FileSystemError } from 'vscode'; +import { EmptyDisposable } from './util'; +import { promises as fs } from 'fs'; + +export class GitCommitFileSystemProvider implements FileSystemProvider { + private _onDidChangeFile = new EventEmitter(); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private disposables: Disposable[] = []; + + private fileCache: Record = {}; + + constructor() { + this.disposables.push( + workspace.registerFileSystemProvider('gitcommit', this, { isReadonly: false, isCaseSensitive: true }), + workspace.registerResourceLabelFormatter({ + scheme: 'gitcommit', + formatting: { + label: '${authority} (gitcommit)', + separator: '/' + } + }) + ); + } + + async stat(uri: Uri): Promise { + // TODO: Should we do error handling here? If so, we may need sth like toFileSystemProviderErrorCode + if (this.fileCache[uri.authority]) { + return { type: FileType.File, ctime: 0, mtime: 0, size: this.fileCache[uri.authority].length }; + } + + const { size } = await fs.stat(uri.fsPath); + return { ctime: 0, mtime: 0, size, type: FileType.File }; + } + + readDirectory(): Thenable<[string, FileType][]> { + throw new Error('Method not implemented.'); + } + + createDirectory(): void { + throw new Error('Method not implemented.'); + } + + async readFile(uri: Uri): Promise { + // TODO: Should we do more error handling here? If so, we may need sth like toFileSystemProviderErrorCode + if (!this.fileCache[uri.authority]) { + try { + this.fileCache[uri.authority] = await fs.readFile(uri.fsPath); + } catch (error) { + // TODO ingore for now, maybe go for toFileSystemProviderErrorCode + } + } + return this.fileCache[uri.authority]; + } + + async writeFile(uri: Uri, content: Uint8Array): Promise { + if (this.fileCache[uri.authority]) { + this.fileCache[uri.authority] = content; + } else { + throw FileSystemError.FileNotFound(); + } + } + + delete(uri: Uri): void { + if (this.fileCache[uri.authority]) { + delete this.fileCache[uri.authority]; + } else { + throw FileSystemError.FileNotFound(); + } + } + + rename(): void { + throw new Error('Method not implemented.'); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } + + watch(): Disposable { + return EmptyDisposable; + } +} diff --git a/extensions/git/src/git-editor-main.ts b/extensions/git/src/git-editor-main.ts new file mode 100644 index 0000000000000..c7705bd012b1f --- /dev/null +++ b/extensions/git/src/git-editor-main.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IPCClient } from './ipc/ipcClient'; + +function fatal(err: any): void { + console.error(err); + process.exit(1); +} + +function main(argv: string[]): void { + const ipcClient = new IPCClient('git-editor'); + const commitMessagePath = argv[2]; + + ipcClient.call({ commitMessagePath }).then(() => { + setTimeout(() => process.exit(0), 0); + }).catch(err => fatal(err)); +} + +main(process.argv); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 7a73b0aeb6e72..45ab0d19f460a 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { promises as fs, exists, realpath } from 'fs'; -import * as path from 'path'; -import * as os from 'os'; +import * as byline from 'byline'; import * as cp from 'child_process'; -import * as which from 'which'; import { EventEmitter } from 'events'; -import * as iconv from 'iconv-lite-umd'; import * as filetype from 'file-type'; -import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions } from './util'; +import { exists, promises as fs, realpath } from 'fs'; +import * as iconv from 'iconv-lite-umd'; +import * as os from 'os'; +import * as path from 'path'; +import { StringDecoder } from 'string_decoder'; import { CancellationToken, Progress, Uri } from 'vscode'; +import * as which from 'which'; +import { Branch, BranchQuery, Change, CommitOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git'; -import * as byline from 'byline'; -import { StringDecoder } from 'string_decoder'; +import { assign, detectUnicodeEncoding, dispose, Encoding, groupBy, IDisposable, Limiter, mkdirp, onceEvent, readBytes, splitInChunks, toDisposable, Versions } from './util'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; @@ -1342,7 +1342,15 @@ export class Repository { } async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { - const args = ['commit', '--quiet', '--allow-empty-message']; + const args = ['commit', '--quiet']; + const options: SpawnOptions = {}; + + if (!opts.useEditor || message) { + options.input = message; + args.push(...['--allow-empty-message', '--file', '-']); + } else if (opts.verbose) { + args.push('--verbose'); + } if (opts.all) { args.push('--all'); @@ -1353,9 +1361,11 @@ export class Repository { } if (opts.amend && !message) { - args.push('--amend', '--no-edit'); - } else { - args.push('--file', '-'); + if (opts.useEditor) { + args.push('--amend'); + } else { + args.push('--amend', '--no-edit'); + } } if (opts.signoff) { @@ -1380,7 +1390,7 @@ export class Repository { } try { - await this.exec(args, !opts.amend || message ? { input: message || '' } : {}); + await this.exec(args, options); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1404,6 +1414,9 @@ export class Repository { if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) { commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges; throw commitErr; + } else if (/Aborting commit due to empty commit message/.test(commitErr.stderr || '')) { + commitErr.gitErrorCode = GitErrorCodes.EmptyCommitMessage; + throw commitErr; } try { diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts new file mode 100644 index 0000000000000..56ab89b0856cb --- /dev/null +++ b/extensions/git/src/gitEditor.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import { v4 as uuid } from 'uuid'; +import { Uri, window, workspace } from 'vscode'; +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { EmptyDisposable, IDisposable } from './util'; + + + +interface GitEditorRequest { + commitMessagePath?: string; +} + +export class GitEditor implements IIPCHandler { + + private disposable: IDisposable = EmptyDisposable; + + constructor(private ipc?: IIPCServer) { + if (ipc) { + this.disposable = ipc.registerHandler('git-editor', this); + } + } + + async handle({ commitMessagePath }: GitEditorRequest): Promise { + if (commitMessagePath) { + const id = uuid(); + const uri = Uri.parse(`gitcommit://${id}/${commitMessagePath}`); + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc); + + return new Promise((c) => { + const onDidClose = window.onDidChangeTabs(async (tabs) => { + const commitEditorTab = tabs.find((tab) => tab.resource?.toString() === uri.toString()); + if (!commitEditorTab) { + onDidClose.dispose(); + + await workspace.fs.writeFile(Uri.file(commitMessagePath), await workspace.fs.readFile(uri)); + await workspace.fs.delete(uri); + return c(true); + } + }); + }); + } + } + + getEnv(): { [key: string]: string; } { + if (!this.ipc) { + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor-empty.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'` + }; + } + + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'`, + ELECTRON_RUN_AS_NODE: '1', + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') + }; + } + + dispose(): void { + this.disposable.dispose(); + } +} diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index 5cea9faf98fdd..b9191741144cb 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -29,6 +29,8 @@ export interface IIPCHandler { export async function createIPCServer(context?: string): Promise { const server = http.createServer(); + server.setTimeout(0); + const hash = crypto.createHash('sha1'); if (!context) { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 0aa3193e39475..f8854b8f77573 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -13,6 +13,7 @@ import { CommandCenter } from './commands'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; +import { GitEditor } from './gitEditor'; import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import { GitExtension } from './api/git'; @@ -24,6 +25,8 @@ import * as os from 'os'; import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; +import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { GitCommitFileSystemProvider } from './commitFileSystemProvider'; const deactivateTasks: { (): Promise; }[] = []; @@ -59,10 +62,21 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann return !skip; }); - const askpass = await Askpass.create(outputChannel, context.storagePath); + let ipc: IIPCServer | undefined = undefined; + + try { + ipc = await createIPCServer(context.storagePath); + } catch (err) { + outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`); + } + + const askpass = new Askpass(ipc); disposables.push(askpass); - const environment = askpass.getEnv(); + const gitEditor = new GitEditor(ipc); + disposables.push(gitEditor); + + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv() }; const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); disposables.push(terminalEnvironmentManager); @@ -99,6 +113,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann disposables.push( cc, new GitFileSystemProvider(model), + new GitCommitFileSystemProvider(), new GitDecorations(model), new GitProtocolHandler(), new GitTimelineProvider(model, cc) diff --git a/extensions/git/src/scripts/git-editor-empty.bat b/extensions/git/src/scripts/git-editor-empty.bat new file mode 100644 index 0000000000000..56eeb8a5801a4 --- /dev/null +++ b/extensions/git/src/scripts/git-editor-empty.bat @@ -0,0 +1 @@ +@ECHO off diff --git a/extensions/git/src/scripts/git-editor-empty.sh b/extensions/git/src/scripts/git-editor-empty.sh new file mode 100644 index 0000000000000..1a2485251c33a --- /dev/null +++ b/extensions/git/src/scripts/git-editor-empty.sh @@ -0,0 +1 @@ +#!/bin/sh diff --git a/extensions/git/src/scripts/git-editor.bat b/extensions/git/src/scripts/git-editor.bat new file mode 100644 index 0000000000000..99235a3c0904c --- /dev/null +++ b/extensions/git/src/scripts/git-editor.bat @@ -0,0 +1,3 @@ +@ECHO off + +"%VSCODE_GIT_EDITOR_NODE%" "%VSCODE_GIT_EDITOR_MAIN%" %* diff --git a/extensions/git/src/scripts/git-editor.sh b/extensions/git/src/scripts/git-editor.sh new file mode 100644 index 0000000000000..9b88de0ff5676 --- /dev/null +++ b/extensions/git/src/scripts/git-editor.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +$VSCODE_GIT_EDITOR_NODE "$VSCODE_GIT_EDITOR_MAIN" $@ diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 93d14932c0811..56ee35f6f95bc 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -31,6 +31,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.43.tgz#26bcbb0595b305400e8ceaf9a127a7f905ae49c8" integrity sha512-3pwDJjp1PWacPTpH0LcfhgjvurQvrZFBrC6xxjaUEZ7ifUtT32jtjPxEMMblpqd2Mvx+k8haqQJLQxolyGN/cQ== +"@types/uuid@^8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" + integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== + "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" @@ -61,6 +66,11 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + vscode-extension-telemetry@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.2.tgz#6ef847a80c9cfc207eb15e3a254f235acebb65a5" From f3cb88ca004b9241f4e5e6c94d5e2f4b66f0ade1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 23 May 2022 15:07:45 +0200 Subject: [PATCH 2/8] refactor git commit editor with latest main --- extensions/git/extension.webpack.config.js | 2 +- extensions/git/package.json | 20 ++--- extensions/git/package.nls.json | 4 +- extensions/git/src/api/git.d.ts | 2 +- extensions/git/src/askpass.ts | 13 --- extensions/git/src/commands.ts | 17 +--- extensions/git/src/git.ts | 19 +--- .../git/src/gitEditor/fileSystemProvider.ts | 88 +++++++++++++++++++ extensions/git/src/gitEditor/gitEditor.ts | 69 +++++++++++++++ extensions/git/src/gitEditor/main.ts | 21 +++++ .../gitEditor/scripts/git-editor-empty.bat | 1 + .../src/gitEditor/scripts/git-editor-empty.sh | 1 + .../git/src/gitEditor/scripts/git-editor.bat | 4 + .../git/src/gitEditor/scripts/git-editor.sh | 4 + extensions/git/src/ipc/ipcServer.ts | 2 - extensions/git/src/main.ts | 17 ++-- extensions/git/tsconfig.json | 1 + extensions/git/yarn.lock | 25 ++---- 18 files changed, 220 insertions(+), 90 deletions(-) create mode 100644 extensions/git/src/gitEditor/fileSystemProvider.ts create mode 100644 extensions/git/src/gitEditor/gitEditor.ts create mode 100644 extensions/git/src/gitEditor/main.ts create mode 100644 extensions/git/src/gitEditor/scripts/git-editor-empty.bat create mode 100755 extensions/git/src/gitEditor/scripts/git-editor-empty.sh create mode 100644 extensions/git/src/gitEditor/scripts/git-editor.bat create mode 100755 extensions/git/src/gitEditor/scripts/git-editor.sh diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js index 3324b6c1d988b..ee6203af47e45 100644 --- a/extensions/git/extension.webpack.config.js +++ b/extensions/git/extension.webpack.config.js @@ -14,6 +14,6 @@ module.exports = withDefaults({ entry: { main: './src/main.ts', ['askpass-main']: './src/askpass-main.ts', - ['git-editor-main']: './src/git-editor-main.ts' + ['git-editor-main']: './src/gitEditor/main.ts' } }); diff --git a/extensions/git/package.json b/extensions/git/package.json index 26f1b56ad4c56..e1cf12ee4cc7d 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -12,6 +12,7 @@ "enabledApiProposals": [ "diffCommand", "contribViewsWelcome", + "resolvers", "scmActionButton", "scmSelectedProvider", "scmValidation", @@ -1986,16 +1987,16 @@ "scope": "machine", "description": "%config.defaultCloneDirectory%" }, - "git.useEditorToCommit": { + "git.useEditorAsCommitInput": { "type": "boolean", "scope": "resource", - "description": "%config.useEditorToCommit%", + "description": "%config.useEditorAsCommitInput%", "default": false }, "git.verboseCommit": { "type": "boolean", "scope": "resource", - "description": "%config.verboseCommit%", + "markdownDescription": "%config.verboseCommit%", "default": false }, "git.enableSmartCommit": { @@ -2642,12 +2643,8 @@ "byline": "^5.0.0", "file-type": "^7.2.0", "jschardet": "3.0.0", -<<<<<<< HEAD - "uuid": "^8.3.2", - "vscode-extension-telemetry": "0.4.2", -======= "picomatch": "2.3.1", ->>>>>>> a00e1380401 + "uuid": "8.3.2", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", "which": "^1.3.0" @@ -2655,15 +2652,10 @@ "devDependencies": { "@types/byline": "4.2.31", "@types/file-type": "^5.2.1", -<<<<<<< HEAD - "@types/mocha": "^8.2.0", - "@types/node": "14.x", - "@types/uuid": "^8.3.1", -======= "@types/mocha": "^9.1.1", "@types/node": "16.x", "@types/picomatch": "2.3.0", ->>>>>>> a00e1380401 + "@types/uuid": "8.3.4", "@types/which": "^1.0.28" }, "repository": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 66d43f30fb3da..d70493951b6c8 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -135,8 +135,8 @@ "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.", "config.ignoreRebaseWarning": "Ignores the warning when it looks like the branch might have been rebased when pulling.", "config.defaultCloneDirectory": "The default location to clone a git repository.", - "config.useEditorToCommit": "Use a full sized editor to write commit messages.", - "config.verboseCommit": "Enable verbose output in the COMMIT_EDITMSG file. Only works when useEditorToCommit is enabled.", + "config.useEditorAsCommitInput": "Use an editor to author the commit message.", + "config.verboseCommit": "Enable verbose output when `#git.useEditorAsCommitInput#` is enabled.", "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.", "config.smartCommitChanges.all": "Automatically stage all changes.", diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 60772f468ee05..e552f03f25d6d 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, ProviderResult, Uri } from 'vscode'; +import { Uri, Event, Disposable, ProviderResult } from 'vscode'; export { ProviderResult } from 'vscode'; export interface Git { diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index fdcfbce6b47da..ffbd7e48a0e2b 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -8,7 +8,6 @@ import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; -import { OutputChannelLogger } from './log'; export class Askpass implements IIPCHandler { @@ -16,18 +15,6 @@ export class Askpass implements IIPCHandler { private cache = new Map(); private credentialsProviders = new Set(); -<<<<<<< HEAD -======= - static async create(outputChannelLogger: OutputChannelLogger, context?: string): Promise { - try { - return new Askpass(await createIPCServer(context)); - } catch (err) { - outputChannelLogger.logError(`Failed to create git askpass IPC: ${err}`); - return new Askpass(); - } - } ->>>>>>> a00e1380401 - constructor(private ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 536aa97f56b15..c8b7ca6714b0e 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1438,16 +1438,11 @@ export class CommandCenter { opts.signoff = true; } - if (config.get('useEditorToCommit')) { + if (config.get('useEditorAsCommitInput')) { opts.useEditor = true; - } - if (config.get('verboseCommit')) { - opts.verbose = true; - if (!opts.useEditor) { - window.showInformationMessage( - localize('useless verbose commit', "Verbose committing has no effect. The setting 'git.useEditorToCommit' is not enabled.") - ); + if (config.get('verboseCommit')) { + opts.verbose = true; } } @@ -1564,7 +1559,7 @@ export class CommandCenter { const getCommitMessage = async () => { let _message: string | undefined = message; - if (!_message && !config.get('useEditorToCommit')) { + if (!_message && !config.get('useEditorAsCommitInput')) { let value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { @@ -3010,16 +3005,12 @@ export class CommandCenter { message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git."); choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))); break; -<<<<<<< HEAD case GitErrorCodes.EmptyCommitMessage: message = localize('empty commit', "Aborting commit due to empty commit message."); type = 'warning'; options.modal = false; break; - default: -======= default: { ->>>>>>> a00e1380401 const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') .replace(/^> husky.*$/mi, '') diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 320a0bc2d9565..189e2e86b5c14 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -<<<<<<< HEAD -======= import { promises as fs, exists, realpath } from 'fs'; import * as path from 'path'; import * as os from 'os'; @@ -18,21 +16,8 @@ import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, import { CancellationToken, ConfigurationChangeEvent, Progress, Uri, workspace } from 'vscode'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git'; ->>>>>>> a00e1380401 import * as byline from 'byline'; -import * as cp from 'child_process'; -import { EventEmitter } from 'events'; -import * as filetype from 'file-type'; -import { exists, promises as fs, realpath } from 'fs'; -import * as iconv from 'iconv-lite-umd'; -import * as os from 'os'; -import * as path from 'path'; import { StringDecoder } from 'string_decoder'; -import { CancellationToken, Progress, Uri } from 'vscode'; -import * as which from 'which'; -import { Branch, BranchQuery, Change, CommitOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git'; -import { detectEncoding } from './encoding'; -import { assign, detectUnicodeEncoding, dispose, Encoding, groupBy, IDisposable, Limiter, mkdirp, onceEvent, readBytes, splitInChunks, toDisposable, Versions } from './util'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; @@ -1418,7 +1403,9 @@ export class Repository { if (!opts.useEditor || message) { options.input = message; args.push(...['--allow-empty-message', '--file', '-']); - } else if (opts.verbose) { + } + + if (opts.verbose) { args.push('--verbose'); } diff --git a/extensions/git/src/gitEditor/fileSystemProvider.ts b/extensions/git/src/gitEditor/fileSystemProvider.ts new file mode 100644 index 0000000000000..a78b17fbb3823 --- /dev/null +++ b/extensions/git/src/gitEditor/fileSystemProvider.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import { workspace, Uri, Disposable, Event, EventEmitter, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileSystemError } from 'vscode'; +import { EmptyDisposable } from '../util'; + +export class GitEditorFileSystemProvider implements FileSystemProvider { + private _onDidChangeFile = new EventEmitter(); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private disposables: Disposable[] = []; + + private fileCache: Record = {}; + + constructor() { + this.disposables.push( + workspace.registerFileSystemProvider('gitcommit', this, { isReadonly: false, isCaseSensitive: true }), + workspace.registerResourceLabelFormatter({ + scheme: 'gitcommit', + formatting: { + label: '${authority} (gitcommit)', + separator: '/' + } + }) + ); + } + + async stat(uri: Uri): Promise { + // TODO: Should we do error handling here? If so, we may need sth like toFileSystemProviderErrorCode + if (this.fileCache[uri.authority]) { + return { type: FileType.File, ctime: 0, mtime: 0, size: this.fileCache[uri.authority].length }; + } + + const { size } = await fs.stat(uri.fsPath); + return { ctime: 0, mtime: 0, size, type: FileType.File }; + } + + async readFile(uri: Uri): Promise { + // TODO: Should we do more error handling here? If so, we may need sth like toFileSystemProviderErrorCode + if (!this.fileCache[uri.authority]) { + try { + this.fileCache[uri.authority] = await fs.readFile(uri.fsPath); + } catch (error) { + // TODO ingore for now, maybe go for toFileSystemProviderErrorCode + } + } + return this.fileCache[uri.authority]; + } + + async writeFile(uri: Uri, content: Uint8Array): Promise { + if (this.fileCache[uri.authority]) { + this.fileCache[uri.authority] = content; + } else { + throw FileSystemError.FileNotFound(); + } + } + + delete(uri: Uri): void { + if (this.fileCache[uri.authority]) { + delete this.fileCache[uri.authority]; + } else { + throw FileSystemError.FileNotFound(); + } + } + + createDirectory(): void { + throw new Error('Method not implemented.'); + } + + readDirectory(): Thenable<[string, FileType][]> { + throw new Error('Method not implemented.'); + } + + rename(): void { + throw new Error('Method not implemented.'); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } + + watch(): Disposable { + return EmptyDisposable; + } +} diff --git a/extensions/git/src/gitEditor/gitEditor.ts b/extensions/git/src/gitEditor/gitEditor.ts new file mode 100644 index 0000000000000..7b5d0af28dc64 --- /dev/null +++ b/extensions/git/src/gitEditor/gitEditor.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import { v4 as uuid } from 'uuid'; +import { TabInputText, Uri, window, workspace } from 'vscode'; +import { IIPCHandler, IIPCServer } from '../ipc/ipcServer'; +import { EmptyDisposable, IDisposable, isWindows } from '../util'; + +interface GitEditorRequest { + commitMessagePath?: string; +} + +export class GitEditor implements IIPCHandler { + + private disposable: IDisposable = EmptyDisposable; + + constructor(private ipc?: IIPCServer) { + if (ipc) { + this.disposable = ipc.registerHandler('git-editor', this); + } + } + + async handle({ commitMessagePath }: GitEditorRequest): Promise { + if (commitMessagePath) { + const id = uuid(); + const uri = Uri.parse(`gitcommit://${id}${isWindows ? '/' : ''}${commitMessagePath}`); + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc); + + return new Promise((c) => { + const onDidClose = window.tabGroups.onDidChangeTabs(async (tabs) => { + if (tabs.closed.some(t => t.input instanceof TabInputText && t.input.uri.toString() === uri.toString())) { + onDidClose.dispose(); + + await workspace.fs.writeFile(Uri.file(commitMessagePath), await workspace.fs.readFile(uri)); + await workspace.fs.delete(uri); + return c(true); + } + }); + }); + } + } + + getEnv(): { [key: string]: string } { + if (!this.ipc) { + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor-empty.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'` + }; + } + + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'main.js') + }; + } + + dispose(): void { + this.disposable.dispose(); + } +} diff --git a/extensions/git/src/gitEditor/main.ts b/extensions/git/src/gitEditor/main.ts new file mode 100644 index 0000000000000..661540e003005 --- /dev/null +++ b/extensions/git/src/gitEditor/main.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IPCClient } from '../ipc/ipcClient'; + +function fatal(err: any): void { + console.error(err); + process.exit(1); +} + +function main(argv: string[]): void { + const ipcClient = new IPCClient('git-editor'); + const commitMessagePath = argv[2]; + + ipcClient.call({ commitMessagePath }).then(() => { + setTimeout(() => process.exit(0), 0); + }).catch(err => fatal(err)); +} + +main(process.argv); diff --git a/extensions/git/src/gitEditor/scripts/git-editor-empty.bat b/extensions/git/src/gitEditor/scripts/git-editor-empty.bat new file mode 100644 index 0000000000000..56eeb8a5801a4 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor-empty.bat @@ -0,0 +1 @@ +@ECHO off diff --git a/extensions/git/src/gitEditor/scripts/git-editor-empty.sh b/extensions/git/src/gitEditor/scripts/git-editor-empty.sh new file mode 100755 index 0000000000000..1a2485251c33a --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor-empty.sh @@ -0,0 +1 @@ +#!/bin/sh diff --git a/extensions/git/src/gitEditor/scripts/git-editor.bat b/extensions/git/src/gitEditor/scripts/git-editor.bat new file mode 100644 index 0000000000000..ce28807cae149 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor.bat @@ -0,0 +1,4 @@ +@ECHO off + +set ELECTRON_RUN_AS_NODE=1 +"%VSCODE_GIT_EDITOR_NODE%" "%VSCODE_GIT_EDITOR_MAIN%" %* diff --git a/extensions/git/src/gitEditor/scripts/git-editor.sh b/extensions/git/src/gitEditor/scripts/git-editor.sh new file mode 100755 index 0000000000000..f4ba2e4fd73d2 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +ELECTRON_RUN_AS_NODE="1" \ +"$VSCODE_GIT_EDITOR_NODE" "$VSCODE_GIT_EDITOR_MAIN" $@ diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index 554312a03336f..ad4583cbfe1ae 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -29,8 +29,6 @@ export interface IIPCHandler { export async function createIPCServer(context?: string): Promise { const server = http.createServer(); - server.setTimeout(0); - const hash = crypto.createHash('sha1'); if (!context) { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 0239379eb6344..264932e13c975 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -13,7 +13,6 @@ import { CommandCenter } from './commands'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; -import { GitEditor } from './gitEditor'; import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from '@vscode/extension-telemetry'; import { GitExtension } from './api/git'; @@ -25,12 +24,10 @@ import * as os from 'os'; import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; -<<<<<<< HEAD -import { createIPCServer, IIPCServer } from './ipc/ipcServer'; -import { GitCommitFileSystemProvider } from './commitFileSystemProvider'; -======= import { OutputChannelLogger } from './log'; ->>>>>>> a00e1380401 +import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { GitEditor } from './gitEditor/gitEditor'; +import { GitEditorFileSystemProvider } from './gitEditor/fileSystemProvider'; const deactivateTasks: { (): Promise }[] = []; @@ -66,19 +63,15 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu return !skip; }); -<<<<<<< HEAD let ipc: IIPCServer | undefined = undefined; try { ipc = await createIPCServer(context.storagePath); } catch (err) { - outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`); + outputChannelLogger.logError(`Failed to create git IPC: ${err}`); } const askpass = new Askpass(ipc); -======= - const askpass = await Askpass.create(outputChannelLogger, context.storagePath); ->>>>>>> a00e1380401 disposables.push(askpass); const gitEditor = new GitEditor(ipc); @@ -119,8 +112,8 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu const cc = new CommandCenter(git, model, outputChannelLogger, telemetryReporter); disposables.push( cc, + new GitEditorFileSystemProvider(), new GitFileSystemProvider(model), - new GitCommitFileSystemProvider(), new GitDecorations(model), new GitProtocolHandler(), new GitTimelineProvider(model, cc) diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 13997275056b0..5a336731c2497 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -11,6 +11,7 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index e79bf3e07a3e0..6bf66ef27c206 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -41,10 +41,10 @@ resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== -"@types/uuid@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" - integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== +"@types/uuid@8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/which@^1.0.28": version "1.0.28" @@ -81,22 +81,15 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -<<<<<<< HEAD -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -vscode-extension-telemetry@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.2.tgz#6ef847a80c9cfc207eb15e3a254f235acebb65a5" - integrity sha512-y0f51mVoFxHIzULQNCC26TBFIKdEC7uckS3tFoK++OOOl8mU2LlOxgmbd52T/SXoXNg5aI7xqs+4V2ug5ITvKw== -======= picomatch@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== ->>>>>>> a00e1380401 + +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== vscode-nls@^4.0.0: version "4.0.0" From 7475efc0811f3eea3da5c6be92718f6ac1d82dca Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 23 May 2022 16:43:58 +0200 Subject: [PATCH 3/8] Tweak notification + open document --- extensions/git/src/commands.ts | 23 +++++++++++++++++------ extensions/git/src/gitEditor/gitEditor.ts | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index c8b7ca6714b0e..5ce491a25a64b 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2942,7 +2942,7 @@ export class CommandCenter { }; let message: string; - let type: 'error' | 'warning' = 'error'; + let type: 'error' | 'warning' | 'information' = 'error'; const choices = new Map void>(); const openOutputChannelChoice = localize('open git log', "Open Git Log"); @@ -3006,8 +3006,9 @@ export class CommandCenter { choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))); break; case GitErrorCodes.EmptyCommitMessage: - message = localize('empty commit', "Aborting commit due to empty commit message."); - type = 'warning'; + message = localize('empty commit', "Commit operation was cancelled due to empty commit message."); + choices.clear(); + type = 'information'; options.modal = false; break; default: { @@ -3031,10 +3032,20 @@ export class CommandCenter { return; } + let result: string | undefined; const allChoices = Array.from(choices.keys()); - const result = type === 'error' - ? await window.showErrorMessage(message, options, ...allChoices) - : await window.showWarningMessage(message, options, ...allChoices); + + switch (type) { + case 'error': + result = await window.showErrorMessage(message, options, ...allChoices); + break; + case 'warning': + result = await window.showWarningMessage(message, options, ...allChoices); + break; + case 'information': + result = await window.showInformationMessage(message, options, ...allChoices); + break; + } if (result) { const resultFn = choices.get(result); diff --git a/extensions/git/src/gitEditor/gitEditor.ts b/extensions/git/src/gitEditor/gitEditor.ts index 7b5d0af28dc64..6c733a50d779f 100644 --- a/extensions/git/src/gitEditor/gitEditor.ts +++ b/extensions/git/src/gitEditor/gitEditor.ts @@ -27,7 +27,7 @@ export class GitEditor implements IIPCHandler { const id = uuid(); const uri = Uri.parse(`gitcommit://${id}${isWindows ? '/' : ''}${commitMessagePath}`); const doc = await workspace.openTextDocument(uri); - await window.showTextDocument(doc); + await window.showTextDocument(doc, { preview: false }); return new Promise((c) => { const onDidClose = window.tabGroups.onDidChangeTabs(async (tabs) => { From 1e2312709d405ac3b5c87d485387e43fb7da18d1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 07:28:23 +0200 Subject: [PATCH 4/8] Disable commit action while commit in progress --- extensions/git/package.json | 48 +++++++++++++------- extensions/git/src/repository.ts | 2 + src/vs/workbench/contrib/scm/browser/util.ts | 2 +- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index e1cf12ee4cc7d..c6a44a4d04394 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -212,83 +212,99 @@ "command": "git.commit", "title": "%command.commit%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStaged", "title": "%command.commitStaged%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmpty", "title": "%command.commitEmpty%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSigned", "title": "%command.commitStagedSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmend", "title": "%command.commitStagedAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAll", "title": "%command.commitAll%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSigned", "title": "%command.commitAllSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmend", "title": "%command.commitAllAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitNoVerify", "title": "%command.commitNoVerify%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedNoVerify", "title": "%command.commitStagedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmptyNoVerify", "title": "%command.commitEmptyNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSignedNoVerify", "title": "%command.commitStagedSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmendNoVerify", "title": "%command.commitStagedAmendNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllNoVerify", "title": "%command.commitAllNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSignedNoVerify", "title": "%command.commitAllSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmendNoVerify", "title": "%command.commitAllAmendNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.restoreCommitTemplate", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c7962fb1a62ff..09dd18b95e4f6 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -450,6 +450,8 @@ class ProgressManager { const onDidChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git', Uri.file(this.repository.root))); onDidChange(_ => this.updateEnablement()); this.updateEnablement(); + + this.repository.onDidChangeOperations(() => commands.executeCommand('setContext', 'commitInProgress', this.repository.operations.isRunning(Operation.Commit))); } private updateEnablement(): void { diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 80d5d19302d5d..c8f4524d017ba 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -37,7 +37,7 @@ export function isSCMResource(element: any): element is ISCMResource { return !!(element as ISCMResource).sourceUri && isSCMResourceGroup((element as ISCMResource).resourceGroup); } -const compareActions = (a: IAction, b: IAction) => a.id === b.id; +const compareActions = (a: IAction, b: IAction) => a.id === b.id && a.enabled === b.enabled; export function connectPrimaryMenu(menu: IMenu, callback: (primary: IAction[], secondary: IAction[]) => void, primaryGroup?: string): IDisposable { let cachedDisposable: IDisposable = Disposable.None; From 1f193d149848935e214b5f5f68bd608965c4dd08 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 13:41:12 +0200 Subject: [PATCH 5/8] Refactor commit arguments --- extensions/git/src/git.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 189e2e86b5c14..21fa0f0bf8ec5 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1400,9 +1400,9 @@ export class Repository { const args = ['commit', '--quiet']; const options: SpawnOptions = {}; - if (!opts.useEditor || message) { + if (message) { options.input = message; - args.push(...['--allow-empty-message', '--file', '-']); + args.push('--file', '-'); } if (opts.verbose) { @@ -1413,16 +1413,21 @@ export class Repository { args.push('--all'); } - if (opts.amend && message) { + if (opts.amend) { args.push('--amend'); } - if (opts.amend && !message) { - if (opts.useEditor) { - args.push('--amend'); - } else { - args.push('--amend', '--no-edit'); + if (!opts.useEditor) { + if (!message) { + if (opts.amend) { + args.push('--no-edit'); + } else { + options.input = ''; + args.push('--file', '-'); + } } + + args.push('--allow-empty-message'); } if (opts.signoff) { From 96518272e11023b9f79876a3bd9ae0cd5d035922 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 13:49:05 +0200 Subject: [PATCH 6/8] Remove file system provider --- extensions/git/package.json | 2 - .../git/src/gitEditor/fileSystemProvider.ts | 88 ------------------- extensions/git/src/gitEditor/gitEditor.ts | 9 +- extensions/git/src/main.ts | 2 - extensions/git/yarn.lock | 10 --- 5 files changed, 2 insertions(+), 109 deletions(-) delete mode 100644 extensions/git/src/gitEditor/fileSystemProvider.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index c6a44a4d04394..c710957d3c318 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2660,7 +2660,6 @@ "file-type": "^7.2.0", "jschardet": "3.0.0", "picomatch": "2.3.1", - "uuid": "8.3.2", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", "which": "^1.3.0" @@ -2671,7 +2670,6 @@ "@types/mocha": "^9.1.1", "@types/node": "16.x", "@types/picomatch": "2.3.0", - "@types/uuid": "8.3.4", "@types/which": "^1.0.28" }, "repository": { diff --git a/extensions/git/src/gitEditor/fileSystemProvider.ts b/extensions/git/src/gitEditor/fileSystemProvider.ts deleted file mode 100644 index a78b17fbb3823..0000000000000 --- a/extensions/git/src/gitEditor/fileSystemProvider.ts +++ /dev/null @@ -1,88 +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 { promises as fs } from 'fs'; -import { workspace, Uri, Disposable, Event, EventEmitter, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileSystemError } from 'vscode'; -import { EmptyDisposable } from '../util'; - -export class GitEditorFileSystemProvider implements FileSystemProvider { - private _onDidChangeFile = new EventEmitter(); - readonly onDidChangeFile: Event = this._onDidChangeFile.event; - - private disposables: Disposable[] = []; - - private fileCache: Record = {}; - - constructor() { - this.disposables.push( - workspace.registerFileSystemProvider('gitcommit', this, { isReadonly: false, isCaseSensitive: true }), - workspace.registerResourceLabelFormatter({ - scheme: 'gitcommit', - formatting: { - label: '${authority} (gitcommit)', - separator: '/' - } - }) - ); - } - - async stat(uri: Uri): Promise { - // TODO: Should we do error handling here? If so, we may need sth like toFileSystemProviderErrorCode - if (this.fileCache[uri.authority]) { - return { type: FileType.File, ctime: 0, mtime: 0, size: this.fileCache[uri.authority].length }; - } - - const { size } = await fs.stat(uri.fsPath); - return { ctime: 0, mtime: 0, size, type: FileType.File }; - } - - async readFile(uri: Uri): Promise { - // TODO: Should we do more error handling here? If so, we may need sth like toFileSystemProviderErrorCode - if (!this.fileCache[uri.authority]) { - try { - this.fileCache[uri.authority] = await fs.readFile(uri.fsPath); - } catch (error) { - // TODO ingore for now, maybe go for toFileSystemProviderErrorCode - } - } - return this.fileCache[uri.authority]; - } - - async writeFile(uri: Uri, content: Uint8Array): Promise { - if (this.fileCache[uri.authority]) { - this.fileCache[uri.authority] = content; - } else { - throw FileSystemError.FileNotFound(); - } - } - - delete(uri: Uri): void { - if (this.fileCache[uri.authority]) { - delete this.fileCache[uri.authority]; - } else { - throw FileSystemError.FileNotFound(); - } - } - - createDirectory(): void { - throw new Error('Method not implemented.'); - } - - readDirectory(): Thenable<[string, FileType][]> { - throw new Error('Method not implemented.'); - } - - rename(): void { - throw new Error('Method not implemented.'); - } - - dispose(): void { - this.disposables.forEach(d => d.dispose()); - } - - watch(): Disposable { - return EmptyDisposable; - } -} diff --git a/extensions/git/src/gitEditor/gitEditor.ts b/extensions/git/src/gitEditor/gitEditor.ts index 6c733a50d779f..34f8ee9f20fca 100644 --- a/extensions/git/src/gitEditor/gitEditor.ts +++ b/extensions/git/src/gitEditor/gitEditor.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { v4 as uuid } from 'uuid'; import { TabInputText, Uri, window, workspace } from 'vscode'; import { IIPCHandler, IIPCServer } from '../ipc/ipcServer'; -import { EmptyDisposable, IDisposable, isWindows } from '../util'; +import { EmptyDisposable, IDisposable } from '../util'; interface GitEditorRequest { commitMessagePath?: string; @@ -24,8 +23,7 @@ export class GitEditor implements IIPCHandler { async handle({ commitMessagePath }: GitEditorRequest): Promise { if (commitMessagePath) { - const id = uuid(); - const uri = Uri.parse(`gitcommit://${id}${isWindows ? '/' : ''}${commitMessagePath}`); + const uri = Uri.file(commitMessagePath); const doc = await workspace.openTextDocument(uri); await window.showTextDocument(doc, { preview: false }); @@ -33,9 +31,6 @@ export class GitEditor implements IIPCHandler { const onDidClose = window.tabGroups.onDidChangeTabs(async (tabs) => { if (tabs.closed.some(t => t.input instanceof TabInputText && t.input.uri.toString() === uri.toString())) { onDidClose.dispose(); - - await workspace.fs.writeFile(Uri.file(commitMessagePath), await workspace.fs.readFile(uri)); - await workspace.fs.delete(uri); return c(true); } }); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 264932e13c975..3cdf7f2375b51 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -27,7 +27,6 @@ import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; import { createIPCServer, IIPCServer } from './ipc/ipcServer'; import { GitEditor } from './gitEditor/gitEditor'; -import { GitEditorFileSystemProvider } from './gitEditor/fileSystemProvider'; const deactivateTasks: { (): Promise }[] = []; @@ -112,7 +111,6 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu const cc = new CommandCenter(git, model, outputChannelLogger, telemetryReporter); disposables.push( cc, - new GitEditorFileSystemProvider(), new GitFileSystemProvider(model), new GitDecorations(model), new GitProtocolHandler(), diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 6bf66ef27c206..60b91dbb32025 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -41,11 +41,6 @@ resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== -"@types/uuid@8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== - "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" @@ -86,11 +81,6 @@ picomatch@2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -uuid@8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - vscode-nls@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" From fa7cead9d83e15a73052f4175d0ed5b3b51e82a8 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 19:06:12 +0200 Subject: [PATCH 7/8] Adopt scmInput enablement api --- extensions/git/package.json | 1 + extensions/git/src/repository.ts | 7 ++++++- extensions/git/tsconfig.json | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c710957d3c318..1c6ca2f4aff84 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -14,6 +14,7 @@ "contribViewsWelcome", "resolvers", "scmActionButton", + "scmInput", "scmSelectedProvider", "scmValidation", "timeline" diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 10902ab8e243d..b5ed89ac94b19 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -451,7 +451,12 @@ class ProgressManager { onDidChange(_ => this.updateEnablement()); this.updateEnablement(); - this.repository.onDidChangeOperations(() => commands.executeCommand('setContext', 'commitInProgress', this.repository.operations.isRunning(Operation.Commit))); + this.repository.onDidChangeOperations(() => { + const commitInProgress = this.repository.operations.isRunning(Operation.Commit); + + this.repository.sourceControl.inputBox.enabled = !commitInProgress; + commands.executeCommand('setContext', 'commitInProgress', commitInProgress); + }); } private updateEnablement(): void { diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 5a336731c2497..899f8c9c33dcf 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -13,6 +13,7 @@ "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", + "../../src/vscode-dts/vscode.proposed.scmInput.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", "../../src/vscode-dts/vscode.proposed.tabs.d.ts", From 264d5ed34cc5ac7f0e1fa9a575814d04e9363655 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 25 May 2022 11:52:42 +0200 Subject: [PATCH 8/8] Remove files that were moved --- .../git/src/commitFileSystemProvider.ts | 87 ------------------- extensions/git/src/git-editor-main.ts | 21 ----- extensions/git/src/gitEditor.ts | 73 ---------------- .../git/src/scripts/git-editor-empty.bat | 1 - .../git/src/scripts/git-editor-empty.sh | 1 - extensions/git/src/scripts/git-editor.bat | 3 - extensions/git/src/scripts/git-editor.sh | 3 - 7 files changed, 189 deletions(-) delete mode 100644 extensions/git/src/commitFileSystemProvider.ts delete mode 100644 extensions/git/src/git-editor-main.ts delete mode 100644 extensions/git/src/gitEditor.ts delete mode 100644 extensions/git/src/scripts/git-editor-empty.bat delete mode 100644 extensions/git/src/scripts/git-editor-empty.sh delete mode 100644 extensions/git/src/scripts/git-editor.bat delete mode 100644 extensions/git/src/scripts/git-editor.sh diff --git a/extensions/git/src/commitFileSystemProvider.ts b/extensions/git/src/commitFileSystemProvider.ts deleted file mode 100644 index 853ab84c1f1c5..0000000000000 --- a/extensions/git/src/commitFileSystemProvider.ts +++ /dev/null @@ -1,87 +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 { FileSystemProvider, FileChangeEvent, EventEmitter, Event, Disposable, FileStat, Uri, FileType, workspace, FileSystemError } from 'vscode'; -import { EmptyDisposable } from './util'; -import { promises as fs } from 'fs'; - -export class GitCommitFileSystemProvider implements FileSystemProvider { - private _onDidChangeFile = new EventEmitter(); - readonly onDidChangeFile: Event = this._onDidChangeFile.event; - - private disposables: Disposable[] = []; - - private fileCache: Record = {}; - - constructor() { - this.disposables.push( - workspace.registerFileSystemProvider('gitcommit', this, { isReadonly: false, isCaseSensitive: true }), - workspace.registerResourceLabelFormatter({ - scheme: 'gitcommit', - formatting: { - label: '${authority} (gitcommit)', - separator: '/' - } - }) - ); - } - - async stat(uri: Uri): Promise { - // TODO: Should we do error handling here? If so, we may need sth like toFileSystemProviderErrorCode - if (this.fileCache[uri.authority]) { - return { type: FileType.File, ctime: 0, mtime: 0, size: this.fileCache[uri.authority].length }; - } - - const { size } = await fs.stat(uri.fsPath); - return { ctime: 0, mtime: 0, size, type: FileType.File }; - } - - readDirectory(): Thenable<[string, FileType][]> { - throw new Error('Method not implemented.'); - } - - createDirectory(): void { - throw new Error('Method not implemented.'); - } - - async readFile(uri: Uri): Promise { - // TODO: Should we do more error handling here? If so, we may need sth like toFileSystemProviderErrorCode - if (!this.fileCache[uri.authority]) { - try { - this.fileCache[uri.authority] = await fs.readFile(uri.fsPath); - } catch (error) { - // TODO ingore for now, maybe go for toFileSystemProviderErrorCode - } - } - return this.fileCache[uri.authority]; - } - - async writeFile(uri: Uri, content: Uint8Array): Promise { - if (this.fileCache[uri.authority]) { - this.fileCache[uri.authority] = content; - } else { - throw FileSystemError.FileNotFound(); - } - } - - delete(uri: Uri): void { - if (this.fileCache[uri.authority]) { - delete this.fileCache[uri.authority]; - } else { - throw FileSystemError.FileNotFound(); - } - } - - rename(): void { - throw new Error('Method not implemented.'); - } - - dispose(): void { - this.disposables.forEach(d => d.dispose()); - } - - watch(): Disposable { - return EmptyDisposable; - } -} diff --git a/extensions/git/src/git-editor-main.ts b/extensions/git/src/git-editor-main.ts deleted file mode 100644 index c7705bd012b1f..0000000000000 --- a/extensions/git/src/git-editor-main.ts +++ /dev/null @@ -1,21 +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 { IPCClient } from './ipc/ipcClient'; - -function fatal(err: any): void { - console.error(err); - process.exit(1); -} - -function main(argv: string[]): void { - const ipcClient = new IPCClient('git-editor'); - const commitMessagePath = argv[2]; - - ipcClient.call({ commitMessagePath }).then(() => { - setTimeout(() => process.exit(0), 0); - }).catch(err => fatal(err)); -} - -main(process.argv); diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts deleted file mode 100644 index 56ab89b0856cb..0000000000000 --- a/extensions/git/src/gitEditor.ts +++ /dev/null @@ -1,73 +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 * as path from 'path'; -import { v4 as uuid } from 'uuid'; -import { Uri, window, workspace } from 'vscode'; -import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; -import { EmptyDisposable, IDisposable } from './util'; - - - -interface GitEditorRequest { - commitMessagePath?: string; -} - -export class GitEditor implements IIPCHandler { - - private disposable: IDisposable = EmptyDisposable; - - constructor(private ipc?: IIPCServer) { - if (ipc) { - this.disposable = ipc.registerHandler('git-editor', this); - } - } - - async handle({ commitMessagePath }: GitEditorRequest): Promise { - if (commitMessagePath) { - const id = uuid(); - const uri = Uri.parse(`gitcommit://${id}/${commitMessagePath}`); - const doc = await workspace.openTextDocument(uri); - await window.showTextDocument(doc); - - return new Promise((c) => { - const onDidClose = window.onDidChangeTabs(async (tabs) => { - const commitEditorTab = tabs.find((tab) => tab.resource?.toString() === uri.toString()); - if (!commitEditorTab) { - onDidClose.dispose(); - - await workspace.fs.writeFile(Uri.file(commitMessagePath), await workspace.fs.readFile(uri)); - await workspace.fs.delete(uri); - return c(true); - } - }); - }); - } - } - - getEnv(): { [key: string]: string; } { - if (!this.ipc) { - const fileType = process.platform === 'win32' ? 'bat' : 'sh'; - const gitEditor = path.join(__dirname, `scripts/git-editor-empty.${fileType}`); - - return { - GIT_EDITOR: `'${gitEditor}'` - }; - } - - const fileType = process.platform === 'win32' ? 'bat' : 'sh'; - const gitEditor = path.join(__dirname, `scripts/git-editor.${fileType}`); - - return { - GIT_EDITOR: `'${gitEditor}'`, - ELECTRON_RUN_AS_NODE: '1', - VSCODE_GIT_EDITOR_NODE: process.execPath, - VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') - }; - } - - dispose(): void { - this.disposable.dispose(); - } -} diff --git a/extensions/git/src/scripts/git-editor-empty.bat b/extensions/git/src/scripts/git-editor-empty.bat deleted file mode 100644 index 56eeb8a5801a4..0000000000000 --- a/extensions/git/src/scripts/git-editor-empty.bat +++ /dev/null @@ -1 +0,0 @@ -@ECHO off diff --git a/extensions/git/src/scripts/git-editor-empty.sh b/extensions/git/src/scripts/git-editor-empty.sh deleted file mode 100644 index 1a2485251c33a..0000000000000 --- a/extensions/git/src/scripts/git-editor-empty.sh +++ /dev/null @@ -1 +0,0 @@ -#!/bin/sh diff --git a/extensions/git/src/scripts/git-editor.bat b/extensions/git/src/scripts/git-editor.bat deleted file mode 100644 index 99235a3c0904c..0000000000000 --- a/extensions/git/src/scripts/git-editor.bat +++ /dev/null @@ -1,3 +0,0 @@ -@ECHO off - -"%VSCODE_GIT_EDITOR_NODE%" "%VSCODE_GIT_EDITOR_MAIN%" %* diff --git a/extensions/git/src/scripts/git-editor.sh b/extensions/git/src/scripts/git-editor.sh deleted file mode 100644 index 9b88de0ff5676..0000000000000 --- a/extensions/git/src/scripts/git-editor.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -$VSCODE_GIT_EDITOR_NODE "$VSCODE_GIT_EDITOR_MAIN" $@