Skip to content

Commit

Permalink
feat: add cy.runExCommand() to run Ex commands in Neovim (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikavilpas authored Nov 24, 2024
1 parent fbbcf7a commit 1140f04
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 8 deletions.
20 changes: 20 additions & 0 deletions packages/integration-tests/cypress/e2e/neovim.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,24 @@ describe("neovim features", () => {
})
})
})

it("can runExCommand and get its result", () => {
cy.visit("/")
cy.startNeovim().then(() => {
// wait until text on the start screen is visible
cy.contains("If you see this text, Neovim is ready!")

cy.runExCommand({ command: "echo 'hello from ex command'" }).then(result => {
expect(result.value).to.equal("hello from ex command")
})

cy.contains("Hello from the subdirectory!").should("not.exist")
cy.runExCommand({ command: `vsplit ${"subdirectory/subdirectory-file.txt" satisfies MyTestDirectoryFile}` }).then(
result => {
expect(result.value).to.equal("")
}
)
cy.contains("Hello from the subdirectory!")
})
})
})
16 changes: 15 additions & 1 deletion packages/integration-tests/cypress/support/tui-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
//
// This file is autogenerated by tui-sandbox. Do not edit it directly.
//
import type { BlockingCommandClientInput, LuaCodeClientInput } from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingCommandClientInput,
ExCommandClientInput,
LuaCodeClientInput,
} from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
} from "@tui-sandbox/library/dist/src/server/types"
Expand All @@ -21,6 +26,7 @@ declare global {
startNeovim(startArguments?: MyStartNeovimServerArguments): Promise<NeovimContext>
runBlockingShellCommand(input: BlockingCommandClientInput): Promise<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Promise<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput>
}
}

Expand Down Expand Up @@ -57,6 +63,12 @@ Cypress.Commands.add("runLuaCode", (input: LuaCodeClientInput) => {
})
})

Cypress.Commands.add("runExCommand", (input: ExCommandClientInput) => {
cy.window().then(async win => {
return await win.runExCommand(input)
})
})

before(function () {
// disable Cypress's default behavior of logging all XMLHttpRequests and
// fetches to the Command Log
Expand All @@ -78,6 +90,8 @@ declare global {
runBlockingShellCommand(input: BlockingCommandClientInput): Chainable<BlockingShellCommandOutput>

runLuaCode(input: LuaCodeClientInput): Chainable<RunLuaCodeOutput>

runExCommand(input: ExCommandClientInput): Chainable<RunExCommandOutput>
}
}
}
8 changes: 7 additions & 1 deletion packages/library/src/browser/neovim-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { TerminalClient } from "../client/index.js"
import type { BlockingCommandClientInput, LuaCodeClientInput } from "../server/server.js"
import type { BlockingCommandClientInput, ExCommandClientInput, LuaCodeClientInput } from "../server/server.js"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
TestDirectory,
Expand Down Expand Up @@ -35,10 +36,15 @@ window.runLuaCode = async function (input: LuaCodeClientInput): Promise<RunLuaCo
return client.runLuaCode(input)
}

window.runExCommand = async function (input: ExCommandClientInput): Promise<RunExCommandOutput> {
return client.runExCommand(input)
}

declare global {
interface Window {
startNeovim(startArguments?: StartNeovimGenericArguments): Promise<TestDirectory>
runBlockingShellCommand(input: BlockingCommandClientInput): Promise<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Promise<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput>
}
}
16 changes: 15 additions & 1 deletion packages/library/src/client/terminal-client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { createTRPCClient, httpBatchLink, splitLink, unstable_httpSubscriptionLink } from "@trpc/client"
import type { Terminal } from "@xterm/xterm"
import "@xterm/xterm/css/xterm.css"
import type { AppRouter, BlockingCommandClientInput, LuaCodeClientInput } from "../server/server.js"
import type {
AppRouter,
BlockingCommandClientInput,
ExCommandClientInput,
LuaCodeClientInput,
} from "../server/server.js"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
TestDirectory,
Expand Down Expand Up @@ -105,4 +111,12 @@ export class TerminalClient {
tabId: this.tabId,
})
}

