Skip to content

Commit

Permalink
feat: treesitter highlights (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcarriga committed Apr 27, 2022
1 parent 2c8f744 commit 6f298f2
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 52 deletions.
27 changes: 27 additions & 0 deletions README.md
Expand Up @@ -6,6 +6,16 @@ A fancy, configurable, notification manager for NeoVim

Credit to [sunjon](https://github.com/sunjon) for [the design](https://neovim.discourse.group/t/wip-animated-notifications-plugin/448) that inspired the appearance of this plugin.

* [Usage](#usage)
- [Viewing History](#viewing-history)
* [Configuration](#configuration)
- [Setup](#setup)
- [Highlights](#highlights)
- [Render Style](#render-style)
- [Animation Style](#animation-style)
+ [Opening the window](#opening-the-window)
+ [Changing the window](#changing-the-window)

## Usage

Simply call the module with a message!
Expand All @@ -31,6 +41,10 @@ Updating an existing notification is also possible!
![](https://user-images.githubusercontent.com/24252670/152641078-92f3da72-f49f-4705-aec8-86512693445f.gif)


Use treesitter highlighting inside notifications with opacity changing

![](https://user-images.githubusercontent.com/24252670/165042795-565878a3-9c6d-4c0b-ab0d-6858515835c5.gif)

There are a number of custom options that can be supplied in a table as the third argument.
See `:h NotifyOptions` for details.

Expand Down Expand Up @@ -71,6 +85,19 @@ async.run(function()
end)
```

Set a custom filetype to take advantage of treesitter highlighting:

```lua
vim.notify(text, "info", {
title = "My Awesome Plugin",
on_open = function(win)
local buf = vim.api.nvim_win_get_buf(win)
vim.api.nvim_buf_set_option(buf, "filetype", "markdown")
end,
})
```


Check out the wiki for more examples!

### Viewing History
Expand Down
2 changes: 1 addition & 1 deletion lua/notify/config/init.lua
Expand Up @@ -102,7 +102,7 @@ function M.level()
end

function M.background_colour()
return user_config.background_colour()
return tonumber(user_config.background_colour():gsub("#", "0x"), 16)
end

function M.icons()
Expand Down
1 change: 1 addition & 0 deletions lua/notify/init.lua
Expand Up @@ -199,6 +199,7 @@ end
---Dismiss all notification windows currently displayed
---@param opts table
---@field pending boolean: Clear pending notifications
---@field silent boolean: Suppress notification that pending notifications were dismissed.
function notify.dismiss(opts)
if service then
service:dismiss(opts or {})
Expand Down
1 change: 1 addition & 0 deletions lua/notify/render/default.lua
Expand Up @@ -50,5 +50,6 @@ return function(bufnr, notif, highlights)
hl_group = highlights.body,
end_line = 1 + #notif.message,
end_col = #notif.message[#notif.message],
priority = 50, -- Allow treesitter to override
})
end
1 change: 1 addition & 0 deletions lua/notify/render/minimal.lua
Expand Up @@ -9,5 +9,6 @@ return function(bufnr, notif, highlights)
hl_group = highlights.icon,
end_line = #notif.message - 1,
end_col = #notif.message[#notif.message],
priority = 50,
})
end
143 changes: 123 additions & 20 deletions lua/notify/service/buffer/highlights.lua
@@ -1,4 +1,5 @@
local config = require("notify.config")

local util = require("notify.util")

---@class NotifyBufHighlights
Expand All @@ -8,13 +9,25 @@ local util = require("notify.util")
---@field border string
---@field icon string
---@field body string
---@field buffer number
local NotifyBufHighlights = {}

local function group_fields(group)
return {
guifg = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID(group)), "fg#"),
guibg = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID(group)), "bg#"),
local function manual_get_hl(name)
local synID = vim.fn.synIDtrans(vim.fn.hlID(name))
local result = {
foreground = tonumber(vim.fn.synIDattr(synID, "fg"):gsub("#", ""), 16),
background = tonumber(vim.fn.synIDattr(synID, "bg"):gsub("#", ""), 16),
}
return result
end

local function get_hl(name)
local definition = vim.api.nvim_get_hl_by_name(name, true)
if definition[true] then
-- https://github.com/neovim/neovim/issues/18024
return manual_get_hl(name)
end
return definition
end

function NotifyBufHighlights:new(level, buffer)
Expand All @@ -24,42 +37,132 @@ function NotifyBufHighlights:new(level, buffer)
orig = "NotifyINFO" .. section
end
local new = orig .. buffer
vim.cmd("silent! hi! link " .. new .. " " .. orig)
return new
end
local title = linked_group("Title")
local border = linked_group("Border")
local body = linked_group("Body")
local icon = linked_group("Icon")

local groups = {}
for _, group in pairs({ title, border, body, icon }) do
groups[group] = group_fields(group)

vim.api.nvim_set_hl(0, new, { link = orig })

return new, get_hl(new)
end

local title, title_def = linked_group("Title")
local border, border_def = linked_group("Border")
local body, body_def = linked_group("Body")
local icon, icon_def = linked_group("Icon")

local groups = {
[title] = title_def,
[border] = border_def,
[body] = body_def,
[icon] = icon_def,
}
local buf_highlights = {
groups = groups,
opacity = 100,
border = border,
body = body,
title = title,
icon = icon,
buffer = buffer,
}
self.__index = self
setmetatable(buf_highlights, self)
return buf_highlights
end

function NotifyBufHighlights:_redefine_treesitter()
local buf_highlighter = require("vim.treesitter.highlighter").active[self.buffer]

if not buf_highlighter then
return
end
local render_namespace = vim.api.nvim_create_namespace("notify-treesitter-override")
vim.api.nvim_buf_clear_namespace(self.buffer, render_namespace, 0, -1)

local function link(orig)
local new = orig .. self.buffer
if self.groups[new] then
return new
end
vim.api.nvim_set_hl(0, new, { link = orig })
self.groups[new] = get_hl(new)
return new
end

local matches = {}

local i = 0
buf_highlighter.tree:for_each_tree(function(tstree, tree)
if not tstree then
return
end

local root = tstree:root()

local query = buf_highlighter:get_query(tree:lang())

-- Some injected languages may not have highlight queries.
if not query:query() then
return
end

local iter = query:query():iter_captures(root, buf_highlighter.bufnr)

for capture, node, metadata in iter do
-- Wait until we get at least a single capture as we don't know when parsing is complete.
self._treesitter_redefined = true
local hl = query.hl_cache[capture]

if hl then
i = i + 1
local c = query._query.captures[capture] -- name of the capture in the query
if c ~= nil then
local general_hl, is_vim_hl = query:_get_hl_from_capture(capture)
local capture_hl = is_vim_hl and general_hl or (tree:lang() .. general_hl)
local start_row, start_col, end_row, end_col = node:range()
local custom_hl = link(capture_hl)

vim.api.nvim_buf_set_extmark(self.buffer, render_namespace, start_row, start_col, {
end_row = end_row,
end_col = end_col,
hl_group = custom_hl,
-- TODO: Not sure how neovim's highlighter doesn't have issues with overriding highlights
-- Three marks on same region always show the second for some reason AFAICT
priority = metadata.priority or i + 200,
conceal = metadata.conceal,
})
end
end
end
end, true)
return matches
end

function NotifyBufHighlights:set_opacity(alpha)
if
not self._treesitter_redefined
and vim.api.nvim_buf_get_option(self.buffer, "filetype") ~= "notify"
then
self:_redefine_treesitter()
end
self.opacity = alpha
local background = config.background_colour()
for group, fields in pairs(self.groups) do
local updated_fields = {}
for name, value in pairs(fields) do
if value ~= "" and value ~= "none" then
updated_fields[name] = util.blend(value, background, alpha / 100)
end
vim.api.nvim_set_hl(0, group, updated_fields)
local hl_string = ""
if fields.foreground then
hl_string = "guifg=#"
.. string.format("%06x", util.blend(fields.foreground, background, alpha / 100))
end
if fields.background then
hl_string = hl_string
.. " guibg=#"
.. string.format("%06x", util.blend(fields.background, background, alpha / 100))
end

if hl_string ~= "" then
-- Can't use nvim_set_hl https://github.com/neovim/neovim/issues/18160
vim.cmd("hi " .. group .. " " .. hl_string)
end
util.highlight(group, updated_fields)
end
end

Expand Down
9 changes: 1 addition & 8 deletions lua/notify/service/init.lua
Expand Up @@ -78,14 +78,7 @@ function NotificationService:replace(id, notif)
end

function NotificationService:dismiss(opts)
local bufs = vim.api.nvim_list_bufs()
local notif_wins = {}
for _, buf in pairs(bufs) do
local win = vim.fn.bufwinid(buf)
if win ~= -1 and vim.api.nvim_buf_get_option(buf, "filetype") == "notify" then
notif_wins[#notif_wins + 1] = win
end
end
local notif_wins = vim.tbl_keys(self._animator.win_stages)
for _, win in pairs(notif_wins) do
pcall(vim.api.nvim_win_close, win, true)
end
Expand Down
34 changes: 11 additions & 23 deletions lua/notify/util/init.lua
@@ -1,5 +1,7 @@
local M = {}

local min, max, floor = math.min, math.max, math.floor
local rshift, lshift, band, bor = bit.rshift, bit.lshift, bit.band, bit.bor
function M.is_callable(obj)
return type(obj) == "function" or (type(obj) == "table" and obj.__call)
end
Expand Down Expand Up @@ -27,32 +29,18 @@ function M.pop(tbl, key, default)
return val
end

function M.crop(val, min, max)
return math.min(math.max(min, val), max)
end

function M.zip(first, second)
local new = {}
for i, val in pairs(first) do
new[i] = { val, second[i] }
end
return new
end

local function split_hex_colour(hex)
hex = hex:gsub("#", "")
return { tonumber(hex:sub(1, 2), 16), tonumber(hex:sub(3, 4), 16), tonumber(hex:sub(5, 6), 16) }
end

function M.blend(fg_hex, bg_hex, alpha)
local channels = M.zip(split_hex_colour(fg_hex), split_hex_colour(bg_hex))

local blended = {}
for i, i_chans in pairs(channels) do
blended[i] = M.round(M.crop(alpha * i_chans[1] + (1 - alpha) * i_chans[2], 0, 255))
local segment = 0xFF0000
local result = 0
for i = 2, 0, -1 do
local blended = alpha * rshift(band(fg_hex, segment), i * 8)
+ (1 - alpha) * rshift(band(bg_hex, segment), i * 8)

result = bor(lshift(result, 8), floor((min(max(blended, 0), 255)) + 0.5))
segment = rshift(segment, 8)
end

return string.format("#%02x%02x%02x", unpack(blended))
return result
end

function M.round(num, decimals)
Expand Down
1 change: 1 addition & 0 deletions tests/init.vim
@@ -1,3 +1,4 @@
set rtp+=.
set rtp+=../plenary.nvim
set termguicolors
runtime! plugin/plenary.vim

0 comments on commit 6f298f2

Please sign in to comment.