22
.emmyrc.json
Normal file
22
.emmyrc.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json",
|
||||||
|
"diagnostics": {
|
||||||
|
"disable": [
|
||||||
|
"access-invisible",
|
||||||
|
"redefined-local"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"version": "LuaJIT"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"ignoreDir": [
|
||||||
|
".prefix"
|
||||||
|
],
|
||||||
|
"library": [
|
||||||
|
"$VIMRUNTIME",
|
||||||
|
"library/busted",
|
||||||
|
"library/luv"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
71
.github/workflows/ci.yaml
vendored
71
.github/workflows/ci.yaml
vendored
@@ -1,29 +1,60 @@
|
|||||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||||
name: NeoVim tests
|
name: ci
|
||||||
on: [push]
|
|
||||||
jobs:
|
on:
|
||||||
code-quality:
|
workflow_dispatch:
|
||||||
runs-on: ubuntu-latest
|
pull_request:
|
||||||
|
push:
|
||||||
|
tags: ["*"]
|
||||||
|
branches: ["*"]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
XDG_CONFIG_HOME: ${{ github.workspace }}/.config/
|
MISE_EXPERIMENTAL: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: cachix/install-nix-action@v31
|
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
submodules: true
|
||||||
|
|
||||||
- name: Populate Nix store
|
- name: Install apt dependencies
|
||||||
run:
|
run: |
|
||||||
nix-shell --pure --run 'true'
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libreadline-dev
|
||||||
|
|
||||||
- name: Type-check with lua-language-server
|
- name: Setup environment
|
||||||
run:
|
run: |
|
||||||
nix-shell --pure --run 'make lint'
|
if [ -n "${{secrets.TOKEN}}" ]; then
|
||||||
|
export GITHUB_TOKEN="${{secrets.TOKEN}}"
|
||||||
|
fi
|
||||||
|
export MISE_GITHUB_TOKEN="$GITHUB_TOKEN"
|
||||||
|
echo "$GITHUB_TOKEN" >> $GITHUB_ENV
|
||||||
|
echo "$MISE_GITHUB_TOKEN" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Check formatting with stylua
|
- name: Install mise
|
||||||
run:
|
run: |
|
||||||
nix-shell --pure --run 'make fmt-check'
|
curl https://mise.run | sh
|
||||||
|
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||||
|
echo "$HOME/.local/share/mise/bin" >> $GITHUB_PATH
|
||||||
|
echo "$HOME/.local/share/mise/shims" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Run busted tests
|
- name: Install mise dependencies
|
||||||
run:
|
run: |
|
||||||
nix-shell --pure --run 'make test'
|
mise install
|
||||||
|
mise list --local
|
||||||
|
|
||||||
|
- name: Check Lua formatting
|
||||||
|
run: mise run fmt:check
|
||||||
|
|
||||||
|
- name: Check for type-errors
|
||||||
|
run: mise run lint
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: mise run test:all
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
.prefix/
|
||||||
*.src.rock
|
*.src.rock
|
||||||
*.aider*
|
*.aider*
|
||||||
luacov.*.out
|
luacov.*.out
|
||||||
|
|||||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[submodule "library/busted"]
|
||||||
|
path = library/busted
|
||||||
|
url = https://github.com/LuaCATS/busted
|
||||||
|
branch = main
|
||||||
|
[submodule "library/luv"]
|
||||||
|
path = library/luv
|
||||||
|
url = https://github.com/LuaCATS/luv
|
||||||
|
branch = main
|
||||||
|
[submodule "nvimv"]
|
||||||
|
path = nvimv
|
||||||
|
url = https://github.com/jrop/nvimv
|
||||||
|
branch = main
|
||||||
16
.luarc.json
Normal file
16
.luarc.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/refs/heads/master/setting/schema.json",
|
||||||
|
"runtime": {
|
||||||
|
"version": "LuaJIT"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"ignoreDir": [
|
||||||
|
".prefix"
|
||||||
|
],
|
||||||
|
"library": [
|
||||||
|
"$VIMRUNTIME",
|
||||||
|
"library/busted",
|
||||||
|
"library/luv"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.styluaignore
Normal file
1
.styluaignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
library/
|
||||||
24
Makefile
24
Makefile
@@ -1,24 +0,0 @@
|
|||||||
all: lint fmt-check test
|
|
||||||
|
|
||||||
lint:
|
|
||||||
@echo "## Typechecking"
|
|
||||||
@lua-language-server --check=lua/u/ --checklevel=Error
|
|
||||||
|
|
||||||
fmt-check:
|
|
||||||
@echo "## Checking code format"
|
|
||||||
@stylua --check .
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
@echo "## Formatting code"
|
|
||||||
@stylua .
|
|
||||||
|
|
||||||
test:
|
|
||||||
@rm -f luacov.*.out
|
|
||||||
@echo "## Running tests"
|
|
||||||
@busted --coverage --verbose
|
|
||||||
@echo "## Generating coverage report"
|
|
||||||
@luacov
|
|
||||||
@awk '/^Summary$$/{flag=1;next} flag{print}' luacov.report.out
|
|
||||||
|
|
||||||
watch:
|
|
||||||
@watchexec -c -e lua make
|
|
||||||
@@ -10,8 +10,12 @@
|
|||||||
-- change on the underlying filesystem.
|
-- change on the underlying filesystem.
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- @alias u.examples.FsDir { kind: 'dir'; path: string; expanded: boolean; children: u.examples.FsNode[] }
|
--- @class u.examples.FsDir
|
||||||
--- @alias u.examples.FsFile { kind: 'file'; path: string }
|
--- @field kind 'dir'
|
||||||
|
--- @field path string
|
||||||
|
--- @field expanded boolean
|
||||||
|
--- @field children u.examples.FsNode[]
|
||||||
|
--- @alias u.examples.FsFile table
|
||||||
--- @alias u.examples.FsNode u.examples.FsDir | u.examples.FsFile
|
--- @alias u.examples.FsNode u.examples.FsDir | u.examples.FsFile
|
||||||
--- @alias u.examples.ShowOpts { root_path?: string, width?: number, focus_path?: string }
|
--- @alias u.examples.ShowOpts { root_path?: string, width?: number, focus_path?: string }
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ function H.relative(path, base)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @param root_path string
|
--- @param root_path string
|
||||||
--- @return { tree: u.examples.FsDir; path_to_node: table<string, u.examples.FsNode> }
|
--- @return { tree: u.examples.FsDir, path_to_node: table<string, u.examples.FsNode> }
|
||||||
function H.get_tree_inf(root_path)
|
function H.get_tree_inf(root_path)
|
||||||
logger:info { 'get_tree_inf', root_path }
|
logger:info { 'get_tree_inf', root_path }
|
||||||
--- @type table<string, u.examples.FsNode>
|
--- @type table<string, u.examples.FsNode>
|
||||||
@@ -112,12 +116,11 @@ function H.populate_dir_children(tree, path_to_node)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param opts {
|
--- @class u.examples.RenderOpts
|
||||||
--- bufnr: number;
|
--- @field bufnr number
|
||||||
--- prev_winnr: number;
|
--- @field prev_winnr number
|
||||||
--- root_path: string;
|
--- @field root_path string
|
||||||
--- focus_path?: string;
|
--- @field focus_path? string
|
||||||
--- }
|
|
||||||
---
|
---
|
||||||
--- @return { expand: fun(path: string), collapse: fun(path: string) }
|
--- @return { expand: fun(path: string), collapse: fun(path: string) }
|
||||||
local function _render_in_buffer(opts)
|
local function _render_in_buffer(opts)
|
||||||
@@ -310,7 +313,7 @@ local function _render_in_buffer(opts)
|
|||||||
--
|
--
|
||||||
local renderer = Renderer.new(opts.bufnr)
|
local renderer = Renderer.new(opts.bufnr)
|
||||||
tracker.create_effect(function()
|
tracker.create_effect(function()
|
||||||
--- @type { tree: u.examples.FsDir; path_to_node: table<string, u.examples.FsNode> }
|
--- @type { tree: u.examples.FsDir, path_to_node: table<string, u.examples.FsNode> }
|
||||||
local tree_inf = s_tree_inf:get()
|
local tree_inf = s_tree_inf:get()
|
||||||
local tree = tree_inf.tree
|
local tree = tree_inf.tree
|
||||||
|
|
||||||
@@ -406,11 +409,10 @@ end
|
|||||||
-- Public API functions:
|
-- Public API functions:
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- @type {
|
--- @class u.examples.CurrentInf
|
||||||
--- bufnr: number;
|
--- @field bufnr number
|
||||||
--- winnr: number;
|
--- @field winnr number
|
||||||
--- controller: { expand: fun(path: string), collapse: fun(path: string) };
|
--- @field controller table
|
||||||
--- } | nil
|
|
||||||
local current_inf = nil
|
local current_inf = nil
|
||||||
|
|
||||||
--- Show the filetree:
|
--- Show the filetree:
|
||||||
|
|||||||
@@ -22,12 +22,11 @@ vim.api.nvim_create_autocmd('VimResized', {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--- @alias u.examples.Notification {
|
--- @class u.examples.Notification
|
||||||
--- kind: number;
|
--- @field kind number
|
||||||
--- id: number;
|
--- @field id number
|
||||||
--- text: string;
|
--- @field text string
|
||||||
--- timer: uv.uv_timer_t;
|
--- @field timer uv.uv_timer_t
|
||||||
--- }
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|||||||
@@ -44,23 +44,21 @@ local function shallow_copy_arr(arr) return vim.iter(arr):totable() end
|
|||||||
-- shortest portion of this function.
|
-- shortest portion of this function.
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- @alias u.examples.SelectController {
|
--- @class u.examples.SelectController<T>
|
||||||
--- get_items: fun(): T[];
|
--- @field get_items fun(): T[]
|
||||||
--- set_items: fun(items: T[]);
|
--- @field set_items fun(items: T[])
|
||||||
--- set_filter_text: fun(filter_text: string);
|
--- @field set_filter_text fun(filter_text: string)
|
||||||
--- get_selected_indices: fun(): number[];
|
--- @field get_selected_indices fun(): number[]
|
||||||
--- get_selected_items: fun(): T[];
|
--- @field get_selected_items fun(): T[]
|
||||||
--- set_selected_indices: fun(indicies: number[], ephemeral?: boolean);
|
--- @field set_selected_indices fun(indices: number[], ephemeral?: boolean)
|
||||||
--- close: fun();
|
--- @field close fun()
|
||||||
--- }
|
--- @class u.examples.SelectOpts<T>
|
||||||
--- @alias u.examples.SelectOpts<T> {
|
--- @field items T[]
|
||||||
--- items: `T`[];
|
--- @field multi? boolean
|
||||||
--- multi?: boolean;
|
--- @field format_item? fun(item: T): u.renderer.Tree
|
||||||
--- format_item?: fun(item: T): u.renderer.Tree;
|
--- @field on_finish? fun(items: T[], indices: number[])
|
||||||
--- on_finish?: fun(items: T[], indicies: number[]);
|
--- @field on_selection_changed? fun(items: T[], indices: number[])
|
||||||
--- on_selection_changed?: fun(items: T[], indicies: number[]);
|
--- @field mappings? table<string, fun(select: u.examples.SelectController)>
|
||||||
--- mappings?: table<string, fun(select: u.examples.SelectController)>;
|
|
||||||
--- }
|
|
||||||
|
|
||||||
--- @generic T
|
--- @generic T
|
||||||
--- @param opts u.examples.SelectOpts<T>
|
--- @param opts u.examples.SelectOpts<T>
|
||||||
@@ -326,7 +324,7 @@ function M.create_picker(opts) -- {{{
|
|||||||
safe_wrap(function()
|
safe_wrap(function()
|
||||||
local items = s_items:get()
|
local items = s_items:get()
|
||||||
local selected_indices = s_selected_indices:get()
|
local selected_indices = s_selected_indices:get()
|
||||||
--- @type { orig_idx: number; item: T }[]
|
--- @type { orig_idx: number, item: T }[]
|
||||||
local filtered_items = s_filtered_items:get()
|
local filtered_items = s_filtered_items:get()
|
||||||
local cursor_index = s_cursor_index:get()
|
local cursor_index = s_cursor_index:get()
|
||||||
local indices = shallow_copy_arr(selected_indices)
|
local indices = shallow_copy_arr(selected_indices)
|
||||||
@@ -477,7 +475,7 @@ function M.create_picker(opts) -- {{{
|
|||||||
local selected_indices = s_selected_indices:get()
|
local selected_indices = s_selected_indices:get()
|
||||||
local top_offset = s_top_offset:get()
|
local top_offset = s_top_offset:get()
|
||||||
local cursor_index = s_cursor_index:get()
|
local cursor_index = s_cursor_index:get()
|
||||||
--- @type { filtered_idx: number; orig_idx: number; item: T; formatted: string }[]
|
--- @type { filtered_idx: number, orig_idx: number, item: T, formatted: string }[]
|
||||||
local visible_items = s_visible_items:get()
|
local visible_items = s_visible_items:get()
|
||||||
|
|
||||||
-- The above has to run in the execution context for the signaling to work, but
|
-- The above has to run in the execution context for the signaling to work, but
|
||||||
@@ -791,14 +789,14 @@ function M.buffers() -- {{{
|
|||||||
-- ensure that `cwd` ends with a trailing slash:
|
-- ensure that `cwd` ends with a trailing slash:
|
||||||
if cwd[#cwd] ~= '/' then cwd = cwd .. '/' end
|
if cwd[#cwd] ~= '/' then cwd = cwd .. '/' end
|
||||||
|
|
||||||
--- @type { name: string; changed: number; bufnr: number }[]
|
--- @type { name: string, changed: number, bufnr: number }[]
|
||||||
local bufs = vim.fn.getbufinfo { buflisted = 1 }
|
local bufs = vim.fn.getbufinfo { buflisted = 1 }
|
||||||
|
|
||||||
M.create_picker {
|
M.create_picker {
|
||||||
multi = true,
|
multi = true,
|
||||||
items = bufs,
|
items = bufs,
|
||||||
|
|
||||||
--- @param item { name: string; changed: number; bufnr: number }
|
--- @param item { name: string, changed: number, bufnr: number }
|
||||||
format_item = function(item)
|
format_item = function(item)
|
||||||
local item_name = item.name
|
local item_name = item.name
|
||||||
if item_name == '' then item_name = '[No Name]' end
|
if item_name == '' then item_name = '[No Name]' end
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ local surrounds = {
|
|||||||
['`'] = { left = '`', right = '`' },
|
['`'] = { left = '`', right = '`' },
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @type { left: string; right: string } | nil
|
--- @type { left: string, right: string } | nil
|
||||||
local CACHED_BOUNDS = nil
|
local CACHED_BOUNDS = nil
|
||||||
|
|
||||||
--- @return { left: string; right: string }|nil
|
--- @return { left: string, right: string }|nil
|
||||||
local function prompt_for_bounds()
|
local function prompt_for_bounds()
|
||||||
if vim_repeat.is_repeating() then
|
if vim_repeat.is_repeating() then
|
||||||
-- If we are repeating, we don't want to prompt for bounds, because
|
-- If we are repeating, we don't want to prompt for bounds, because
|
||||||
@@ -55,7 +55,7 @@ local function prompt_for_bounds()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @param range u.Range
|
--- @param range u.Range
|
||||||
--- @param bounds { left: string; right: string }
|
--- @param bounds { left: string, right: string }
|
||||||
local function do_surround(range, bounds)
|
local function do_surround(range, bounds)
|
||||||
local left = bounds.left
|
local left = bounds.left
|
||||||
local right = bounds.right
|
local right = bounds.right
|
||||||
|
|||||||
1
library/busted
Submodule
1
library/busted
Submodule
Submodule library/busted added at 5ed85d0e01
1
library/luv
Submodule
1
library/luv
Submodule
Submodule library/luv added at 3615eb12c9
@@ -62,7 +62,7 @@ end
|
|||||||
function Buffer:lines(start, stop) return Range.from_lines(self.bufnr, start, stop) end
|
function Buffer:lines(start, stop) return Range.from_lines(self.bufnr, start, stop) end
|
||||||
|
|
||||||
--- @param motion string
|
--- @param motion string
|
||||||
--- @param opts? { contains_cursor?: boolean; pos?: u.Pos }
|
--- @param opts? { contains_cursor?: boolean, pos?: u.Pos }
|
||||||
function Buffer:motion(motion, opts)
|
function Buffer:motion(motion, opts)
|
||||||
opts = vim.tbl_extend('force', opts or {}, { bufnr = self.bufnr })
|
opts = vim.tbl_extend('force', opts or {}, { bufnr = self.bufnr })
|
||||||
return Range.from_motion(motion, opts)
|
return Range.from_motion(motion, opts)
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ local Range = require 'u.range'
|
|||||||
--- @type fun(range: u.Range): nil|(fun():any)
|
--- @type fun(range: u.Range): nil|(fun():any)
|
||||||
local __U__OpKeymapOpFunc_rhs = nil
|
local __U__OpKeymapOpFunc_rhs = nil
|
||||||
|
|
||||||
--- This is the global utility function used for operatorfunc
|
--- This is the global utility function used for operatorfunc in opkeymap
|
||||||
--- in opkeymap
|
|
||||||
--- @type nil|fun(range: u.Range): fun():any|nil
|
|
||||||
--- @param ty 'line'|'char'|'block'
|
--- @param ty 'line'|'char'|'block'
|
||||||
-- selene: allow(unused_variable)
|
-- selene: allow(unused_variable)
|
||||||
function _G.__U__OpKeymapOpFunc(ty)
|
function _G.__U__OpKeymapOpFunc(ty)
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function Range.from_lines(bufnr, start_line, stop_line)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @param motion string
|
--- @param motion string
|
||||||
--- @param opts? { bufnr?: number; contains_cursor?: boolean; pos?: u.Pos, user_defined?: boolean }
|
--- @param opts? { bufnr?: number, contains_cursor?: boolean, pos?: u.Pos, user_defined?: boolean }
|
||||||
--- @return u.Range|nil
|
--- @return u.Range|nil
|
||||||
function Range.from_motion(motion, opts)
|
function Range.from_motion(motion, opts)
|
||||||
-- Options handling:
|
-- Options handling:
|
||||||
@@ -513,7 +513,7 @@ function Range:text() return vim.fn.join(self:lines(), '\n') end
|
|||||||
|
|
||||||
--- @param l number
|
--- @param l number
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
--- @return { line: string; idx0: { start: number; stop: number; }; lnum: number; range: fun():u.Range; text: fun():string }|nil
|
--- @return { line: string, idx0: { start: number, stop: number }, lnum: number, range: fun():u.Range, text: fun():string }|nil
|
||||||
function Range:line(l)
|
function Range:line(l)
|
||||||
if l < 0 then l = self:line_count() + l + 1 end
|
if l < 0 then l = self:line_count() + l + 1 end
|
||||||
if l > self:line_count() then return end
|
if l > self:line_count() then return end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ local H = {}
|
|||||||
|
|
||||||
--- @alias u.renderer.TagEventHandler fun(tag: u.renderer.Tag, mode: string, lhs: string): string
|
--- @alias u.renderer.TagEventHandler fun(tag: u.renderer.Tag, mode: string, lhs: string): string
|
||||||
|
|
||||||
--- @alias u.renderer.TagAttributes { [string]?: unknown; imap?: table<string, u.renderer.TagEventHandler>; nmap?: table<string, u.renderer.TagEventHandler>; vmap?: table<string, u.renderer.TagEventHandler>; xmap?: table<string, u.renderer.TagEventHandler>; omap?: table<string, u.renderer.TagEventHandler>, on_change?: fun(text: string): unknown }
|
--- @alias u.renderer.TagAttributes { [string]?: unknown, imap?: table<string, u.renderer.TagEventHandler>, nmap?: table<string, u.renderer.TagEventHandler>, vmap?: table<string, u.renderer.TagEventHandler>, xmap?: table<string, u.renderer.TagEventHandler>, omap?: table<string, u.renderer.TagEventHandler>, on_change?: fun(text: string): unknown }
|
||||||
|
|
||||||
--- @class u.renderer.Tag
|
--- @class u.renderer.Tag
|
||||||
--- @field kind 'tag'
|
--- @field kind 'tag'
|
||||||
@@ -56,8 +56,8 @@ M.h = setmetatable({}, {
|
|||||||
--- @field bufnr number
|
--- @field bufnr number
|
||||||
--- @field ns number
|
--- @field ns number
|
||||||
--- @field changedtick number
|
--- @field changedtick number
|
||||||
--- @field old { lines: string[]; extmarks: u.renderer.RendererExtmark[] }
|
--- @field old { lines: string[], extmarks: u.renderer.RendererExtmark[] }
|
||||||
--- @field curr { lines: string[]; extmarks: u.renderer.RendererExtmark[] }
|
--- @field curr { lines: string[], extmarks: u.renderer.RendererExtmark[] }
|
||||||
local Renderer = {}
|
local Renderer = {}
|
||||||
Renderer.__index = Renderer
|
Renderer.__index = Renderer
|
||||||
M.Renderer = Renderer
|
M.Renderer = Renderer
|
||||||
@@ -98,10 +98,9 @@ function Renderer.new(bufnr) -- {{{
|
|||||||
return self
|
return self
|
||||||
end -- }}}
|
end -- }}}
|
||||||
|
|
||||||
--- @param opts {
|
--- @class u.renderer.MarkupOpts
|
||||||
--- tree: u.renderer.Tree;
|
--- @field tree u.renderer.Tree
|
||||||
--- on_tag?: fun(tag: u.renderer.Tag, start0: [number, number], stop0: [number, number]): any;
|
--- @field on_tag? fun(tag: u.renderer.Tag, start0: [number, number], stop0: [number, number]): any
|
||||||
--- }
|
|
||||||
function Renderer.markup_to_lines(opts) -- {{{
|
function Renderer.markup_to_lines(opts) -- {{{
|
||||||
--- @type string[]
|
--- @type string[]
|
||||||
local lines = {}
|
local lines = {}
|
||||||
@@ -158,10 +157,9 @@ function Renderer.markup_to_lines(opts) -- {{{
|
|||||||
return lines
|
return lines
|
||||||
end -- }}}
|
end -- }}}
|
||||||
|
|
||||||
--- @param opts {
|
--- @class u.renderer.StringOpts
|
||||||
--- tree: u.renderer.Tree;
|
--- @field tree u.renderer.Tree
|
||||||
--- format_tag?: fun(tag: u.renderer.Tag): string;
|
--- @field format_tag? fun(tag: u.renderer.Tag): string
|
||||||
--- }
|
|
||||||
function Renderer.markup_to_string(opts) return table.concat(Renderer.markup_to_lines(opts), '\n') end
|
function Renderer.markup_to_string(opts) return table.concat(Renderer.markup_to_lines(opts), '\n') end
|
||||||
|
|
||||||
--- @param bufnr number
|
--- @param bufnr number
|
||||||
@@ -534,9 +532,9 @@ end -- }}}
|
|||||||
--- (outermost).
|
--- (outermost).
|
||||||
---
|
---
|
||||||
--- @private (private for now)
|
--- @private (private for now)
|
||||||
--- @param pos0 [number; number]
|
--- @param pos0 [number, number]
|
||||||
--- @param mode string?
|
--- @param mode string?
|
||||||
--- @return { extmark: u.renderer.RendererExtmark; tag: u.renderer.Tag; }[]
|
--- @return { extmark: u.renderer.RendererExtmark, tag: u.renderer.Tag }[]
|
||||||
function Renderer:get_tags_at(pos0, mode) -- {{{
|
function Renderer:get_tags_at(pos0, mode) -- {{{
|
||||||
local cursor_line0, cursor_col0 = pos0[1], pos0[2]
|
local cursor_line0, cursor_col0 = pos0[1], pos0[2]
|
||||||
if not mode then mode = vim.api.nvim_get_mode().mode end
|
if not mode then mode = vim.api.nvim_get_mode().mode end
|
||||||
@@ -560,9 +558,8 @@ function Renderer:get_tags_at(pos0, mode) -- {{{
|
|||||||
--- @type u.renderer.RendererExtmark[]
|
--- @type u.renderer.RendererExtmark[]
|
||||||
local mapped_extmarks = vim
|
local mapped_extmarks = vim
|
||||||
.iter(raw_overlapping_extmarks)
|
.iter(raw_overlapping_extmarks)
|
||||||
--- @return u.renderer.RendererExtmark
|
|
||||||
:map(function(ext)
|
:map(function(ext)
|
||||||
--- @type number, number, number, { end_row?: number; end_col?: number }|nil
|
--- @type number, number, number, { end_row?: number, end_col?: number }|nil
|
||||||
local id, line0, col0, details = unpack(ext)
|
local id, line0, col0, details = unpack(ext)
|
||||||
local start = { line0, col0 }
|
local start = { line0, col0 }
|
||||||
local stop = { line0, col0 }
|
local stop = { line0, col0 }
|
||||||
@@ -575,7 +572,6 @@ function Renderer:get_tags_at(pos0, mode) -- {{{
|
|||||||
|
|
||||||
local intersecting_extmarks = vim
|
local intersecting_extmarks = vim
|
||||||
.iter(mapped_extmarks)
|
.iter(mapped_extmarks)
|
||||||
--- @param ext u.renderer.RendererExtmark
|
|
||||||
:filter(function(ext)
|
:filter(function(ext)
|
||||||
if ext.stop[1] ~= nil and ext.stop[2] ~= nil then
|
if ext.stop[1] ~= nil and ext.stop[2] ~= nil then
|
||||||
-- If we've "ciw" and "collapsed" an extmark onto the cursor,
|
-- If we've "ciw" and "collapsed" an extmark onto the cursor,
|
||||||
@@ -640,10 +636,9 @@ function Renderer:get_tags_at(pos0, mode) -- {{{
|
|||||||
-- created extmarks in self.curr.extmarks, which also has which tag each
|
-- created extmarks in self.curr.extmarks, which also has which tag each
|
||||||
-- extmark is associated with. Cross-reference with that list to get a list
|
-- extmark is associated with. Cross-reference with that list to get a list
|
||||||
-- of tags that we need to fire events for:
|
-- of tags that we need to fire events for:
|
||||||
--- @type { extmark: u.renderer.RendererExtmark; tag: u.renderer.Tag }[]
|
--- @type { extmark: u.renderer.RendererExtmark, tag: u.renderer.Tag }[]
|
||||||
local matching_tags = vim
|
local matching_tags = vim
|
||||||
.iter(intersecting_extmarks)
|
.iter(intersecting_extmarks)
|
||||||
--- @param ext u.renderer.RendererExtmark
|
|
||||||
:map(function(ext)
|
:map(function(ext)
|
||||||
for _, extmark_cache in ipairs(self.curr.extmarks) do
|
for _, extmark_cache in ipairs(self.curr.extmarks) do
|
||||||
if extmark_cache.id == ext.id then return { extmark = ext, tag = extmark_cache.tag } end
|
if extmark_cache.id == ext.id then return { extmark = ext, tag = extmark_cache.tag } end
|
||||||
@@ -656,7 +651,7 @@ end -- }}}
|
|||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
--- @param tag_or_id string | u.renderer.Tag
|
--- @param tag_or_id string | u.renderer.Tag
|
||||||
--- @return { start: [number, number]; stop: [number, number] } | nil
|
--- @return { start: [number, number], stop: [number, number] } | nil
|
||||||
function Renderer:get_tag_bounds(tag_or_id) -- {{{
|
function Renderer:get_tag_bounds(tag_or_id) -- {{{
|
||||||
for _, x in ipairs(self.curr.extmarks) do
|
for _, x in ipairs(self.curr.extmarks) do
|
||||||
local pos = { start = x.start, stop = x.stop }
|
local pos = { start = x.start, stop = x.stop }
|
||||||
@@ -733,12 +728,12 @@ function TreeBuilder:tree() return self.nodes end
|
|||||||
|
|
||||||
-- Levenshtein utility {{{
|
-- Levenshtein utility {{{
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
--- @alias u.renderer.LevenshteinChange<T> ({ kind: 'add'; item: T; index: number; } | { kind: 'delete'; item: T; index: number; } | { kind: 'change'; from: T; to: T; index: number; })
|
--- @alias u.renderer.LevenshteinChange<T> ({ kind: 'add', item: T, index: number } | { kind: 'delete', item: T, index: number } | { kind: 'change', from: T, to: T, index: number })
|
||||||
--- @private
|
--- @private
|
||||||
--- @generic T
|
--- @generic T
|
||||||
--- @param x `T`[]
|
--- @param x `T`[]
|
||||||
--- @param y T[]
|
--- @param y T[]
|
||||||
--- @param cost? { of_delete?: fun(x: T): number; of_add?: fun(x: T): number; of_change?: fun(x: T, y: T): number; }
|
--- @param cost? { of_delete?: (fun(x: T): number), of_add?: (fun(x: T): number), of_change?: (fun(x: T, y: T): number) }
|
||||||
--- @return u.renderer.LevenshteinChange<T>[] The changes, from last (greatest index) to first (smallest index).
|
--- @return u.renderer.LevenshteinChange<T>[] The changes, from last (greatest index) to first (smallest index).
|
||||||
function H.levenshtein(x, y, cost)
|
function H.levenshtein(x, y, cost)
|
||||||
-- At the moment, this whole `cost` plumbing is not used. Deletes have the
|
-- At the moment, this whole `cost` plumbing is not used. Deletes have the
|
||||||
|
|||||||
107
mise.toml
Normal file
107
mise.toml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
################################################################################
|
||||||
|
## Tool Alias
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Tools
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
[tools]
|
||||||
|
# nvimv needs jq:
|
||||||
|
jq = "1.8.1"
|
||||||
|
"asdf:mise-plugins/mise-lua" = "5.1.5"
|
||||||
|
# Since we have busted configured to look for "nvim", have a "default" version
|
||||||
|
# installed. In the tests, we will override with `eval $(nvimv env VERSION)`,
|
||||||
|
# but to avoid having to litter a bunch of commands with that environment
|
||||||
|
# initialization, this just makes things simpler:
|
||||||
|
neovim = "0.11.5"
|
||||||
|
stylua = "2.3.1"
|
||||||
|
"cargo:emmylua_ls" = "0.20.0"
|
||||||
|
"cargo:emmylua_check" = "0.20.0"
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Env
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
[env]
|
||||||
|
ASDF_LUA_LUAROCKS_VERSION = "3.12.2"
|
||||||
|
_.source = { path = "./scripts/env.sh", tools = true }
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Tasks
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
lint = "emmylua_check --ignore '.prefix/**/*.*' ."
|
||||||
|
fmt = "stylua ."
|
||||||
|
"fmt:check" = "stylua --check ."
|
||||||
|
"test:prepare" = '''
|
||||||
|
# Install Lua test dependencies (busted, etc.):
|
||||||
|
luarocks test --prepare
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check that the nightly version of Neovim is not more than a day old:
|
||||||
|
if find .prefix -path '**/nightly/**/nvim' -mtime -1 2>/dev/null | grep -q .; then
|
||||||
|
echo "Neovim Nightly is up-to-date"
|
||||||
|
else
|
||||||
|
if ./nvimv/nvimv ls | grep nightly >/dev/null; then
|
||||||
|
./nvimv/nvimv upgrade nightly
|
||||||
|
else
|
||||||
|
./nvimv/nvimv install nightly
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tasks."test:version:no-prep"]
|
||||||
|
hide = true
|
||||||
|
usage = '''
|
||||||
|
arg "<version>" help="The version of Neovim to test with"
|
||||||
|
'''
|
||||||
|
run = '''
|
||||||
|
echo
|
||||||
|
echo -----------------------------
|
||||||
|
echo -----------------------------
|
||||||
|
echo Neovim version=$usage_version
|
||||||
|
echo -----------------------------
|
||||||
|
echo -----------------------------
|
||||||
|
echo
|
||||||
|
./nvimv/nvimv install $usage_version
|
||||||
|
eval $(./nvimv/nvimv env $usage_version)
|
||||||
|
busted --verbose
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tasks."test:version"]
|
||||||
|
depends = ["test:prepare"]
|
||||||
|
usage = '''
|
||||||
|
arg "<version>" help="The version of Neovim to test with"
|
||||||
|
'''
|
||||||
|
run = 'mise test:version:no-prep $usage_version'
|
||||||
|
|
||||||
|
[tasks."test"]
|
||||||
|
run = 'mise test:version 0.11.5'
|
||||||
|
|
||||||
|
[tasks."test:all"]
|
||||||
|
depends = ["test:prepare"]
|
||||||
|
run = '''
|
||||||
|
VERSIONS="0.11.5 nightly"
|
||||||
|
for v in $VERSIONS; do
|
||||||
|
mise test:version:no-prep $v
|
||||||
|
done
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tasks."test:coverage"]
|
||||||
|
depends = ["test:prepare"]
|
||||||
|
run = '''
|
||||||
|
rm -f ./luacov.*.*
|
||||||
|
busted --coverage --verbose
|
||||||
|
luacov
|
||||||
|
awk '/^Summary$/{flag=1;next} flag{print}' luacov.report.out
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tasks."ci"]
|
||||||
|
run = '''
|
||||||
|
mise run fmt:check
|
||||||
|
mise run lint
|
||||||
|
mise run test:all
|
||||||
|
'''
|
||||||
1
nvimv
Submodule
1
nvimv
Submodule
Submodule nvimv added at bd5c243b96
6
scripts/env.sh
Normal file
6
scripts/env.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export PREFIX="$(pwd)/.prefix"
|
||||||
|
export VIMRUNTIME="$(nvim -u NORC --headless +'echo $VIMRUNTIME' +'quitall' 2>&1)"
|
||||||
|
|
||||||
|
eval $(luarocks path)
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil
|
||||||
local Buffer = require 'u.buffer'
|
local Buffer = require 'u.buffer'
|
||||||
local withbuf = loadfile './spec/withbuf.lua'()
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil, need-check-nil
|
||||||
local CodeWriter = require 'u.codewriter'
|
local CodeWriter = require 'u.codewriter'
|
||||||
|
|
||||||
describe('CodeWriter', function()
|
describe('CodeWriter', function()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil
|
||||||
local Pos = require 'u.pos'
|
local Pos = require 'u.pos'
|
||||||
local withbuf = loadfile './spec/withbuf.lua'()
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil
|
||||||
local Pos = require 'u.pos'
|
local Pos = require 'u.pos'
|
||||||
local Range = require 'u.range'
|
local Range = require 'u.range'
|
||||||
local withbuf = loadfile './spec/withbuf.lua'()
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil
|
||||||
local R = require 'u.renderer'
|
local R = require 'u.renderer'
|
||||||
local withbuf = loadfile './spec/withbuf.lua'()
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--- @diagnostic disable: undefined-field, need-check-nil
|
||||||
local tracker = require 'u.tracker'
|
local tracker = require 'u.tracker'
|
||||||
local Signal = tracker.Signal
|
local Signal = tracker.Signal
|
||||||
local ExecutionContext = tracker.ExecutionContext
|
local ExecutionContext = tracker.ExecutionContext
|
||||||
|
|||||||
16
u-0.0.0-0.rockspec
Normal file
16
u-0.0.0-0.rockspec
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
rockspec_format = '3.0'
|
||||||
|
package = 'u'
|
||||||
|
version = '0.0.0-0'
|
||||||
|
|
||||||
|
source = {
|
||||||
|
url = 'https://github.com/jrop/u.nvim',
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies = {
|
||||||
|
'lua = 5.1',
|
||||||
|
}
|
||||||
|
|
||||||
|
test_dependencies = {
|
||||||
|
'busted == 2.2.0-1',
|
||||||
|
'luacov == 0.16.0-1',
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user