Compare commits
1 Commits
7f85848620
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| 12945a4cdf |
@@ -1,8 +1,7 @@
|
|||||||
local Buffer = require 'u.buffer'
|
local Renderer = require('u.renderer').Renderer
|
||||||
local TreeBuilder = require('u.renderer').TreeBuilder
|
local TreeBuilder = require('u.renderer').TreeBuilder
|
||||||
local tracker = require 'u.tracker'
|
local tracker = require 'u.tracker'
|
||||||
local utils = require 'u.utils'
|
local utils = require 'u.utils'
|
||||||
local Window = require 'my.window'
|
|
||||||
|
|
||||||
local TIMEOUT = 4000
|
local TIMEOUT = 4000
|
||||||
local ICONS = {
|
local ICONS = {
|
||||||
@@ -14,15 +13,25 @@ local ICONS = {
|
|||||||
}
|
}
|
||||||
local DEFAULT_ICON = { text = '', group = 'DiagnosticSignOk' }
|
local DEFAULT_ICON = { text = '', group = 'DiagnosticSignOk' }
|
||||||
|
|
||||||
--- @alias Notification {
|
local S_EDITOR_DIMENSIONS =
|
||||||
|
tracker.create_signal(utils.get_editor_dimensions(), 's:editor_dimensions')
|
||||||
|
vim.api.nvim_create_autocmd('VimResized', {
|
||||||
|
callback = function()
|
||||||
|
local new_dim = utils.get_editor_dimensions()
|
||||||
|
S_EDITOR_DIMENSIONS:set(new_dim)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- @alias u.example.Notification {
|
||||||
--- kind: number;
|
--- kind: number;
|
||||||
--- id: number;
|
--- id: number;
|
||||||
--- text: string;
|
--- text: string;
|
||||||
|
--- timer: uv.uv_timer_t;
|
||||||
--- }
|
--- }
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
--- @type Window | nil
|
--- @type { win: integer, buf: integer, renderer: u.Renderer } | nil
|
||||||
local notifs_w
|
local notifs_w
|
||||||
|
|
||||||
local s_notifications_raw = tracker.create_signal {}
|
local s_notifications_raw = tracker.create_signal {}
|
||||||
@@ -30,44 +39,49 @@ local s_notifications = s_notifications_raw:debounce(50)
|
|||||||
|
|
||||||
-- Render effect:
|
-- Render effect:
|
||||||
tracker.create_effect(function()
|
tracker.create_effect(function()
|
||||||
--- @type Notification[]
|
--- @type u.example.Notification[]
|
||||||
local notifs = s_notifications:get()
|
local notifs = s_notifications:get()
|
||||||
|
--- @type { width: integer, height: integer }
|
||||||
|
local editor_size = S_EDITOR_DIMENSIONS:get()
|
||||||
|
|
||||||
if #notifs == 0 then
|
if #notifs == 0 then
|
||||||
if notifs_w then
|
if notifs_w then
|
||||||
notifs_w:close(true)
|
if vim.api.nvim_win_is_valid(notifs_w.win) then vim.api.nvim_win_close(notifs_w.win, true) end
|
||||||
notifs_w = nil
|
notifs_w = nil
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local avail_width = editor_size.width
|
||||||
|
local float_width = 40
|
||||||
|
local float_height = math.min(#notifs, editor_size.height - 3)
|
||||||
|
local win_config = {
|
||||||
|
relative = 'editor',
|
||||||
|
anchor = 'NE',
|
||||||
|
row = 0,
|
||||||
|
col = avail_width,
|
||||||
|
width = float_width,
|
||||||
|
height = float_height,
|
||||||
|
border = 'single',
|
||||||
|
focusable = false,
|
||||||
|
zindex = 900,
|
||||||
|
}
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
local editor_size = utils.get_editor_dimensions()
|
|
||||||
local avail_width = editor_size.width
|
|
||||||
local float_width = 40
|
|
||||||
local win_config = {
|
|
||||||
relative = 'editor',
|
|
||||||
anchor = 'NE',
|
|
||||||
row = 0,
|
|
||||||
col = avail_width,
|
|
||||||
width = float_width,
|
|
||||||
height = math.min(#notifs, editor_size.height - 3),
|
|
||||||
border = 'single',
|
|
||||||
focusable = false,
|
|
||||||
}
|
|
||||||
if not notifs_w or not vim.api.nvim_win_is_valid(notifs_w.win) then
|
if not notifs_w or not vim.api.nvim_win_is_valid(notifs_w.win) then
|
||||||
notifs_w = Window.new(Buffer.create(false, true), win_config)
|
local b = vim.api.nvim_create_buf(false, true)
|
||||||
vim.wo[notifs_w.win].cursorline = false
|
local w = vim.api.nvim_open_win(b, false, win_config)
|
||||||
vim.wo[notifs_w.win].list = false
|
vim.wo[w].cursorline = false
|
||||||
vim.wo[notifs_w.win].listchars = ''
|
vim.wo[w].list = false
|
||||||
vim.wo[notifs_w.win].number = false
|
vim.wo[w].listchars = ''
|
||||||
vim.wo[notifs_w.win].relativenumber = false
|
vim.wo[w].number = false
|
||||||
vim.wo[notifs_w.win].wrap = false
|
vim.wo[w].relativenumber = false
|
||||||
|
vim.wo[w].wrap = false
|
||||||
|
notifs_w = { win = w, buf = b, renderer = Renderer.new(b) }
|
||||||
else
|
else
|
||||||
notifs_w:set_config(win_config)
|
vim.api.nvim_win_set_config(notifs_w.win, win_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
notifs_w:render(TreeBuilder.new()
|
notifs_w.renderer:render(TreeBuilder.new()
|
||||||
:nest(function(tb)
|
:nest(function(tb)
|
||||||
for idx, notif in ipairs(notifs) do
|
for idx, notif in ipairs(notifs) do
|
||||||
if idx > 1 then tb:put '\n' end
|
if idx > 1 then tb:put '\n' end
|
||||||
@@ -79,48 +93,81 @@ tracker.create_effect(function()
|
|||||||
end)
|
end)
|
||||||
:tree())
|
:tree())
|
||||||
vim.api.nvim_win_call(notifs_w.win, function()
|
vim.api.nvim_win_call(notifs_w.win, function()
|
||||||
-- scroll to bottom:
|
vim.fn.winrestview {
|
||||||
vim.cmd.normal 'G'
|
-- scroll all the way left:
|
||||||
-- scroll all the way to the left:
|
leftcol = 0,
|
||||||
vim.cmd.normal '9999zh'
|
-- set the bottom line to be at the bottom of the window:
|
||||||
|
topline = vim.api.nvim_buf_line_count(notifs_w.buf) - win_config.height + 1,
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
--- @param id number
|
||||||
|
local function _delete_notif(id)
|
||||||
|
--- @param notifs u.example.Notification[]
|
||||||
|
s_notifications_raw:schedule_update(function(notifs)
|
||||||
|
for i, notif in ipairs(notifs) do
|
||||||
|
if notif.id == id then
|
||||||
|
notif.timer:stop()
|
||||||
|
notif.timer:close()
|
||||||
|
table.remove(notifs, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return notifs
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local _orig_notify
|
local _orig_notify
|
||||||
|
|
||||||
--- @param msg string
|
--- @param msg string
|
||||||
--- @param level integer|nil
|
--- @param level integer|nil
|
||||||
--- @param opts table|nil
|
--- @param opts? { id: number }
|
||||||
local function my_notify(msg, level, opts)
|
function M.notify(msg, level, opts)
|
||||||
vim.schedule(function() _orig_notify(msg, level, opts) end)
|
|
||||||
if level == nil then level = vim.log.levels.INFO end
|
if level == nil then level = vim.log.levels.INFO end
|
||||||
if level < vim.log.levels.INFO then return end
|
|
||||||
|
|
||||||
local id = math.random(math.huge)
|
opts = opts or {}
|
||||||
|
local id = opts.id or math.random(999999999)
|
||||||
|
|
||||||
--- @param notifs Notification[]
|
--- @type u.example.Notification?
|
||||||
s_notifications_raw:schedule_update(function(notifs)
|
local notif = vim.iter(s_notifications_raw:get()):find(function(n) return n.id == id end)
|
||||||
table.insert(notifs, { kind = level, id = id, text = msg })
|
if not notif then
|
||||||
return notifs
|
-- Create a new notification (maybe):
|
||||||
end)
|
if vim.trim(msg) == '' then return id end
|
||||||
|
if level < vim.log.levels.INFO then return id end
|
||||||
|
|
||||||
vim.defer_fn(function()
|
local timer = assert((vim.uv or vim.loop).new_timer(), 'could not create timer')
|
||||||
--- @param notifs Notification[]
|
timer:start(TIMEOUT, 0, function() _delete_notif(id) end)
|
||||||
|
notif = {
|
||||||
|
id = id,
|
||||||
|
kind = level,
|
||||||
|
text = msg,
|
||||||
|
timer = timer,
|
||||||
|
}
|
||||||
|
--- @param notifs u.example.Notification[]
|
||||||
s_notifications_raw:schedule_update(function(notifs)
|
s_notifications_raw:schedule_update(function(notifs)
|
||||||
for i, notif in ipairs(notifs) do
|
table.insert(notifs, notif)
|
||||||
if notif.id == id then
|
|
||||||
table.remove(notifs, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return notifs
|
return notifs
|
||||||
end)
|
end)
|
||||||
end, TIMEOUT)
|
else
|
||||||
|
-- Update an existing notification:
|
||||||
|
s_notifications_raw:schedule_update(function(notifs)
|
||||||
|
-- We already have a copy-by-reference of the notif we want to modify:
|
||||||
|
notif.timer:stop()
|
||||||
|
notif.text = msg
|
||||||
|
notif.kind = level
|
||||||
|
notif.timer:start(TIMEOUT, 0, function() _delete_notif(id) end)
|
||||||
|
|
||||||
|
return notifs
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
local _once_msgs = {}
|
local _once_msgs = {}
|
||||||
local function my_notify_once(msg, level, opts)
|
function M.notify_once(msg, level, opts)
|
||||||
if vim.tbl_contains(_once_msgs, msg) then return false end
|
if vim.tbl_contains(_once_msgs, msg) then return false end
|
||||||
table.insert(_once_msgs, msg)
|
table.insert(_once_msgs, msg)
|
||||||
vim.notify(msg, level, opts)
|
vim.notify(msg, level, opts)
|
||||||
@@ -130,8 +177,8 @@ end
|
|||||||
function M.setup()
|
function M.setup()
|
||||||
if _orig_notify == nil then _orig_notify = vim.notify end
|
if _orig_notify == nil then _orig_notify = vim.notify end
|
||||||
|
|
||||||
vim.notify = my_notify
|
vim.notify = M.notify
|
||||||
vim.notify_once = my_notify_once
|
vim.notify_once = M.notify_once
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
local Pos = require 'u.pos'
|
local Pos = require 'u.pos'
|
||||||
|
|
||||||
local ESC = vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
|
local ESC = vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
|
||||||
local NS = vim.api.nvim_create_namespace 'u.range'
|
|
||||||
|
|
||||||
---@class u.ExtmarkRange
|
|
||||||
---@field bufnr number
|
|
||||||
---@field id number
|
|
||||||
local ExtmarkRange = {}
|
|
||||||
ExtmarkRange.__index = ExtmarkRange
|
|
||||||
|
|
||||||
--- @class u.Range
|
--- @class u.Range
|
||||||
--- @field start u.Pos
|
--- @field start u.Pos
|
||||||
@@ -90,30 +83,6 @@ function Range.from_marks(lpos, rpos)
|
|||||||
return Range.new(start, stop, mode)
|
return Range.new(start, stop, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param bufnr number
|
|
||||||
--- @param id number
|
|
||||||
function Range.from_extmark(bufnr, id)
|
|
||||||
local mode = 'v'
|
|
||||||
|
|
||||||
---@type integer, integer, vim.api.keyset.extmark_details | nil
|
|
||||||
local start_row0, start_col0, details =
|
|
||||||
unpack(vim.api.nvim_buf_get_extmark_by_id(bufnr, NS, id, { details = true }))
|
|
||||||
|
|
||||||
local start = Pos.new(bufnr, start_row0 + 1, start_col0 + 1)
|
|
||||||
local stop = details and Pos.new(bufnr, details.end_row + 1, details.end_col)
|
|
||||||
|
|
||||||
-- Check for invalid extmark range:
|
|
||||||
if stop and stop < start then return Range.new(stop) end
|
|
||||||
|
|
||||||
if stop and stop.col == 0 then
|
|
||||||
mode = 'V'
|
|
||||||
stop = stop:must_next(-1)
|
|
||||||
stop.col = Pos.MAX_COL
|
|
||||||
end
|
|
||||||
|
|
||||||
return Range.new(start, stop, mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param bufnr? number
|
--- @param bufnr? number
|
||||||
function Range.from_buf_text(bufnr)
|
function Range.from_buf_text(bufnr)
|
||||||
if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
|
if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
|
||||||
@@ -303,13 +272,6 @@ function Range:to_linewise()
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function Range:to_charwise()
|
|
||||||
local r = self:clone()
|
|
||||||
r.mode = 'v'
|
|
||||||
if r.stop:is_col_max() then r.stop = r.stop:as_real() end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param x u.Pos | u.Range
|
--- @param x u.Pos | u.Range
|
||||||
function Range:contains(x)
|
function Range:contains(x)
|
||||||
if getmetatable(x) == Pos then
|
if getmetatable(x) == Pos then
|
||||||
@@ -372,21 +334,6 @@ function Range:save_to_marks(left, right)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Range:save_to_extmark()
|
|
||||||
local r = self:to_charwise()
|
|
||||||
local end_row = r.stop.lnum - 1
|
|
||||||
local end_col = r.stop.col
|
|
||||||
if self.mode == 'V' then
|
|
||||||
end_row = end_row + 1
|
|
||||||
end_col = 0
|
|
||||||
end
|
|
||||||
local id = vim.api.nvim_buf_set_extmark(r.start.bufnr, NS, r.start.lnum - 1, r.start.col - 1, {
|
|
||||||
end_row = end_row,
|
|
||||||
end_col = end_col,
|
|
||||||
})
|
|
||||||
return ExtmarkRange.new(r.start.bufnr, id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Range:set_visual_selection()
|
function Range:set_visual_selection()
|
||||||
if self:is_empty() then return end
|
if self:is_empty() then return end
|
||||||
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
|
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
|
||||||
@@ -649,10 +596,4 @@ function Range:highlight(group, opts)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function ExtmarkRange.new(bufnr, id) return setmetatable({ bufnr = bufnr, id = id }, ExtmarkRange) end
|
|
||||||
|
|
||||||
function ExtmarkRange:range() return Range.from_extmark(self.bufnr, self.id) end
|
|
||||||
|
|
||||||
function ExtmarkRange:delete() vim.api.nvim_buf_del_extmark(self.bufnr, NS, self.id) end
|
|
||||||
|
|
||||||
return Range
|
return Range
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ local M = {}
|
|||||||
--- @alias QfItem { col: number, filename: string, kind: string, lnum: number, text: string }
|
--- @alias QfItem { col: number, filename: string, kind: string, lnum: number, text: string }
|
||||||
--- @alias KeyMaps table<string, fun(): any | string> }
|
--- @alias KeyMaps table<string, fun(): any | string> }
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
--- @alias RawCmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any }
|
|
||||||
-- luacheck: ignore
|
|
||||||
--- @alias CmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any; info: u.Range|nil }
|
--- @alias CmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any; info: u.Range|nil }
|
||||||
|
|
||||||
--- @generic T
|
--- @generic T
|
||||||
@@ -53,17 +51,6 @@ function M.ucmd(name, cmd, opts)
|
|||||||
vim.api.nvim_create_user_command(name, cmd2, opts or {})
|
vim.api.nvim_create_user_command(name, cmd2, opts or {})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param current_args RawCmdArgs
|
|
||||||
function M.create_forward_cmd_args(current_args)
|
|
||||||
local args = { args = current_args.fargs }
|
|
||||||
if current_args.range == 1 then
|
|
||||||
args.range = { current_args.line1 }
|
|
||||||
elseif current_args.range == 2 then
|
|
||||||
args.range = { current_args.line1, current_args.line2 }
|
|
||||||
end
|
|
||||||
return args
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_editor_dimensions() return { width = vim.go.columns, height = vim.go.lines } end
|
function M.get_editor_dimensions() return { width = vim.go.columns, height = vim.go.lines } end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import
|
import
|
||||||
# nixpkgs-unstable (neovim@0.11.2):
|
# nixpkgs-unstable (neovim@0.11.2):
|
||||||
(fetchTarball {
|
(fetchTarball {
|
||||||
url = "https://github.com/nixos/nixpkgs/archive/f72be405a10668b8b00937b452f2145244103ebc.tar.gz";
|
url = "https://github.com/nixos/nixpkgs/archive/e4b09e47ace7d87de083786b404bf232eb6c89d8.tar.gz";
|
||||||
sha256 = "0m1vnvngpxrawsgg306c9sdhbzsiigjgb03yfbdpa2fsb1fs0zm9";
|
sha256 = "1a2qvp2yz8j1jcggl1yvqmdxicbdqq58nv7hihmw3bzg9cjyqm26";
|
||||||
})
|
})
|
||||||
{ },
|
{ },
|
||||||
}:
|
}:
|
||||||
|
|||||||
@@ -617,54 +617,4 @@ describe('Range', function()
|
|||||||
}, vim.api.nvim_buf_get_lines(b, 0, -1, false))
|
}, vim.api.nvim_buf_get_lines(b, 0, -1, false))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can save to extmark', function()
|
|
||||||
withbuf({
|
|
||||||
'The quick brown',
|
|
||||||
'fox',
|
|
||||||
'jumps',
|
|
||||||
'over',
|
|
||||||
'the lazy dog',
|
|
||||||
}, function()
|
|
||||||
-- Construct a range over 'fox jumps'
|
|
||||||
local r = Range.new(Pos.new(nil, 2, 1), Pos.new(nil, 3, 5), 'v')
|
|
||||||
local extrange = r:save_to_extmark()
|
|
||||||
assert.are.same({ 'fox', 'jumps' }, extrange:range():lines())
|
|
||||||
-- change 'jumps' to 'leaps':
|
|
||||||
vim.api.nvim_buf_set_text(extrange.bufnr, 2, 0, 2, 4, { 'leap' })
|
|
||||||
assert.are.same({
|
|
||||||
'The quick brown',
|
|
||||||
'fox',
|
|
||||||
'leaps',
|
|
||||||
'over',
|
|
||||||
'the lazy dog',
|
|
||||||
}, vim.api.nvim_buf_get_lines(extrange.bufnr, 0, -1, false))
|
|
||||||
assert.are.same({ 'fox', 'leaps' }, extrange:range():lines())
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can save linewise extmark', function()
|
|
||||||
withbuf({
|
|
||||||
'The quick brown',
|
|
||||||
'fox',
|
|
||||||
'jumps',
|
|
||||||
'over',
|
|
||||||
'the lazy dog',
|
|
||||||
}, function()
|
|
||||||
-- Construct a range over 'fox jumps'
|
|
||||||
local r = Range.new(Pos.new(nil, 2, 1), Pos.new(nil, 3, Pos.MAX_COL), 'V')
|
|
||||||
local extrange = r:save_to_extmark()
|
|
||||||
assert.are.same({ 'fox', 'jumps' }, extrange:range():lines())
|
|
||||||
|
|
||||||
local extmark = vim.api.nvim_buf_get_extmark_by_id(
|
|
||||||
extrange.bufnr,
|
|
||||||
vim.api.nvim_create_namespace 'u.range',
|
|
||||||
extrange.id,
|
|
||||||
{ details = true }
|
|
||||||
)
|
|
||||||
local row0, col0, details = unpack(extmark)
|
|
||||||
assert.are.same({ 1, 0 }, { row0, col0 })
|
|
||||||
assert.are.same({ 3, 0 }, { details.end_row, details.end_col })
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user