Skip to content

Commit

Permalink
fix: close the floating terminal if it loses focus (#269)
Browse files Browse the repository at this point in the history
* refactor(tests): add mouse click support

* fix: close the floating terminal if it loses focus

The floating terminal that shows yazi running can lose focus when e.g.
clicked outside of. This commit adds an autocmd that closes the floating
terminal in these cases.

The reason for this is that it's inconvenient to navigate back to the
floating terminal to close it manually. Even if you did this, it was in
normal mode - not insert mode which is typically what you want to be in
when you're using yazi.
  • Loading branch information
mikavilpas authored Jul 26, 2024
1 parent 0099e63 commit c9ebbf6
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 4 deletions.
18 changes: 17 additions & 1 deletion integration-tests/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLDivElement>("#app")
if (!app) {
Expand All @@ -24,7 +27,6 @@ const terminal = new Terminal({
cursorBlink: false,
convertEol: true,
fontSize: 13,
// letterSpacing: 0.5,
})
{
const colors = flavors.macchiato.colors
Expand Down Expand Up @@ -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)
}
})
17 changes: 17 additions & 0 deletions integration-tests/client/validateMouseEvent.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand All @@ -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}")

Expand All @@ -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}")

Expand Down Expand Up @@ -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}")
Expand Down
28 changes: 28 additions & 0 deletions integration-tests/cypress/e2e/using-ya-to-read-events/mouse.cy.ts
Original file line number Diff line number Diff line change
@@ -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")
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand All @@ -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}")

Expand All @@ -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}")

Expand Down Expand Up @@ -102,14 +105,15 @@ 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.
// - Next, the editor must make changes to the file names and save the
// 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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Cypress.Commands.add(
(text: string, options?: Partial<Cypress.TypeOptions>) => {
// 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)
},
)

Expand Down
7 changes: 7 additions & 0 deletions integration-tests/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
8 changes: 8 additions & 0 deletions integration-tests/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
},
)
},
)
})
Expand Down
7 changes: 7 additions & 0 deletions lua/yazi/window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c9ebbf6

Please sign in to comment.