public async runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput> {
await this.ready
return this.trpc.neovim.runExCommand.mutate({
command: input.command,
tabId: this.tabId,
})
}
}
16 changes: 15 additions & 1 deletion packages/library/src/server/cypress-support/contents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ it("should return the expected contents", async () => {
//
// This file is autogenerated by tui-sandbox. Do not edit it directly.
//
import type { BlockingCommandClientInput, LuaCodeClientInput } from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingCommandClientInput,
ExCommandClientInput,
LuaCodeClientInput,
} from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
} from "@tui-sandbox/library/dist/src/server/types"
Expand All @@ -26,6 +31,7 @@ it("should return the expected contents", async () => {
startNeovim(startArguments?: MyStartNeovimServerArguments): Promise<NeovimContext>
runBlockingShellCommand(input: BlockingCommandClientInput): Promise<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Promise<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput>
}
}
Expand Down Expand Up @@ -62,6 +68,12 @@ it("should return the expected contents", async () => {
})
})
Cypress.Commands.add("runExCommand", (input: ExCommandClientInput) => {
cy.window().then(async win => {
return await win.runExCommand(input)
})
})
before(function () {
// disable Cypress's default behavior of logging all XMLHttpRequests and
// fetches to the Command Log
Expand All @@ -83,6 +95,8 @@ it("should return the expected contents", async () => {
runBlockingShellCommand(input: BlockingCommandClientInput): Chainable<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Chainable<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Chainable<RunExCommandOutput>
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions packages/library/src/server/cypress-support/contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ const __filename = fileURLToPath(import.meta.url)
export async function createCypressSupportFileContents(): Promise<string> {
// this is the interface of tui-sandbox as far as cypress in the user's
// application is concerned
let text = `
/// <reference types="cypress" />
let text = `/// <reference types="cypress" />
//
// This file is autogenerated by tui-sandbox. Do not edit it directly.
//
import type { BlockingCommandClientInput, LuaCodeClientInput } from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingCommandClientInput,
ExCommandClientInput,
LuaCodeClientInput,
} from "@tui-sandbox/library/dist/src/server/server"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
} from "@tui-sandbox/library/dist/src/server/types"
Expand All @@ -30,6 +34,7 @@ declare global {
startNeovim(startArguments?: MyStartNeovimServerArguments): Promise<NeovimContext>
runBlockingShellCommand(input: BlockingCommandClientInput): Promise<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Promise<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput>
}
}
Expand Down Expand Up @@ -66,6 +71,12 @@ Cypress.Commands.add("runLuaCode", (input: LuaCodeClientInput) => {
})
})
Cypress.Commands.add("runExCommand", (input: ExCommandClientInput) => {
cy.window().then(async win => {
return await win.runExCommand(input)
})
})
before(function () {
// disable Cypress's default behavior of logging all XMLHttpRequests and
// fetches to the Command Log
Expand All @@ -87,6 +98,8 @@ declare global {
runBlockingShellCommand(input: BlockingCommandClientInput): Chainable<BlockingShellCommandOutput>
runLuaCode(input: LuaCodeClientInput): Chainable<RunLuaCodeOutput>
runExCommand(input: ExCommandClientInput): Chainable<RunExCommandOutput>
}
}
}
Expand Down
29 changes: 28 additions & 1 deletion packages/library/src/server/neovim/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import assert from "assert"
import { exec } from "child_process"
import "core-js/proposals/async-explicit-resource-management.js"
import util from "util"
import type { BlockingCommandInput, LuaCodeInput } from "../server.js"
import type { BlockingCommandInput, ExCommandInput, LuaCodeInput } from "../server.js"
import type {
BlockingShellCommandOutput,
RunExCommandOutput,
RunLuaCodeOutput,
StartNeovimGenericArguments,
TestDirectory,
Expand Down Expand Up @@ -141,3 +142,29 @@ export async function runLuaCode(options: LuaCodeInput): Promise<RunLuaCodeOutpu
throw new Error(`Error running Lua code: ${options.luaCode}`, { cause: e })
}
}

export async function runExCommand(options: ExCommandInput): Promise<RunExCommandOutput> {
const neovim = neovims.get(options.tabId.tabId)
assert(
neovim !== undefined,
`Neovim instance for clientId not found - cannot runExCommand. Maybe neovim's not started yet?`
)
assert(
neovim.application,
`Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
)

const api = await neovim.state?.client.get()
if (!api) {
throw new Error(`Neovim API not available for client id ${options.tabId.tabId}. Maybe it's not started yet?`)
}

console.log(`Neovim ${neovim.application.processId()} running Ex command: ${options.command}`)
try {
const output = await api.commandOutput(options.command)
return { value: output }
} catch (e) {
console.warn(`Error running Ex command: ${options.command}`, e)
throw new Error(`Error running Ex command: ${options.command}`, { cause: e })
}
}
8 changes: 8 additions & 0 deletions packages/library/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const luaCodeInputSchema = z.object({ tabId: tabIdSchema, luaCode: z.string() })
export type LuaCodeClientInput = Except<LuaCodeInput, "tabId">
export type LuaCodeInput = z.infer<typeof luaCodeInputSchema>

const exCommandInputSchema = z.object({ tabId: tabIdSchema, command: z.string() })
export type ExCommandClientInput = Except<ExCommandInput, "tabId">
export type ExCommandInput = z.infer<typeof exCommandInputSchema>

/** @private */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export async function createAppRouter(config: TestServerConfig) {
Expand Down Expand Up @@ -80,6 +84,10 @@ export async function createAppRouter(config: TestServerConfig) {
runLuaCode: trpc.procedure.input(luaCodeInputSchema).mutation(options => {
return neovim.runLuaCode(options.input)
}),

runExCommand: trpc.procedure.input(exCommandInputSchema).mutation(options => {
return neovim.runExCommand(options.input)
}),
}),
})

Expand Down
2 changes: 2 additions & 0 deletions packages/library/src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ export type RunLuaCodeOutput = {
value?: VimValue
// to catch errors, use pcall() in the Lua code
}

export type RunExCommandOutput = { value?: string }

0 comments on commit 1140f04

Please sign in to comment.