-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #4137 adds experimental gk cli integration
- Loading branch information
Showing
11 changed files
with
405 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import type { Disposable } from 'vscode'; | ||
import type { CompareWithCommandArgs } from '../../../../commands/compareWith'; | ||
import type { Container } from '../../../../container'; | ||
import { cherryPick, merge, rebase } from '../../../../git/actions/repository'; | ||
import type { Repository } from '../../../../git/models/repository'; | ||
import { executeCommand } from '../../../../system/-webview/command'; | ||
import type { CliCommandRequest, CliCommandResponse, CliIpcServer } from './integration'; | ||
|
||
interface CliCommand { | ||
command: string; | ||
handler: (request: CliCommandRequest, repo?: Repository | undefined) => Promise<CliCommandResponse>; | ||
} | ||
|
||
const commandHandlers: CliCommand[] = []; | ||
function command(command: string) { | ||
return function ( | ||
target: unknown, | ||
contextOrKey?: string | ClassMethodDecoratorContext, | ||
descriptor?: PropertyDescriptor, | ||
) { | ||
// ES Decorator | ||
if (contextOrKey && typeof contextOrKey === 'object' && 'kind' in contextOrKey) { | ||
if (contextOrKey.kind !== 'method') { | ||
throw new Error('The command decorator can only be applied to methods'); | ||
} | ||
|
||
commandHandlers.push({ command: command, handler: target as CliCommand['handler'] }); | ||
return; | ||
} | ||
|
||
// TypeScript experimental decorator | ||
if (descriptor) { | ||
commandHandlers.push({ command: command, handler: descriptor.value as CliCommand['handler'] }); | ||
return descriptor; | ||
} | ||
|
||
throw new Error('Invalid decorator usage'); | ||
}; | ||
} | ||
|
||
export class CliCommandHandlers implements Disposable { | ||
constructor( | ||
private readonly container: Container, | ||
private readonly server: CliIpcServer, | ||
) { | ||
for (const { command, handler } of commandHandlers) { | ||
this.server.registerHandler(command, rq => this.wrapHandler(rq, handler)); | ||
} | ||
} | ||
|
||
dispose(): void {} | ||
|
||
private wrapHandler( | ||
request: CliCommandRequest, | ||
handler: (request: CliCommandRequest, repo?: Repository | undefined) => Promise<CliCommandResponse>, | ||
) { | ||
let repo: Repository | undefined; | ||
if (request?.cwd) { | ||
repo = this.container.git.getRepository(request.cwd); | ||
} | ||
|
||
return handler(request, repo); | ||
} | ||
|
||
@command('cherry-pick') | ||
async handleCherryPickCommand( | ||
_request: CliCommandRequest, | ||
repo?: Repository | undefined, | ||
): Promise<CliCommandResponse> { | ||
return cherryPick(repo); | ||
} | ||
|
||
@command('compare') | ||
async handleCompareCommand( | ||
_request: CliCommandRequest, | ||
repo?: Repository | undefined, | ||
): Promise<CliCommandResponse> { | ||
if (!repo || !_request.args?.length) { | ||
await executeCommand('gitlens.compareWith'); | ||
return; | ||
} | ||
|
||
const [ref1, ref2] = _request.args; | ||
if (!ref1 || !ref2) { | ||
await executeCommand('gitlens.compareWith'); | ||
return; | ||
} | ||
|
||
if (ref1) { | ||
if (!(await repo.git.refs().validateReference(ref1))) { | ||
// TODO: Send an error back to the CLI? | ||
await executeCommand('gitlens.compareWith'); | ||
return; | ||
} | ||
} | ||
|
||
if (ref2) { | ||
if (!(await repo.git.refs().validateReference(ref2))) { | ||
// TODO: Send an error back to the CLI? | ||
await executeCommand<CompareWithCommandArgs>('gitlens.compareWith', { ref1: ref1 }); | ||
return; | ||
} | ||
} | ||
|
||
await executeCommand<CompareWithCommandArgs>('gitlens.compareWith', { ref1: ref1, ref2: ref2 }); | ||
} | ||
|
||
@command('graph') | ||
async handleGraphCommand(request: CliCommandRequest, repo?: Repository | undefined): Promise<CliCommandResponse> { | ||
if (!repo || !request.args?.length) { | ||
await executeCommand('gitlens.showGraphView'); | ||
return; | ||
} | ||
|
||
const [ref] = request.args; | ||
const reference = await repo.git.refs().getReference(ref); | ||
if (ref && !reference) { | ||
// TODO: Send an error back to the CLI? | ||
await executeCommand('gitlens.showInCommitGraph', repo); | ||
return; | ||
} | ||
|
||
await executeCommand('gitlens.showInCommitGraph', { ref: reference }); | ||
} | ||
|
||
@command('merge') | ||
async handleMergeCommand(request: CliCommandRequest, repo?: Repository | undefined): Promise<CliCommandResponse> { | ||
if (!repo || !request.args?.length) return merge(repo); | ||
|
||
const [ref] = request.args; | ||
const reference = await repo.git.refs().getReference(ref); | ||
if (ref && !reference) { | ||
// TODO: Send an error back to the CLI? | ||
} | ||
return merge(repo, reference); | ||
} | ||
|
||
@command('rebase') | ||
async handleRebaseCommand(request: CliCommandRequest, repo?: Repository | undefined): Promise<CliCommandResponse> { | ||
if (!repo || !request.args?.length) return rebase(repo); | ||
|
||
const [ref] = request.args; | ||
const reference = await repo.git.refs().getReference(ref); | ||
if (ref && !reference) { | ||
// TODO: Send an error back to the CLI? | ||
} | ||
|
||
return rebase(repo, reference); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import type { ConfigurationChangeEvent } from 'vscode'; | ||
import { Disposable } from 'vscode'; | ||
import type { Container } from '../../../../container'; | ||
import { configuration } from '../../../../system/-webview/configuration'; | ||
import { CliCommandHandlers } from './commands'; | ||
import type { IpcServer } from './server'; | ||
import { createIpcServer } from './server'; | ||
|
||
export interface CliCommandRequest { | ||
cwd?: string; | ||
args?: string[]; | ||
} | ||
export type CliCommandResponse = string | void; | ||
export type CliIpcServer = IpcServer<CliCommandRequest, CliCommandResponse>; | ||
|
||
export class GkCliIntegrationProvider implements Disposable { | ||
private readonly _disposable: Disposable; | ||
private _runningDisposable: Disposable | undefined; | ||
|
||
constructor(private readonly container: Container) { | ||
this._disposable = configuration.onDidChange(e => this.onConfigurationChanged(e)); | ||
|
||
this.onConfigurationChanged(); | ||
} | ||
|
||
dispose(): void { | ||
this.stop(); | ||
this._disposable?.dispose(); | ||
} | ||
|
||
private onConfigurationChanged(e?: ConfigurationChangeEvent): void { | ||
if (e == null || configuration.changed(e, 'gitKraken.cli.integration.enabled')) { | ||
if (!configuration.get('gitKraken.cli.integration.enabled')) { | ||
this.stop(); | ||
} else { | ||
void this.start(); | ||
} | ||
} | ||
} | ||
|
||
private async start() { | ||
const server = await createIpcServer<CliCommandRequest, CliCommandResponse>(); | ||
|
||
const { environmentVariableCollection: envVars } = this.container.context; | ||
|
||
envVars.clear(); | ||
envVars.persistent = false; | ||
envVars.replace('GK_GL_ADDR', server.ipcAddress); | ||
envVars.description = 'Enables GK CLI integration'; | ||
|
||
this._runningDisposable = Disposable.from(new CliCommandHandlers(this.container, server), server); | ||
} | ||
|
||
private stop() { | ||
this.container.context.environmentVariableCollection.clear(); | ||
this._runningDisposable?.dispose(); | ||
this._runningDisposable = undefined; | ||
} | ||
} |
Oops, something went wrong.