diff --git a/.neoconf.json b/.neoconf.json new file mode 100644 index 0000000..0690d1a --- /dev/null +++ b/.neoconf.json @@ -0,0 +1,18 @@ +{ + "neodev": { + "library": { + "enabled": true, + "plugins": ["neoconf.nvim", "nvim-lspconfig"] + } + }, + "neoconf": { + "plugins": { + "lua_ls": { + "enabled": true + } + } + }, + "lspconfig": { + "sumneko_lua": {} + } +} diff --git a/lua/.luarc.json b/lua/.luarc.json new file mode 100644 index 0000000..0344964 --- /dev/null +++ b/lua/.luarc.json @@ -0,0 +1,64 @@ +{ + "workspace.library": [ + "/nix/store/7mwww77s7jads5ymvn9vv11vkywfh1sp-vim-pack-dir", + "/Users/hungtran/.local/share/nvim/plugged/plenary.nvim", + "/Users/hungtran/.local/share/nvim/plugged/nlua.nvim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-treesitter", + "/Users/hungtran/.local/share/nvim/plugged/nvim-treesitter-textobjects", + "/Users/hungtran/.local/share/nvim/plugged/telescope.nvim", + "/Users/hungtran/.local/share/nvim/plugged/telescope-fzf-native.nvim", + "/Users/hungtran/.local/share/nvim/plugged/telescope-file-browser.nvim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-lspconfig", + "/Users/hungtran/.local/share/nvim/plugged/cmp-nvim-lsp", + "/Users/hungtran/.local/share/nvim/plugged/cmp-path", + "/Users/hungtran/.local/share/nvim/plugged/cmp-buffer", + "/Users/hungtran/.local/share/nvim/plugged/cmp-cmdline", + "/Users/hungtran/.local/share/nvim/plugged/nvim-cmp", + "/Users/hungtran/.local/share/nvim/plugged/lspkind-nvim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-yati", + "/Users/hungtran/.local/share/nvim/plugged/yang.vim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-autopairs", + "/Users/hungtran/.local/share/nvim/plugged/nvim-ts-autotag", + "/Users/hungtran/.local/share/nvim/plugged/guess-indent.nvim", + "/Users/hungtran/.local/share/nvim/plugged/Comment.nvim", + "/Users/hungtran/.local/share/nvim/plugged/gitsigns.nvim", + "/Users/hungtran/.local/share/nvim/plugged/vim-fugitive", + "/Users/hungtran/.local/share/nvim/plugged/mason.nvim", + "/Users/hungtran/.local/share/nvim/plugged/mason-lspconfig.nvim", + "/Users/hungtran/.local/share/nvim/plugged/harpoon", + "/Users/hungtran/.local/share/nvim/plugged/neogit", + "/Users/hungtran/.local/share/nvim/plugged/trouble.nvim", + "/Users/hungtran/.local/share/nvim/plugged/vim-dispatch", + "/Users/hungtran/.local/share/nvim/plugged/vim-jack-in", + "/Users/hungtran/.local/share/nvim/plugged/vim-dispatch-neovim", + "/Users/hungtran/.local/share/nvim/plugged/conjure", + "/Users/hungtran/.local/share/nvim/plugged/nvim-jqx", + "/Users/hungtran/.local/share/nvim/plugged/nvim-surround", + "/Users/hungtran/.local/share/nvim/plugged/rust-tools.nvim", + "/Users/hungtran/.local/share/nvim/plugged/inlay-hints.nvim", + "/Users/hungtran/.local/share/nvim/plugged/gruvbox", + "/Users/hungtran/.local/share/nvim/plugged/lualine.nvim", + "/Users/hungtran/.local/share/nvim/plugged/indent-blankline.nvim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-web-devicons", + "/Users/hungtran/.local/share/nvim/plugged/hlargs.nvim", + "/Users/hungtran/.local/share/nvim/plugged/todo-comments.nvim", + "/Users/hungtran/.local/share/nvim/plugged/nvim-treesitter-context", + "/Users/hungtran/.local/share/nvim/plugged/playground", + "/Users/hungtran/.local/share/nvim/plugged/cmp_luasnip", + "/Users/hungtran/.local/share/nvim/plugged/LuaSnip", + "/Users/hungtran/.local/share/nvim/plugged/zk-nvim", + "/Users/hungtran/.local/share/nvim/plugged/vim-caser", + "/Users/hungtran/.config/nvim", + "/Users/hungtran/.local/share/nvim/site", + "/nix/store/2xr2f568qzslyfmb7zw3sdp8kdv0w7qi-neovim-unwrapped-236c207/share/nvim/runtime", + "/nix/store/2xr2f568qzslyfmb7zw3sdp8kdv0w7qi-neovim-unwrapped-236c207/lib/nvim", + "/Users/hungtran/.local/share/nvim/plugged/nlua.nvim/after", + "/Users/hungtran/.local/share/nvim/plugged/cmp-nvim-lsp/after", + "/Users/hungtran/.local/share/nvim/plugged/cmp-path/after", + "/Users/hungtran/.local/share/nvim/plugged/cmp-buffer/after", + "/Users/hungtran/.local/share/nvim/plugged/cmp-cmdline/after", + "/Users/hungtran/.local/share/nvim/plugged/playground/after", + "/Users/hungtran/.local/share/nvim/plugged/cmp_luasnip/after", + "${3rd}/luassert/library" + ] +} \ No newline at end of file diff --git a/lua/tsql.lua b/lua/tsql.lua new file mode 100644 index 0000000..859c2d1 --- /dev/null +++ b/lua/tsql.lua @@ -0,0 +1,74 @@ +---@type mod_buf_select +local buf_select = require('tsql.buf_select') + +---@module 'tsql' +local M = {} + +M.ts_query = {} +---@class TSQuery +M.TSQuery = {} +M.sink_by = {} +---@module 'tsql.format' +---@alias Format fun(self, nodes: QNode[]) +M.format = {} +---@class Tsql +M.Tsql = {} + +---NOTE: re-export with implementation +M.buf_match = require('tsql.buf_select') + +function M.ts_query.from_scm(treesitter_query) + -- TODO: implement + return M.TSQuery +end + +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 + +---@return Tsql +---@param external_dsl string +function M.s(external_dsl) + -- TODO: implement +end + +---@return Tsql +---@param buf_match BufMatch +---@param codeql TSQuery +---@param sink Sink +function M.t(buf_match, codeql, sink) + -- TODO: implement +end + +---NOTE: This is now exiting the functional core and entering +--- imperative shell +function M.Tsql:do_nvim() + -- TODO: implement +end + +return M diff --git a/lua/tsql/buf_select.lua b/lua/tsql/buf_select.lua new file mode 100644 index 0000000..2e0c212 --- /dev/null +++ b/lua/tsql/buf_select.lua @@ -0,0 +1,223 @@ +---@module 'buf_select' +---@class mod_buf_select +---@alias MatchPredicate fun(nodes: QNode[]): boolean +local M = {} + +---@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 +---`vim.api.nvim_buf_get_name(bufnr: number) -> string` +---Assume [""] if it's erroneous (like a terminal buffer) +---@field filetype string the associated filetypes gotten from. This uses +---`vim.api.nvim_buf_get_option(bufnr: number, 'filetype')` +---@field lang string The language of the treesitter parser. This is gotten +---from `vim.treesitter.get_parser(bufnr: number):lang() -> string [may fail]` +---@field is_loaded boolean whether it is loaded +M.QBuf = {} + +---@return string language +---NOTE: may fail with string +local function get_lang(bufnr) + local status, lang = pcall(function() + return vim.treesitter.get_parser(bufnr):lang() + end) + + if not status then + local path = vim.api.nvim_buf_get_name(bufnr) + error(string.format("Error determining language for buffer %d: %s", bufnr, path)) + end + + return lang +end + +---@param bufnr number +---@param path string +---@param filetype string +---@param lang string +---@param is_loaded boolean +---@return QBuf +function M.QBuf:new(bufnr, path, filetype, lang, is_loaded) + assert(type(bufnr) == "number", "bufnr must be a number") + assert(type(path) == "string", "path 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 = { + bufnr = bufnr, + path = path, + filetype = filetype, + lang = lang, + is_loaded = is_loaded + } + setmetatable(qbuf, self) + self.__index = self + return qbuf +end + +---@param bufnr number +function M.QBuf.from_nvim_bufnr(bufnr) + local path = vim.api.nvim_buf_get_name(bufnr) + local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') + + local status, lang = pcall(get_lang, bufnr) + local is_loaded = status + + return M.QBuf:new(bufnr, path, filetype, lang, is_loaded) +end + +M.buf_match = {} +---@class BufMatch +---@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[] +M.BufMatch = {} + +function M.buf_match.is_loaded() + return M.BufMatch.new(function(buf) + return buf.is_loaded + end) +end + +---@param match_fn fun(buf: QBuf): boolean +---@return BufMatch +---NOTE: assigns `match_fn` private field +function M.BufMatch.new(match_fn) + local self = setmetatable({}, M.BufMatch) + self.match_fn = match_fn + return self +end + +---@vararg string OR for filetypes. It doesn't make a lot of sense for AND filetypes +---@return BufMatch +function M.buf_match.filetype(...) + local filetypes = { ... } + return M.BufMatch.new(function(buf) + for _, filetype in ipairs(filetypes) do + if buf.filetype == filetype then + return true + end + end + return false + end) +end + +---@vararg string OR for path +---@return BufMatch +function M.buf_match.path(...) + local paths = { ... } + return M.BufMatch.new(function(buf) + for _, path in ipairs(paths) do + if string.find(buf.path, path) ~= nil then + return true + end + end + return false + end) +end + +---@vararg string OR for path +---@return BufMatch _ f +function M.buf_match.path_or(...) + return M.buf_match.path(...) +end + +---@vararg string AND for path +---@return BufMatch +function M.buf_match.path_and(...) + local paths = { ... } + return M.BufMatch.new(function(buf) + for _, path in ipairs(paths) do + if string.find(buf.path, path) == nil then + return false + end + end + return true + end) +end + +---@vararg string +---@return BufMatch +function M.buf_match.ext(...) + local exts = { ... } + return M.BufMatch.new(function(buf) + for _, ext in ipairs(exts) do + if buf.path:sub(- #ext) == ext then + return true + end + end + return false + end) +end + +---@param q BufMatch +---@return BufMatch +function M.BufMatch:or_(q) + return M.BufMatch.new(function(buf) + return self.matched_fn --[[@as MatchPredicate]](buf) + or q.matched_fn --[[@as MatchPredicate]](buf) + end) +end + +---@param q BufMatch +---@return BufMatch +function M.BufMatch:then_(q) + return M.BufMatch.new(function(buf) + return self.matched_fn --[[@as MatchPredicate]](buf) + and q.matched_fn --[[@as MatchPredicate]](buf) + end) +end + +---@return BufMatch +function M.BufMatch:not_() + return M.BufMatch.new(function(buf) + return not self.matched_fn --[[@as MatchPredicate]](buf) + end) +end + +---@param itr QBuf[] +---@return QBuf[] +function M.BufMatch:filter_on(itr) + ---@type QBuf[] + local matched = {} + for _, buf in ipairs(itr) do + if (self.match_fn --[[@as MatchPredicate]])(buf) then + table.insert(matched, buf) + end + end + return matched +end + +---@return number[] bufnrs that can be loaded or not loaded +local function list_bufs() + return vim.api.nvim_list_bufs() +end + +---@return QBuf[] +function M.nvim_get_qbufs() + local bufnrs = list_bufs() + local qbufs = {} + + for _, bufnr in ipairs(bufnrs) do + local path = vim.api.nvim_buf_get_name(bufnr) + local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') + + 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) + end + + return qbufs +end + +return M diff --git a/lua/tsql/sink.lua b/lua/tsql/sink.lua new file mode 100644 index 0000000..0d85397 --- /dev/null +++ b/lua/tsql/sink.lua @@ -0,0 +1,15 @@ +local M = {} + +---@class Sink +M.Sink = {} + +function M.print() +end + +function M.highlight() +end + +function M.mutate() +end + +return M diff --git a/lua/tsql/token_select.lua b/lua/tsql/token_select.lua new file mode 100644 index 0000000..9df137d --- /dev/null +++ b/lua/tsql/token_select.lua @@ -0,0 +1,24 @@ +local M = {} + +---@class FileLoc +---@field +M.FileLoc = { +} + +---@class QNode +M.QNode = {} + +---@class TSQuery +---@field find_nodes fun(self: TSQuery, bufs: QBuf[]): TSNode[] +M.TSQuery = {} + +---@param treesitter_q string Treesitter DSL for query +---TODO: some examples of `treesitter_q` here +--- +---@return TSQuery +function M.from_ts_scm(treesitter_q) + +end + +return M + diff --git a/plugin/tsql.lua b/plugin/tsql.lua new file mode 100644 index 0000000..e69de29 diff --git a/selene.toml b/selene.toml new file mode 100644 index 0000000..6540d6f --- /dev/null +++ b/selene.toml @@ -0,0 +1 @@ +std="lua51+vim" diff --git a/stylua.toml b/stylua.toml new file mode 100644 index 0000000..0f90030 --- /dev/null +++ b/stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 120 diff --git a/vim.toml b/vim.toml new file mode 100644 index 0000000..0fa5c4f --- /dev/null +++ b/vim.toml @@ -0,0 +1,2 @@ +[vim] +any = true