Skip to content

Commit

Permalink
feat: open currently visible splits as yazi tabs (opt-in) (#359)
Browse files Browse the repository at this point in the history
If you have multiple splits open in Neovim, yazi will now open all of
them in tabs when you open yazi. This is useful when you want to perform
file operations to whatever you are working on.

This feature is off by default. To enable it, you can set
`open_multiple_tabs = true` in your configuration.

By default, the number keys currently switch between tabs. See all the
default keybindings here:
https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/keymap.toml

Limitations:
- the bleeding edge yazi version from yesterday is required
  (sxyazi/yazi@dac72eb)
- yazi can only display up to 9 paths, so only the first 9 files will be
  opened in tabs
  • Loading branch information
mikavilpas authored Aug 11, 2024
1 parent 46bac67 commit c57a4ea
Show file tree
Hide file tree
Showing 21 changed files with 267 additions and 88 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ jobs:
# yazi-fm is the `yazi` executable
crate: yazi-fm
git: https://github.com/sxyazi/yazi
# feat: new extract builtin plugin for archive extracting (#1321)
# https://github.com/sxyazi/yazi/commit/bce0fc1175c855ecb2dbee6f8575857b1e9616d4
commit: bce0fc1175c855ecb2dbee6f8575857b1e9616d4
# feat: start with multiple tabs with different paths (#1443)
# https://github.com/sxyazi/yazi/commit/dac72eb39a846d15db7f1a7c54e9261675a90e53
commit: dac72eb39a846d15db7f1a7c54e9261675a90e53

- name: Compile and install yazi from source
uses: baptiste0928/[email protected]
with:
# yazi-cli is the `ya` command line interface
crate: yazi-cli
git: https://github.com/sxyazi/yazi
# feat: new extract builtin plugin for archive extracting (#1321)
# https://github.com/sxyazi/yazi/commit/bce0fc1175c855ecb2dbee6f8575857b1e9616d4
commit: bce0fc1175c855ecb2dbee6f8575857b1e9616d4
# feat: start with multiple tabs with different paths (#1443)
# https://github.com/sxyazi/yazi/commit/dac72eb39a846d15db7f1a7c54e9261675a90e53
commit: dac72eb39a846d15db7f1a7c54e9261675a90e53

- name: Run tests
uses: nvim-neorocks/[email protected]
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ You can optionally configure yazi.nvim by setting any of the options below.
-- an upcoming optional feature
use_yazi_client_id_flag = false,

-- open visible splits as yazi tabs for easy navigation. Requires a yazi
-- version more recent than 2024-08-10
open_multiple_tabs = false,

-- NOTE: these only work if `use_ya_for_events_reading` is enabled, etc.
highlight_groups = {
-- See https://github.com/mikavilpas/yazi.nvim/pull/180
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/cypress/e2e/healthcheck.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ describe("the healthcheck", () => {
)

// the `yazi` and `ya` applications should be found successfully
cy.contains("Found yazi version Yazi 0.2.5")
cy.contains("Found ya version Ya 0.2.5")
cy.contains(new RegExp("Found yazi version Yazi \\d+?.\\d+?.\\d+?"))
cy.contains(new RegExp("Found ya version Ya \\d+?.\\d+?.\\d+?"))
cy.contains("OK yazi")
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { startNeovimWithYa } from "./startNeovimWithYa"
import { isHovered, isNotHovered } from "./utils/hover-utils"
import { isHoveredInNeovim, isNotHoveredInNeovim } from "./utils/hover-utils"

// 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
Expand Down Expand Up @@ -38,9 +38,9 @@ describe("'cd' to another buffer's directory", () => {

// 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)
isNotHoveredInNeovim(view.leftFile.text)
isNotHoveredInNeovim(view.centerFile.text)
isNotHoveredInNeovim(view.rightFile.text)

// start yazi
cy.typeIntoTerminal("{upArrow}")
Expand All @@ -50,25 +50,25 @@ describe("'cd' to another buffer's directory", () => {
//
// 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)
isNotHoveredInNeovim(view.leftFile.text)
isHoveredInNeovim(view.centerFile.text)
isNotHoveredInNeovim(view.rightFile.text)

cy.typeIntoTerminal("{control+i}")
isHovered(view.leftFile.text)
isNotHovered(view.centerFile.text)
isNotHovered(view.rightFile.text)
isHoveredInNeovim(view.leftFile.text)
isNotHoveredInNeovim(view.centerFile.text)
isNotHoveredInNeovim(view.rightFile.text)

cy.typeIntoTerminal("{control+i}")
isNotHovered(view.leftFile.text)
isNotHovered(view.centerFile.text)
isHovered(view.rightFile.text)
isNotHoveredInNeovim(view.leftFile.text)
isNotHoveredInNeovim(view.centerFile.text)
isHoveredInNeovim(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)
isNotHoveredInNeovim(view.leftFile.text)
isHoveredInNeovim(view.centerFile.text)
isNotHoveredInNeovim(view.rightFile.text)
})
})

Expand All @@ -91,29 +91,29 @@ describe("'cd' to another buffer's directory", () => {
"modify_yazi_config_and_add_hovered_buffer_background.lua",
],
}).then(() => {
isNotHovered(view.leftAndCenterFile.text)
isNotHovered(view.rightFile.text)
isNotHoveredInNeovim(view.leftAndCenterFile.text)
isNotHoveredInNeovim(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)
isNotHoveredInNeovim(view.leftAndCenterFile.text)
isHoveredInNeovim(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)
isHoveredInNeovim(view.leftAndCenterFile.text)
isNotHoveredInNeovim(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)
isNotHoveredInNeovim(view.leftAndCenterFile.text)
isHoveredInNeovim(view.rightFile.text)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import tinycolor2 from "tinycolor2"
import { startNeovimWithYa } from "./startNeovimWithYa"
import {
darkBackgroundColors,
isHovered,
isNotHovered,
isHoveredInNeovim,
isNotHoveredInNeovim,
lightBackgroundColors,
} from "./utils/hover-utils"

Expand Down Expand Up @@ -37,7 +37,7 @@ describe("highlighting the buffer with 'hover' events", () => {
],
}).then((dir) => {
// wait until text on the start screen is visible
isNotHovered("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")

// start yazi
cy.typeIntoTerminal("{upArrow}")
Expand All @@ -53,12 +53,12 @@ describe("highlighting the buffer with 'hover' events", () => {
// 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
isHovered("If you see this text, Neovim is ready!")
isHoveredInNeovim("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")
isNotHovered("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")
})
})

Expand All @@ -69,7 +69,7 @@ describe("highlighting the buffer with 'hover' events", () => {
],
}).then((dir) => {
// wait until text on the start screen is visible
isNotHovered("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")

// start yazi
cy.typeIntoTerminal("{upArrow}")
Expand All @@ -84,12 +84,12 @@ describe("highlighting the buffer with 'hover' events", () => {
// 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
isHovered("If you see this text, Neovim is ready!")
isHoveredInNeovim("If you see this text, Neovim is ready!")

// hover another file - the highlight should be removed
cy.typeIntoTerminal(`/^${dir.contents["test-setup.lua"].name}{enter}`)

isNotHovered("If you see this text, Neovim is ready!")
isNotHoveredInNeovim("If you see this text, Neovim is ready!")
})
})

Expand All @@ -100,7 +100,7 @@ describe("highlighting the buffer with 'hover' events", () => {
],
}).then((dir) => {
// wait until text on the start screen is visible
isNotHovered("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")

const testFile = dir.contents["test-setup.lua"].name
// open an adjacent file and wait for it to be displayed
Expand All @@ -118,14 +118,14 @@ describe("highlighting the buffer with 'hover' events", () => {
// yazi should be visible now
cy.contains(dir.contents["test-setup.lua"].name)
hoverAnotherFileToEnsureHoverEventIsReceivedInCI(testFile)
isHovered("how to initialize the test environment")
isHoveredInNeovim("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,
})
isNotHovered("how to initialize the test environment")
isHovered("If you see this text, Neovim is ready!")
isNotHoveredInNeovim("how to initialize the test environment")
isHoveredInNeovim("If you see this text, Neovim is ready!")
})
})

