1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 04:00:03 +08:00

feat(bundle): add bundle nvim-if-lua-compat

This commit is contained in:
wsdjeg 2022-06-12 16:40:40 +08:00
parent 12004cb080
commit 35e132d8f9
10 changed files with 678 additions and 0 deletions

View File

@ -98,6 +98,7 @@ function! SpaceVim#layers#core#plugins() abort
endif
call add(plugins, [g:_spacevim_root_dir . 'bundle/gruvbox', {'loadconf' : 1, 'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-clipboard', {'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/nvim-if-lua-compat', {'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/open-browser.vim', {
\ 'merged' : 0, 'loadconf' : 1,
\}])

67
bundle/nvim-if-lua-compat/README.md vendored Normal file
View File

@ -0,0 +1,67 @@
# nvim-if-lua-compat
An `if_lua` compatibility layer for Neovim (WIP, needs testing)
## Goals
Maintain some amount of compatibility with the existing Vim interface for Lua (see also: [neovim/neovim#12537](https://github.com/neovim/neovim/issues/12537)).
## Progress
Items annotated with an asterisk `*` are only partially supported.
- [x] * `vim.list()` (actually a Lua table, `luaeval()` just makes a copy and transforms it into a Vim list...)
- [x] `#l`
- [x] `l[k]`
- [x] `l()`
- [x] `table.insert(l, newitem)`
- [x] `table.insert(l, position, newitem)`
- [x] `table.remove(l, position)`
- [x] `l:add(item)`
- [x] `l:insert(item[, pos])`
- [x] * `vim.dict()` (actually a Lua table, `luaeval()` just makes a copy and transforms it into a Vim dict...)
- [x] * `#d` (Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions)
- [x] `d.key` or `d['key']`
- [x] `d()`
- [x] * `vim.blob()` (actually a Lua table)
- [x] * `#b` (Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions)
- [x] `b[k]`
- [x] `b:add(bytes)`
- [x] `vim.funcref()` (exists in Neovim core, but the implementation is different here)
- [x] * `#f` (Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions)
- [x] `f(...)`
- [x] `vim.buffer()`
- [x] `b()`
- [x] * `#b` (Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions)
- [x] `b[k]`
- [x] `b.name`
- [x] `b.fname`
- [x] `b.number`
- [x] `b:insert(newline[, pos])`
- [x] `b:next()`
- [x] `b:previous()`
- [x] `b:isvalid()`
- [x] `vim.window()`
- [x] `w()`
- [x] `w.buffer`
- [x] `w.line` (get and set)
- [x] `w.col` (get and set)
- [x] `w.width` (get and set)
- [x] `w.height` (get and set)
- [x] `w:next()`
- [x] `w:previous()`
- [x] `w:isvalid()`
- [x] `vim.type()`
- [x] `list`
- [x] `dict`
- [x] `blob`
- [x] `funcref`
- [x] `buffer`
- [x] `window`
- [x] `vim.command()` (alias to `vim.api.nvim_command()`)
- [x] * `vim.eval()` (alias to `vim.api.nvim_eval()`, doesn't actually return a `vim.list/dict/blob`)
- [x] `vim.line()` (alias to `vim.api.nvim_get_current_line()`)
- [x] `vim.beep()`
- [x] `vim.open()`
- [x] `vim.call()` (in Neovim core)
- [x] `vim.fn()` (in Neovim core)

View File

@ -0,0 +1,61 @@
--- Wrapper class to interact with blobs
--- @class Blob
local Blob
--- @param str string
--- @param index number
--- @return number, number
local function str_iter(str, index)
if index == #str then return end
index = index + 1
return index, str:sub(index, index):byte()
end
--- @param bytes_str string|number
--- @return function, string, number
local function str_to_bytes(bytes_str)
if type(bytes_str) == 'number' then bytes_str = tostring(bytes_str) end
if type(bytes_str) ~= 'string' then error('string expected, got ' .. type(bytes_str)) end
return str_iter, bytes_str, 0
end
local blob_methods = {
--- @param self Blob
--- @param bytes_str string|number
add = function(self, bytes_str)
local is_empty = not self[0]
for _, byte in str_to_bytes(bytes_str) do
if is_empty then
table.insert(self, 0, byte)
is_empty = false
else
table.insert(self, byte)
end
end
end,
}
local blob_mt = {
_vim_type = 'blob',
__index = blob_methods,
__newindex = function(blob, key, value)
if type(key) == 'number' then rawset(blob, key, value) end
return
end,
__len = function(blob) return rawlen(blob) + 1 end,
}
--- @param bytes_str string|number
--- @return Blob
function Blob(bytes_str)
local blob = {}
if bytes_str ~= nil then
for idx, byte in str_to_bytes(bytes_str) do
blob[idx - 1] = byte
end
end
return setmetatable(blob, blob_mt)
end
return Blob

View File

@ -0,0 +1,115 @@
local api = vim.api
local fn = vim.fn
--- Wrapper to interact with buffers
--- @class Buffer
local Buffer
local buf_methods = {
--- @param self Buffer
--- @param newline string
--- @param pos number
insert = function(self, newline, pos)
api.nvim_buf_set_lines(self._bufnr, pos or -1, pos or -1, false, {newline})
end,
--- @param self Buffer
--- @return boolean
isvalid = function(self)
return api.nvim_buf_is_valid(self._bufnr)
end,
--- @param self Buffer
--- @return Buffer|nil
next = function(self)
local bufnr = self._bufnr
local buffers = api.nvim_list_bufs()
local next_buf
for k, v in ipairs(buffers) do
if v == bufnr then
next_buf = k + 1
break
end
end
if next_buf and buffers[next_buf] then
return Buffer(buffers[next_buf])
end
return nil
end,
--- @param self Buffer
--- @return Buffer|nil
previous = function(self)
local bufnr = self._bufnr
local buffers = api.nvim_list_bufs()
local prev_buf
for k, v in ipairs(buffers) do
if v == bufnr then
prev_buf = k - 1
break
end
end
if prev_buf and buffers[prev_buf] then
return Buffer(buffers[prev_buf])
end
return nil
end,
}
local buf_getters = {
--- @param bufnr number
--- @return number
number = function(bufnr)
return bufnr
end,
--- @param bufnr number
--- @return string
fname = api.nvim_buf_get_name,
--- @param bufnr number
--- @return string
name = fn.bufname,
}
local buf_mt = {
_vim_type = 'buffer',
__index = function(tbl, key)
if type(key) == 'number' then
return api.nvim_buf_get_lines(tbl._bufnr, key - 1, key, false)[1]
end
if buf_methods[key] then return buf_methods[key] end
if buf_getters[key] then return buf_getters[key](tbl._bufnr) end
end,
__newindex = function() return end,
__call = function(tbl)
api.nvim_set_current_buf(tbl._bufnr)
end,
-- Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions
__len = function(tbl)
return api.nvim_buf_line_count(tbl._bufnr)
end,
}
--- @param arg ?any
--- @return Buffer|nil
function Buffer(arg)
local bufnr
if not arg then
bufnr = api.nvim_get_current_buf()
elseif type(arg) == 'string' then
bufnr = fn.bufnr(arg)
if bufnr == -1 then return nil end
elseif type(arg) == 'number' then
if not api.nvim_buf_is_valid(arg) then return nil end
bufnr = arg
else
bufnr = api.nvim_list_bufs()[1]
end
return setmetatable({_bufnr = bufnr}, buf_mt)
end
return Buffer

View File

@ -0,0 +1,46 @@
local validate = vim.validate
--- Wrapper class to interact with vim dictionaries
--- @class Dict
local valid_key_types = {
string = true,
number = true,
}
local dict_mt = {
__index = function(dict, key)
if not valid_key_types[type(key)] then
return error(("bad argument #2 to '__index' (string expected, got %s)"):format(type(key)))
end
return rawget(dict, tostring(key))
end,
__newindex = function(dict, key, value)
if not valid_key_types[type(key)] then
return error(("bad argument #2 to '__newindex' (string expected, got %s)"):format(type(key)))
end
rawset(dict, tostring(key), value)
end,
__call = pairs,
__len = vim.tbl_count,
_vim_type = 'dict',
}
--- @param tbl table
--- @return Dict
function Dict(tbl)
validate {
tbl = {tbl, 'table', true}
}
local dict = vim.empty_dict()
if tbl then
for k, v in pairs(tbl) do
if not valid_key_types[type(k)] then return nil end
dict[tostring(k)] = v
end
end
return setmetatable(dict, dict_mt)
end
return Dict

View File

@ -0,0 +1,24 @@
local fn = vim.fn
if fn.has('nvim') == 0 then return end
local api = vim.api
local Buffer = require('if_lua_compat.buffer')
local Window = require('if_lua_compat.window')
local List = require('if_lua_compat.list')
local Dict = require('if_lua_compat.dict')
local Blob = require('if_lua_compat.blob')
local misc = require('if_lua_compat.misc')
vim.command = api.nvim_command
vim.eval = api.nvim_eval
vim.line = api.nvim_get_current_line
vim.buffer = Buffer
vim.window = Window
vim.list = List
vim.dict = Dict
vim.blob = Blob
vim.open = misc.open
vim.type = misc.type
vim.beep = misc.beep
vim.funcref = misc.funcref

View File

@ -0,0 +1,59 @@
local validate = vim.validate
--- Wrapper class to interact with vim lists
--- @class List
local list_methods = {
--- @param self List
--- @param item any
add = table.insert,
--- @param self List
--- @param item any
--- @param position number
insert = function(self, item, position)
if position then
if position > #self then position = #self end
if position < 0 then position = 0 end
table.insert(self, position + 1, item)
else
table.insert(self, 1, item)
end
end,
}
local list_mt = {
__index = list_methods,
__newindex = function(list, key, value)
if type(key) == 'number' then rawset(list, key, value) end
end,
__call = function(list)
local index = 0
local length = #list
local function list_iter()
if index == length then return end
index = index + 1
return list[index]
end
return list_iter, list, nil
end,
_vim_type = 'list',
}
--- @param tbl table
--- @return List
function List(tbl)
validate {
tbl = {tbl, 'table', true}
}
local list = {}
if tbl then
for _, v in ipairs(tbl) do
table.insert(list, v)
end
end
return setmetatable(list, list_mt)
end
return List

View File

@ -0,0 +1,58 @@
local api = vim.api
local fn = vim.fn
local Buffer = require('if_lua_compat.buffer')
local valid_fname_types = {
number = true,
string = true,
}
--- @param fname ?any
--- @return Buffer
local function vim_open(fname)
fname = valid_fname_types[type(fname)] and tostring(fname) or nil
local bufnr = fn.bufadd(fname or '')
api.nvim_buf_set_option(bufnr, 'buflisted', true)
return Buffer(bufnr)
end
--- @param object any
--- @return string
local function vim_type(object)
local mt = getmetatable(object) or {}
return mt._vim_type or type(object)
end
local function vim_beep()
local belloff = api.nvim_get_option('belloff')
if belloff:match('all') or belloff:match('lang') then return end
io.stdout:write('\a')
end
--- Wrapper class to interact with vim funcrefs
--- @class Funcref
local funcref_mt = {
_vim_type = 'funcref',
__call = function(tbl, ...) return vim.call(tbl._funcname, ...) end,
-- Only works with Lua 5.2+ or LuaJIT built with 5.2 extensions
__len = function(tbl) return tbl._funcname end,
}
--- @param funcname string
--- @return Funcref
local function vim_funcref(funcname)
if type(funcname) ~= 'string' then
return error(("Bad argument #1 to 'funcref' (string expected, got %s)"):format(type(funcname)))
end
return setmetatable({_funcname = funcname}, funcref_mt)
end
return {
open = vim_open,
type = vim_type,
beep = vim_beep,
funcref = vim_funcref,
}

View File

@ -0,0 +1,110 @@
require('if_lua_compat')
local b = vim.buffer()
-- b()
-- print(#b)
-- print(b[154])
-- print(b.name)
-- print(b.fname)
-- print(b.number)
-- b:insert('hello', 127)
-- print(b:isvalid())
-- print(b:next())
-- print(b:previous())
-- print(vim.type(b))
-- print(b:next().name)
-- print(b:previous().name)
local w = vim.window()
-- w()
-- print(w.buffer.name)
-- print(w.line)
-- w.line = 20
-- print(w.col)
-- w.col = 3
-- print(w.width)
-- w.width = 60
-- print(w.height)
-- w.height = 20
-- w:next()
-- w:previous()
-- print(w:isvalid())
-- print(vim.type(w))
-- print(w:next().height)
-- print(w:previous().line)
-- print(vim.type(3))
-- print(vim.type({}))
-- print(vim.type('str'))
local l = vim.list({1, test = 2, 3})
-- print(l[2])
-- print(l.test)
-- print(vim.type(l))
-- print(#l)
-- for v in l() do
-- print(v)
-- end
-- table.insert(l, 4)
-- table.insert(l, 2, 2)
-- table.insert(l, 2, 2)
-- print(l[2])
-- print(l[4])
-- print(vim.inspect(l))
-- table.remove(l, 4)
-- print(vim.inspect(l))
-- l.test = 'hello'
-- print(l.test)
-- l:add(4)
-- l:insert('test', 1)
-- l:insert('test2')
-- for k, v in l() do
-- print(k, v)
-- end
local d = vim.dict({1, test = 2, 3})
-- print(d.test)
-- print(d['test'])
-- print(d[1])
-- print(d[2])
-- for k, v in d() do
-- print(k, v)
-- end
-- print(#d)
-- print(vim.type(d))
-- d[4] = 4
-- print(d['4'], d[4])
-- d[true] = 1
vim.beep()
-- vim.open('init.lua')
-- vim.open('not_a_file')
-- vim.open(1)
-- vim.open()
-- vim.open(true)
-- vim.open({})
local f = vim.funcref('printf')
-- local invalid_f = vim.funcref('ffffff')
-- local invalid_type = vim.funcref({})
-- f()
local bl = vim.blob('test')
local bl2 = vim.blob('😀')
local bl3 = vim.blob()
-- print(vim.inspect(bl))
-- print(vim.inspect(bl2))
-- local tbl = {}
-- bl2:add(1.1)
--
-- print(vim.inspect(bl2))
--
-- for i = 0, #bl2 do
-- table.insert(tbl, string.char(bl2[i]))
-- end
--
-- print(table.concat(tbl))
-- bl3:add('test')

View File

@ -0,0 +1,137 @@
local api = vim.api
local Buffer = require('if_lua_compat.buffer')
--- Wrapper to interact with windows
--- @class Window
local Window
local win_methods = {
--- @param self Window
--- @return boolean
isvalid = function(self)
return api.nvim_win_is_valid(self._winnr)
end,
--- @param self Window
--- @return Window|nil
next = function(self)
local winnr = self._winnr
local windows = api.nvim_tabpage_list_wins(api.nvim_win_get_tabpage(winnr))
local next_win
for k, v in ipairs(windows) do
if v == winnr then
next_win = k + 1
break
end
end
if next_win and windows[next_win] then
return Window(next_win)
end
return nil
end,
--- @param self Window
--- @return Window|nil
previous = function(self)
local winnr = self._winnr
local windows = api.nvim_tabpage_list_wins(api.nvim_win_get_tabpage(winnr))
local prev_win
for k, v in ipairs(windows) do
if v == winnr then
prev_win = k - 1
break
end
end
if prev_win and windows[prev_win] then
return Window(prev_win)
end
return nil
end,
}
local win_getters = {
--- @param winnr number
--- @return Buffer
buffer = function(winnr)
return Buffer(api.nvim_win_get_buf(winnr))
end,
--- @param winnr number
--- @return number
line = function(winnr)
return api.nvim_win_get_cursor(winnr)[1]
end,
--- @param winnr number
--- @return number
col = function(winnr)
return api.nvim_win_get_cursor(winnr)[2] + 1
end,
--- @param winnr number
--- @return number
width = api.nvim_win_get_width,
--- @param winnr number
--- @return number
height = api.nvim_win_get_height,
}
local win_setters = {
--- @param winnr number
--- @param line number
line = function(winnr, line)
api.nvim_win_set_cursor(winnr, {line, 0})
end,
--- @param winnr number
--- @param col number
col = function(winnr, col)
api.nvim_win_set_cursor(winnr, {api.nvim_win_get_cursor(winnr)[1], col - 1})
end,
--- @param winnr number
--- @param width number
width = api.nvim_win_set_width,
--- @param winnr number
--- @param height number
height = api.nvim_win_set_height,
}
local win_mt = {
_vim_type = 'window',
__index = function(tbl, key)
if win_methods[key] then return win_methods[key] end
if win_getters[key] then return win_getters[key](tbl._winnr) end
end,
__newindex = function(tbl, key, value)
if win_setters[key] then return win_setters[key](tbl._winnr, value)
else error(('Invalid window property: %s'):format(key))
end
end,
__call = function(tbl)
api.nvim_set_current_win(tbl._winnr)
end,
}
--- @param arg ?any
--- @return Window|nil
function Window(arg)
local windows = api.nvim_tabpage_list_wins(0)
local winnr
if not arg then
winnr = api.nvim_get_current_win()
elseif type(arg) == 'number' then
if not windows[arg] then return nil end
winnr = windows[arg]
else
winnr = windows[1]
end
return setmetatable({_winnr = winnr}, win_mt)
end
return Window