From f285844ff0e657e41cf3380133725fec99e17129 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 15 Apr 2023 18:53:48 +0200 Subject: [PATCH] feat: initial PoC --- lua/neo-tree-symbolmap/commands.lua | 102 ++++++++++++++++++++++++++ lua/neo-tree-symbolmap/components.lua | 94 ++++++++++++++++++++++++ lua/neo-tree-symbolmap/init.lua | 47 ++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 lua/neo-tree-symbolmap/commands.lua create mode 100644 lua/neo-tree-symbolmap/components.lua create mode 100644 lua/neo-tree-symbolmap/init.lua diff --git a/lua/neo-tree-symbolmap/commands.lua b/lua/neo-tree-symbolmap/commands.lua new file mode 100644 index 0000000..52b4552 --- /dev/null +++ b/lua/neo-tree-symbolmap/commands.lua @@ -0,0 +1,102 @@ +local cc = require("neo-tree.sources.common.commands") +local vim = vim + +local M = {} + +local function kind_to_str(kind) + return string.format("%s", vim.lsp.protocol.SymbolKind[kind]) +end + +local function parse_tree(tree, id, name) + if tree.name ~= nil then + return { + id = id .. math.random(0, bit.lshift(1, 30)), + name = tree.name, + type = 'file', + extra = { kind = kind_to_str(tree.kind) }, + path = tree.location.uri, + } + end + local children = {} + for key, val in pairs(tree) do + table.insert(children, parse_tree(val, id .. '.' .. key, key)) + end + table.sort(children, function(a, b) + if a.type == 'directory' and b.type ~= 'directory' then + return true + end + if a.type ~= 'directory' and b.type == 'directory' then + return false + end + return a.name < b.name + end) + return { + id = id, + name = name, + type = 'directory', + children = children + } +end + +local function array_to_tree(array) + local root = {} + for _, node in pairs(array) do + local fragments = {} + if node.containerName ~= nil then + fragments = vim.fn.split(node.containerName, "\\.") + end + local target = root + for _, x in pairs(fragments) do + if target[x] == nil then + target[x] = {} + end + target = target[x] + end + target[node.name] = node + end + return root +end + +M.refresh = require("neo-tree.utils").wrap(require("neo-tree.sources.manager").refresh, "symbolmap") + +local function find_last_buffer() + local cur = vim.api.nvim_get_current_buf() + local tabpage = vim.api.nvim_win_get_tabpage(0) + local winlist = vim.api.nvim_tabpage_list_wins(tabpage) + for _, win in ipairs(winlist) do + local buf = vim.api.nvim_win_get_buf(win) + if (buf ~= cur) then + return buf + end + end + return nil +end + +M.add = function(state) + vim.ui.input({ prompt = "query" }, function(input) + local buf = find_last_buffer() + local _ = vim.lsp.buf_request(buf, 'workspace/symbol', { query = input }, function(err, data, _, _) + local root = { + id = "root", + name = "no workspace symbols loaded", + type = "directory", + children = {} + } + if data ~= nil then + local map = array_to_tree(data) + root = parse_tree(map, 'root', 'workspace symbols') + end + state.symboltree = { root } + require("neo-tree.sources.manager").refresh("symbolmap") + end) + state.symboltree = { { + id = "root", + name = "reloading workspace symbols", + type = "directory", + children = {} + } } + end) +end + +cc._add_common_commands(M) +return M diff --git a/lua/neo-tree-symbolmap/components.lua b/lua/neo-tree-symbolmap/components.lua new file mode 100644 index 0000000..3e99d25 --- /dev/null +++ b/lua/neo-tree-symbolmap/components.lua @@ -0,0 +1,94 @@ +-- This file contains the built-in components. Each componment is a function +-- that takes the following arguments: +-- config: A table containing the configuration provided by the user +-- when declaring this component in their renderer config. +-- node: A NuiNode object for the currently focused node. +-- state: The current state of the source providing the items. +-- +-- The function should return either a table, or a list of tables, each of which +-- contains the following keys: +-- text: The text to display for this item. +-- highlight: The highlight group to apply to this text. + +local highlights = require("neo-tree.ui.highlights") +local common = require("neo-tree.sources.common.components") +local lspkind = require('lspkind') + +local lsp_highlights = { + Text = highlights.NORMAL, + Method = highlights.GIT_STAGED, + Function = highlights.GIT_STAGED, + Constructor = highlights.GIT_STAGED, + Field = highlights.GIT_ADDED, + Variable = highlights.GIT_ADDED, + Class = highlights.GIT_CONFLICT, + Interface = highlights.GIT_CONFLICT, + Module = highlights.GIT_UNTRACKED, + Property = highlights.GIT_ADDED, + Unit = highlights.GIT_CONFLICT, + Value = highlights.GIT_CONFLICT, + Enum = highlights.GIT_CONFLICT, + Keyword = highlights.GIT_DELETED, + Snippet = highlights.GIT_IGNORED, + Color = highlights.GIT_IGNORED, + File = highlights.GIT_RENAMED, + Reference = highlights.GIT_IGNORED, + Folder = highlights.DIRECTORY_ICON, + EnumMember = highlights.GIT_CONFLICT, + Constant = highlights.GIT_DELETED, + Struct = highlights.GIT_RENAMED, + Event = highlights.GIT_UNSTAGED, + Operator = highlights.GIT_DELETED, + TypeParameter = highlights.GIT_UNSTAGED, +} + +local M = {} + +M.icon = function(config, node, state) + local icon = config.default or " " + local padding = config.padding or " " + local highlight = config.highlight or highlights.FILE_ICON + if node.type == "directory" then + highlight = highlights.DIRECTORY_ICON + if node:is_expanded() then + icon = config.folder_open or "-" + else + icon = config.folder_closed or "+" + end + elseif node.type == "file" then + if node.extra.kind ~= nil then + icon = lspkind.symbolic(node.extra.kind, { mode = "symbol" }) + if #icon == 0 then + icon = '?' + end + highlight = lsp_highlights[node.extra.kind] or highlights.DIM_TEXT + else + local success, web_devicons = pcall(require, "nvim-web-devicons") + if success then + local devicon, hl = web_devicons.get_icon(node.name, node.ext) + icon = devicon or icon + highlight = hl or highlight + end + end + end + return { + text = icon .. padding, + highlight = highlight, + } +end + +M.name = function(config, node, state) + local highlight = config.highlight or highlights.FILE_NAME + if node.type == "directory" then + highlight = highlights.DIRECTORY_NAME + end + if node:get_depth() == 1 then + highlight = highlights.ROOT_NAME + end + return { + text = node.name, + highlight = highlight, + } +end + +return vim.tbl_deep_extend("force", common, M) diff --git a/lua/neo-tree-symbolmap/init.lua b/lua/neo-tree-symbolmap/init.lua new file mode 100644 index 0000000..c99d3f4 --- /dev/null +++ b/lua/neo-tree-symbolmap/init.lua @@ -0,0 +1,47 @@ + +--This file should have all functions that are in the public api and either set +--or read the state of this source. + +-- local vim = vim +local renderer = require("neo-tree.ui.renderer") +local manager = require("neo-tree.sources.manager") +local events = require("neo-tree.events") + +local M = { name = "symbolmap" } + +---Navigate to the given path. +---@param path string Path to navigate to. If empty, will navigate to the cwd. +M.navigate = function(state, path) + if path == nil then + path = vim.fn.getcwd() + end + state.path = path + + if state.symboltree == nil then + state.symboltree = { { + id = "root", + name = "symbols", + type = "directory", + children = {} + } } + end + + renderer.show_nodes(state.symboltree, state) +end + +---Configures the plugin, should be called before the plugin is used. +---@param config table Configuration table containing any keys that the user +--wants to change from the defaults. May be empty to accept default values. +M.setup = function(config, global_config) + -- You most likely want to use this function to subscribe to events + if config.use_libuv_file_watcher then + -- manager.subscribe(M.name, { + -- event = events.FS_EVENT, + -- handler = function(args) + -- manager.refresh(M.name) + -- end, + -- }) + end +end + +return M