diff --git a/lua/tsql.lua b/lua/tsql.lua index 859c2d1..0971ed7 100644 --- a/lua/tsql.lua +++ b/lua/tsql.lua @@ -1,55 +1,114 @@ ---@type mod_buf_select local buf_select = require('tsql.buf_select') +---@type mod_token_select +local token_select = require('tsql.token_select') ---@module 'tsql' local M = {} -M.ts_query = {} ----@class TSQuery -M.TSQuery = {} +---@class mod_sink_by M.sink_by = {} ----@module 'tsql.format' ----@alias Format fun(self, nodes: QNode[]) + +---@class mod_format +---@alias Format fun(nodes: QNode[]): string M.format = {} ----@class Tsql -M.Tsql = {} + +M.nvim_ns = vim.api.nvim_create_namespace("tsql") +M.nvim_hl_group = "Search" + +---@class Sink +---@field sink fun(self, nodes: QNode[]) +M.Sink = {} +M.Sink.__index = M.Sink + +---@return Sink +function M.sink_by.highlight() + return setmetatable({ + ---@type fun(nodes: QNode[]) + sink = function(nodes) + for _, node in ipairs(nodes) do + vim.highlight.range( + node.buf.bufnr, + M.nvim_ns, + M.nvim_hl_group, + { node.start.row_0, node.start.col_0 }, + { node.end_ex_col.row_0, node.end_ex_col.col_0 }, + {} + ) + end + end + }, M.Sink) +end + +---@type Format +---Something that can be represented in string in a concise/DSL format. +---In the context of QNode, it should just be the text content. If it's multiline, +---just join by newline +--- +---`vim.api.nvim_buf_get_text( +--- bufnr: number, +--- start_row: number, +--- start_col: number, +--- end_row: number, +--- end_col_exclusive: number, +--- opt: {} +---) -> string[]` return value is array of lines, empty array for unloaded buffer +function M.format.display(nodes) + local texts = {} + for _, node in ipairs(nodes) do + local text = vim.api.nvim_buf_get_text(node.buf.bufnr, node.start.row_0, node.start.col_0, node.end_ex_col.row_0, + node.end_ex_col.col_0) + table.insert(texts, table.concat(text, '\n')) + end + return table.concat(texts, '\n\n') +end + +---@type Format +---Something like a JSON if natively possible, or RON for Rust for clarity +---Basically return a string that is pretty-printed that represents +---a Lua table onto string +function M.format.dump(nodes) + return vim.inspect(nodes, { newline = '\n', indent = ' ' }) +end + +---@param format Format +---@return Sink +function M.sink_by.print(format) + return setmetatable({ + ---@type fun(nodes: QNode[]) + sink = function(nodes) + print(format(nodes)) + end + }, M.Sink) +end + +---@param format Format +---@return Sink +function M.sink_by.nvim_yank_buf(format) + return setmetatable({ + ---@type fun(nodes: QNode[]) + sink = function(nodes) + local text = format(nodes) + vim.fn.setreg('"', text) + end + }, M.Sink) +end ---NOTE: re-export with implementation -M.buf_match = require('tsql.buf_select') +M.buf_match = buf_select.buf_match +M.BufMatch = buf_select.BufMatch +M.QBuf = buf_select.QBuf +M.nvim_get_qbufs = buf_select.nvim_get_qbufs -function M.ts_query.from_scm(treesitter_query) - -- TODO: implement - return M.TSQuery -end +M.ts_query = token_select.ts_query +M.TSQuery = token_select.TSQuery -function M.TSQuery:find_locs(files) - -- TODO: implement - return {} -end - -function M.sink_by.highlight() - -- TODO: implement -end - ----@type Format -function M.format.display() - -- TODO: implement -end - ----@type Format -function M.format.dump() - -- TODO: implement -end - ----@param format Format -function M.sink_by.print(format) - -- TODO: implement -end - ----@param format Format -function M.sink_by.nvim_yank_buf(format) - -- TODO: implement -end +---@class Tsql +---@field buf_match BufMatch +---@field codeql TSQuery +---@field sink Sink +M.Tsql = {} +M.Tsql.__index = M.Tsql ---@return Tsql ---@param external_dsl string @@ -62,13 +121,21 @@ end ---@param codeql TSQuery ---@param sink Sink function M.t(buf_match, codeql, sink) - -- TODO: implement + return setmetatable({ + buf_match = buf_match, + codeql = codeql, + sink = sink + }, M.Tsql) end ---NOTE: This is now exiting the functional core and entering --- imperative shell function M.Tsql:do_nvim() - -- TODO: implement + self.sink:sink( + self.codeql:find_nodes( + self.buf_match:filter_on(M.nvim_get_qbufs()) + ) + ) end return M diff --git a/lua/tsql/buf_select.lua b/lua/tsql/buf_select.lua index 2e0c212..570e2b0 100644 --- a/lua/tsql/buf_select.lua +++ b/lua/tsql/buf_select.lua @@ -1,8 +1,9 @@ ---@module 'buf_select' ---@class mod_buf_select ----@alias MatchPredicate fun(nodes: QNode[]): boolean local M = {} +---@alias MatchPredicate fun(nodes: QBuf): boolean + ---@class QBuf ---@field bufnr number representing the vim runtime's bufnr, 0 is current buf ---@field path string the absolute path to the buffer. This uses @@ -71,7 +72,7 @@ M.buf_match = {} ---@field not_ fun(self): BufMatch ---@field or_ fun(self, q: BufMatch): BufMatch ---@field then_ fun(self, q: BufMatch): BufMatch ----@field filter_on fun(self, q: QNode[]): QNode[] +---@field filter_on fun(self, q: QBuf[]): QBuf[] M.BufMatch = {} function M.buf_match.is_loaded() diff --git a/lua/tsql/sink.lua b/lua/tsql/sink.lua index 0d85397..5282f7a 100644 --- a/lua/tsql/sink.lua +++ b/lua/tsql/sink.lua @@ -1,6 +1,5 @@ local M = {} ----@class Sink M.Sink = {} function M.print() diff --git a/lua/tsql/token_select.lua b/lua/tsql/token_select.lua index 9df137d..e298126 100644 --- a/lua/tsql/token_select.lua +++ b/lua/tsql/token_select.lua @@ -1,24 +1,59 @@ +---@module 'tsql.token_select' +---@class mod_token_select local M = {} ----@class FileLoc ----@field -M.FileLoc = { -} - ----@class QNode -M.QNode = {} - +---@class mod_ts_query +M.ts_query = {} ---@class TSQuery ----@field find_nodes fun(self: TSQuery, bufs: QBuf[]): TSNode[] +---@field query string the passthru for treesitter language parser M.TSQuery = {} ----@param treesitter_q string Treesitter DSL for query ----TODO: some examples of `treesitter_q` here ---- +---@class FileLoc +---@field row_0 number 0-index row location +---@field col_0 number 0-index col location +M.FileLoc = {} +M.FileLoc.__index = M.FileLoc + +---@class QNode +---@field start FileLoc +---@field end_ex_col FileLoc +---@field buf QBuf +M.QNode = {} +M.QNode.__index = M.QNode + +---@param treesitter_query string the passthru for treesitter language +---parser ---@return TSQuery -function M.from_ts_scm(treesitter_q) - +function M.ts_query.from_scm(treesitter_query) + return { + query = treesitter_query + } +end + +---@param files QBuf[] +---@return QNode[] +function M.TSQuery:find_nodes(files) + local result = {} + for _, file in ipairs(files) do + local parser = vim.treesitter.get_parser(file.bufnr, file.filetype) + local tree = parser:parse()[1] + local root = tree:root() + ---@type Query + local query = vim.treesitter.parse_query(file.lang, self.query) + for _, match, _ in query:iter_matches(root, file.bufnr, 0, -1) do + for id, node in pairs(match) do + local start_row, start_col, end_row, end_col = node:range(false) + local start = { row_0 = start_row, col_0 = start_col } + -- NOTE: Will need to validate that this is correct to be exclusive + -- :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 qnode = { buf = file, start = start, end_ex_col = end_ex_col } + setmetatable(qnode, M.QNode) + table.insert(result, qnode) + end + end + end + return result end return M - diff --git a/plugin/tsql.lua b/plugin/tsql.lua deleted file mode 100644 index e69de29..0000000