Expand All @@ -137,7 +137,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
isNotHovered("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")

// start yazi
cy.typeIntoTerminal("{upArrow}")
Expand Down Expand Up @@ -240,8 +240,8 @@ describe("highlighting the buffer with 'hover' events", () => {
},
}).then((dir) => {
// wait until text on the start screen is visible
isNotHovered("f you see this text, Neovim is ready!")
isNotHovered("Hello")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("Hello")

// start yazi
cy.typeIntoTerminal("{upArrow}")
Expand All @@ -252,16 +252,16 @@ describe("highlighting the buffer with 'hover' events", () => {
dir.contents["test-setup.lua"].name,
)

isHovered(
isHoveredInNeovim(
"f you see this text, Neovim is ready!",
darkBackgroundColors.hoveredInSameDirectory,
)
isHovered("Hello", darkBackgroundColors.hoveredInSameDirectory)
isHoveredInNeovim("Hello", darkBackgroundColors.hoveredInSameDirectory)

// highlights are cleared when yazi is closed
cy.typeIntoTerminal("q")
isNotHovered("f you see this text, Neovim is ready!")
isNotHovered("Hello")
isNotHoveredInNeovim("f you see this text, Neovim is ready!")
isNotHoveredInNeovim("Hello")
})
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { IntegrationTestFile } from "server/application/neovim/environment/testEnvironmentTypes"
import { startNeovimWithYa } from "./startNeovimWithYa"
import {
isFileNotSelectedInYazi,
isFileSelectedInYazi,
} from "./utils/yazi-utils"

describe("opening files", () => {
beforeEach(() => {
Expand Down Expand Up @@ -295,7 +299,7 @@ describe("opening files", () => {

it("can open multiple files in a directory whose name contains a space character", () => {
startNeovimWithYa({ filename: "dir with spaces/file1.txt" }).then((dir) => {
cy.contains("this is file1.txt")
cy.contains("this is the first file")

cy.typeIntoTerminal("{upArrow}")
cy.contains(dir.contents["dir with spaces/file2.txt"].name)
Expand All @@ -311,4 +315,42 @@ describe("opening files", () => {
cy.contains("dir with spaces/file2.txt" satisfies IntegrationTestFile)
})
})

it("can open multiple open files in yazi tabs", () => {
startNeovimWithYa({
filename: {
openInVerticalSplits: [
"file.txt",
"test-setup.lua",
"dir with spaces/file1.txt",
],
},
startupScriptModifications: [
"modify_yazi_config_and_open_multiple_files.lua",
],
}).then((dir) => {
cy.contains("Hello")

// now that multiple files are open, and the configuration has been set
// to open multiple files in yazi tabs, opening yazi should show the
// tabs
cy.typeIntoTerminal("{upArrow}")

// this is the first yazi tab (1)
isFileSelectedInYazi(dir.contents["file.txt"].name)
isFileNotSelectedInYazi(dir.contents["test-setup.lua"].name)

// next, move to the second tab (2)
cy.typeIntoTerminal("2")
isFileSelectedInYazi(dir.contents["test-setup.lua"].name)
isFileNotSelectedInYazi(dir.contents["file.txt"].name)

// next, move to the third tab (3). This tab should be in a different
// directory, so other adjacent files should be visible than before
cy.typeIntoTerminal("3")
cy.contains(dir.contents["dir with spaces/file1.txt"].name)
isFileSelectedInYazi(dir.contents["dir with spaces/file1.txt"].name)
isFileNotSelectedInYazi(dir.contents["dir with spaces/file2.txt"].name)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { flavors } from "@catppuccin/palette"
const darkTheme = flavors.macchiato.colors
const lightTheme = flavors.latte.colors

function rgbify(color: (typeof darkTheme)["surface0"]["rgb"]) {
export function rgbify(color: (typeof darkTheme)["surface0"]["rgb"]): string {
return `rgb(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()})`
}

Expand All @@ -21,7 +21,7 @@ export const lightBackgroundColors = {
}

// only works for the dark colorscheme for now
export function isHovered(text: string, color?: string): void {
export function isHoveredInNeovim(text: string, color?: string): void {
cy.contains(text).should(
"have.css",
"background-color",
Expand All @@ -30,7 +30,7 @@ export function isHovered(text: string, color?: string): void {
}

// only works for the dark colorscheme for now
export function isNotHovered(text: string): void {
export function isNotHoveredInNeovim(text: string): void {
cy.contains(text).should(
"have.css",
"background-color",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { flavors } from "@catppuccin/palette"
import { rgbify } from "./hover-utils"

const darkTheme = flavors.macchiato.colors

export function isFileSelectedInYazi(text: string): void {
cy.contains(text).should(
"have.css",
"background-color",
rgbify(darkTheme.text.rgb),
)
}

export function isFileNotSelectedInYazi(text: string): void {
cy.contains(text).should(
"have.css",
"background-color",
rgbify(darkTheme.base.rgb),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const startupScriptModification = z.enum([
"disable_a_keybinding.lua",
"notify_hover_events.lua",
"modify_yazi_config_and_highlight_buffers_in_same_directory.lua",
"modify_yazi_config_and_open_multiple_files.lua",
])
export type StartupScriptModification = z.infer<
typeof startupScriptModification
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---@module "yazi"

require("yazi").setup(
---@type YaziConfig
{
open_multiple_tabs = true,
}
)
Loading

0 comments on commit c57a4ea

Please sign in to comment.