fix: prototype now works! check repl.md
parent
2440a208d1
commit
63567fa9fd
28
lua/tsql.lua
28
lua/tsql.lua
|
@ -1,5 +1,4 @@
|
||||||
---TODO: how does this work with changing texts?
|
---TODO: how does this work with changing texts? TODO: add reducer as formatter
|
||||||
---TODO: add reducer as formatter
|
|
||||||
---TODO: Add reducer as buf_select predicate
|
---TODO: Add reducer as buf_select predicate
|
||||||
|
|
||||||
---@type mod_buf_select
|
---@type mod_buf_select
|
||||||
|
@ -96,12 +95,11 @@ function M.sink_by.print(format)
|
||||||
if format == nil then
|
if format == nil then
|
||||||
format = M.format.default
|
format = M.format.default
|
||||||
end
|
end
|
||||||
return setmetatable({
|
return M.sink_by.pure_fn(
|
||||||
---@type fun(nodes: QNode[])
|
function(nodes)
|
||||||
sink = function(nodes)
|
|
||||||
print(format(nodes))
|
print(format(nodes))
|
||||||
end
|
end
|
||||||
}, M.Sink)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param format Format
|
---@param format Format
|
||||||
|
@ -110,13 +108,12 @@ function M.sink_by.nvim_yank_buf(format)
|
||||||
if format == nil then
|
if format == nil then
|
||||||
format = M.format.default
|
format = M.format.default
|
||||||
end
|
end
|
||||||
return setmetatable({
|
return M.sink.pure_fn(
|
||||||
---@type fun(nodes: QNode[])
|
function(nodes)
|
||||||
sink = function(nodes)
|
|
||||||
local text = format(nodes)
|
local text = format(nodes)
|
||||||
vim.fn.setreg('"', text)
|
vim.fn.setreg('"', text)
|
||||||
end
|
end
|
||||||
}, M.Sink)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
---NOTE: re-export with implementation
|
---NOTE: re-export with implementation
|
||||||
|
@ -155,9 +152,11 @@ end
|
||||||
|
|
||||||
---NOTE: requires nvim runtime
|
---NOTE: requires nvim runtime
|
||||||
function M.Tsql:q_nodes()
|
function M.Tsql:q_nodes()
|
||||||
return self.codeql:find_nodes(
|
return self.codeql:find_nodes(self:qbufs())
|
||||||
self.buf_match.filter_on(self.buf_match, M.nvim_get_qbufs())
|
end
|
||||||
)
|
|
||||||
|
function M.Tsql:qbufs()
|
||||||
|
return self.buf_match.filter_on(self.buf_match, M.nvim_get_qbufs())
|
||||||
end
|
end
|
||||||
|
|
||||||
---NOTE: This is now exiting the functional core and entering
|
---NOTE: This is now exiting the functional core and entering
|
||||||
|
@ -220,7 +219,7 @@ end
|
||||||
function M.Store:new()
|
function M.Store:new()
|
||||||
local o = { highlighting = {} }
|
local o = { highlighting = {} }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
o.__index = self
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -240,7 +239,6 @@ function M.setup(config)
|
||||||
end, { nargs = "?", desc = "tsql DSL invocation" })
|
end, { nargs = "?", desc = "tsql DSL invocation" })
|
||||||
|
|
||||||
M.store = M.Store:new()
|
M.store = M.Store:new()
|
||||||
print("tsql v0.0.1")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
@ -11,9 +11,9 @@ local M = {}
|
||||||
---Assume [""] if it's erroneous (like a terminal buffer)
|
---Assume [""] if it's erroneous (like a terminal buffer)
|
||||||
---@field filetype string the associated filetypes gotten from. This uses
|
---@field filetype string the associated filetypes gotten from. This uses
|
||||||
---`vim.api.nvim_buf_get_option(bufnr: number, 'filetype')`
|
---`vim.api.nvim_buf_get_option(bufnr: number, 'filetype')`
|
||||||
---@field lang string The language of the treesitter parser. This is gotten
|
---@field lang string | nil The language of the treesitter parser. This is gotten
|
||||||
---from `vim.treesitter.get_parser(bufnr: number):lang() -> string [may fail]`
|
---from `pcall of vim.treesitter.get_parser(bufnr: number):lang() -> string | nil`
|
||||||
---@field is_loaded boolean whether it is loaded
|
---@field is_loaded boolean whether it is loaded (`vim.api.nvim_buf_is_loaded`)
|
||||||
M.QBuf = {}
|
M.QBuf = {}
|
||||||
M.QBuf.__index = M.QBuf
|
M.QBuf.__index = M.QBuf
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ local function get_lang(bufnr)
|
||||||
return vim.treesitter.get_parser(bufnr):lang()
|
return vim.treesitter.get_parser(bufnr):lang()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- PURPOSE: enriches the message
|
||||||
if not status then
|
if not status then
|
||||||
local path = vim.api.nvim_buf_get_name(bufnr)
|
local path = vim.api.nvim_buf_get_name(bufnr)
|
||||||
error(string.format("Error determining language for buffer %d: %s", bufnr, path))
|
error(string.format("Error determining language for buffer %d: %s", bufnr, path))
|
||||||
|
@ -35,15 +36,13 @@ end
|
||||||
---@param bufnr number
|
---@param bufnr number
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param filetype string
|
---@param filetype string
|
||||||
---@param lang string
|
---@param lang string | nil
|
||||||
---@param is_loaded boolean
|
---@param is_loaded boolean
|
||||||
---@return QBuf
|
---@return QBuf
|
||||||
function M.QBuf:new(bufnr, path, filetype, lang, is_loaded)
|
function M.QBuf:new(bufnr, path, filetype, lang, is_loaded)
|
||||||
assert(type(bufnr) == "number", "bufnr must be a number")
|
assert(type(bufnr) == "number", "bufnr must be a number")
|
||||||
assert(type(path) == "string", "path must be a string")
|
assert(type(path) == "string", "path must be a string")
|
||||||
assert(type(filetype) == "string", "filetype must be a string")
|
assert(type(filetype) == "string", "filetype must be a string")
|
||||||
assert(type(lang) == "string", "lang must be a string")
|
|
||||||
assert(type(is_loaded) == "boolean", "is_loaded must be a boolean")
|
|
||||||
|
|
||||||
local qbuf = {
|
local qbuf = {
|
||||||
bufnr = bufnr,
|
bufnr = bufnr,
|
||||||
|
@ -58,14 +57,33 @@ function M.QBuf:new(bufnr, path, filetype, lang, is_loaded)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param bufnr number
|
---@param bufnr number
|
||||||
|
---@return QBuf | nil
|
||||||
function M.QBuf.from_nvim_bufnr(bufnr)
|
function M.QBuf.from_nvim_bufnr(bufnr)
|
||||||
local path = vim.api.nvim_buf_get_name(bufnr)
|
local path = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
|
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
|
||||||
|
|
||||||
local status, lang = pcall(get_lang, bufnr)
|
assert(type(filetype) == "string")
|
||||||
local is_loaded = status
|
|
||||||
|
|
||||||
return M.QBuf:new(bufnr, path, filetype, lang, is_loaded)
|
if #filetype == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local sts, _lang = pcall(get_lang, bufnr)
|
||||||
|
---@type nil | string
|
||||||
|
local lang = nil
|
||||||
|
if sts then
|
||||||
|
lang = _lang
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_loaded = vim.api.nvim_buf_is_loaded(bufnr)
|
||||||
|
|
||||||
|
return M.QBuf:new(
|
||||||
|
bufnr,
|
||||||
|
path,
|
||||||
|
filetype,
|
||||||
|
lang,
|
||||||
|
is_loaded
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.buf_match = {}
|
M.buf_match = {}
|
||||||
|
@ -75,6 +93,7 @@ M.buf_match = {}
|
||||||
---@field then_ fun(self, q: BufMatch): BufMatch
|
---@field then_ fun(self, q: BufMatch): BufMatch
|
||||||
---@field filter_on fun(self, q: QBuf[]): QBuf[]
|
---@field filter_on fun(self, q: QBuf[]): QBuf[]
|
||||||
M.BufMatch = {}
|
M.BufMatch = {}
|
||||||
|
M.BufMatch.__index = M.BufMatch
|
||||||
|
|
||||||
function M.buf_match.is_loaded()
|
function M.buf_match.is_loaded()
|
||||||
return M.BufMatch.new(function(buf)
|
return M.BufMatch.new(function(buf)
|
||||||
|
@ -201,27 +220,16 @@ local function list_bufs()
|
||||||
return vim.api.nvim_list_bufs()
|
return vim.api.nvim_list_bufs()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return QBuf[]
|
---@return QBuf[] qbufs
|
||||||
function M.nvim_get_qbufs()
|
function M.nvim_get_qbufs()
|
||||||
local bufnrs = list_bufs()
|
local bufnrs = list_bufs()
|
||||||
local qbufs = {}
|
local qbufs = {}
|
||||||
|
|
||||||
for _, bufnr in ipairs(bufnrs) do
|
for _, bufnr in ipairs(bufnrs) do
|
||||||
local path = vim.api.nvim_buf_get_name(bufnr)
|
local qbuf = M.QBuf.from_nvim_bufnr(bufnr)
|
||||||
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
|
if qbuf ~= nil then
|
||||||
|
table.insert(qbufs, qbuf)
|
||||||
local status, lang = pcall(get_lang, bufnr)
|
end
|
||||||
local is_loaded = status
|
|
||||||
|
|
||||||
local qbuf = {
|
|
||||||
bufnr = bufnr,
|
|
||||||
path = path,
|
|
||||||
filetype = filetype,
|
|
||||||
lang = lang,
|
|
||||||
is_loaded = is_loaded
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(qbufs, qbuf)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return qbufs
|
return qbufs
|
||||||
|
|
|
@ -7,6 +7,15 @@ M.ts_query = {}
|
||||||
---@class TSQuery
|
---@class TSQuery
|
||||||
---@field query string the passthru for treesitter language parser
|
---@field query string the passthru for treesitter language parser
|
||||||
M.TSQuery = {}
|
M.TSQuery = {}
|
||||||
|
M.TSQuery.__index = M.TSQuery
|
||||||
|
|
||||||
|
---@type Ctor<{query: string}, TSQuery>
|
||||||
|
function M.TSQuery.new(q)
|
||||||
|
local self = q
|
||||||
|
setmetatable(self, M.TSQuery)
|
||||||
|
self.__index = M.TSQuery
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
---@class FileLoc
|
---@class FileLoc
|
||||||
---@field row_0 number 0-index row location
|
---@field row_0 number 0-index row location
|
||||||
|
@ -14,6 +23,15 @@ M.TSQuery = {}
|
||||||
M.FileLoc = {}
|
M.FileLoc = {}
|
||||||
M.FileLoc.__index = M.FileLoc
|
M.FileLoc.__index = M.FileLoc
|
||||||
|
|
||||||
|
---@type Ctor<{row_0: number, col_0: number}, FileLoc>
|
||||||
|
function M.FileLoc.new(file_loc)
|
||||||
|
assert(file_loc ~= nil)
|
||||||
|
local self = file_loc
|
||||||
|
setmetatable(self, M.FileLoc)
|
||||||
|
self.__index = M.FileLoc
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
---@class QNode
|
---@class QNode
|
||||||
---@field start FileLoc
|
---@field start FileLoc
|
||||||
---@field end_ex_col FileLoc
|
---@field end_ex_col FileLoc
|
||||||
|
@ -21,11 +39,20 @@ M.FileLoc.__index = M.FileLoc
|
||||||
M.QNode = {}
|
M.QNode = {}
|
||||||
M.QNode.__index = M.QNode
|
M.QNode.__index = M.QNode
|
||||||
|
|
||||||
|
---@type Ctor<{start: FileLoc, end_ex_col: FileLoc, buf: QBuf}, QNode>
|
||||||
|
function M.QNode.new(qnode)
|
||||||
|
assert(qnode ~= nil)
|
||||||
|
local self = qnode
|
||||||
|
setmetatable(self, M.QNode)
|
||||||
|
self.__index = M.QNode
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
---@param treesitter_query string the passthru for treesitter language
|
---@param treesitter_query string the passthru for treesitter language
|
||||||
---parser
|
---parser
|
||||||
---@return TSQuery
|
---@return TSQuery
|
||||||
function M.ts_query.from_scm(treesitter_query)
|
function M.ts_query.from_scm(treesitter_query)
|
||||||
return {
|
return M.TSQuery.new {
|
||||||
query = treesitter_query
|
query = treesitter_query
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -35,23 +62,35 @@ end
|
||||||
function M.TSQuery:find_nodes(files)
|
function M.TSQuery:find_nodes(files)
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, file in ipairs(files) do
|
for _, file in ipairs(files) do
|
||||||
local parser = vim.treesitter.get_parser(file.bufnr, file.filetype)
|
if file.lang == nil then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
local sts, parser = pcall(vim.treesitter.get_parser, file.bufnr, file.filetype)
|
||||||
|
if not sts then
|
||||||
|
-- NOTE: no parser for language
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
local tree = parser:parse()[1]
|
local tree = parser:parse()[1]
|
||||||
local root = tree:root()
|
local root = tree:root()
|
||||||
---@type Query
|
---@type Query
|
||||||
local query = vim.treesitter.query.parse_query(file.lang, self.query)
|
local sts, query = pcall(vim.treesitter.query.parse, file.lang, self.query)
|
||||||
|
if not sts then
|
||||||
|
error("Error parsing query \"" .. self.query .. "\" on lang " .. file.lang .. " for file " .. file.path)
|
||||||
|
end
|
||||||
|
|
||||||
for _, match, _ in query:iter_matches(root, file.bufnr, 0, -1) do
|
for _, match, _ in query:iter_matches(root, file.bufnr, 0, -1) do
|
||||||
for id, node in pairs(match) do
|
for id, node in pairs(match) do
|
||||||
local start_row, start_col, end_row, end_col = node:range(false)
|
local start_row, start_col, end_row, end_col = node:range(false)
|
||||||
local start = { row_0 = start_row, col_0 = start_col }
|
local start = M.FileLoc.new { row_0 = start_row, col_0 = start_col }
|
||||||
-- CORRECTNESS:
|
-- CORRECTNESS:
|
||||||
-- :lua local parser = vim.treesitter.get_parser(0, 'lua'); local tree = parser:parse()[1]; local query = vim.treesitter.parse_query('lua', '(identifier) @name'); for id, node in query:iter_captures(tree:root(), 0) do local name = query.captures[id]; if name == 'name' and vim.treesitter.get_node_text(node, 0) == 'TSQuery' then local sr, sc, er, ec = node:range(); print(string.format("TSQuery Start: (%d, %d), End: (%d, %d)", sr, sc, er, ec)); end; end
|
-- :lua local parser = vim.treesitter.get_parser(0, 'lua'); local tree = parser:parse()[1]; local query = vim.treesitter.parse_query('lua', '(identifier) @name'); for id, node in query:iter_captures(tree:root(), 0) do local name = query.captures[id]; if name == 'name' and vim.treesitter.get_node_text(node, 0) == 'TSQuery' then local sr, sc, er, ec = node:range(); print(string.format("TSQuery Start: (%d, %d), End: (%d, %d)", sr, sc, er, ec)); end; end
|
||||||
local end_ex_col = { row_0 = end_row, col_0 = end_col }
|
local end_ex_col = M.FileLoc.new { row_0 = end_row, col_0 = end_col }
|
||||||
local qnode = { buf = file, start = start, end_ex_col = end_ex_col }
|
local qnode = M.QNode.new { buf = file, start = start, end_ex_col = end_ex_col }
|
||||||
setmetatable(qnode, M.QNode)
|
|
||||||
table.insert(result, qnode)
|
table.insert(result, qnode)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
::continue::
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
35
repl.md
35
repl.md
|
@ -1,13 +1,30 @@
|
||||||
# Simple location for me to use as a neovim repl
|
# Simple location for me to use as a neovim repl
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
:lua local ts = require('tsql'); ts.t(ts.buf_match.any(), ts.ts_query.from_scm("string"), ts.sink_by.print()):do_nvim();
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(string) @_"), ts.sink_by.print()):do_nvim(ts.store);
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(string) @_"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
:lua local ts = require('tsql'); local tsql = ts.t(ts.buf_match.any(), ts.ts_query.from_scm("string"), ts.sink_by.print()); print(vim.inspect(tsql.buf_match));
|
:Noh
|
||||||
|
-- QNodes from current buffer
|
||||||
-- print found bufs
|
:lua local ts = require('tsql'); print(vim.inspect(ts.ts_query.from_scm("(string) @_"):find_nodes({ts.QBuf.from_nvim_bufnr(0)})))
|
||||||
:lua local t = require('tsql'); print(vim.inspect(t.nvim_get_qbufs()))
|
```
|
||||||
|
|
||||||
-- check that buf filter works
|
Non-tsql poc
|
||||||
:lua local t = require('tsql'); print(vim.inspect(t.buf_match.any():filter_on(t.nvim_get_qbufs())))
|
```lua
|
||||||
|
:lua local parser = vim.treesitter.get_parser(0, 'lua'); local tree = parser:parse()[1]; local query = vim.treesitter.parse_query('lua', '(identifier) @name'); for id, node in query:iter_captures(tree:root(), 0) do local name = query.captures[id]; if name == 'name' and vim.treesitter.get_node_text(node, 0) == 'TSQuery' then local sr, sc, er, ec = node:range(); print(string.format("TSQuery Start: (%d, %d), End: (%d, %d)", sr, sc, er, ec)); end; end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```lua
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(string) @_"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(identifier) @_"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
|
-- Highlight all function calls `hello.world("what the heck!")`
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(function_call) @_"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
|
-- Highlight only function calls that are "pure": Not `hello.world` but `world`
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("(function_call (identifier) @_)"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
|
-- Highlight function calls, but only the function identifier
|
||||||
|
:lua local ts = require('tsql'); ts.t(ts.buf_match.filetype("lua"), ts.ts_query.from_scm("[(function_call name: (dot_index_expression) @_) (function_call name: (identifier) @_) (function_call name: (method_index_expression) @_)]"), ts.sink_by.highlight()):do_nvim(ts.store);
|
||||||
|
|
||||||
|
:Noh
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue