diff --git a/package.json b/package.json index b80cb450..9651af58 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,12 @@ "category": "Ruby LSP", "when": "editorActive && editorLangId == ruby" }, + { + "command": "rubyLsp.runMigrationInTerminal", + "title": "Run current migration in terminal", + "category": "Ruby LSP", + "when": "editorActive && editorLangId == ruby" + }, { "command": "rubyLsp.showSyntaxTree", "title": "Show syntax tree", diff --git a/src/common.ts b/src/common.ts index eb968cb8..a0325bf4 100644 --- a/src/common.ts +++ b/src/common.ts @@ -19,6 +19,7 @@ export enum Command { RunTest = "rubyLsp.runTest", RunTestInTerminal = "rubyLsp.runTestInTerminal", DebugTest = "rubyLsp.debugTest", + RunMigrationInTerminal = "rubyLsp.runMigrationInTerminal", OpenLink = "rubyLsp.openLink", ShowSyntaxTree = "rubyLsp.showSyntaxTree", } diff --git a/src/migrationController.ts b/src/migrationController.ts new file mode 100644 index 00000000..c06cbf58 --- /dev/null +++ b/src/migrationController.ts @@ -0,0 +1,64 @@ +import * as vscode from "vscode"; + +import { Telemetry } from "./telemetry"; +import { Workspace } from "./workspace"; + +export class MigrationController { + private terminal: vscode.Terminal | undefined; + private readonly telemetry: Telemetry; + private readonly currentWorkspace: () => Workspace | undefined; + + constructor( + _context: vscode.ExtensionContext, + telemetry: Telemetry, + currentWorkspace: () => Workspace | undefined, + ) { + this.telemetry = telemetry; + this.currentWorkspace = currentWorkspace; + + vscode.window.onDidCloseTerminal((terminal: vscode.Terminal): void => { + if (terminal === this.terminal) this.terminal = undefined; + }); + } + + async runMigrationInTerminal(command?: string) { + // eslint-disable-next-line no-param-reassign + command ??= "bin/rails db:migrate"; + + if (this.terminal === undefined) { + this.terminal = this.getTerminal(); + } + + this.terminal.show(); + this.terminal.sendText(command); + + const workspace = this.currentWorkspace(); + + if (workspace?.lspClient?.serverVersion) { + await this.telemetry.sendCodeLensEvent( + "migrate_in_terminal", + workspace.lspClient.serverVersion, + ); + } + } + + // Get an existing terminal or create a new one. For multiple workspaces, it's important to create a new terminal for + // each workspace because they might be using different Ruby versions. If there's no workspace, we fallback to a + // generic name + private getTerminal() { + const workspace = this.currentWorkspace(); + const name = workspace + ? `${workspace.workspaceFolder.name}: migration` + : "Ruby LSP: migration"; + + const previousTerminal = vscode.window.terminals.find( + (terminal) => terminal.name === name, + ); + + return previousTerminal + ? previousTerminal + : vscode.window.createTerminal({ + name, + }); + } +} diff --git a/src/rubyLsp.ts b/src/rubyLsp.ts index 48fca8d3..08fb2ae2 100644 --- a/src/rubyLsp.ts +++ b/src/rubyLsp.ts @@ -10,6 +10,7 @@ import { Command, STATUS_EMITTER, pathExists } from "./common"; import { VersionManager } from "./ruby"; import { StatusItems } from "./status"; import { TestController } from "./testController"; +import { MigrationController } from "./migrationController"; import { Debugger } from "./debugger"; // The RubyLsp class represents an instance of the entire extension. This should only be instantiated once at the @@ -21,6 +22,7 @@ export class RubyLsp { private readonly context: vscode.ExtensionContext; private readonly statusItems: StatusItems; private readonly testController: TestController; + private readonly migrationController: MigrationController; private readonly debug: Debugger; constructor(context: vscode.ExtensionContext) { @@ -31,6 +33,11 @@ export class RubyLsp { this.telemetry, this.currentActiveWorkspace.bind(this), ); + this.migrationController = new MigrationController( + context, + this.telemetry, + this.currentActiveWorkspace.bind(this), + ); this.debug = new Debugger(context, this.workspaceResolver.bind(this)); this.registerCommands(context); @@ -328,6 +335,12 @@ export class RubyLsp { Command.DebugTest, this.testController.debugTest.bind(this.testController), ), + vscode.commands.registerCommand( + Command.RunMigrationInTerminal, + this.migrationController.runMigrationInTerminal.bind( + this.migrationController, + ), + ), ); } diff --git a/src/telemetry.ts b/src/telemetry.ts index ffa9f58f..5d8f4701 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -20,7 +20,7 @@ export interface ConfigurationEvent { } export interface CodeLensEvent { - type: "test" | "debug" | "test_in_terminal" | "link"; + type: "test" | "debug" | "test_in_terminal" | "migrate_in_terminal" | "link"; lspVersion: string; }