From d7691ecaef4f18cde5235e1111a0ed04fcbb022a Mon Sep 17 00:00:00 2001 From: Hung Date: Thu, 1 Jun 2023 08:20:36 -0700 Subject: [PATCH] docs --- .gitignore | 2 + LICENSE | 19 +++++ README.md | 143 ++++++++++++++++++++++++++++++++++++++ lua/.luarc.json | 64 ----------------- lua/tsql.lua | 23 +++++- lua/tsql/buf_select.lua | 4 ++ lua/tsql/token_select.lua | 4 +- 7 files changed, 192 insertions(+), 67 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE delete mode 100644 lua/.luarc.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a0d2a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.worktree +.luarc.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..eee8491 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2023 Pegasust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index cee0728..34090e9 100644 --- a/README.md +++ b/README.md @@ -1 +1,144 @@ # Treesitter QL + +A Neovim plugin allowing users to perform workspace-wise operations (highlighting, +list processing, mutation) on existing Treesitter query in Scheme. + +## Features + +### Buffer selection (`require('tsql').buf_match`) + +* Filter logic via `buf_match.{path,filetype,ext,any}`. + +#### Combinators + +* With Tacit programming `BufMatch.or_(buf_match.path("world"), BufMatch.not_(buf_match.ext("txt")))` +* With method pipelines `buf_match.path("world").or(buf_match.ext("txt").not_())` +* Hybrid works too `buf_match.path("world").or(BufMatch.not(buf_match.ext("txt")))` + +### Node query (`require('tsql').token_select`) + +Currently support string-passthru of Treesitter query in Scheme + +`token_select.from_scm("function")` + +### Sink (`require('tsql').sink_by`) + +* Any `{sink: fun(self, QNode[]) -> void}` works! +* Processes all workspace nodes. +* Highlight specific patterns in your text with `sink_by.highlight()`. +Clear all highlights by `require('tsql').clear_highlights()` +* Format and print your nodes with `M.sink_by.print()`. This allows you to easily inspect your nodes. +* Copy nodes to your clipboard with `M.sink_by.nvim_yank_buf()`. + +#### Format (`require('tsql').format`) + +- Type: `Format = fun(QNode[]): string` +- `display: Format`: Representation in a concise/DSL format. This is inspired by Rust's `Display` trait +- `dump: Format`: Pretty-print string format for Lua table. Think of this like RON for Rust, +some language-native object representation. +- `debug: Format`: Aliased from `dump` so that it's consistent with Rust's `Debug` trait + +#### Pre-sink list processing + +WIP + + +## Usage + +Here's a basic example of how to use tsql.nvim: + +```lua +local ts = require('tsql') +-- ts.t(, , ) +-- Matches all strings in our neovim workspace. +ts.t(ts.buf_match.any(), ts.ts_query.from_scm("string"), ts.sink_by.print()) +``` + +## Installation + +Use your favorite package manager to install the plugin. For example, with [vim-plug](https://github.com/junegunn/vim-plug): + +```vim +Plug 'pegasust/tsql.nvim' +``` + +Don't forget to run `:PlugInstall` to actually install the plugin. + +## Configuration + +To configure tsql.nvim, you can provide a configuration table to the `M.setup` function. Here's an example: + +```lua +local tsql = require('tsql') + +tsql.setup({ + nvim_hl_group = "Search" -- defines the highlight group used for highlighting +}) +``` + +By default, `nvim_hl_group` is set to "Search". + +## Commands + +The following commands are available: + +* `:Noh` - Clear all highlights added by this plugin. +* `:lua local ts = require('tsql'); ts.t(, , )`: Perform tsql in Lua bindings + * Example: `:lua local ts = require('tsql'); ts.t(ts.buf_match.any(), ts.ts_query.from_scm("string"), ts.sink_by.print())` + * Prints all strings in all buffers reachable from `nvim` + * Note that `ts.sink_by.print()` will use `ts.format.default`, which is `ts.format.display` without additional configurations + +### TDSL (Work in progress) + +* `:Tdsl */scm:string/p` - DSL without interacting with Lua API. This example +prints all strings on all buffers in default (display) format + +#### Advanced example (`feat:TDSL` + `feat:list_process`) + +Highlight all strings within the current buffer that has more than one occurences + +``` +:Tdsl bufnr:0/scm:string/group_by(t:qnode:text) | values | filter(count | ge(2)) | flatten | h +``` + +If you're a FP nerd, power to you! Here's the breakdown of the pre-sink processing: +- `group_by`: Group items in a list based on a key returned by a function. + +Function Signature: `group_by(func: (item: T) -> K, list: T[]) -> Map` + +- `flatten`: Flatten a list of lists into a single list. `flatten([[a], [b, c], []]) -> [a, b, c]` + +Function Signature: `flatten(list: T[][]) -> T[]` + +- `values`: Creates an iterator that goes through all values of a map (created by `group_by` in this case) + +Function Signature: `values(map: Map) -> V[]` + +- `filter_map`: Return a new list containing only the items where the given function maps to non-null + +Function Signature: `filter_map(fn: (item: T) -> Option) -> (T[] -> T[])` + +- `some_if`: Lifts a predicate (`T -> bool`) into an "option predicate": `T -> Option` + +Function Signature: `some_if(fn: (item: T) -> bool) -> (T -> Option)` + +- `count`: Counts the number of elements in an interable + +Function Signature: `count(list: T[]) -> number` + +- `ge`: A higher-order function to compare if a number is greater or equal to a set number. `ge(2)(3) == 3 >= 2` + +Function Signature: `ge(lower: number) -> (number -> bool)` + +## Documentation + +For detailed information on each function and class, refer to the source code. +It contains extensive inline documentation that should be enough to understand each part of the plugin. + +## Contribution + +If you want to contribute to the development of tsql.nvim, feel free to open a pull request. + +## License + +Tsql.nvim is distributed under the MIT license. See the LICENSE file in the repository for details. diff --git a/lua/.luarc.json b/lua/.luarc.json deleted file mode 100644 index 0344964..0000000 --- a/lua/.luarc.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "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 index 95f5982..2acd26f 100644 --- a/lua/tsql.lua +++ b/lua/tsql.lua @@ -17,11 +17,21 @@ M.sink_by = {} ---@alias Format fun(nodes: QNode[]): string M.format = {} +M.format.default = M.format.display + ---@class Sink ---@field sink fun(self, nodes: QNode[]) M.Sink = {} M.Sink.__index = M.Sink +---@return Sink +---@param func fun(self, nodes:QNode[]) +function M.sink_by.pure_fn(func) + return setmetatable({ + sink = func + }, M.Sink) +end + ---@return Sink function M.sink_by.highlight() return setmetatable({ @@ -72,9 +82,14 @@ function M.format.dump(nodes) return vim.inspect(nodes, { newline = '\n', indent = ' ' }) end ----@param format Format +M.format.debug = M.format.dump + +---@param format Format | nil ---@return Sink function M.sink_by.print(format) + if format == nil then + format = M.format.default + end return setmetatable({ ---@type fun(nodes: QNode[]) sink = function(nodes) @@ -86,6 +101,9 @@ end ---@param format Format ---@return Sink function M.sink_by.nvim_yank_buf(format) + if format == nil then + format = M.format.default + end return setmetatable({ ---@type fun(nodes: QNode[]) sink = function(nodes) @@ -209,6 +227,9 @@ function M.setup(config) M.config.nvim_ns = vim.api.nvim_create_namespace("tsql") vim.api.nvim_create_user_command("Noh", M.clear_highlights) + vim.api.nvim_create_user_command("Tdsl", function(cmd) + M.s(cmd.args):do_nvim(M.store) + end) M.store = M.Store:new() end diff --git a/lua/tsql/buf_select.lua b/lua/tsql/buf_select.lua index 570e2b0..43dd5c7 100644 --- a/lua/tsql/buf_select.lua +++ b/lua/tsql/buf_select.lua @@ -104,6 +104,10 @@ function M.buf_match.filetype(...) end) end +function M.buf_match.any() + return M.BufMatch.new(function(_) return true end) +end + ---@vararg string OR for path ---@return BufMatch function M.buf_match.path(...) diff --git a/lua/tsql/token_select.lua b/lua/tsql/token_select.lua index e298126..ef19eed 100644 --- a/lua/tsql/token_select.lua +++ b/lua/tsql/token_select.lua @@ -39,12 +39,12 @@ function M.TSQuery:find_nodes(files) local tree = parser:parse()[1] local root = tree:root() ---@type Query - local query = vim.treesitter.parse_query(file.lang, self.query) + local query = vim.treesitter.query.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 + -- 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 local end_ex_col = { row_0 = end_row, col_0 = end_col } local qnode = { buf = file, start = start, end_ex_col = end_ex_col }