diff --git a/integration-tests/client/client.ts b/integration-tests/client/client.ts index 872dd17..eff2019 100644 --- a/integration-tests/client/client.ts +++ b/integration-tests/client/client.ts @@ -6,14 +6,17 @@ import { FitAddon } from "@xterm/addon-fit" import { Terminal } from "@xterm/xterm" import io from "socket.io-client" import type { + MouseEventMessage, StartNeovimMessage, StdinMessage, StdoutMessage, } from "../server/server" + import type { StartNeovimArguments, StartNeovimServerArguments, } from "./testEnvironmentTypes" +import { validateMouseEvent } from "./validateMouseEvent" const app = document.querySelector("#app") if (!app) { @@ -24,7 +27,6 @@ const terminal = new Terminal({ cursorBlink: false, convertEol: true, fontSize: 13, - // letterSpacing: 0.5, }) { const colors = flavors.macchiato.colors @@ -112,3 +114,17 @@ socket.on( terminal.onKey((event) => { socket.emit("stdin" satisfies StdinMessage, event.key) }) + +terminal.onData((data) => { + // this gets called for mouse events. However, some mouse events seem to + // confuse Neovim, so for now let's just send click events + + if (typeof data !== "string") { + throw new Error(`unexpected onData message type: '${JSON.stringify(data)}'`) + } + + const mouseEvent = validateMouseEvent(data) + if (mouseEvent) { + socket.emit("mouseEvent" satisfies MouseEventMessage, data) + } +}) diff --git a/integration-tests/client/validateMouseEvent.ts b/integration-tests/client/validateMouseEvent.ts new file mode 100644 index 0000000..50af9c7 --- /dev/null +++ b/integration-tests/client/validateMouseEvent.ts @@ -0,0 +1,17 @@ +// Function to parse mouse events +// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Button-event-tracking +export function validateMouseEvent(data: string): string | undefined { + const match = /\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(data) + if (match) { + const buttonCode = parseInt(match[1], 10) + const column = parseInt(match[2], 10) + const row = parseInt(match[3], 10) + const isRelease = match[4] === "m" + + console.log( + `Mouse event: buttonCode=${buttonCode}, column=${column}, row=${row}, isRelease=${isRelease}`, + ) + + return data + } +} diff --git a/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/opening-files.cy.ts b/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/opening-files.cy.ts index bc79377..99d790c 100644 --- a/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/opening-files.cy.ts +++ b/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/opening-files.cy.ts @@ -31,6 +31,7 @@ describe("opening files", () => { it("can open a file in a vertical split", () => { cy.startNeovim().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+v}") @@ -46,6 +47,7 @@ describe("opening files", () => { it("can open a file in a horizontal split", () => { cy.startNeovim().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+x}") @@ -61,6 +63,7 @@ describe("opening files", () => { it("can open a file in a new tab", () => { cy.startNeovim().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+t}") @@ -101,6 +104,7 @@ describe("opening files", () => { it("can open files with complex characters in their name", () => { cy.startNeovim().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) // enter the routes/ directory cy.typeIntoTerminal("/routes{enter}") diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/mouse.cy.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/mouse.cy.ts new file mode 100644 index 0000000..de3f49c --- /dev/null +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/mouse.cy.ts @@ -0,0 +1,28 @@ +import { startNeovimWithYa } from "./startNeovimWithYa" + +describe("mouse support", () => { + beforeEach(() => { + cy.visit("http://localhost:5173") + }) + + it("can use grug-far.nvim to search and replace in the cwd", () => { + startNeovimWithYa().then((dir) => { + // wait until text on the start screen is visible + cy.contains("If you see this text, Neovim is ready!") + + // open yazi + cy.typeIntoTerminal("{upArrow}") + + // yazi should be showing adjacent files + cy.contains(dir.contents["test.lua"].name) + + // click outside of the yazi floating window. This should close it + // because it's designed to close when it loses focus + cy.contains("-- TERMINAL --").click() + + // clicking outside of the yazi window should close it, after which + // Neovim should not be showing the TERMINAL buffer any longer + cy.contains("-- TERMINAL --").should("not.exist") + }) + }) +}) diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/opening-files.cy.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/opening-files.cy.ts index 6cacdb1..6d51f4a 100644 --- a/integration-tests/cypress/e2e/using-ya-to-read-events/opening-files.cy.ts +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/opening-files.cy.ts @@ -33,6 +33,7 @@ describe("opening files", () => { it("can open a file in a vertical split", () => { startNeovimWithYa().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+v}") @@ -48,6 +49,7 @@ describe("opening files", () => { it("can open a file in a horizontal split", () => { startNeovimWithYa().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+x}") @@ -63,6 +65,7 @@ describe("opening files", () => { it("can open a file in a new tab", () => { startNeovimWithYa().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("/test.lua{enter}") cy.typeIntoTerminal("{control+t}") @@ -102,7 +105,7 @@ describe("opening files", () => { describe("bulk renaming", () => { it("can bulk rename files", () => { - startNeovimWithYa().then((_dir) => { + startNeovimWithYa().then((dir) => { // in yazi, bulk renaming is done by // - selecting files and pressing "r". // - It opens the editor with the names of the selected files. @@ -110,6 +113,7 @@ describe("opening files", () => { // file. // - Finally, yazi should rename the files to match the new names. cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) cy.typeIntoTerminal("{control+a}r") // yazi should now have opened an embedded Neovim. The file name should say @@ -129,8 +133,9 @@ describe("opening files", () => { }) it("can rename a buffer that's open in Neovim", () => { - startNeovimWithYa().then((_dir) => { + startNeovimWithYa().then((dir) => { cy.typeIntoTerminal("{upArrow}") + cy.contains(dir.contents["test.lua"].name) // select only the current file to make the test easier cy.typeIntoTerminal("v") cy.typeIntoTerminal("r") // start renaming @@ -166,6 +171,7 @@ describe("opening files", () => { cy.typeIntoTerminal("{upArrow}") // enter the routes/ directory + cy.contains("routes") cy.typeIntoTerminal("/routes{enter}") cy.typeIntoTerminal("{rightArrow}") cy.contains(dir.contents["routes/posts.$postId/route.tsx"].name) // file in the directory diff --git a/integration-tests/cypress/support/commands.ts b/integration-tests/cypress/support/commands.ts index 88d7b45..4b3825c 100644 --- a/integration-tests/cypress/support/commands.ts +++ b/integration-tests/cypress/support/commands.ts @@ -59,7 +59,7 @@ Cypress.Commands.add( (text: string, options?: Partial) => { // the syntax for keys is described here: // https://docs.cypress.io/api/commands/type - cy.get("#app").type(text, options) + cy.get("textarea").focus().type(text, options) }, ) diff --git a/integration-tests/eslint.config.mjs b/integration-tests/eslint.config.mjs index 71af3d5..79f2a67 100644 --- a/integration-tests/eslint.config.mjs +++ b/integration-tests/eslint.config.mjs @@ -55,6 +55,13 @@ export default [ }, ], + "@typescript-eslint/restrict-template-expressions": [ + "error", + { + allowNumber: true, + allowBoolean: true, + }, + ], "@typescript-eslint/consistent-type-imports": "error", "@typescript-eslint/no-import-type-side-effects": "error", "@typescript-eslint/explicit-module-boundary-types": ["warn"], diff --git a/integration-tests/server/server.ts b/integration-tests/server/server.ts index 9468b58..179b43f 100644 --- a/integration-tests/server/server.ts +++ b/integration-tests/server/server.ts @@ -14,6 +14,7 @@ const testDirectory = path.join(__dirname, "..", "test-environment/") export type StdinMessage = "stdin" export type StdoutMessage = "stdout" export type StartNeovimMessage = "startNeovim" +export type MouseEventMessage = "mouseEvent" const expressApp = express() const server = createServer(expressApp) @@ -139,6 +140,13 @@ io.on("connection", function connection(socket) { assert(typeof data === "string", "stdin message must be a string") app.write(data) }) + + socket.on( + "mouseEvent" satisfies MouseEventMessage, + function (data: string) { + app.write(data) + }, + ) }, ) }) diff --git a/lua/yazi/window.lua b/lua/yazi/window.lua index 161a615..66e1aaa 100644 --- a/lua/yazi/window.lua +++ b/lua/yazi/window.lua @@ -86,6 +86,13 @@ function YaziFloatingWindow:open_and_display() vim.cmd('setlocal winhl=NormalFloat:YaziFloat') vim.cmd('set winblend=' .. self.config.yazi_floating_window_winblend) + vim.api.nvim_create_autocmd({ 'WinLeave', 'TermLeave' }, { + buffer = yazi_buffer, + callback = function() + self:close() + end, + }) + if self.config.enable_mouse_support == true then self:add_hacky_mouse_support(yazi_buffer) end