fix: prototype now works! check repl.md

master
Hung 2023-06-03 15:34:25 -07:00
parent 2440a208d1
commit 63567fa9fd
4 changed files with 118 additions and 56 deletions

View File

@ -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

View File

@ -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,28 +220,17 @@ 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
local status, lang = pcall(get_lang, bufnr)
local is_loaded = status
local qbuf = {
bufnr = bufnr,
path = path,
filetype = filetype,
lang = lang,
is_loaded = is_loaded
}
table.insert(qbufs, qbuf) table.insert(qbufs, qbuf)
end end
end
return qbufs return qbufs
end end

View File

@ -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
View File

@ -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
``` ```