Skip to content

Commit

Permalink
feat: highlight the currently hovered file in yazi (opt-in)
Browse files Browse the repository at this point in the history
For now, this change is opt-in (disabled by default).

This change makes it possible to highlight the currently hovered file
with a style of the user's own choosing.

To enable it, you need to do the following steps in your configuration:
- set `use_ya_for_events_reading = true`
- set these new settings in your configuration:

  ```lua
  ---@type LazySpec
  {
    "mikavilpas/yazi.nvim",
    -- ...other settings you might already have
    ---@type YaziConfig
    opts = {
      -- add these:
      use_ya_for_events_reading = true,
      highlight_groups = {
        hovered_buffer_background = { bg = "#363a4f" },
      },
      -- ...other settings you might already have
    }
  }
  ```

For now, the color needs to be configured manually. In the future we may
have a good default color. If you use catppuccin, you can find all the
colors in the palette here: <https://catppuccin.com/palette>. I used the
`Surface 0` color from my catppuccin macchiato palette.
  • Loading branch information
mikavilpas committed Jul 11, 2024
1 parent 34aaf69 commit 19bd29e
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 22 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ You can optionally configure yazi.nvim by setting any of the options below.
-- https://github.com/mikavilpas/yazi.nvim/pull/152
use_ya_for_events_reading = false,

-- an upcoming optional feature
highlight_groups = {
-- NOTE: this only works if `use_ya_for_events_reading` is enabled, etc.
hovered_buffer_background = nil,
},

-- the floating window scaling factor. 1 means 100%, 0.9 means 90%, etc.
floating_window_scaling_factor = 0.9,

Expand Down
1 change: 1 addition & 0 deletions integration-tests/client/testEnvironmentTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type TestDirectory = {
["initial-file.txt"]: FileEntry
["test.lua"]: FileEntry
["file.txt"]: FileEntry
["modify_yazi_config_to_use_ya_as_event_reader.lua"]: FileEntry
["subdirectory/sub.txt"]: FileEntry
["routes/posts.$postId/route.tsx"]: FileEntry
["routes/posts.$postId/adjacent-file.tsx"]: FileEntry
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ export default defineConfig({
stem: "sub",
extension: ".txt",
},
"modify_yazi_config_to_use_ya_as_event_reader.lua": {
name: "modify_yazi_config_to_use_ya_as_event_reader.lua",
stem: "modify_yazi_config_to_use_ya_as_event_reader",
extension: ".lua",
},
"routes/posts.$postId/adjacent-file.tsx": {
name: "adjacent-file.tsx",
stem: "adjacent-file",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { startNeovimWithYa } from "./startNeovimWithYa"

describe("highlighting the buffer with 'hover' events", () => {
beforeEach(() => {
cy.visit("http://localhost:5173")
})

const backgroundColors = {
normal: "rgb(36, 39, 58)",
hovered: "rgb(73, 77, 100)",
} as const

// 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.

it("can highlight the buffer when hovered", () => {
startNeovimWithYa().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", backgroundColors.normal)

// start yazi
cy.typeIntoTerminal("{upArrow}")

// yazi is shown and adjacent files should be visible now
//
// the current file (initial-file.txt) 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",
backgroundColors.hovered,
)

// 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",
backgroundColors.normal,
)
})
})

it("can remove the highlight when the cursor is moved away", () => {
startNeovimWithYa().then((dir) => {
// wait until text on the start screen is visible
cy.contains("Neovim is ready!").should(
"have.css",
"background-color",
backgroundColors.normal,
)

// start yazi
cy.typeIntoTerminal("{upArrow}")

// yazi is shown and adjacent files should be visible now
cy.contains(dir.contents["test.lua"].name)

// the current file (initial-file.txt) 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",
backgroundColors.hovered,
)

// 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",
backgroundColors.normal,
)
})
})

it("can move the highlight to another buffer when hovering over it", () => {
startNeovimWithYa().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", backgroundColors.normal)

// open an adjacent file and wait for it to be displayed
cy.typeIntoTerminal(
`:vsplit ${dir.rootPath}/${dir.contents["test.lua"].name}{enter}`,
{ delay: 1 },
)
cy.contains("how to initialize the test environment")

// start yazi - the initial file should be highlighted
cy.typeIntoTerminal("{upArrow}")
cy.contains("how to initialize the test environment").should(
"have.css",
"background-color",
backgroundColors.hovered,
)

// 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",
backgroundColors.normal,
)
cy.contains("If you see this text, Neovim is ready!").should(
"have.css",
"background-color",
backgroundColors.hovered,
)
})
})
})
18 changes: 12 additions & 6 deletions integration-tests/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,23 @@ Cypress.Commands.add("startNeovim", (startArguments?: StartNeovimArguments) => {
})
})

Cypress.Commands.add("typeIntoTerminal", (text: string) => {
// the syntax for keys is described here:
// https://docs.cypress.io/api/commands/type
cy.get("#app").type(text)
})
Cypress.Commands.add(
"typeIntoTerminal",
(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)
},
)

declare global {
namespace Cypress {
interface Chainable {
startNeovim(args?: StartNeovimArguments): Chainable<TestDirectory>
typeIntoTerminal(text: string): Chainable<void>
typeIntoTerminal(
text: string,
options?: Partial<Cypress.TypeOptions>,
): Chainable<void>
task(event: "createTempDir"): Chainable<TestDirectory>
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ require('yazi').setup(
---@type YaziConfig
{
use_ya_for_events_reading = true,
highlight_groups = {
hovered_buffer_background = { bg = '#494d64' },
},
}
)
46 changes: 46 additions & 0 deletions lua/yazi/buffer_highlighting/disposable_highlight.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
local Log = require('yazi.log')

---@class yazi.DisposableHighlight
---@field private old_winhighlight string
---@field private window_id integer
local DisposableHighlight = {}
DisposableHighlight.__index = DisposableHighlight

---@param window_id integer
---@param highlight_config YaziConfigHighlightGroups
function DisposableHighlight.new(window_id, highlight_config)
local self = setmetatable({}, DisposableHighlight)
self.window_id = window_id
self.old_winhighlight = vim.wo.winhighlight

vim.api.nvim_set_hl(
0,
'YaziBufferHoveredBackground',
highlight_config.hovered_buffer_background
)

vim.api.nvim_set_option_value(
'winhighlight',
'Normal:YaziBufferHoveredBackground',
{ win = window_id }
)

return self
end

function DisposableHighlight:dispose()
Log:debug(
string.format(
'Disposing of the DisposableHighlight for window_id %s',
self.window_id
)
)
-- Revert winhighlight to its old value
if vim.api.nvim_win_is_valid(self.window_id) then
vim.api.nvim_set_option_value('winhighlight', self.old_winhighlight, {
win = self.window_id,
})
end
end

return DisposableHighlight
65 changes: 65 additions & 0 deletions lua/yazi/buffer_highlighting/highlight_hovered_buffer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
local Log = require('yazi.log')
local utils = require('yazi.utils')
local DisposableHighlight =
require('yazi.buffer_highlighting.disposable_highlight')

local M = {}

-- The currently highlighted windows. Global because there can only be one yazi
-- at a time.
---@type table<WindowId, yazi.DisposableHighlight>
local window_highlights = {}

function M.clear_highlights()
for _, hl in pairs(window_highlights) do
pcall(hl.dispose, hl)
end

window_highlights = {}
end

---@param url string
---@param highlight_config YaziConfigHighlightGroups
function M.highlight_hovered_buffer(url, highlight_config)
local visible_open_buffers = utils.get_visible_open_buffers()
for _, buffer in ipairs(visible_open_buffers) do
if buffer.renameable_buffer:matches_exactly(url) then
Log:debug(
'highlighting buffer '
.. buffer.renameable_buffer.bufnr
.. ' in window '
.. buffer.window_id
)
local hl = window_highlights[buffer.window_id]
if hl == nil then
hl = DisposableHighlight.new(buffer.window_id, highlight_config)
window_highlights[buffer.window_id] = hl
end
else
-- only one file can be hovered at a time, so let's clear
-- the highlight
local hl = window_highlights[buffer.window_id]
if hl ~= nil then
local success = pcall(hl.dispose, hl)
window_highlights[buffer.window_id] = nil
if success then
Log:debug(
'disposed of highlight for buffer '
.. buffer.renameable_buffer.bufnr
.. ' in window '
.. buffer.renameable_buffer.bufnr
)
else
Log:debug(
'failed to dispose of highlight for buffer '
.. buffer.renameable_buffer.bufnr
.. ' in window '
.. buffer.window_id
)
end
end
end
end
end

return M
4 changes: 4 additions & 0 deletions lua/yazi/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ function M.default()
yazi_opened_multiple_files = openers.send_files_to_quickfix_list,
},

highlight_groups = {
hovered_buffer_background = nil,
},

integrations = {
grep_in_directory = function(directory)
require('telescope.builtin').live_grep({
Expand Down
Loading

0 comments on commit 19bd29e

Please sign in to comment.