diff --git a/README.md b/README.md index 0540f865..79a69493 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ open yazi in a floating window in Neovim. - 🆕 Plugin manager for Yazi plugins and flavors ([documentation](./documentation/plugin-manager.md)). Please provide your feedback! +- Features available if you are using a development version of yazi (see + [installing-yazi-from-source.md](documentation/installing-yazi-from-source.md)): + - Highlight the currently hovered yazi file in Neovim + - Restart the last yazi session with a keybinding + - `` makes yazi jump to the directory of the next open split For previewing images with yazi, see Yazi's documentation related to Neovim [here](https://yazi-rs.github.io/docs/image-preview/#neovim). diff --git a/integration-tests/client/testEnvironmentTypes.ts b/integration-tests/client/testEnvironmentTypes.ts index 96b3778f..8dfc3acf 100644 --- a/integration-tests/client/testEnvironmentTypes.ts +++ b/integration-tests/client/testEnvironmentTypes.ts @@ -1,6 +1,12 @@ +export type MultipleFiles = { + openInVerticalSplits: File[] +} + +type File = TestDirectoryFile | "." + /** The arguments given from the tests to send to the server */ export type StartNeovimArguments = { - filename?: TestDirectoryFile | "." + filename?: File | MultipleFiles startupScriptModifications?: StartupScriptModification[] } @@ -57,9 +63,10 @@ export type TestDirectory = { ["test.lua"]: FileEntry ["file.txt"]: FileEntry ["modify_yazi_config_to_use_ya_as_event_reader.lua"]: FileEntry - ["subdirectory/sub.txt"]: FileEntry + ["subdirectory/subdirectory-file.txt"]: FileEntry + ["other-subdirectory/other-sub-file.txt"]: FileEntry ["routes/posts.$postId/route.tsx"]: FileEntry - ["routes/posts.$postId/adjacent-file.tsx"]: FileEntry + ["routes/posts.$postId/adjacent-file.txt"]: FileEntry } } diff --git a/integration-tests/cypress.config.ts b/integration-tests/cypress.config.ts index 2209eed3..b81adf90 100644 --- a/integration-tests/cypress.config.ts +++ b/integration-tests/cypress.config.ts @@ -82,9 +82,14 @@ export default defineConfig({ stem: "file", extension: ".txt", }, - "subdirectory/sub.txt": { - name: "sub.txt", - stem: "sub", + "subdirectory/subdirectory-file.txt": { + name: "subdirectory-file.txt", + stem: "subdirectory-file", + extension: ".txt", + }, + ["other-subdirectory/other-sub-file.txt"]: { + name: "other-sub-file.txt", + stem: "other-sub-file", extension: ".txt", }, "modify_yazi_config_to_use_ya_as_event_reader.lua": { @@ -92,10 +97,10 @@ export default defineConfig({ stem: "modify_yazi_config_to_use_ya_as_event_reader", extension: ".lua", }, - "routes/posts.$postId/adjacent-file.tsx": { - name: "adjacent-file.tsx", + "routes/posts.$postId/adjacent-file.txt": { + name: "adjacent-file.txt", stem: "adjacent-file", - extension: ".tsx", + extension: ".txt", }, "routes/posts.$postId/route.tsx": { name: "route.tsx", @@ -110,6 +115,7 @@ export default defineConfig({ execSync(`cp ./test-environment/file.txt ${dir}/`) execSync(`cp ./test-environment/test-setup.lua ${dir}/test.lua`) execSync(`cp -r ./test-environment/subdirectory ${dir}/`) + execSync(`cp -r ./test-environment/other-subdirectory ${dir}/`) execSync(`cp -r ./test-environment/config-modifications/ ${dir}/`) execSync(`cp -r ./test-environment/routes ${dir}/`) console.log(`Created test directory at ${dir}`) 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 029edb30..e61317c2 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 @@ -55,10 +55,17 @@ describe("opening files", () => { it("can send file names to the quickfix list", () => { cy.startNeovim().then((dir) => { cy.typeIntoTerminal("{upArrow}") - cy.typeIntoTerminal("{control+a}{enter}") + + // wait for yazi to open + cy.contains(dir.contents["test.lua"].name) + + // select the initial file, the cursor moves one line down to the next file + cy.typeIntoTerminal(" ") + // also select the next file because multiple files have to be selected + cy.typeIntoTerminal(" ") + cy.typeIntoTerminal("{enter}") // items in the quickfix list should now be visible - cy.contains(`${dir.contents["file.txt"].name}||`) cy.contains(`${dir.contents["initial-file.txt"].name}||`) }) }) @@ -85,7 +92,7 @@ describe("opening files", () => { // close yazi just to be sure the file preview is not found instead cy.get( - dir.contents["routes/posts.$postId/adjacent-file.tsx"].name, + dir.contents["routes/posts.$postId/adjacent-file.txt"].name, ).should("not.exist") // the file contents should now be visible diff --git a/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/reading-events.cy.ts b/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/reading-events.cy.ts index 52d3b59b..49d34ec5 100644 --- a/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/reading-events.cy.ts +++ b/integration-tests/cypress/e2e/using-shell-redirection-to-read-events/reading-events.cy.ts @@ -17,11 +17,11 @@ describe("reading events", () => { // telescope should now be visible. Let's search for the contents of the // file, which we know beforehand cy.contains("Grep in") - cy.typeIntoTerminal("Hello") + cy.typeIntoTerminal("This") // we should see text indicating the search is limited to the current // directory - cy.contains("Hello from the subdirectory! 👋") + cy.contains("This is other-sub-file.txt") }) }) diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/cd-to-buffer.cy.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/cd-to-buffer.cy.ts new file mode 100644 index 00000000..4f14e955 --- /dev/null +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/cd-to-buffer.cy.ts @@ -0,0 +1,119 @@ +import { isHovered, isNotHovered } from "./hover-utils" +import { startNeovimWithYa } from "./startNeovimWithYa" + +// NOTE: cypress doesn't support the tab key, but control+i seems to work fine +// https://docs.cypress.io/api/commands/type#Typing-tab-key-does-not-work + +// TODO make this more robust once we can jump to a file, not just a +// directory - could test with directories that have multiple files + +describe("'cd' to another buffer's directory", () => { + beforeEach(() => { + cy.visit("http://localhost:5173") + }) + + it("can highlight the buffer when hovered", () => { + const view = { + leftFile: { text: "This is other-sub-file.txt" }, + centerFile: { text: "this file is adjacent-file.txt" }, + rightFile: { text: "ello from the subdirectory!" }, + } as const + + startNeovimWithYa({ + filename: { + openInVerticalSplits: [ + "subdirectory/subdirectory-file.txt", + "routes/posts.$postId/adjacent-file.txt", + "other-subdirectory/other-sub-file.txt", + ], + }, + startupScriptModifications: [ + "modify_yazi_config_and_add_hovered_buffer_background.lua", + ], + }).then(() => { + // sanity check to make sure the files are open + cy.contains(view.leftFile.text) + cy.contains(view.centerFile.text) + cy.contains(view.rightFile.text) + + // before doing anything, both files should be unhovered (have the + // default background color) + isNotHovered(view.leftFile.text) + isNotHovered(view.centerFile.text) + isNotHovered(view.rightFile.text) + + // start yazi + cy.typeIntoTerminal("{upArrow}") + + // Switch to the other buffers' directories in yazi. This should make + // yazi send a hover event for the new, highlighted file. + // + // Since each directory only has one file, it should be highlighted :) + cy.typeIntoTerminal("{control+i}") + isNotHovered(view.leftFile.text) + isHovered(view.centerFile.text) + isNotHovered(view.rightFile.text) + + cy.typeIntoTerminal("{control+i}") + isHovered(view.leftFile.text) + isNotHovered(view.centerFile.text) + isNotHovered(view.rightFile.text) + + cy.typeIntoTerminal("{control+i}") + isNotHovered(view.leftFile.text) + isNotHovered(view.centerFile.text) + isHovered(view.rightFile.text) + + // tab once more to make sure it wraps around + cy.typeIntoTerminal("{control+i}") + isNotHovered(view.leftFile.text) + isHovered(view.centerFile.text) + isNotHovered(view.rightFile.text) + }) + }) + + it("skips highlighting splits for the same file", () => { + const view = { + leftAndCenterFile: { text: "ello from the subdirectory!" }, + rightFile: { text: "This is other-sub-file.txt" }, + } as const + + startNeovimWithYa({ + filename: { + openInVerticalSplits: [ + // open the same file in two splits + "subdirectory/subdirectory-file.txt", + "subdirectory/subdirectory-file.txt", + "other-subdirectory/other-sub-file.txt", + ], + }, + startupScriptModifications: [ + "modify_yazi_config_and_add_hovered_buffer_background.lua", + ], + }).then(() => { + isNotHovered(view.leftAndCenterFile.text) + isNotHovered(view.rightFile.text) + + // start yazi + cy.typeIntoTerminal("{upArrow}") + + cy.typeIntoTerminal("{control+i}") + + // the right file should be highlighted + isNotHovered(view.leftAndCenterFile.text) + isHovered(view.rightFile.text) + + // tab again to make sure it wraps around. It should highlight both splits + cy.typeIntoTerminal("{control+i}") + isHovered(view.leftAndCenterFile.text) + isNotHovered(view.rightFile.text) + + // tab again. Since the left and center file are the same, it should + // skip the center file and highlight the right file + + cy.typeIntoTerminal("{control+i}") + isNotHovered(view.leftAndCenterFile.text) + isHovered(view.rightFile.text) + }) + }) +}) diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/hover-highlights.cy.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/hover-highlights.cy.ts index 9f25b07c..989e7c9e 100644 --- a/integration-tests/cypress/e2e/using-ya-to-read-events/hover-highlights.cy.ts +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/hover-highlights.cy.ts @@ -1,29 +1,22 @@ -import { flavors } from "@catppuccin/palette" import * as tinycolor2 from "tinycolor2" +import { + darkBackgroundColors, + isHovered, + isNotHovered, + lightBackgroundColors, +} from "./hover-utils" import { startNeovimWithYa } from "./startNeovimWithYa" -const darkTheme = flavors.macchiato.colors -const lightTheme = flavors.latte.colors - -function rgbify(color: (typeof darkTheme)["surface0"]["rgb"]) { - return `rgb(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()})` -} - describe("highlighting the buffer with 'hover' events", () => { beforeEach(() => { cy.visit("http://localhost:5173") }) - const darkBackgroundColors = { - normal: rgbify(darkTheme.base.rgb), - hovered: rgbify(darkTheme.surface1.rgb), - } - // NOTE: when opening the file, the cursor is placed at the beginning of // the file. This causes the web terminal to render multiple elements for the // same text, and this can cause issues when matching colors, as in the DOM - // there are multiple colors. Work around this by matching a substring of the - // text instead of the whole text. + // there really are multiple colors present. Work around this by matching a + // substring of the text instead of the whole text. /** HACK in CI, there can be timing issues where the first hover event is * lost. Right now we work around this by selecting another file first, then @@ -44,9 +37,7 @@ describe("highlighting the buffer with 'hover' events", () => { ], }).then((dir) => { // wait until text on the start screen is visible - cy.contains("If you see this text, Neovim is ready!") - .children() - .should("have.css", "background-color", darkBackgroundColors.normal) + isNotHovered("f you see this text, Neovim is ready!") // start yazi cy.typeIntoTerminal("{upArrow}") @@ -57,23 +48,15 @@ describe("highlighting the buffer with 'hover' events", () => { // yazi is shown and adjacent files should be visible now // - // the current file (initial-file.txt) is highlighted by default when + // the current file is highlighted by default when // opening yazi. This should have sent the 'hover' event and caused the // Neovim window to be shown with a different background color - cy.contains("If you see this text, Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.hovered, - ) + isHovered("If you see this text, Neovim is ready!") // close yazi - the highlight should be removed and we should see the // same color as before cy.typeIntoTerminal("q") - cy.contains("Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.normal, - ) + isNotHovered("f you see this text, Neovim is ready!") }) }) @@ -84,11 +67,7 @@ describe("highlighting the buffer with 'hover' events", () => { ], }).then((dir) => { // wait until text on the start screen is visible - cy.contains("Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.normal, - ) + isNotHovered("f you see this text, Neovim is ready!") // start yazi cy.typeIntoTerminal("{upArrow}") @@ -100,23 +79,15 @@ describe("highlighting the buffer with 'hover' events", () => { dir.contents["initial-file.txt"].name, ) - // the current file (initial-file.txt) is highlighted by default when + // the current file is highlighted by default when // opening yazi. This should have sent the 'hover' event and caused the // Neovim window to be shown with a different background color - cy.contains("Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.hovered, - ) + isHovered("If you see this text, Neovim is ready!") // hover another file - the highlight should be removed cy.typeIntoTerminal(`/^${dir.contents["test.lua"].name}{enter}`) - cy.contains("Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.normal, - ) + isNotHovered("If you see this text, Neovim is ready!") }) }) @@ -127,9 +98,7 @@ describe("highlighting the buffer with 'hover' events", () => { ], }).then((dir) => { // wait until text on the start screen is visible - cy.contains("If you see this text, Neovim is ready!") - .children() - .should("have.css", "background-color", darkBackgroundColors.normal) + isNotHovered("f you see this text, Neovim is ready!") const testFile = dir.contents["test.lua"].name // open an adjacent file and wait for it to be displayed @@ -142,26 +111,14 @@ describe("highlighting the buffer with 'hover' events", () => { cy.typeIntoTerminal("{upArrow}") hoverAnotherFileToEnsureHoverEventIsReceivedInCI(testFile) - cy.contains("how to initialize the test environment").should( - "have.css", - "background-color", - darkBackgroundColors.hovered, - ) + isHovered("how to initialize the test environment") // select the other file - the highlight should move to it cy.typeIntoTerminal(`/^${dir.contents["initial-file.txt"].name}{enter}`, { delay: 1, }) - cy.contains("how to initialize the test environment").should( - "have.css", - "background-color", - darkBackgroundColors.normal, - ) - cy.contains("If you see this text, Neovim is ready!").should( - "have.css", - "background-color", - darkBackgroundColors.hovered, - ) + isNotHovered("how to initialize the test environment") + isHovered("If you see this text, Neovim is ready!") }) }) @@ -173,9 +130,7 @@ describe("highlighting the buffer with 'hover' events", () => { it("for a dark colorscheme, hovers appear lighter in color", () => { startNeovimWithYa({ startupScriptModifications: [] }).then((dir) => { // wait until text on the start screen is visible - cy.contains("If you see this text, Neovim is ready!") - .children() - .should("have.css", "background-color", darkBackgroundColors.normal) + isNotHovered("f you see this text, Neovim is ready!") // start yazi cy.typeIntoTerminal("{upArrow}") @@ -202,10 +157,6 @@ describe("highlighting the buffer with 'hover' events", () => { }) it("for a light colorscheme", () => { - const lightBackgroundColors = { - normal: rgbify(lightTheme.base.rgb), - } - startNeovimWithYa({ startupScriptModifications: ["use_light_neovim_colorscheme.lua"], }).then((dir) => { diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/hover-utils.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/hover-utils.ts new file mode 100644 index 00000000..517e8694 --- /dev/null +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/hover-utils.ts @@ -0,0 +1,38 @@ +import { flavors } from "@catppuccin/palette" + +const darkTheme = flavors.macchiato.colors +const lightTheme = flavors.latte.colors + +function rgbify(color: (typeof darkTheme)["surface0"]["rgb"]) { + return `rgb(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()})` +} + +export const darkBackgroundColors = { + normal: rgbify(darkTheme.base.rgb), + // NOTE: this abstraction is super leaky. It assumes + // config-modifications/modify_yazi_config_and_add_hovered_buffer_background.lua + // is being used + hovered: rgbify(darkTheme.surface1.rgb), +} + +export const lightBackgroundColors = { + normal: rgbify(lightTheme.base.rgb), +} + +// only works for the dark colorscheme for now +export function isHovered(text: string): void { + cy.contains(text).should( + "have.css", + "background-color", + darkBackgroundColors.hovered, + ) +} + +// only works for the dark colorscheme for now +export function isNotHovered(text: string): void { + cy.contains(text).should( + "have.css", + "background-color", + darkBackgroundColors.normal, + ) +} 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 d0da4042..20c41d41 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 @@ -57,10 +57,17 @@ describe("opening files", () => { it("can send file names to the quickfix list", () => { startNeovimWithYa().then((dir) => { cy.typeIntoTerminal("{upArrow}") - cy.typeIntoTerminal("{control+a}{enter}") + + // wait for yazi to open + cy.contains(dir.contents["test.lua"].name) + + // select the initial file, the cursor moves one line down to the next file + cy.typeIntoTerminal(" ") + // also select the next file because multiple files have to be selected + cy.typeIntoTerminal(" ") + cy.typeIntoTerminal("{enter}") // items in the quickfix list should now be visible - cy.contains(`${dir.contents["file.txt"].name}||`) cy.contains(`${dir.contents["initial-file.txt"].name}||`) }) }) @@ -148,7 +155,7 @@ describe("opening files", () => { // close yazi just to be sure the file preview is not found instead cy.get( - dir.contents["routes/posts.$postId/adjacent-file.tsx"].name, + dir.contents["routes/posts.$postId/adjacent-file.txt"].name, ).should("not.exist") // the file contents should now be visible diff --git a/integration-tests/cypress/e2e/using-ya-to-read-events/reading-events.cy.ts b/integration-tests/cypress/e2e/using-ya-to-read-events/reading-events.cy.ts index d69faf55..cfdd1543 100644 --- a/integration-tests/cypress/e2e/using-ya-to-read-events/reading-events.cy.ts +++ b/integration-tests/cypress/e2e/using-ya-to-read-events/reading-events.cy.ts @@ -22,11 +22,11 @@ describe("reading events", () => { // telescope should now be visible. Let's search for the contents of the // file, which we know beforehand cy.contains("Grep in") - cy.typeIntoTerminal("Hello") + cy.typeIntoTerminal("This") // we should see text indicating the search is limited to the current // directory - cy.contains("Hello from the subdirectory! 👋") + cy.contains("This is other-sub-file.txt") }) it("can read 'trash' events and close an open buffer when its file was trashed", () => { diff --git a/integration-tests/server/server.ts b/integration-tests/server/server.ts index 62a8bb43..25b44a74 100644 --- a/integration-tests/server/server.ts +++ b/integration-tests/server/server.ts @@ -82,8 +82,18 @@ io.on("connection", function connection(socket) { } } if (startArgs.filename) { - const file = path.join(startArgs.directory, startArgs.filename) - args.push(file) + if (typeof startArgs.filename === "string") { + const file = path.join(startArgs.directory, startArgs.filename) + args.push(file) + } else if (startArgs.filename.openInVerticalSplits.length > 0) { + // `-O[N]` Open N vertical windows (default: one per file) + args.push("-O") + + for (const file of startArgs.filename.openInVerticalSplits) { + const filePath = path.join(startArgs.directory, file) + args.push(filePath) + } + } } const app = TerminalApplication.start({ diff --git a/integration-tests/test-environment/other-subdirectory/other-sub-file.txt b/integration-tests/test-environment/other-subdirectory/other-sub-file.txt new file mode 100644 index 00000000..f6707166 --- /dev/null +++ b/integration-tests/test-environment/other-subdirectory/other-sub-file.txt @@ -0,0 +1 @@ +This is other-sub-file.txt diff --git a/integration-tests/test-environment/routes/posts.$postId/adjacent-file.tsx b/integration-tests/test-environment/routes/posts.$postId/adjacent-file.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/integration-tests/test-environment/routes/posts.$postId/adjacent-file.txt b/integration-tests/test-environment/routes/posts.$postId/adjacent-file.txt new file mode 100644 index 00000000..10d5d874 --- /dev/null +++ b/integration-tests/test-environment/routes/posts.$postId/adjacent-file.txt @@ -0,0 +1 @@ +this file is adjacent-file.txt diff --git a/lua/yazi.lua b/lua/yazi.lua index cdb3fd93..1334966f 100644 --- a/lua/yazi.lua +++ b/lua/yazi.lua @@ -94,7 +94,11 @@ function M.yazi(config, input_path) ) config.hooks.yazi_opened(path.filename, win.content_buffer, config) - config.set_keymappings_function(win.content_buffer, config) + + config.set_keymappings_function(win.content_buffer, config, { + api = yazi_process.api, + input_path = path, + }) win.on_resized = function(event) vim.fn.jobresize( diff --git a/lua/yazi/config.lua b/lua/yazi/config.lua index b84f5d90..297fa363 100644 --- a/lua/yazi/config.lua +++ b/lua/yazi/config.lua @@ -47,7 +47,8 @@ end --- this function as the basis. ---@param yazi_buffer integer ---@param config YaziConfig -function M.default_set_keymappings_function(yazi_buffer, config) +---@param context YaziActiveContext +function M.default_set_keymappings_function(yazi_buffer, config, context) vim.keymap.set({ 't' }, '', function() keybinding_helpers.open_file_in_vertical_split(config) end, { buffer = yazi_buffer }) @@ -87,6 +88,10 @@ function M.default_set_keymappings_function(yazi_buffer, config) end, }) end, { buffer = yazi_buffer }) + + vim.keymap.set({ 't' }, '', function() + keybinding_helpers.cycle_open_buffers(context) + end, { buffer = yazi_buffer }) end return M diff --git a/lua/yazi/keybinding_helpers.lua b/lua/yazi/keybinding_helpers.lua index 1d17dae0..281a064c 100644 --- a/lua/yazi/keybinding_helpers.lua +++ b/lua/yazi/keybinding_helpers.lua @@ -1,4 +1,8 @@ +---@module "plenary.path" + local openers = require('yazi.openers') +local Log = require('yazi.log') +local utils = require('yazi.utils') --- Hacky actions that can be used when yazi is open. They typically select the --- current file and execute some useful operation on the selected file. @@ -57,4 +61,64 @@ function YaziOpenerActions.select_current_file_and_close_yazi(config, callbacks) ) end +---@param context YaziActiveContext +function YaziOpenerActions.cycle_open_buffers(context) + assert(context.input_path, 'No input path found') + assert(context.input_path.filename, 'No input path filename found') + + local current_cycle_position = ( + context.cycled_file and context.cycled_file.path + ) or context.input_path + local visible_buffers = utils.get_visible_open_buffers() + + for i, buffer in ipairs(visible_buffers) do + if + buffer.renameable_buffer:matches_exactly(current_cycle_position.filename) + then + Log:debug( + string.format( + 'Found buffer for path: "%s", will open the next buffer', + context.input_path + ) + ) + local other_buffers = vim.list_slice(visible_buffers, i + 1) + other_buffers = vim.list_extend(other_buffers, visible_buffers, 1, i - 1) + local next_buffer = vim.iter(other_buffers):find(function(b) + return b.renameable_buffer.path.filename + ~= current_cycle_position.filename + end) + assert( + next_buffer, + vim.inspect({ + 'Could not find next buffer', + #visible_buffers, + i, + next, + }) + ) + + local directory = + vim.fn.fnamemodify(next_buffer.renameable_buffer.path.filename, ':h') + assert( + directory, + string.format( + 'Found the next buffer, but could not find its base directory. The buffer: "%s", aborting.', + next_buffer.renameable_buffer.path.filename + ) + ) + + context.api:cd(directory) + context.cycled_file = next_buffer.renameable_buffer + return + end + end + + Log:debug( + string.format( + 'Could not find cycle_open_buffers for path: "%s"', + context.input_path + ) + ) +end + return YaziOpenerActions diff --git a/lua/yazi/process/yazi_process_api.lua b/lua/yazi/process/yazi_process_api.lua index 4e2702d4..8eb9f61c 100644 --- a/lua/yazi/process/yazi_process_api.lua +++ b/lua/yazi/process/yazi_process_api.lua @@ -1,7 +1,8 @@ ----@class YaziProcessApi +---@class YaziProcessApi # Provides yazi.nvim -> yazi process interactions ---@field private config YaziConfig ---@field private yazi_id string local YaziProcessApi = {} +YaziProcessApi.__index = YaziProcessApi ---@param config YaziConfig ---@param yazi_id string diff --git a/lua/yazi/types.lua b/lua/yazi/types.lua index 34c5e78d..8cccc13f 100644 --- a/lua/yazi/types.lua +++ b/lua/yazi/types.lua @@ -10,7 +10,7 @@ ---@field public use_yazi_client_id_flag? boolean "use the `--client-id` flag with yazi, allowing communication with that specific instance as opposed to all yazis on the system" ---@field public enable_mouse_support? boolean ---@field public open_file_function? fun(chosen_file: string, config: YaziConfig, state: YaziClosedState): nil "a function that will be called when a file is chosen in yazi" ----@field public set_keymappings_function? fun(buffer: integer, config: YaziConfig): nil "the function that will set the keymappings for the yazi floating window. It will be called after the floating window is created." +---@field public set_keymappings_function? fun(buffer: integer, config: YaziConfig, context: YaziActiveContext): nil "the function that will set the keymappings for the yazi floating window. It will be called after the floating window is created." ---@field public hooks? YaziConfigHooks ---@field public highlight_groups? YaziConfigHighlightGroups ---@field public integrations? YaziConfigIntegrations @@ -19,6 +19,11 @@ ---@field public yazi_floating_window_border? any "the type of border to use. See nvim_open_win() for the values your neovim version supports" ---@field public log_level? yazi.LogLevel +---@class (exact) YaziActiveContext # context state for a single yazi session +---@field api YaziProcessApi +---@field input_path Path the path that is first selected by yazi when it's opened +---@field cycled_file? RenameableBuffer the last file that was cycled to with e.g. the key + ---@class (exact) YaziConfigHooks ---@field public yazi_opened fun(preselected_path: string | nil, buffer: integer, config: YaziConfig):nil ---@field public yazi_closed_successfully fun(chosen_file: string | nil, config: YaziConfig, state: YaziClosedState): nil diff --git a/spec/yazi/helpers/fake_yazi_process.lua b/spec/yazi/helpers/fake_yazi_process.lua index 5f51d252..aea71008 100644 --- a/spec/yazi/helpers/fake_yazi_process.lua +++ b/spec/yazi/helpers/fake_yazi_process.lua @@ -6,13 +6,15 @@ local M = {} ---@field code integer ---@field selected_files string[] ---@field events YaziEvent[] +---@field api YaziProcessApi M.mocks = {} ----@param arguments { code?: integer, selected_files?: string[], events?: YaziEvent[]} +---@param arguments { code?: integer, selected_files?: string[], events?: YaziEvent[], api: YaziProcessApi } function M.setup_created_instances_to_instantly_exit(arguments) M.mocks.code = arguments.code or 0 M.mocks.selected_files = arguments.selected_files or {} M.mocks.events = arguments.events or {} + M.api = arguments.api or {} end -- Fake yazi process that instantly exits with the mocked data that was set up @@ -21,6 +23,7 @@ end ---@diagnostic disable-next-line: unused-local function M:start(_, _, on_exit) on_exit(M.mocks.code, M.mocks.selected_files, M.mocks.events) + return self end return M diff --git a/spec/yazi/yazi_spec.lua b/spec/yazi/yazi_spec.lua index ca61c5c4..bcc1524b 100644 --- a/spec/yazi/yazi_spec.lua +++ b/spec/yazi/yazi_spec.lua @@ -34,7 +34,7 @@ describe('opening a file', function() assert.equals(file, path.filename) end - it('opens yazi with the current file selected', function() + it('#focus opens yazi with the current file selected', function() fake_yazi_process.setup_created_instances_to_instantly_exit({}) -- the file name should have a space as well as special characters, in order to test that