From 1125d6b8cf3e6481be8549f38b7f1149a73bd23c Mon Sep 17 00:00:00 2001 From: luokai Date: Thu, 8 Aug 2024 20:47:39 +0800 Subject: [PATCH 01/11] loadData --- lua/java-deps.lua | 421 +----------------- lua/java-deps/context.lua | 33 -- lua/java-deps/java/ContainerEntryKind.lua | 22 + lua/java-deps/java/IPackageRootNodeData.lua | 20 + .../java/hieararchicalPackageNodeData.lua | 82 ++++ lua/java-deps/java/jdtls.lua | 84 ++++ lua/java-deps/java/nodeData.lua | 41 ++ lua/java-deps/lsp-command.lua | 104 ++--- lua/java-deps/parser.lua | 70 ++- lua/java-deps/preview.lua | 378 ++++++++-------- lua/java-deps/utils/init.lua | 56 ++- lua/java-deps/view.lua | 91 ++-- lua/java-deps/view/data_node.lua | 204 +++++++++ lua/java-deps/view/explorer_node.lua | 28 ++ lua/java-deps/view/node.lua | 19 - 15 files changed, 816 insertions(+), 837 deletions(-) delete mode 100644 lua/java-deps/context.lua create mode 100644 lua/java-deps/java/ContainerEntryKind.lua create mode 100644 lua/java-deps/java/IPackageRootNodeData.lua create mode 100644 lua/java-deps/java/hieararchicalPackageNodeData.lua create mode 100644 lua/java-deps/java/jdtls.lua create mode 100644 lua/java-deps/java/nodeData.lua create mode 100644 lua/java-deps/view/data_node.lua create mode 100644 lua/java-deps/view/explorer_node.lua delete mode 100644 lua/java-deps/view/node.lua diff --git a/lua/java-deps.lua b/lua/java-deps.lua index 55f15d1..e3e6dae 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps.lua @@ -1,26 +1,11 @@ -local context = require("java-deps.context") -local parser = require("java-deps.parser") -local providers = require("java-deps.providers.init") -local lsp_command = require("java-deps.lsp-command") -local ui = require("java-deps.ui") -local writer = require("java-deps.writer") +local jdtls = require("java-deps.java.jdtls") local config = require("java-deps.config") -local utils = require("java-deps.utils.init") local View = require("java-deps.view") -local folding = require("java-deps.folding") -local node_kind = require("java-deps.symbols").NodeKind -local t_utils = require("java-deps.utils.table") local M = { view = nil, - ------------------------- - -- STATE - ------------------------- state = { - preview_buf = nil, - preview_win = nil, - hover_buf = nil, - hover_win = nil, + root_uri = nil, flattened_outline_items = {}, code_buf = nil, code_win = nil, @@ -29,365 +14,10 @@ local M = { }, } -local function setup_global_autocmd() - if config.options.highlight_hovered_item or config.options.auto_unfold_hover then - vim.api.nvim_create_autocmd("CursorHold", { - pattern = "*", - callback = function() - M._highlight_current_item(nil) - end, - }) - end - - vim.api.nvim_create_autocmd("WinEnter", { - pattern = "*", - callback = require("java-deps.preview").close, - }) -end - -local function setup_buffer_autocmd() - if config.options.auto_preview then - vim.api.nvim_create_autocmd("CursorHold", { - buffer = 0, - callback = require("java-deps.preview").show, - }) - else - vim.api.nvim_create_autocmd("CursorMoved", { - buffer = 0, - callback = require("java-deps.preview").close, - }) - end -end - -local function wipe_state() - M.state = { - preview_buf = nil, - preview_win = nil, - hover_buf = nil, - hover_win = nil, - flattened_outline_items = {}, - code_buf = nil, - code_win = nil, - root_items = nil, - current_node = nil, - } -end - -local function _update_lines() - M.state.flattened_outline_items = parser.flatten(M.state.root_items) - writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items) -end - -function M._current_node() - local current_line = vim.api.nvim_win_get_cursor(M.view.winnr)[1] - return M.state.flattened_outline_items[current_line] -end - -local function goto_location(change_focus) - local node = M._current_node() - vim.api.nvim_win_set_cursor(M.state.code_win, { node.line + 1, node.character }) - if change_focus then - vim.fn.win_gotoid(M.state.code_win) - end - if config.options.auto_close then - M.close_outline() - end -end - -local function reveal_paths(children, parent) - if children and #children < 1 then +function handle_projects(projects) + if not projects or #projects < 1 then return end - for i = 1, #children, 1 do - local node = children[i] - if node == nil then - return - end - local perfix - node.parent = parent - if node.cname == nil and node.kind == node_kind.Package then - node.cname = node.name - local name = node.name:match(".+%.(%w+)$") - if name ~= nil then - node.name = name - end - perfix = node.cname .. "." - else - perfix = node.name .. "." - end - local j = i + 1 - local next_children = nil - while j < #children do - local next_node = children[j] - if next_node.cname == nil and node.kind == node_kind.Package then - next_node.cname = next_node.name - local name = next_node.name:match(".+%.(%w+)$") - if name ~= nil then - next_node.name = name - end - end - if node.kind == next_node.kind and vim.startswith(next_node.cname, perfix) then - if next_children == nil then - next_children = {} - end - table.insert(next_children, next_node) - table.remove(children, j) - else - j = j + 1 - end - end - node.children = next_children - if node.children ~= nil then - reveal_paths(node.children, node) - end - end -end - -local function node_eq(a, b) - if a == nil or b == nil then - return false - end - return a.kind == b.kind and a.name == b.name and a.path == b.path -end -local function find_pkg(node) - if node == nil then - return nil - end - if node.kind > node_kind.Package then - return nil - end - if node.kind == node_kind.Package then - return node - else - find_pkg(node.parent) - end -end - -function open_pkg(node) - if node.kind == node_kind.Package then - if node.children == nil then - node.children = {} - end - local c = lsp_command.get_package_data(M.state.code_buf, node) - if c ~= nil and type(c) == "table" and #c > 0 then - vim.list_extend(node.children, c) - end - end -end -function open_pkgs(node) - if node.kind == node_kind.Package then - if node.children == nil then - node.children = {} - else - open_pkgs(node.children[1]) - end - local c = lsp_command.get_package_data(M.state.code_buf, node) - if c ~= nil and type(c) == "table" and #c > 0 then - vim.list_extend(node.children, c) - end - end -end - -local function package_handler(node) - if not folding.is_foldable(node) then - return - end - if M.view:is_open() then - local response = lsp_command.get_package_data(M.state.code_buf, node) - if response == nil or type(response) ~= "table" or #response < 1 then - return - end - parser.sort_result(response) - local child_hir = t_utils.array_copy(node.hierarchy) - table.insert(child_hir, node.isLast) - node.children = parser.parse(response, node.depth + 1, child_hir, node) - return response - end -end - -local function open_file(node) - node = node or M._current_node() - -- open_file - local fname = node.uri - if vim.startswith(fname, "file://") or vim.startswith(fname, "jdt://") then - vim.fn.win_gotoid(M.state.code_win) - local bufnr = vim.uri_to_bufnr(fname) - vim.bo[bufnr].buflisted = true - vim.api.nvim_win_set_buf(M.state.code_win, bufnr) - - if config.options.auto_close then - M.close_outline() - end - end -end - -function M._set_folded_or_open(open, move_cursor, node_index) - local node = M.state.flattened_outline_items[node_index] or M._current_node() - M.state.current_node = node - local folded = false - if node.folded ~= nil then - folded = not node.folded - end - if node.kind == node_kind.File or node.kind == node_kind.PrimaryType or node.kind == node_kind.ClassFile then - if move_cursor then - vim.api.nvim_win_set_cursor(M.view.winnr, { node_index, 0 }) - end - if open then - open_file(node) - end - else - M._set_folded(folded, move_cursor, node_index) - end -end -function M._set_folded(folded, move_cursor, node_index) - local node = M.state.flattened_outline_items[node_index] or M._current_node() - M.state.current_node = node - if folding.is_foldable(node) then - node.folded = folded - - if move_cursor then - vim.api.nvim_win_set_cursor(M.view.winnr, { node_index, 0 }) - end - - package_handler(node) - _update_lines() - elseif node.parent then - local parent_node = M.state.flattened_outline_items[node.parent.line_in_outline] - - if parent_node then - M._set_folded(folded, not parent_node.folded and folded, parent_node.line_in_outline) - end - else - node.folded = false - end -end - -function M._set_all_folded(folded, nodes) - nodes = nodes or M.state.root_items - - for _, node in ipairs(nodes) do - node.folded = folded - if node.children then - M._set_all_folded(folded, node.children) - end - end - - _update_lines() -end - -function M._highlight_current_item(winnr) - local has_provider = providers.has_provider() - - local is_current_buffer_the_outline = M.view.bufnr == vim.api.nvim_get_current_buf() - - local doesnt_have_outline_buf = not M.view.bufnr - - local should_exit = not has_provider or doesnt_have_outline_buf or is_current_buffer_the_outline - - -- Make a special case if we have a window number - -- Because we might use this to manually focus so we dont want to quit this - -- function - if winnr then - should_exit = false - end - - if should_exit then - return - end - - local win = winnr or vim.api.nvim_get_current_win() - - local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1 - - local leaf_node = nil - - local cb = function(value) - value.hovered = nil - - if value.line == hovered_line then - value.hovered = true - leaf_node = value - end - end - - utils.items_dfs(cb, M.state.root_items) - - _update_lines() - - if leaf_node then - for index, node in ipairs(M.state.flattened_outline_items) do - if node == leaf_node then - vim.api.nvim_win_set_cursor(M.view.winnr, { index, 1 }) - break - end - end - end -end - -local function setup_keymaps(bufnr) - local map = function(...) - utils.nmap(bufnr, ...) - end - -- show help - map(config.options.keymaps.show_help, require("java-deps.config").show_help) - -- close outline - map(config.options.keymaps.close, function() - M.view:close() - end) - -- open_file - map(config.options.keymaps.open_file, function() - M._set_folded_or_open(true) - end) - -- preview symbol - map(config.options.keymaps.toggle_preview, require("java-deps.preview").toggle) - -- fold selection - map(config.options.keymaps.fold, function() - M._set_folded(true) - end) - -- unfold selection - map(config.options.keymaps.unfold, function() - M._set_folded(false) - end) - -- fold all - map(config.options.keymaps.fold_all, function() - M._set_all_folded(true) - end) - -- unfold all - map(config.options.keymaps.unfold_all, function() - M._set_all_folded(false) - end) - -- fold reset - map(config.options.keymaps.fold_reset, function() - M._set_all_folded(nil) - end) -end - -local function handler(response) - if response == nil or type(response) ~= "table" then - return - end - - M.state.code_win = vim.api.nvim_get_current_win() - - M.view:setup_view() - -- clear state when buffer is closed - vim.api.nvim_buf_attach(M.view.bufnr, false, { - on_detach = function(_, _) - wipe_state() - end, - }) - - setup_keymaps(M.view.bufnr) - setup_buffer_autocmd() - - local items = parser.parse(response) - - M.state.root_items = items - M.state.flattened_outline_items = parser.flatten(items) - - writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items) - - M._highlight_current_item(M.state.code_win) end function M.toggle_outline() @@ -398,42 +28,12 @@ function M.toggle_outline() end end -local function resolve_path(path) - local resp = lsp_command.resolve_path(M.state.code_buf, path) - local function find_root(node) - for _, value in ipairs(M.state.flattened_outline_items) do - if value.kind == node.kind then - if node.kind == node_kind.PrimaryType then - if value.name == node.name then - return value - end - elseif node.kind == node_kind.CompilationUnit then - if value.uri == node.uri then - return value - end - elseif node.path ~= nil and value.path == node.path then - return value - end - end - end - end - if resp ~= nil then - for _, value in ipairs(resp) do - local node = find_root(value) - if node ~= nil then - M._set_folded_or_open(false, true, node.line_in_outline) - end - end - end -end - function M.open_outline() if not M.view:is_open() then M.state.code_buf = vim.api.nvim_get_current_buf() - local resp = lsp_command.get_projects(M.state.code_buf, context.current_config().root_uri) - local path = vim.uri_from_bufnr(M.state.code_buf) - handler(resp) - resolve_path(path) + local uri = vim.uri_from_fname(jdtls.root_dir()) + local resp = jdtls.getProjects(uri) + handle_projects(resp) end end @@ -443,14 +43,7 @@ end function M.setup(opts) config.setup(opts) - ui.setup_highlights() - M.view = View:new() - setup_global_autocmd() -end - -M.attach = function(client, buf, root_dir) - context.attach(client, buf, root_dir) end return M diff --git a/lua/java-deps/context.lua b/lua/java-deps/context.lua deleted file mode 100644 index 4bd32ae..0000000 --- a/lua/java-deps/context.lua +++ /dev/null @@ -1,33 +0,0 @@ -local utils = require("java-deps.utils") -local config = require("java-deps.config") -local M = { - current_client = nil, - root_dir = nil, - root_uri = nil, -} -M.current_config = function() - if M.root_dir == nil then - M.attach(utils.get_client(config.jdtls_name)) - end - return M -end - -M.attach = function(client, _, root_dir) - if client == nil then - vim.notify(config.jdtls_name .. " client not found", vim.log.levels.ERROR) - return - end - M.current_client = client - M.root_dir = root_dir or client.config.root_dir - M.root_uri = "file://" .. M.root_dir - if M.current_config().root_dir == nil then - vim.notify(config.jdtls_name .. " client root_dir is empty", vim.log.levels.ERROR) - end -end - -M.clear = function() - M.current_client = nil - M.client_configs = {} -end - -return M diff --git a/lua/java-deps/java/ContainerEntryKind.lua b/lua/java-deps/java/ContainerEntryKind.lua new file mode 100644 index 0000000..059b3ac --- /dev/null +++ b/lua/java-deps/java/ContainerEntryKind.lua @@ -0,0 +1,22 @@ +local INodeData = require("java-deps.java.nodeData").INodeData +local M = {} + +---@enum ContainerEntryKind +M.ContainerEntryKind = { + CPE_LIBRARY = 1, + CPE_PROJECT = 2, + CPE_SOURCE = 3, + CPE_VARIABLE = 4, + CPE_CONTAINER = 5, +} +---@class IContainerNodeData: INodeData +---@field entryKind ContainerEntryKind +local IContainerNodeData = INodeData:new() +IContainerNodeData.__index = IContainerNodeData + +function IContainerNodeData:new() + return setmetatable(INodeData:new(), self) +end +M.IContainerNodeData = IContainerNodeData + +return M diff --git a/lua/java-deps/java/IPackageRootNodeData.lua b/lua/java-deps/java/IPackageRootNodeData.lua new file mode 100644 index 0000000..62990a9 --- /dev/null +++ b/lua/java-deps/java/IPackageRootNodeData.lua @@ -0,0 +1,20 @@ +local INodeData = require("java-deps.java.nodeData").INodeData +local M = {} +---@enum PackageRootKind +M.PackageRootKind = { + K_SOURCE = 1, + K_BINARY = 2, +} + +---@class IPackageRootNodeData:INodeData +---@field entryKind PackageRootKind +---@field attributes table +local IPackageRootNodeData = INodeData:new() +IPackageRootNodeData.__index = IPackageRootNodeData +function IPackageRootNodeData:new() + return setmetatable(INodeData:new(), self) +end + +M.IPackageRootNodeData = IPackageRootNodeData + +return M diff --git a/lua/java-deps/java/hieararchicalPackageNodeData.lua b/lua/java-deps/java/hieararchicalPackageNodeData.lua new file mode 100644 index 0000000..a017163 --- /dev/null +++ b/lua/java-deps/java/hieararchicalPackageNodeData.lua @@ -0,0 +1,82 @@ +local nodeData = require("java-deps.java.nodeData") +local NodeKind = nodeData.NodeKind +local INodeData = nodeData.INodeData + +---@class HierarchicalPackageNodeData: INodeData +---@field displayName string +---@field name string +---@field nodeData? INodeData +---@field children HierarchicalPackageNodeData[] +local HieararchicalPackageNodeData = nodeData.INodeData:new() +HieararchicalPackageNodeData.__index = HieararchicalPackageNodeData + +---@param displayName string +---@param parentName? string +function HieararchicalPackageNodeData:new(displayName, parentName) + local name = (parentName == nil or parentName == "") and displayName or parentName .. "." .. displayName + local base = INodeData:new() + base.displayName = displayName + base.name = name + base.children = {} + return setmetatable(base, self) +end + +function HieararchicalPackageNodeData:compressTree() + while self.name ~= "" and #self.children == 1 and not self:isPackage() do + local child = self.children[1] + self.name = self.name .. "." .. child.displayName + self.displayName = self.displayName .. "." .. child.displayName + self.children = self.children + self.nodeData = self.nodeData + end + for _, child in ipairs(self.children) do + child:compressTree() + end +end +---@param packages string[] +---@param _nodeData INodeData +function HieararchicalPackageNodeData:addSubPackage(packages, _nodeData) + if #packages == 0 then + self.nodeData = _nodeData + return + end + local subPackageDisplayName = table.remove(packages, 1) + local childNode = nil + for _, child in ipairs(self.children) do + if child.displayName == subPackageDisplayName then + childNode = child + break + end + end + if childNode then + childNode:addSubPackage(packages, _nodeData) + else + local newNode = HieararchicalPackageNodeData:new(subPackageDisplayName, self.name) + newNode:addSubPackage(packages, _nodeData) + table.insert(self.children, newNode) + end +end +function HieararchicalPackageNodeData:getUri() + return self.nodeData and self.nodeData.uri +end +function HieararchicalPackageNodeData:moduleName() + return self.nodeData and self.nodeData.moduleName +end + +function HieararchicalPackageNodeData:path() + return self.nodeData and self.nodeData.path +end + +function HieararchicalPackageNodeData:kind() + return self.nodeData and self.nodeData.kind or NodeKind.Package +end + +function HieararchicalPackageNodeData:isPackage() + return self.nodeData ~= nil +end + +function HieararchicalPackageNodeData:handlerIdentifier() + return self.nodeData and self.nodeData.handlerIdentifier +end + +return HieararchicalPackageNodeData diff --git a/lua/java-deps/java/jdtls.lua b/lua/java-deps/java/jdtls.lua new file mode 100644 index 0000000..f565d11 --- /dev/null +++ b/lua/java-deps/java/jdtls.lua @@ -0,0 +1,84 @@ +local lsp_command = require("java-deps.lsp-command") +local node_data = require("java-deps.java.nodeData") +local NodeKind = node_data.NodeKind +local M = {} + +---@param params string +---@return INodeData[] +M.getProjects = function(params) + local err, resp = lsp_command.execute_command({ + command = "java.project.list", + arguments = params, + }, nil, 0) + if err then + vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) + return {} + end + return resp and resp.reslut or {} +end +M.root_dir = function() + return lsp_command.get_client().root_dir +end + +M.getProjectUris = function() + local err, resp = lsp_command.execute_command({ + command = lsp_command.GET_ALL_PROJECTS, + }) + if err then + vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) + return {} + end + return resp and resp.reslut or {} +end + +-- interface IPackageDataParam { +-- projectUri: string | undefined; +-- [key: string]: any; +-- } + +---@return INodeData[] +M.getPackageData = function(params) + local excludePatterns = {} + local err, resp = lsp_command.execute_command(lsp_command.JAVA_GETPACKAGEDATA, params) + if err then + vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) + return {} + end + ---@type INodeData[] + local nodeData = resp and resp.reslut or {} + -- Filter out non java resources + if true then + nodeData = vim.tbl_filter(function(data) + return data.kind ~= NodeKind.Folder and data.kind ~= NodeKind.File + end, nodeData) + end + + if excludePatterns and #nodeData > 0 then + local uriOfChildren = vim.tbl_map(function(node) + return node.uri + end, nodeData) + + local urisToExclude = {} + for _, pattern in pairs(excludePatterns) do + if excludePatterns[pattern] then + local toExclude = vim.tbl_filter(function(urio) + return string.match(urio, pattern) + end, uriOfChildren) + for _, uriToExclude in ipairs(toExclude) do + table.insert(urisToExclude, uriToExclude) + end + end + end + if #urisToExclude > 0 then + nodeData = vim.tbl_filter(function(node) + if not node.uri then + return true + end + return not vim.tbl_contains(urisToExclude, node.uri) + end, nodeData) + end + end + return nodeData +end + +return M diff --git a/lua/java-deps/java/nodeData.lua b/lua/java-deps/java/nodeData.lua new file mode 100644 index 0000000..4485a08 --- /dev/null +++ b/lua/java-deps/java/nodeData.lua @@ -0,0 +1,41 @@ +local M = {} +---@enum NodeKind +M.NodeKind = { + Workspace = 1, + Project = 2, + PackageRoot = 3, + Package = 4, + PrimaryType = 5, + CompilationUnit = 6, + ClassFile = 7, + Container = 8, + Folder = 9, + File = 10, +} + +---@enum TypeKind +M.TypeKind = { + Class = 1, + Interface = 2, + Enum = 3, +} + +---@class INodeData +---@field displayName? string +---@field name string +---@field moduleName? string +---@field path? string +---@field handlerIdentifier? string +---@field uri? string +---@field kind NodeKind +---@field children? any[] +---@field metaData? table +local INodeData = {} +INodeData.__index = INodeData +function INodeData:new() + return setmetatable({}, self) +end + +M.INodeData = INodeData + +return M diff --git a/lua/java-deps/lsp-command.lua b/lua/java-deps/lsp-command.lua index bacd2ca..ddaa47d 100644 --- a/lua/java-deps/lsp-command.lua +++ b/lua/java-deps/lsp-command.lua @@ -1,91 +1,41 @@ local config = require("java-deps.config") -local context = require("java-deps.context") -local symbols = require("java-deps.symbols") local M = {} -local request = function(bufnr, method, params, handler) - local client = context.current_client - client.request(method, params, handler, bufnr) -end -local request_sync = function(bufnr, method, params, timeout) - timeout = timeout or config.options.request_timeout - local client = context.current_client - return client.request_sync(method, params, timeout, bufnr) -end +M.JAVA_PROJECT_LIST = "java.project.list" +M.GET_ALL_PROJECTS = "java.project.getAll" +M.JAVA_PROJECT_REFRESH_LIB_SERVER = "java.project.refreshLib" +M.JAVA_GETPACKAGEDATA = "java.getPackageData" +M.JAVA_RESOLVEPATH = "java.resolvePath" +M.JAVA_PROJECT_GETMAINCLASSES = "java.project.getMainClasses" -M.command = function(buf, params, handler) - request(buf, "workspace/executeCommand", params, function(err, projects) - if err then - vim.notify(err.message, vim.log.levels.WARN) - elseif projects then - handler(projects) - end - end) -end - -M.command_sync = function(buf, command, arguments, timeout) - local params0 = {} - params0.command = command - params0.arguments = arguments - local resp, err = request_sync(buf, "workspace/executeCommand", params0, timeout) - if err then - vim.notify("executeCommand " .. command .. " error: " .. err) +---@return vim.lsp.Client? +M.get_client = function() + local clients = vim.lsp.get_clients({ name = config.jdtls_name or "jdtls" }) + if not clients or #clients == 0 then + vim.notify("No jdtls client found", vim.log.levels.WARN) return end - if resp.result ~= nil then - return resp.result - elseif resp.error ~= nil then - vim.notify(vim.inspect(resp), vim.log.levels.WARN) - end + return clients[1] end -local function root_project(node) - local root = node - while root ~= nil do - if root.kind == symbols.NodeKind.Project then - return root +M.execute_command = function(command, callback, bufnr) + local client = M.get_client() + if not client then + return + end + local co + if not callback then + co = coroutine.running() + if co then + callback = function(err, resp) + coroutine.resume(co, err, resp) + end end - root = root.parent end -end - -M.get_package_data = function(buf, node) - local arguments = { - kind = node.kind, - } - if node.kind == symbols.NodeKind.Project then - arguments.projectUri = node.uri - elseif node.kind == symbols.NodeKind.Container then - arguments.projectUri = root_project(node).uri - arguments.path = node.path - elseif node.kind == symbols.NodeKind.PackageRoot then - arguments.projectUri = root_project(node).uri - arguments.rootPath = node.path - arguments.handlerIdentifier = node.handlerIdentifier - arguments.isHierarchicalView = config.options.hierarchical_view - elseif node.kind == symbols.NodeKind.Package then - arguments.projectUri = root_project(node).uri - arguments.path = node.name - arguments.handlerIdentifier = node.handlerIdentifier - else - arguments.projectUri = root_project(node).uri - arguments.path = node.path + client.request("workspace/executeCommand", command, callback, bufnr) + if co then + return coroutine.yield() end - return M.command_sync(buf, "java.getPackageData", arguments) end -M.get_projects = function(buf, rootUri) - rootUri = rootUri or context.root_uri - local arguments = { - rootUri, - } - return M.command_sync(buf, "java.project.list", arguments) -end - -M.resolve_path = function(buf, uri) - local arguments = { - uri, - } - return M.command_sync(buf, "java.resolvePath", arguments) -end return M diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index ce05700..a5726dc 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -9,46 +9,42 @@ local function parse_result(result, depth, hierarchy, parent) local ret = nil for index, value in pairs(result) do - if not config.is_symbol_blacklisted(symbols.kinds[value.kind]) then - -- the hierarchy is basically a table of booleans which tells whether - -- the parent was the last in its group or not - local hir = hierarchy or {} - -- how many parents this node has, 1 is the lowest value because its - -- easier to work it - local level = depth or 1 - -- whether this node is the last in its group - local isLast = index == #result - - local node = { - entryKind = value.entryKind, - metaData = value.metaData, - handlerIdentifier = value.handlerIdentifier, - kind = value.kind, - uri = value.uri, - path = value.path, - name = value.name, - icon = symbols.icon_from_kind(value), - depth = level, - isLast = isLast, - hierarchy = hir, - parent = parent, - } - if ret == nil then - ret = {} - end + local hir = hierarchy or {} + -- how many parents this node has, 1 is the lowest value because its + -- easier to work it + local level = depth or 1 + -- whether this node is the last in its group + local isLast = index == #result + + local node = { + entryKind = value.entryKind, + metaData = value.metaData, + handlerIdentifier = value.handlerIdentifier, + kind = value.kind, + uri = value.uri, + path = value.path, + name = value.name, + icon = symbols.icon_from_kind(value), + depth = level, + isLast = isLast, + hierarchy = hir, + parent = parent, + } + if ret == nil then + ret = {} + end - table.insert(ret, node) + table.insert(ret, node) - local children = nil - if value.children ~= nil then - -- copy by value because we dont want it messing with the hir table - local child_hir = t_utils.array_copy(hir) - table.insert(child_hir, isLast) - children = parse_result(value.children, level + 1, child_hir, node) - end - - node.children = children + local children = nil + if value.children ~= nil then + -- copy by value because we dont want it messing with the hir table + local child_hir = t_utils.array_copy(hir) + table.insert(child_hir, isLast) + children = parse_result(value.children, level + 1, child_hir, node) end + + node.children = children end return ret end diff --git a/lua/java-deps/preview.lua b/lua/java-deps/preview.lua index ea6ac15..d77c7b3 100644 --- a/lua/java-deps/preview.lua +++ b/lua/java-deps/preview.lua @@ -4,252 +4,252 @@ local jd = require("java-deps") local M = {} local state = { - preview_buf = nil, - preview_win = nil, - hover_buf = nil, - hover_win = nil, + preview_buf = nil, + preview_win = nil, + hover_buf = nil, + hover_win = nil, } local function is_current_win_outline() - local curwin = vim.api.nvim_get_current_win() - return curwin == jd.view.winnr + local curwin = vim.api.nvim_get_current_win() + return curwin == jd.view.winnr end local function has_code_win() - if jd.state.code_win == nil then - return false - end - local isWinValid = vim.api.nvim_win_is_valid(jd.state.code_win) - if not isWinValid then - return false - end - local bufnr = vim.api.nvim_win_get_buf(jd.state.code_win) - local isBufValid = vim.api.nvim_buf_is_valid(bufnr) - return isBufValid + if jd.state.code_win == nil then + return false + end + local isWinValid = vim.api.nvim_win_is_valid(jd.state.code_win) + if not isWinValid then + return false + end + local bufnr = vim.api.nvim_win_get_buf(jd.state.code_win) + local isBufValid = vim.api.nvim_buf_is_valid(bufnr) + return isBufValid end local function get_offset() - local outline_winnr = jd.view.winnr - local width = 53 - local height = 0 + local outline_winnr = jd.view.winnr + local width = 53 + local height = 0 - if config.has_numbers() then - width = width + 4 - end + if config.has_numbers() then + width = width + 4 + end - if config.options.position == "right" then - width = 0 - width - else - width = vim.api.nvim_win_get_width(outline_winnr) + 1 - end - return { height, width } + if config.options.position == "right" then + width = 0 - width + else + width = vim.api.nvim_win_get_width(outline_winnr) + 1 + end + return { height, width } end local function get_height() - local uis = vim.api.nvim_list_uis() - return math.ceil(uis[1].height / 3) + local uis = vim.api.nvim_list_uis() + return math.ceil(uis[1].height / 3) end local function get_hovered_node() - local hovered_line = vim.api.nvim_win_get_cursor(jd.view.winnr)[1] - local node = jd.state.flattened_outline_items[hovered_line] - return node + local hovered_line = vim.api.nvim_win_get_cursor(jd.view.winnr)[1] + local node = jd.state.flattened_outline_items[hovered_line] + return node end local function update_preview(code_buf) - code_buf = code_buf or vim.api.nvim_win_get_buf(jd.state.code_win) + code_buf = code_buf or vim.api.nvim_win_get_buf(jd.state.code_win) - local node = get_hovered_node() - if not node then - return - end - local lines = vim.api.nvim_buf_get_lines(code_buf, 0, -1, false) + local node = get_hovered_node() + if not node then + return + end + local lines = vim.api.nvim_buf_get_lines(code_buf, 0, -1, false) - if state.preview_buf ~= nil then - vim.api.nvim_buf_set_lines(state.preview_buf, 0, -1, 0, lines) - vim.api.nvim_win_set_cursor(state.preview_win, { node.line + 1, node.character }) - end + if state.preview_buf ~= nil then + vim.api.nvim_buf_set_lines(state.preview_buf, 0, -1, 0, lines) + vim.api.nvim_win_set_cursor(state.preview_win, { node.line + 1, node.character }) + end end local function setup_preview_buf() - local code_buf = vim.api.nvim_win_get_buf(jd.state.code_win) - local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") + local code_buf = vim.api.nvim_win_get_buf(jd.state.code_win) + local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") - local function treesitter_attach() - local ts_highlight = require("nvim-treesitter.highlight") + local function treesitter_attach() + local ts_highlight = require("nvim-treesitter.highlight") - ts_highlight.attach(state.preview_buf, ft) - end + ts_highlight.attach(state.preview_buf, ft) + end - -- user might not have tree sitter installed - pcall(treesitter_attach) + -- user might not have tree sitter installed + pcall(treesitter_attach) - vim.api.nvim_buf_set_option(state.preview_buf, "syntax", ft) - vim.api.nvim_buf_set_option(state.preview_buf, "bufhidden", "delete") - vim.api.nvim_win_set_option(state.preview_win, "cursorline", true) - update_preview(code_buf) + vim.api.nvim_buf_set_option(state.preview_buf, "syntax", ft) + vim.api.nvim_buf_set_option(state.preview_buf, "bufhidden", "delete") + vim.api.nvim_win_set_option(state.preview_win, "cursorline", true) + update_preview(code_buf) end local function get_hover_params(node, winnr) - local bufnr = vim.api.nvim_win_get_buf(winnr) - -- local uri = vim.uri_from_bufnr(bufnr) - return { - node = node, - bufnr = bufnr, - } + local bufnr = vim.api.nvim_win_get_buf(winnr) + -- local uri = vim.uri_from_bufnr(bufnr) + return { + node = node, + bufnr = bufnr, + } end local function update_hover() - if not has_code_win() then - return - end - - local node = get_hovered_node() - if not node then - return - end - - local content - if node.metaData and node.metaData["maven.groupId"] then - content = "```xml\n\n" - .. "\t" - .. node.metaData["maven.groupId"] - .. "\n" - .. "\t" - .. node.metaData["maven.artifactId"] - .. "\n" - .. "\t" - .. node.metaData["maven.version"] - .. "\n" - - if node.metaData["maven.scope"] then - content = content .. "\t" .. node.metaData["maven.scope"] .. "\n" - end - if node.metaData["maven.optionaldependency"] then - content = content .. "\t" .. node.metaData["maven.optionaldependency"] .. "\n" - end - content = content .. "\n```" - else - return - end - - local mdstring = { - { kind = "markdown", value = content }, - { kind = "markdown", value = "[" .. node.name .. "](" .. node.path .. ")" }, - } - local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(mdstring) - markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { "###No info available!" } - end - - markdown_lines = vim.lsp.util.stylize_markdown(state.hover_buf, markdown_lines, {}) - - if state.hover_buf ~= nil then - vim.api.nvim_buf_set_lines(state.hover_buf, 0, -1, 0, markdown_lines) - end + if not has_code_win() then + return + end + + local node = get_hovered_node() + if not node then + return + end + + local content + if node.metaData and node.metaData["maven.groupId"] then + content = "```xml\n\n" + .. "\t" + .. node.metaData["maven.groupId"] + .. "\n" + .. "\t" + .. node.metaData["maven.artifactId"] + .. "\n" + .. "\t" + .. node.metaData["maven.version"] + .. "\n" + + if node.metaData["maven.scope"] then + content = content .. "\t" .. node.metaData["maven.scope"] .. "\n" + end + if node.metaData["maven.optionaldependency"] then + content = content .. "\t" .. node.metaData["maven.optionaldependency"] .. "\n" + end + content = content .. "\n```" + else + return + end + + local mdstring = { + { kind = "markdown", value = content }, + { kind = "markdown", value = "[" .. node.name .. "](" .. node.path .. ")" }, + } + local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(mdstring) + markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + markdown_lines = { "###No info available!" } + end + + markdown_lines = vim.lsp.util.stylize_markdown(state.hover_buf, markdown_lines, {}) + + if state.hover_buf ~= nil then + vim.api.nvim_buf_set_lines(state.hover_buf, 0, -1, 0, markdown_lines) + end end local function setup_hover_buf() - if not has_code_win() then - return - end - -- local code_buf = vim.api.nvim_win_get_buf(jd.state.code_win) - -- local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") - -- vim.api.nvim_buf_set_option(state.hover_buf, "syntax", "xml") - vim.api.nvim_buf_set_option(state.hover_buf, "bufhidden", "delete") - vim.api.nvim_win_set_option(state.hover_win, "wrap", true) - vim.api.nvim_win_set_option(state.hover_win, "cursorline", false) - update_hover() + if not has_code_win() then + return + end + -- local code_buf = vim.api.nvim_win_get_buf(jd.state.code_win) + -- local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") + -- vim.api.nvim_buf_set_option(state.hover_buf, "syntax", "xml") + vim.api.nvim_buf_set_option(state.hover_buf, "bufhidden", "delete") + vim.api.nvim_win_set_option(state.hover_win, "wrap", true) + vim.api.nvim_win_set_option(state.hover_win, "cursorline", false) + update_hover() end local function set_bg_hl() - local winhi = "Normal:" .. config.options.preview_bg_highlight - -- vim.api.nvim_win_set_option(state.preview_win, "winhighlight", winhi) - vim.api.nvim_win_set_option(state.hover_win, "winhighlight", winhi) - local winblend = config.options.winblend - -- vim.api.nvim_win_set_option(state.preview_win, "winblend", winblend) - vim.api.nvim_win_set_option(state.hover_win, "winblend", winblend) + local winhi = "Normal:" .. config.options.preview_bg_highlight + -- vim.api.nvim_win_set_option(state.preview_win, "winhighlight", winhi) + vim.api.nvim_win_set_option(state.hover_win, "winhighlight", winhi) + local winblend = config.options.winblend + -- vim.api.nvim_win_set_option(state.preview_win, "winblend", winblend) + vim.api.nvim_win_set_option(state.hover_win, "winblend", winblend) end local function show_preview() - if state.preview_win == nil and state.preview_buf == nil then - state.preview_buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_attach(state.preview_buf, false, { - on_detach = function() - state.preview_buf = nil - state.preview_win = nil - end, - }) - local offsets = get_offset() - state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, { - relative = "win", - width = 50, - height = get_height(), - bufpos = { 0, 0 }, - row = offsets[1], - col = offsets[2], - border = config.options.border, - }) - setup_preview_buf() - else - update_preview() - end + if state.preview_win == nil and state.preview_buf == nil then + state.preview_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_attach(state.preview_buf, false, { + on_detach = function() + state.preview_buf = nil + state.preview_win = nil + end, + }) + local offsets = get_offset() + state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, { + relative = "win", + width = 50, + height = get_height(), + bufpos = { 0, 0 }, + row = offsets[1], + col = offsets[2], + border = config.options.border, + }) + setup_preview_buf() + else + update_preview() + end end local function show_hover() - if state.hover_win == nil and state.hover_buf == nil then - state.hover_buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_attach(state.hover_buf, false, { - on_detach = function() - state.hover_buf = nil - state.hover_win = nil - end, - }) - local offsets = get_offset() - local height = get_height() - state.hover_win = vim.api.nvim_open_win(state.hover_buf, false, { - relative = "win", - width = 50, - height = height, - bufpos = { 0, 0 }, - row = offsets[1] + height + 2, - col = offsets[2], - border = config.options.border, - }) - setup_hover_buf() - else - update_hover() - end + if state.hover_win == nil and state.hover_buf == nil then + state.hover_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_attach(state.hover_buf, false, { + on_detach = function() + state.hover_buf = nil + state.hover_win = nil + end, + }) + local offsets = get_offset() + local height = get_height() + state.hover_win = vim.api.nvim_open_win(state.hover_buf, false, { + relative = "win", + width = 50, + height = height, + bufpos = { 0, 0 }, + row = offsets[1] + height + 2, + col = offsets[2], + border = config.options.border, + }) + setup_hover_buf() + else + update_hover() + end end function M.show() - if not is_current_win_outline() or #vim.api.nvim_list_wins() < 2 then - return - end + if not is_current_win_outline() or #vim.api.nvim_list_wins() < 2 then + return + end - -- show_preview() - show_hover() - set_bg_hl() + -- show_preview() + show_hover() + set_bg_hl() end function M.close() - if has_code_win() then - if state.preview_win ~= nil and vim.api.nvim_win_is_valid(state.preview_win) then - vim.api.nvim_win_close(state.preview_win, true) - end - if state.hover_win ~= nil and vim.api.nvim_win_is_valid(state.hover_win) then - vim.api.nvim_win_close(state.hover_win, true) - end - end + if has_code_win() then + if state.preview_win ~= nil and vim.api.nvim_win_is_valid(state.preview_win) then + vim.api.nvim_win_close(state.preview_win, true) + end + if state.hover_win ~= nil and vim.api.nvim_win_is_valid(state.hover_win) then + vim.api.nvim_win_close(state.hover_win, true) + end + end end function M.toggle() - if state.preview_win ~= nil then - M.close() - else - M.show() - end + if state.preview_win ~= nil then + M.close() + else + M.show() + end end return M diff --git a/lua/java-deps/utils/init.lua b/lua/java-deps/utils/init.lua index 00c66db..d48ec19 100644 --- a/lua/java-deps/utils/init.lua +++ b/lua/java-deps/utils/init.lua @@ -1,20 +1,37 @@ local M = {} +M.is_instance = function(obj, class) + local mt = getmetatable(obj) + while mt do + if mt == class then + return true + end + mt = getmetatable(mt) + end + return false +end + +M.super = function(obj, class) + local mt = getmetatable(obj) + while mt do + if mt == class then + return mt.__index + end + mt = getmetatable(mt) + end + return nil +end + ---maps the table|string of keys to the action ---@param keys table|string ---@param action function|string function M.nmap(bufnr, keys, action) - if type(keys) == 'string' then + if type(keys) == "string" then keys = { keys } end for _, lhs in ipairs(keys) do - vim.keymap.set( - 'n', - lhs, - action, - { silent = true, noremap = true, buffer = bufnr } - ) + vim.keymap.set("n", lhs, action, { silent = true, noremap = true, buffer = bufnr }) end end @@ -64,20 +81,20 @@ M.merge_items_rec = function(new_node, old_node, index, parent) for key, _ in pairs(new_node) do if vim.tbl_contains({ - 'parent', - 'children', - 'folded', - 'hovered', - 'line_in_outline', - 'hierarchy', + "parent", + "children", + "folded", + "hovered", + "line_in_outline", + "hierarchy", }, key) then goto continue end - if key == 'name' then + if key == "name" then -- in the case of a rename, just rename the existing node - old_node['name'] = new_node['name'] + old_node["name"] = new_node["name"] else if not vim.deep_equal(new_node[key], old_node[key]) then failed = true @@ -110,13 +127,4 @@ M.merge_items_rec = function(new_node, old_node, index, parent) end end -M.get_client = function(server_name) - local clients = vim.lsp.get_active_clients() - for _, client in ipairs(clients) do - if client.name == server_name then - return client - end - end -end - return M diff --git a/lua/java-deps/view.lua b/lua/java-deps/view.lua index df78c39..92ad25e 100644 --- a/lua/java-deps/view.lua +++ b/lua/java-deps/view.lua @@ -2,60 +2,63 @@ local config = require("java-deps.config") local View = {} function View:new() - return setmetatable({ bufnr = nil, winnr = nil }, { __index = View }) + return setmetatable({ bufnr = nil, winnr = nil }, { __index = View }) end ---creates the outline window and sets it up function View:setup_view() - -- create a scratch unlisted buffer - self.bufnr = vim.api.nvim_create_buf(false, true) - - -- delete buffer when window is closed / buffer is hidden - vim.api.nvim_buf_set_option(self.bufnr, "bufhidden", "delete") - -- create a split - vim.cmd(config.get_split_command()) - -- resize to a % of the current window size - vim.cmd("vertical resize " .. config.get_window_width()) - - -- get current (outline) window and attach our buffer to it - self.winnr = vim.api.nvim_get_current_win() - vim.api.nvim_win_set_buf(self.winnr, self.bufnr) - - -- window stuff - vim.api.nvim_win_set_option(self.winnr, "number", false) - vim.api.nvim_win_set_option(self.winnr, "relativenumber", false) - vim.api.nvim_win_set_option(self.winnr, "winfixwidth", true) - vim.api.nvim_win_set_option(self.winnr, "list", false) - vim.api.nvim_win_set_option(self.winnr, "wrap", config.options.wrap) - vim.api.nvim_win_set_option(self.winnr, "linebreak", true) -- only has effect when wrap=true - vim.api.nvim_win_set_option(self.winnr, "breakindent", true) -- only has effect when wrap=true - -- Would be nice to use ui.markers.vertical as part of showbreak to keep - -- continuity of the tree UI, but there's currently no way to style the - -- color, apart from globally overriding hl-NonText, which will potentially - -- mess with other theme/user settings. So just use empty spaces for now. - vim.api.nvim_win_set_option(self.winnr, "showbreak", " ") -- only has effect when wrap=true. - -- buffer stuff - vim.api.nvim_buf_set_name(self.bufnr, "JavaProjects") - vim.api.nvim_buf_set_option(self.bufnr, "filetype", "JavaProjects") - vim.api.nvim_buf_set_option(self.bufnr, "modifiable", false) - - if config.options.show_numbers or config.options.show_relative_numbers then - vim.api.nvim_win_set_option(self.winnr, "nu", true) - end - - if config.options.show_relative_numbers then - vim.api.nvim_win_set_option(self.winnr, "rnu", true) - end + -- create a scratch unlisted buffer + self.bufnr = vim.api.nvim_create_buf(false, true) + + -- delete buffer when window is closed / buffer is hidden + vim.api.nvim_buf_set_option(self.bufnr, "bufhidden", "delete") + -- create a split + vim.cmd(config.get_split_command()) + -- resize to a % of the current window size + vim.cmd("vertical resize " .. config.get_window_width()) + + -- get current (outline) window and attach our buffer to it + self.winnr = vim.api.nvim_get_current_win() + vim.api.nvim_win_set_buf(self.winnr, self.bufnr) + + -- window stuff + vim.api.nvim_win_set_option(self.winnr, "spell", false) + vim.api.nvim_win_set_option(self.winnr, "signcolumn", "no") + vim.api.nvim_win_set_option(self.winnr, "foldcolumn", "0") + vim.api.nvim_win_set_option(self.winnr, "number", false) + vim.api.nvim_win_set_option(self.winnr, "relativenumber", false) + vim.api.nvim_win_set_option(self.winnr, "winfixwidth", true) + vim.api.nvim_win_set_option(self.winnr, "list", false) + vim.api.nvim_win_set_option(self.winnr, "wrap", config.options.wrap) + vim.api.nvim_win_set_option(self.winnr, "linebreak", true) -- only has effect when wrap=true + vim.api.nvim_win_set_option(self.winnr, "breakindent", true) -- only has effect when wrap=true + -- Would be nice to use ui.markers.vertical as part of showbreak to keep + -- continuity of the tree UI, but there's currently no way to style the + -- color, apart from globally overriding hl-NonText, which will potentially + -- mess with other theme/user settings. So just use empty spaces for now. + vim.api.nvim_win_set_option(self.winnr, "showbreak", " ") -- only has effect when wrap=true. + -- buffer stuff + vim.api.nvim_buf_set_name(self.bufnr, "JavaProjects") + vim.api.nvim_buf_set_option(self.bufnr, "filetype", "JavaProjects") + vim.api.nvim_buf_set_option(self.bufnr, "modifiable", false) + + if config.options.show_numbers or config.options.show_relative_numbers then + vim.api.nvim_win_set_option(self.winnr, "nu", true) + end + + if config.options.show_relative_numbers then + vim.api.nvim_win_set_option(self.winnr, "rnu", true) + end end function View:close() - vim.api.nvim_win_close(self.winnr, true) - self.winnr = nil - self.bufnr = nil + vim.api.nvim_win_close(self.winnr, true) + self.winnr = nil + self.bufnr = nil end function View:is_open() - return self.winnr and self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) and vim.api.nvim_win_is_valid(self.winnr) + return self.winnr and self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) and vim.api.nvim_win_is_valid(self.winnr) end return View diff --git a/lua/java-deps/view/data_node.lua b/lua/java-deps/view/data_node.lua new file mode 100644 index 0000000..a4b92af --- /dev/null +++ b/lua/java-deps/view/data_node.lua @@ -0,0 +1,204 @@ +local NodeKind = require("java-deps.java.nodeData").NodeKind +local jdtls = require("java-deps.java.jdtls") +local ExplorerNode = require("java-deps.view.explorer_node").ExplorerNode + +local util = require("java-deps.utils") +local M = {} + +M.K_TYPE_KIND = "TypeKind" +---@class DataNode: ExplorerNode +---@field _childrenNodes ExplorerNode[] +---@field _nodeData INodeData +---@field _parent DataNode? +---@field _project DataNode? +---@field _rootNode DataNode? +local DataNode = ExplorerNode:new() +DataNode.__index = DataNode + +---@param nodeData INodeData +---@param parent DataNode? +---@param project DataNode? +---@param rootNode DataNode? +function DataNode:new(nodeData, parent, project, rootNode) + local data = setmetatable(ExplorerNode:new(), self) + data._nodeData = nodeData + data._parent = parent + data._project = project + data._rootNode = rootNode +end + +function DataNode:createChildNodeList() + local kind = self:kind() + if kind == NodeKind.Workspace then + if self._nodeData.children then + return vim.tbl_map(function(child) + return M.createNode(child, self, nil, nil) + end, self._nodeData.children) + end + elseif kind == NodeKind.Project then + if self._nodeData.children then + return vim.tbl_map(function(child) + return M.createNode(child, self, self, nil) + end, self._nodeData.children) + end + end +end + +function DataNode:loadData() + local kind = self:kind() + if kind == NodeKind.Workspace then + return jdtls.getProjects(self._nodeData.uri) + elseif kind == NodeKind.Project then + return jdtls.getPackageData({ + kind = NodeKind.Project, + projectUri = self._nodeData.uri, + }) + elseif kind == NodeKind.Container then + return jdtls.getPackageData({ + kind = NodeKind.Container, + projectUri = self._nodeData.uri, + path = self._nodeData.path, + }) + elseif kind == NodeKind.PackageRoot then + return jdtls.getPackageData({ + kind = NodeKind.PackageRoot, + projectUri = self._project._nodeData.uri, + rootPath = self._nodeData.path, + handlerIdentifier = self._nodeData.handlerIdentifier, + isHierarchicalView = true, + }) + elseif kind == NodeKind.Package then + return jdtls.getPackageData({ + kind = NodeKind.Package, + projectUri = self._project._nodeData.uri, + path = self._nodeData.name, + handlerIdentifier = self._nodeData.handlerIdentifier, + }) + elseif kind == NodeKind.Folder then + return jdtls.getPackageData({ + kind = NodeKind.Folder, + projectUri = self._project._nodeData.uri, + path = self._nodeData.path, + rootPath = self._rootNode and self._rootNode._nodeData.path or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier, + }) + elseif kind == NodeKind.PrimaryType then + return nil + elseif kind == NodeKind.Folder then + return jdtls.getPackageData({ + kind = NodeKind.Folder, + projectUri = self._project._nodeData.uri, + path = self._nodeData.path, + rootPath = self._rootNode and self._rootNode._nodeData.path or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier or nil, + }) + else + return nil + end +end + +function DataNode:icon() end +function DataNode:kind() + return self._nodeData.kind +end + +function DataNode:sort() + table.sort(self._childrenNodes, function(a, b) + ---@diagnostic disable: undefined-field + if a._nodeData.kind and a._nodeData.kind then + if a._nodeData.kind == b._nodeData.kind then + return a._nodeData.name < b._nodeData.name and false or true + else + return a._nodeData.kind - b._nodeData.kind + end + end + return false + end) +end + +---@param paths INodeData[] +function DataNode:revealPaths(paths) + if #paths == 0 then + return self + end + local childNodeData = table.remove(paths, 1) + local children = self:getChildren() + for _, child in ipairs(children) do + if + util.is_instance(child, DataNode) + and child._nodeData.name == childNodeData.name + and child.path == childNodeData.path + then + return #paths > 0 and child:revealPaths(paths) or child + end + end +end + +function DataNode:getChildren() + if not self._nodeData.children then + local data = self:loadData() + self._nodeData.children = data + self._childrenNodes = self:createChildNodeList() or {} + self:sort() + return self._childrenNodes + end + return self._childrenNodes +end + +M.DataNode = DataNode + +---@param nodeData INodeData +---@param parent DataNode? +---@param project DataNode? +---@param rootNode DataNode? +M.createNode = function(nodeData, parent, project, rootNode) + if nodeData.kind == NodeKind.Workspace then + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Project then + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Container then + if not parent or not project then + vim.notify("Container node must have parent and project", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.PackageRoot then + if not parent or not project then + vim.notify("Package root node must have parent and project", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Package then + if not parent or not project or not rootNode then + vim.notify("Package node must have parent, project and root node", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.PrimaryType then + if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then + if not parent then + vim.notify("Primary type node must have parent", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + end + elseif nodeData.kind == NodeKind.Folder then + if not parent or not project or not rootNode then + vim.notify("Folder node must have parent, project and root node", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif + nodeData.kind == NodeKind.CompilationUnit + or nodeData.kind == NodeKind.ClassFile + or nodeData.kind == NodeKind.File + then + if not parent then + vim.notify("File node must have parent", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + end +end + +return M diff --git a/lua/java-deps/view/explorer_node.lua b/lua/java-deps/view/explorer_node.lua new file mode 100644 index 0000000..cf1d272 --- /dev/null +++ b/lua/java-deps/view/explorer_node.lua @@ -0,0 +1,28 @@ +---@class ExplorerNode +---@field _parent? ExplorerNode + +local M = {} + +local ExplorerNode = {} +ExplorerNode.__index = ExplorerNode + +function ExplorerNode:new() + return setmetatable({}, self) +end + +---@param node ExplorerNode +---@param levelToCheck number +function ExplorerNode:isItselfOrAncestorOf(node, levelToCheck) + levelToCheck = levelToCheck or math.huge + while node and levelToCheck >= 0 do + if self == node then + return true + end + node = node._parent + levelToCheck = levelToCheck - 1 + end + return false +end + +M.ExplorerNode = ExplorerNode +return M diff --git a/lua/java-deps/view/node.lua b/lua/java-deps/view/node.lua deleted file mode 100644 index fa75179..0000000 --- a/lua/java-deps/view/node.lua +++ /dev/null @@ -1,19 +0,0 @@ -local M = {} - -DataNode = {} - -function DataNode:new(parent) - local o = parent or {} - setmetatable(o, self) - self.__index = self - return o -end -function DataNode:add_child(child) - if self.childrens == nil then - self.childrens = {} - end - table.insert(self.childrens, child) - child.parent = self -end - -return M From b87cfda4ce0fd4b7aac1647e98425c3d48861242 Mon Sep 17 00:00:00 2001 From: luokai Date: Thu, 8 Aug 2024 23:31:33 +0800 Subject: [PATCH 02/11] data_node --- lua/java-deps.lua | 16 +- lua/java-deps/folding.lua | 44 -- .../java/hieararchicalPackageNodeData.lua | 58 ++- lua/java-deps/java/nodeData.lua | 4 + lua/java-deps/parser.lua | 20 +- lua/java-deps/preview.lua | 24 - lua/java-deps/providers/init.lua | 32 -- lua/java-deps/providers/nvim-lsp.lua | 17 - lua/java-deps/symbols.lua | 118 ----- lua/java-deps/ui.lua | 58 --- lua/java-deps/utils/init.lua | 130 ------ lua/java-deps/utils/table.lua | 31 -- lua/java-deps/view/data_node.lua | 204 -------- lua/java-deps/views/data_node.lua | 437 ++++++++++++++++++ lua/java-deps/views/data_provider.lua | 34 ++ .../{view => views}/explorer_node.lua | 0 lua/java-deps/writer.lua | 1 - 17 files changed, 530 insertions(+), 698 deletions(-) delete mode 100644 lua/java-deps/folding.lua delete mode 100644 lua/java-deps/providers/init.lua delete mode 100644 lua/java-deps/providers/nvim-lsp.lua delete mode 100644 lua/java-deps/symbols.lua delete mode 100644 lua/java-deps/ui.lua delete mode 100644 lua/java-deps/utils/init.lua delete mode 100644 lua/java-deps/utils/table.lua delete mode 100644 lua/java-deps/view/data_node.lua create mode 100644 lua/java-deps/views/data_node.lua create mode 100644 lua/java-deps/views/data_provider.lua rename lua/java-deps/{view => views}/explorer_node.lua (100%) diff --git a/lua/java-deps.lua b/lua/java-deps.lua index e3e6dae..fbdf7b1 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps.lua @@ -1,6 +1,7 @@ local jdtls = require("java-deps.java.jdtls") local config = require("java-deps.config") local View = require("java-deps.view") +local data_node = require("java-deps.views.data_node") local M = { view = nil, @@ -14,10 +15,17 @@ local M = { }, } -function handle_projects(projects) +local function handle_projects(projects) if not projects or #projects < 1 then return end + local project_nodes = {} + for _, project in ipairs(projects) do + if project then + local root = data_node.createNode(project) + table.insert(project_nodes, root) + end + end end function M.toggle_outline() @@ -32,8 +40,10 @@ function M.open_outline() if not M.view:is_open() then M.state.code_buf = vim.api.nvim_get_current_buf() local uri = vim.uri_from_fname(jdtls.root_dir()) - local resp = jdtls.getProjects(uri) - handle_projects(resp) + vim.defer_fn(function() + local resp = jdtls.getProjects(uri) + handle_projects(resp) + end, 0) end end diff --git a/lua/java-deps/folding.lua b/lua/java-deps/folding.lua deleted file mode 100644 index 9ca94b4..0000000 --- a/lua/java-deps/folding.lua +++ /dev/null @@ -1,44 +0,0 @@ -local M = {} -local config = require("java-deps.config") -local node_kind = require("java-deps.symbols").NodeKind - -local is_pkg = function(node) - if - node_kind.Workspace == node.kind - or node_kind.Container == node.kind - or node_kind.Project == node.kind - or node_kind.PackageRoot == node.kind - or node_kind.Package == node.kind - or node_kind.Folder == node.kind - then - return true - end - return false -end -M.is_foldable = function(node) - if node.children and #node.children > 0 then - return true - end - return is_pkg(node) -end - -local get_default_folded = function(depth) - local fold_past = config.options.autofold_depth - if not fold_past then - return false - else - return depth >= fold_past - end -end - -M.is_folded = function(node) - if node.folded ~= nil then - return node.folded - elseif node.hovered and config.options.auto_unfold_hover then - return false - else - return get_default_folded(node.depth) - end -end - -return M diff --git a/lua/java-deps/java/hieararchicalPackageNodeData.lua b/lua/java-deps/java/hieararchicalPackageNodeData.lua index a017163..20b8ee1 100644 --- a/lua/java-deps/java/hieararchicalPackageNodeData.lua +++ b/lua/java-deps/java/hieararchicalPackageNodeData.lua @@ -1,27 +1,29 @@ -local nodeData = require("java-deps.java.nodeData") -local NodeKind = nodeData.NodeKind -local INodeData = nodeData.INodeData +local _n = require("java-deps.java.nodeData") +local NodeKind = _n.NodeKind +local INodeData = _n.INodeData +local M = {} ---@class HierarchicalPackageNodeData: INodeData ---@field displayName string ---@field name string ---@field nodeData? INodeData ---@field children HierarchicalPackageNodeData[] -local HieararchicalPackageNodeData = nodeData.INodeData:new() -HieararchicalPackageNodeData.__index = HieararchicalPackageNodeData +local HierarchicalPackageNodeData = INodeData:new() +HierarchicalPackageNodeData.__index = HierarchicalPackageNodeData ---@param displayName string ---@param parentName? string -function HieararchicalPackageNodeData:new(displayName, parentName) +---@return HierarchicalPackageNodeData +function HierarchicalPackageNodeData:new(displayName, parentName) local name = (parentName == nil or parentName == "") and displayName or parentName .. "." .. displayName - local base = INodeData:new() - base.displayName = displayName - base.name = name - base.children = {} - return setmetatable(base, self) + return setmetatable({ + displayName = displayName, + name = name, + children = {}, + }, self) end -function HieararchicalPackageNodeData:compressTree() +function HierarchicalPackageNodeData:compressTree() while self.name ~= "" and #self.children == 1 and not self:isPackage() do local child = self.children[1] self.name = self.name .. "." .. child.displayName @@ -35,9 +37,10 @@ function HieararchicalPackageNodeData:compressTree() end ---@param packages string[] ---@param _nodeData INodeData -function HieararchicalPackageNodeData:addSubPackage(packages, _nodeData) +function HierarchicalPackageNodeData:addSubPackage(packages, _nodeData) if #packages == 0 then self.nodeData = _nodeData + -- TODO return end local subPackageDisplayName = table.remove(packages, 1) @@ -51,32 +54,45 @@ function HieararchicalPackageNodeData:addSubPackage(packages, _nodeData) if childNode then childNode:addSubPackage(packages, _nodeData) else - local newNode = HieararchicalPackageNodeData:new(subPackageDisplayName, self.name) + local newNode = HierarchicalPackageNodeData:new(subPackageDisplayName, self.name) newNode:addSubPackage(packages, _nodeData) table.insert(self.children, newNode) end end -function HieararchicalPackageNodeData:getUri() +function HierarchicalPackageNodeData:get_getUri() return self.nodeData and self.nodeData.uri end -function HieararchicalPackageNodeData:moduleName() +function HierarchicalPackageNodeData:get_moduleName() return self.nodeData and self.nodeData.moduleName end -function HieararchicalPackageNodeData:path() +function HierarchicalPackageNodeData:get_path() return self.nodeData and self.nodeData.path end -function HieararchicalPackageNodeData:kind() +function HierarchicalPackageNodeData:get_kind() return self.nodeData and self.nodeData.kind or NodeKind.Package end -function HieararchicalPackageNodeData:isPackage() +function HierarchicalPackageNodeData:isPackage() return self.nodeData ~= nil end -function HieararchicalPackageNodeData:handlerIdentifier() +function HierarchicalPackageNodeData:handlerIdentifier() return self.nodeData and self.nodeData.handlerIdentifier end +M.HierarchicalPackageNodeData = HierarchicalPackageNodeData -return HieararchicalPackageNodeData +---@param packageList INodeData[] +---@return HierarchicalPackageNodeData +M.createHierarchicalNodeDataByPackageList = function(packageList) + local result = HierarchicalPackageNodeData:new("", "") + for _, nodeData in ipairs(packageList) do + local packages = vim.split(nodeData.name, "%.") + result:addSubPackage(packages, nodeData) + end + result:compressTree() + return result +end + +return M diff --git a/lua/java-deps/java/nodeData.lua b/lua/java-deps/java/nodeData.lua index 4485a08..9893e13 100644 --- a/lua/java-deps/java/nodeData.lua +++ b/lua/java-deps/java/nodeData.lua @@ -36,6 +36,10 @@ function INodeData:new() return setmetatable({}, self) end +function INodeData:print() + print(vim.inspect(self)) +end + M.INodeData = INodeData return M diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index a5726dc..7bc201a 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -4,30 +4,20 @@ local folding = require("java-deps.folding") local t_utils = require("java-deps.utils.table") local ui = require("java-deps.ui") local M = {} - -local function parse_result(result, depth, hierarchy, parent) +---@param result DataNode[] +---@param depth integer +---@return table +local function parse_result(result, depth) local ret = nil for index, value in pairs(result) do - local hir = hierarchy or {} - -- how many parents this node has, 1 is the lowest value because its - -- easier to work it local level = depth or 1 -- whether this node is the last in its group local isLast = index == #result local node = { - entryKind = value.entryKind, - metaData = value.metaData, - handlerIdentifier = value.handlerIdentifier, kind = value.kind, - uri = value.uri, - path = value.path, - name = value.name, - icon = symbols.icon_from_kind(value), depth = level, - isLast = isLast, - hierarchy = hir, parent = parent, } if ret == nil then @@ -41,7 +31,7 @@ local function parse_result(result, depth, hierarchy, parent) -- copy by value because we dont want it messing with the hir table local child_hir = t_utils.array_copy(hir) table.insert(child_hir, isLast) - children = parse_result(value.children, level + 1, child_hir, node) + children = parse_result(value._childrenNodes, level + 1, child_hir, node) end node.children = children diff --git a/lua/java-deps/preview.lua b/lua/java-deps/preview.lua index d77c7b3..3b08731 100644 --- a/lua/java-deps/preview.lua +++ b/lua/java-deps/preview.lua @@ -172,30 +172,6 @@ local function set_bg_hl() vim.api.nvim_win_set_option(state.hover_win, "winblend", winblend) end -local function show_preview() - if state.preview_win == nil and state.preview_buf == nil then - state.preview_buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_attach(state.preview_buf, false, { - on_detach = function() - state.preview_buf = nil - state.preview_win = nil - end, - }) - local offsets = get_offset() - state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, { - relative = "win", - width = 50, - height = get_height(), - bufpos = { 0, 0 }, - row = offsets[1], - col = offsets[2], - border = config.options.border, - }) - setup_preview_buf() - else - update_preview() - end -end local function show_hover() if state.hover_win == nil and state.hover_buf == nil then diff --git a/lua/java-deps/providers/init.lua b/lua/java-deps/providers/init.lua deleted file mode 100644 index 6a128b2..0000000 --- a/lua/java-deps/providers/init.lua +++ /dev/null @@ -1,32 +0,0 @@ -local M = {} - -local providers = { - "java-deps/providers/nvim-lsp", -} - -_G._java_deps_outline_current_provider = nil - -function M.has_provider() - local ret = false - for _, value in ipairs(providers) do - local provider = require(value) - if provider.should_use_provider(0) then - ret = true - break - end - end - return ret -end - -function M.request_symbols(bufnr, node, on_symbols) - for _, value in ipairs(providers) do - local provider = require(value) - if provider.should_use_provider(0) then - _G._java_deps_outline_current_provider = provider - provider.request_symbols(bufnr, node, on_symbols) - break - end - end -end - -return M diff --git a/lua/java-deps/providers/nvim-lsp.lua b/lua/java-deps/providers/nvim-lsp.lua deleted file mode 100644 index 2aa9b5d..0000000 --- a/lua/java-deps/providers/nvim-lsp.lua +++ /dev/null @@ -1,17 +0,0 @@ -local M = {} - -function M.hover_info(bufnr, params, on_info) - on_info(nil, { - contents = { - kind = "markdown", - content = { "No extra information availaible!" }, - }, - }) -end - --- probably change this -function M.should_use_provider(bufnr) - return true -end - -return M diff --git a/lua/java-deps/symbols.lua b/lua/java-deps/symbols.lua deleted file mode 100644 index 2e8b1aa..0000000 --- a/lua/java-deps/symbols.lua +++ /dev/null @@ -1,118 +0,0 @@ -local config = require("java-deps.config") -local M = {} -M.NodeKind = { - Workspace = 1, - Project = 2, - PackageRoot = 3, - Package = 4, - PrimaryType = 5, - CompilationUnit = 6, - ClassFile = 7, - Container = 8, - Folder = 9, - File = 10, - - -- metaData.TypeKind - CLASS = 11, - INTERFACE = 12, - ENUM = 13, - - JAR = 24, -} - -M.TypeKind = { - Class = 1, - Interface = 2, - Enum = 3, -} - -M.kinds = { - "Workspace", - "Project", - "PackageRoot", - "Package", - "PrimaryType", - "CompilationUnit", - "ClassFile", - "Container", - "Folder", - "File", - - -- metaData.TypeKind - [M.NodeKind.CLASS] = "CLASS", - [M.NodeKind.INTERFACE] = "INTERFACE", - [M.NodeKind.ENUM] = "ENUM", - - [M.NodeKind.JAR] = "JAR", -} - -M.ContainerEntryKind = { - CPE_LIBRARY = 1, - CPE_PROJECT = 2, - CPE_SOURCE = 3, - CPE_VARIABLE = 4, - CPE_CONTAINER = 5, -} -M.PackageRootKind = { - K_SOURCE = 1, - K_BINARY = 2, -} - -M.ContainerType = { - JRE = "jre", - Maven = "maven", - Gradle = "gradle", - ReferencedLibrary = "referencedLibrary", - Unknown = "", -} - -M.ContainerPath = { - JRE = "org.eclipse.jdt.launching.JRE_CONTAINER", - Maven = "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER", - Gradle = "org.eclipse.buildship.core.gradleclasspathcontainer", - ReferencedLibrary = "REFERENCED_LIBRARIES_PATH", -} - -M.ContainerType = { - JRE = "jre", - Maven = "maven", - Gradle = "gradle", - ReferencedLibrary = "referencedLibrary", - Unknown = "", -} - -M.type_kind = function(node) - if node.metaData and node.metaData.TypeKind then - return node.metaData.TypeKind + 10 - end - if node.name and vim.endswith(node.name, ".jar") then - return M.NodeKind.JAR - end - return node.kind -end - -function M.icon_from_kind(node) - local symbols = config.options.symbols - - if type(node) == "string" then - return symbols[node].icon - end - - local kind = M.type_kind(node) - return symbols[M.kinds[kind]].icon -end - -function M.get_container_type(containerPath) - if vim.startswith(containerPath, M.ContainerPath.JRE) then - return M.ContainerType.JRE - elseif vim.startswith(containerPath, M.ContainerPath.Maven) then - return M.ContainerType.Maven - elseif vim.startswith(containerPath, M.ContainerPath.Gradle) then - return M.ContainerType.Gradle - elseif vim.startswith(containerPath, M.ContainerPath.ReferencedLibrary) then - return M.ContainerType.ReferencedLibrary - end - return M.ContainerType.Unknown -end - -return M diff --git a/lua/java-deps/ui.lua b/lua/java-deps/ui.lua deleted file mode 100644 index 032ff78..0000000 --- a/lua/java-deps/ui.lua +++ /dev/null @@ -1,58 +0,0 @@ -local M = {} - -M.markers = { - bottom = '└', - middle = '├', - vertical = '│', - horizontal = '─', -} - -M.hovered_hl_ns = vim.api.nvim_create_namespace 'hovered_item' - -function M.clear_hover_highlight(bufnr) - vim.api.nvim_buf_clear_namespace(bufnr, M.hovered_hl_ns, 0, -1) -end - -function M.add_hover_highlight(bufnr, line, col_start) - vim.api.nvim_buf_add_highlight( - bufnr, - M.hovered_hl_ns, - 'FocusedSymbol', - line, - col_start, - -1 - ) -end - -function M.setup_highlights() - -- Setup the FocusedSymbol highlight group if it hasn't been done already by - -- a theme or manually set - if vim.fn.hlexists 'FocusedSymbol' == 0 then - local cline_hl = vim.api.nvim_get_hl_by_name('CursorLine', true) - local string_hl = vim.api.nvim_get_hl_by_name('String', true) - - vim.api.nvim_set_hl( - 0, - 'FocusedSymbol', - { bg = cline_hl.background, fg = string_hl.foreground } - ) - end - - -- Some colorschemes do some funky things with the comment highlight, most - -- notably making them italic, which messes up the outline connector. Fix - -- this by copying the foreground color from the comment hl into a new - -- highlight. - local comment_fg_gui = vim.fn.synIDattr( - vim.fn.synIDtrans(vim.fn.hlID 'Comment'), - 'fg', - 'gui' - ) - - if vim.fn.hlexists 'JavaDespOutlineConnector' == 0 then - vim.cmd( - string.format('hi JavaDespOutlineConnector guifg=%s', comment_fg_gui) - ) - end -end - -return M diff --git a/lua/java-deps/utils/init.lua b/lua/java-deps/utils/init.lua deleted file mode 100644 index d48ec19..0000000 --- a/lua/java-deps/utils/init.lua +++ /dev/null @@ -1,130 +0,0 @@ -local M = {} - -M.is_instance = function(obj, class) - local mt = getmetatable(obj) - while mt do - if mt == class then - return true - end - mt = getmetatable(mt) - end - return false -end - -M.super = function(obj, class) - local mt = getmetatable(obj) - while mt do - if mt == class then - return mt.__index - end - mt = getmetatable(mt) - end - return nil -end - ----maps the table|string of keys to the action ----@param keys table|string ----@param action function|string -function M.nmap(bufnr, keys, action) - if type(keys) == "string" then - keys = { keys } - end - - for _, lhs in ipairs(keys) do - vim.keymap.set("n", lhs, action, { silent = true, noremap = true, buffer = bufnr }) - end -end - ---- @param f function ---- @param delay number ---- @return function -function M.debounce(f, delay) - local timer = vim.loop.new_timer() - - return function(...) - local args = { ... } - - timer:start( - delay, - 0, - vim.schedule_wrap(function() - timer:stop() - f(unpack(args)) - end) - ) - end -end - -function M.items_dfs(callback, children) - for _, val in ipairs(children) do - callback(val) - - if val.children then - M.items_dfs(callback, val.children) - end - end -end - ----Merges a symbol tree recursively, only replacing nodes ----which have changed. This will maintain the folding ----status of any unchanged nodes. ----@param new_node table New node ----@param old_node table Old node ----@param index? number Index of old_item in parent ----@param parent? table Parent of old_item -M.merge_items_rec = function(new_node, old_node, index, parent) - local failed = false - - if not new_node or not old_node then - failed = true - else - for key, _ in pairs(new_node) do - if - vim.tbl_contains({ - "parent", - "children", - "folded", - "hovered", - "line_in_outline", - "hierarchy", - }, key) - then - goto continue - end - - if key == "name" then - -- in the case of a rename, just rename the existing node - old_node["name"] = new_node["name"] - else - if not vim.deep_equal(new_node[key], old_node[key]) then - failed = true - break - end - end - - ::continue:: - end - end - - if failed then - if parent and index then - parent[index] = new_node - end - else - local next_new_item = new_node.children or {} - - -- in case new children are created on a node which - -- previously had no children - if #next_new_item > 0 and not old_node.children then - old_node.children = {} - end - - local next_old_item = old_node.children or {} - - for i = 1, math.max(#next_new_item, #next_old_item) do - M.merge_items_rec(next_new_item[i], next_old_item[i], i, next_old_item) - end - end -end - -return M diff --git a/lua/java-deps/utils/table.lua b/lua/java-deps/utils/table.lua deleted file mode 100644 index 62246ff..0000000 --- a/lua/java-deps/utils/table.lua +++ /dev/null @@ -1,31 +0,0 @@ -local M = {} - -function M.table_to_str(t) - local ret = "" - for _, value in ipairs(t) do - ret = ret .. tostring(value) - end - return ret -end - -function M.str_to_table(str) - local t = {} - for i = 1, #str do - t[i] = str:sub(i, i) - end - return t -end - ---- Copies an array and returns it because lua usually does references ----@generic T ----@param t T[] ----@return T[] -function M.array_copy(t) - local ret = {} - for _, value in ipairs(t) do - table.insert(ret, value) - end - return ret -end - -return M diff --git a/lua/java-deps/view/data_node.lua b/lua/java-deps/view/data_node.lua deleted file mode 100644 index a4b92af..0000000 --- a/lua/java-deps/view/data_node.lua +++ /dev/null @@ -1,204 +0,0 @@ -local NodeKind = require("java-deps.java.nodeData").NodeKind -local jdtls = require("java-deps.java.jdtls") -local ExplorerNode = require("java-deps.view.explorer_node").ExplorerNode - -local util = require("java-deps.utils") -local M = {} - -M.K_TYPE_KIND = "TypeKind" ----@class DataNode: ExplorerNode ----@field _childrenNodes ExplorerNode[] ----@field _nodeData INodeData ----@field _parent DataNode? ----@field _project DataNode? ----@field _rootNode DataNode? -local DataNode = ExplorerNode:new() -DataNode.__index = DataNode - ----@param nodeData INodeData ----@param parent DataNode? ----@param project DataNode? ----@param rootNode DataNode? -function DataNode:new(nodeData, parent, project, rootNode) - local data = setmetatable(ExplorerNode:new(), self) - data._nodeData = nodeData - data._parent = parent - data._project = project - data._rootNode = rootNode -end - -function DataNode:createChildNodeList() - local kind = self:kind() - if kind == NodeKind.Workspace then - if self._nodeData.children then - return vim.tbl_map(function(child) - return M.createNode(child, self, nil, nil) - end, self._nodeData.children) - end - elseif kind == NodeKind.Project then - if self._nodeData.children then - return vim.tbl_map(function(child) - return M.createNode(child, self, self, nil) - end, self._nodeData.children) - end - end -end - -function DataNode:loadData() - local kind = self:kind() - if kind == NodeKind.Workspace then - return jdtls.getProjects(self._nodeData.uri) - elseif kind == NodeKind.Project then - return jdtls.getPackageData({ - kind = NodeKind.Project, - projectUri = self._nodeData.uri, - }) - elseif kind == NodeKind.Container then - return jdtls.getPackageData({ - kind = NodeKind.Container, - projectUri = self._nodeData.uri, - path = self._nodeData.path, - }) - elseif kind == NodeKind.PackageRoot then - return jdtls.getPackageData({ - kind = NodeKind.PackageRoot, - projectUri = self._project._nodeData.uri, - rootPath = self._nodeData.path, - handlerIdentifier = self._nodeData.handlerIdentifier, - isHierarchicalView = true, - }) - elseif kind == NodeKind.Package then - return jdtls.getPackageData({ - kind = NodeKind.Package, - projectUri = self._project._nodeData.uri, - path = self._nodeData.name, - handlerIdentifier = self._nodeData.handlerIdentifier, - }) - elseif kind == NodeKind.Folder then - return jdtls.getPackageData({ - kind = NodeKind.Folder, - projectUri = self._project._nodeData.uri, - path = self._nodeData.path, - rootPath = self._rootNode and self._rootNode._nodeData.path or nil, - handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier, - }) - elseif kind == NodeKind.PrimaryType then - return nil - elseif kind == NodeKind.Folder then - return jdtls.getPackageData({ - kind = NodeKind.Folder, - projectUri = self._project._nodeData.uri, - path = self._nodeData.path, - rootPath = self._rootNode and self._rootNode._nodeData.path or nil, - handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier or nil, - }) - else - return nil - end -end - -function DataNode:icon() end -function DataNode:kind() - return self._nodeData.kind -end - -function DataNode:sort() - table.sort(self._childrenNodes, function(a, b) - ---@diagnostic disable: undefined-field - if a._nodeData.kind and a._nodeData.kind then - if a._nodeData.kind == b._nodeData.kind then - return a._nodeData.name < b._nodeData.name and false or true - else - return a._nodeData.kind - b._nodeData.kind - end - end - return false - end) -end - ----@param paths INodeData[] -function DataNode:revealPaths(paths) - if #paths == 0 then - return self - end - local childNodeData = table.remove(paths, 1) - local children = self:getChildren() - for _, child in ipairs(children) do - if - util.is_instance(child, DataNode) - and child._nodeData.name == childNodeData.name - and child.path == childNodeData.path - then - return #paths > 0 and child:revealPaths(paths) or child - end - end -end - -function DataNode:getChildren() - if not self._nodeData.children then - local data = self:loadData() - self._nodeData.children = data - self._childrenNodes = self:createChildNodeList() or {} - self:sort() - return self._childrenNodes - end - return self._childrenNodes -end - -M.DataNode = DataNode - ----@param nodeData INodeData ----@param parent DataNode? ----@param project DataNode? ----@param rootNode DataNode? -M.createNode = function(nodeData, parent, project, rootNode) - if nodeData.kind == NodeKind.Workspace then - return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.Project then - return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.Container then - if not parent or not project then - vim.notify("Container node must have parent and project", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.PackageRoot then - if not parent or not project then - vim.notify("Package root node must have parent and project", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.Package then - if not parent or not project or not rootNode then - vim.notify("Package node must have parent, project and root node", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.PrimaryType then - if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then - if not parent then - vim.notify("Primary type node must have parent", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - end - elseif nodeData.kind == NodeKind.Folder then - if not parent or not project or not rootNode then - vim.notify("Folder node must have parent, project and root node", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - elseif - nodeData.kind == NodeKind.CompilationUnit - or nodeData.kind == NodeKind.ClassFile - or nodeData.kind == NodeKind.File - then - if not parent then - vim.notify("File node must have parent", vim.log.levels.ERROR) - return nil - end - return DataNode:new(nodeData, parent, project, rootNode) - end -end - -return M diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua new file mode 100644 index 0000000..0dadd46 --- /dev/null +++ b/lua/java-deps/views/data_node.lua @@ -0,0 +1,437 @@ +local NodeKind = require("java-deps.java.nodeData").NodeKind +local jdtls = require("java-deps.java.jdtls") +local ExplorerNode = require("java-deps.views.explorer_node").ExplorerNode +local hieararchicalPackageNodeData = require("java-deps.java.hieararchicalPackageNodeData") + +local M = {} + +M.K_TYPE_KIND = "TypeKind" +M.NATURE_ID = "NatureId" + +M.NatureId = { + Maven = "org.eclipse.m2e.core.maven2Nature", + Gradle = "org.eclipse.buildship.core.gradleprojectnature", + BspGradle = "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature", + UnmanagedFolder = "org.eclipse.jdt.ls.core.unmanagedFolder", + Java = "org.eclipse.jdt.core.javanature", +} + +M.ReadableNature = { + Maven = "maven", + Gradle = "gradle", + BspGradle = "bsp-gradle", + UnmanagedFolder = "unmanagedFolder", + Java = "java", +} +M.NatureIdMap = { + [M.NatureId.Maven] = M.ReadableNature.Maven, + [M.NatureId.Gradle] = M.ReadableNature.Gradle, + [M.NatureId.BspGradle] = M.ReadableNature.BspGradle, + [M.NatureId.UnmanagedFolder] = M.ReadableNature.UnmanagedFolder, + [M.NatureId.Java] = M.ReadableNature.Java, +} + +M.ContainerType = { + JRE = "jre", + Maven = "maven", + Gradle = "gradle", + ReferencedLibrary = "referencedLibrary", + Unknown = "", +} + +M.ContainerPath = { + JRE = "org.eclipse.jdt.launching.JRE_CONTAINER", + Maven = "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER", + Gradle = "org.eclipse.buildship.core.gradleclasspathcontainer", + ReferencedLibrary = "REFERENCED_LIBRARIES_PATH", +} + +---@enum TreeItemCollapsibleState +local TreeItemCollapsibleState = { + None = 0, + Collapsed = 1, + Expanded = 2, +} + +---@param natureId string +---@return string +M.getProjectType = function(natureId) + return M.NatureIdMap[natureId] or "" +end + +---@class DataNode: ExplorerNode +---@field _childrenNodes ExplorerNode[] +---@field _nodeData INodeData +---@field _parent DataNode? +---@field _project DataNode? +---@field _rootNode DataNode? +---@field _hierarchicalNode boolean +local DataNode = ExplorerNode:new() + +DataNode.__index = DataNode +DataNode._hierarchicalNode = false + +---@param nodeData INodeData +---@param parent DataNode? +---@param project DataNode? +---@param rootNode DataNode? +---@return DataNode +function DataNode:new(nodeData, parent, project, rootNode) + local data = setmetatable(ExplorerNode:new(), self) + data._nodeData = nodeData + data._parent = parent + data._project = project + data._rootNode = rootNode + return data +end + +function DataNode:createChildNodeList() + local kind = self:kind() + if kind == NodeKind.Workspace then + if self._nodeData.children then + return vim.tbl_map(function(child) + return M.createNode(child, self, nil, nil) + end, self._nodeData.children) + end + elseif kind == NodeKind.Project then + local result = {} + local packageData = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + if child.kind == NodeKind.Package then + table.insert(packageData, child) + else + table.insert(result, M.createNode(child, self, self, nil)) + end + end + if #packageData > 0 then + local data = hieararchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData) + if data and data.children then + for _, child in ipairs(data.children) do + table.insert(result, M.createNode(child, self, self, self)) + end + end + end + end + return result + elseif kind == NodeKind.Container then + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, nil)) + end + end + return result + elseif kind == NodeKind.PackageRoot then + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self)) + end + end + return result + elseif kind == NodeKind.Package then + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self._rootNode)) + end + end + return result + elseif kind == NodeKind.Folder then + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self._rootNode)) + end + end + return result + elseif kind == NodeKind.PrimaryType then + return nil + else + return nil + end +end + +function DataNode:loadData() + local kind = self:kind() + if kind == NodeKind.Workspace then + return jdtls.getProjects(self._nodeData.uri) + elseif kind == NodeKind.Project then + return jdtls.getPackageData({ + kind = NodeKind.Project, + projectUri = self._nodeData.uri, + }) + elseif kind == NodeKind.Container then + return jdtls.getPackageData({ + kind = NodeKind.Container, + projectUri = self._nodeData.uri, + path = self._nodeData.path, + }) + elseif kind == NodeKind.PackageRoot then + return jdtls.getPackageData({ + kind = NodeKind.PackageRoot, + projectUri = self._project._nodeData.uri, + rootPath = self._nodeData.path, + handlerIdentifier = self._nodeData.handlerIdentifier, + isHierarchicalView = true, + }) + elseif kind == NodeKind.Package then + return jdtls.getPackageData({ + kind = NodeKind.Package, + projectUri = self._project._nodeData.uri, + path = self._nodeData.name, + handlerIdentifier = self._nodeData.handlerIdentifier, + }) + elseif kind == NodeKind.Folder then + return jdtls.getPackageData({ + kind = NodeKind.Folder, + projectUri = self._project._nodeData.uri, + path = self._nodeData.path, + rootPath = self._rootNode and self._rootNode._nodeData.path or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier, + }) + elseif kind == NodeKind.PrimaryType then + return nil + elseif kind == NodeKind.Folder then + return jdtls.getPackageData({ + kind = NodeKind.Folder, + projectUri = self._project._nodeData.uri, + path = self._nodeData.path, + rootPath = self._rootNode and self._rootNode._nodeData.path or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier or nil, + }) + else + return nil + end +end + +function DataNode:icon() end +function DataNode:kind() + return self._nodeData.kind +end + +function DataNode:sort() + table.sort(self._childrenNodes, function(a, b) + ---@diagnostic disable: undefined-field + if a._nodeData.kind and a._nodeData.kind then + if a._nodeData.kind == b._nodeData.kind then + return a._nodeData.name < b._nodeData.name and false or true + else + return a._nodeData.kind - b._nodeData.kind + end + end + return false + end) +end + +---@param paths INodeData[] +function DataNode:revealPaths(paths) + if #paths == 0 then + return self + end + local kind = self:kind() + if kind == NodeKind.PackageRoot then + local hierarchicalNodeData = paths[1] + local children = self:getChildren() + ---@type DataNode? + local childNode = vim.tbl_filter(function(child) + return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") + or hierarchicalNodeData.name == child._nodeData.name + end, children) + childNode = childNode and #childNode > 0 and childNode[1] or nil + if childNode and not childNode[1]._hierarchicalNode then + table.remove(paths, 1) + end + return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode + elseif kind == NodeKind.Package then + local hierarchicalNodeData = paths[1] + if hierarchicalNodeData.name == self._nodeData.name then + table.remove(paths, 1) + return self:revealPaths(paths) + else + local children = self:getChildren() + ---@type DataNode? + local childNode = vim.tbl_filter(function(child) + return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") + or hierarchicalNodeData.name == child._nodeData.name + end, children) + childNode = childNode and #childNode > 0 and childNode[1] or nil + return (childNode and #paths > 0) and childNode:revealPaths(paths) or nil + end + else + local childNodeData = table.remove(paths, 1) + local children = self:getChildren() + ---@type DataNode? + local childNode = vim.tbl_filter(function(child) + return childNodeData.name == child._nodeData.name and childNodeData.path == child._nodeData.path + end, children) + childNode = childNode and #childNode > 0 and childNode[1] or nil + return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode + end +end + +local function uniqBy(arr, fn) + local seen = {} + local result = {} + for _, value in ipairs(arr) do + local key = fn(value) + if not seen[key] then + seen[key] = true + table.insert(result, value) + end + end + return result +end +---@return ExplorerNode[] +function DataNode:getChildren() + if self:kind() == NodeKind.Package then + local data = self:loadData() + if data then + if self._nodeData.children then + for _, child in ipairs(data) do + table.insert(self._nodeData.children, child) + end + self._nodeData.children = uniqBy(self._nodeData.children, function(child) + return child.path .. child.name + end) + else + self._nodeData.children = data + end + self._childrenNodes = self:createChildNodeList() or {} + self:sort() + return self._childrenNodes + end + else + if not self._nodeData.children then + local data = self:loadData() + self._nodeData.children = data + self._childrenNodes = self:createChildNodeList() or {} + self:sort() + return self._childrenNodes + end + return self._childrenNodes + end +end + +M.DataNode = DataNode + +---@param nodeData INodeData +---@param parent DataNode? +---@param project DataNode? +---@param rootNode DataNode? +M.createNode = function(nodeData, parent, project, rootNode) + if nodeData.kind == NodeKind.Workspace then + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Project then + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Container then + if not parent or not project then + vim.notify("Container node must have parent and project", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.PackageRoot then + if not parent or not project then + vim.notify("Package root node must have parent and project", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif nodeData.kind == NodeKind.Package then + if not parent or not project or not rootNode then + vim.notify("Package node must have parent, project and root node", vim.log.levels.ERROR) + return nil + end + local data = DataNode:new(nodeData, parent, project, rootNode) + data._hierarchicalNode = true + return data + elseif nodeData.kind == NodeKind.PrimaryType then + if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then + if not parent then + vim.notify("Primary type node must have parent", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + end + elseif nodeData.kind == NodeKind.Folder then + if not parent or not project or not rootNode then + vim.notify("Folder node must have parent, project and root node", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + elseif + nodeData.kind == NodeKind.CompilationUnit + or nodeData.kind == NodeKind.ClassFile + or nodeData.kind == NodeKind.File + then + if not parent then + vim.notify("File node must have parent", vim.log.levels.ERROR) + return nil + end + return DataNode:new(nodeData, parent, project, rootNode) + end +end + +function DataNode:isUnmanagedFolder() + local natureIds = self._nodeData.metaData and self._nodeData.metaData[M.NATURE_ID] or {} + for _, natureId in ipairs(natureIds) do + if natureId == M.NatureId.UnmanagedFolder then + return true + end + end + return false +end + +function DataNode:getContainerType() + local containerPath = self._nodeData.path or "" + if containerPath.startsWith(M.ContainerPath.JRE) then + return M.ContainerType.JRE + elseif containerPath.startsWith(M.ContainerPath.Maven) then + return M.ContainerType.Maven + elseif containerPath.startsWith(M.ContainerPath.Gradle) then + return M.ContainerType.Gradle + elseif containerPath.startsWith(M.ContainerPath.ReferencedLibrary) and self._project:isUnmanagedFolder() then + return M.ContainerType.ReferencedLibrary + end + return M.ContainerType.Unknown +end + +function DataNode:description() + --TODO +end +function DataNode:command() + --TODO +end + +function DataNode:hasChildren() + return self._childrenNodes and #self._childrenNodes > 0 +end + +function DataNode:getTreeItem() + ---@type TreeItem + local item = { + label = self._nodeData.displayName or self._nodeData.name, + collapsible = self:hasChildren() and TreeItemCollapsibleState.Collapsed or TreeItemCollapsibleState.None, + } + item.description = self:description() + item.icon = self:icon() + item.command = self:command() + if self._nodeData.uri then + local kind = self:kind() + if + kind == NodeKind.Project + or kind == NodeKind.PackageRoot + or kind == NodeKind.Package + or kind == NodeKind.PrimaryType + or kind == NodeKind.CompilationUnit + or kind == NodeKind.ClassFile + or kind == NodeKind.Folder + or kind == NodeKind.File + then + item.resourceUri = self._nodeData.uri + end + end + return item +end + +return M diff --git a/lua/java-deps/views/data_provider.lua b/lua/java-deps/views/data_provider.lua new file mode 100644 index 0000000..3866c9a --- /dev/null +++ b/lua/java-deps/views/data_provider.lua @@ -0,0 +1,34 @@ +local M + +---@class TreeItem +---@field label? string +---@field id? string +---@field icon? string +---@field level? number +---@field description? string +---@field resourceUri? string +---@field command? string +---@field collapsibleState? TreeItemCollapsibleState +---@field data? ExplorerNode +local TreeItem = {} + +---@param nodes DataNode[] +---@return TreeItem[] +function M.flattenTree(nodes, _level) + local level = _level or 0 + local result = {} + for _, node in ipairs(nodes) do + local c = node:getTreeItem() + c.level = level + table.insert(result, c) + if node:hasChildren() then + local children = M.flattenTree(node._childrenNodes, level + 1) + for _, child in ipairs(children) do + table.insert(result, child) + end + end + end + return result +end + +return M diff --git a/lua/java-deps/view/explorer_node.lua b/lua/java-deps/views/explorer_node.lua similarity index 100% rename from lua/java-deps/view/explorer_node.lua rename to lua/java-deps/views/explorer_node.lua diff --git a/lua/java-deps/writer.lua b/lua/java-deps/writer.lua index f65a87f..614425d 100644 --- a/lua/java-deps/writer.lua +++ b/lua/java-deps/writer.lua @@ -1,6 +1,5 @@ local parser = require 'java-deps.parser' local config = require 'java-deps.config' -local ui = require 'java-deps.ui' local M = {} From b76325738a10209964ea6ffbbbf6459c39c45082 Mon Sep 17 00:00:00 2001 From: luokai Date: Fri, 9 Aug 2024 14:46:44 +0800 Subject: [PATCH 03/11] tree item --- lua/java-deps.lua | 20 ++++- lua/java-deps/java/jdtls.lua | 11 ++- lua/java-deps/parser.lua | 110 +++++++++----------------- lua/java-deps/view.lua | 10 ++- lua/java-deps/views/data_node.lua | 24 +++--- lua/java-deps/views/data_provider.lua | 15 ++-- lua/java-deps/writer.lua | 57 ++++--------- 7 files changed, 107 insertions(+), 140 deletions(-) diff --git a/lua/java-deps.lua b/lua/java-deps.lua index fbdf7b1..1599c9c 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps.lua @@ -2,6 +2,8 @@ local jdtls = require("java-deps.java.jdtls") local config = require("java-deps.config") local View = require("java-deps.view") local data_node = require("java-deps.views.data_node") +local provider = require("java-deps.views.data_provider") +local writer = require("java-deps.writer") local M = { view = nil, @@ -16,16 +18,21 @@ local M = { } local function handle_projects(projects) - if not projects or #projects < 1 then + if not projects or #projects == 0 then return end local project_nodes = {} for _, project in ipairs(projects) do if project then local root = data_node.createNode(project) - table.insert(project_nodes, root) + if root then + root:getChildren() + table.insert(project_nodes, root) + end end end + local result = provider.flattenTree(project_nodes, 0) + writer.parse_and_write(M.view.bufnr, result) end function M.toggle_outline() @@ -39,11 +46,16 @@ end function M.open_outline() if not M.view:is_open() then M.state.code_buf = vim.api.nvim_get_current_buf() + M.view:open() local uri = vim.uri_from_fname(jdtls.root_dir()) - vim.defer_fn(function() + local wf = coroutine.wrap(function() local resp = jdtls.getProjects(uri) handle_projects(resp) - end, 0) + end) + local ok, err = pcall(wf) + if not ok then + print(err.message or vim.inspect(err)) + end end end diff --git a/lua/java-deps/java/jdtls.lua b/lua/java-deps/java/jdtls.lua index f565d11..ca991a9 100644 --- a/lua/java-deps/java/jdtls.lua +++ b/lua/java-deps/java/jdtls.lua @@ -14,7 +14,7 @@ M.getProjects = function(params) vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} end - return resp and resp.reslut or {} + return resp or {} end M.root_dir = function() return lsp_command.get_client().root_dir @@ -28,7 +28,7 @@ M.getProjectUris = function() vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} end - return resp and resp.reslut or {} + return resp or {} end -- interface IPackageDataParam { @@ -39,13 +39,16 @@ end ---@return INodeData[] M.getPackageData = function(params) local excludePatterns = {} - local err, resp = lsp_command.execute_command(lsp_command.JAVA_GETPACKAGEDATA, params) + local err, resp = lsp_command.execute_command({ + command = lsp_command.JAVA_GETPACKAGEDATA, + arguments = params, + }) if err then vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} end ---@type INodeData[] - local nodeData = resp and resp.reslut or {} + local nodeData = resp and resp or {} -- Filter out non java resources if true then nodeData = vim.tbl_filter(function(data) diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index 7bc201a..82e1fe7 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -1,80 +1,43 @@ local config = require("java-deps.config") -local symbols = require("java-deps.symbols") -local folding = require("java-deps.folding") -local t_utils = require("java-deps.utils.table") -local ui = require("java-deps.ui") +local data_node = require("java-deps.views.data_node") local M = {} ----@param result DataNode[] ----@param depth integer ----@return table -local function parse_result(result, depth) - local ret = nil - - for index, value in pairs(result) do - local level = depth or 1 - -- whether this node is the last in its group - local isLast = index == #result - - local node = { - kind = value.kind, - depth = level, - parent = parent, - } - if ret == nil then - ret = {} - end - - table.insert(ret, node) - - local children = nil - if value.children ~= nil then - -- copy by value because we dont want it messing with the hir table - local child_hir = t_utils.array_copy(hir) - table.insert(child_hir, isLast) - children = parse_result(value._childrenNodes, level + 1, child_hir, node) - end - node.children = children +local function str_to_table(str) + local t = {} + for i = 1, #str do + t[i] = str:sub(i, i) end - return ret + return t end -M.sort_result = function(result) - table.sort(result, function(a, b) - if a.kind == b.kind then - return a.name < b.name - end - return a.kind < b.kind - end) - return result -end - -function M.parse(response, depth, hierarchy, parent) - local sorted = M.sort_result(response) - return parse_result(sorted, depth, hierarchy, parent) -end - -function M.flatten(outline_items, ret) - ret = ret or {} - for _, value in ipairs(outline_items) do - table.insert(ret, value) - value.line_in_outline = #ret - if value.children ~= nil and not folding.is_folded(value) then - M.flatten(value.children, ret) - end +local function table_to_str(t) + local ret = "" + for _, value in ipairs(t) do + ret = ret .. tostring(value) end return ret end +local guides = { + markers = { + bottom = "└", + middle = "├", + vertical = "│", + horizontal = "─", + }, +} +---@param flattened_outline_items TreeItem +---@return table +---@return table function M.get_lines(flattened_outline_items) local lines = {} local hl_info = {} for node_line, node in ipairs(flattened_outline_items) do local depth = node.depth - local marker_space = (config.options.fold_markers and 1) or 0 + local marker_space = config.options.fold_markers and 1 or 0 - local line = t_utils.str_to_table(string.rep(" ", depth + marker_space)) + local line = str_to_table(string.rep(" ", depth + marker_space)) local running_length = 1 local function add_guide_hl(from, to) @@ -96,8 +59,8 @@ function M.get_lines(flattened_outline_items) -- else add a middle marker elseif index == #line then -- add fold markers - if config.options.fold_markers and folding.is_foldable(node) then - if folding.is_folded(node) then + if config.options.fold_markers and data_node.is_folded(node) then + if data_node.is_folded(node) then line[index] = config.options.fold_markers[1] else line[index] = config.options.fold_markers[2] @@ -108,21 +71,21 @@ function M.get_lines(flattened_outline_items) -- the root level has no vertical markers elseif depth > 1 then if node.isLast then - line[index] = ui.markers.bottom - add_guide_hl(running_length, running_length + vim.fn.strlen(ui.markers.bottom) - 1) + line[index] = guides.markers.bottom + add_guide_hl(running_length, running_length + vim.fn.strlen(guides.markers.bottom) - 1) else - line[index] = ui.markers.middle - add_guide_hl(running_length, running_length + vim.fn.strlen(ui.markers.middle) - 1) + line[index] = guides.markers.middle + add_guide_hl(running_length, running_length + vim.fn.strlen(guides.markers.middle) - 1) end end -- else if the parent was not the last in its group, add a -- vertical marker because there are items under us and we need -- to point to those elseif not node.hierarchy[index] and depth > 1 then - line[index + marker_space] = ui.markers.vertical + line[index + marker_space] = guides.markers.vertical add_guide_hl( running_length - 1 + 2 * marker_space, - running_length + vim.fn.strlen(ui.markers.vertical) - 1 + 2 * marker_space + running_length + vim.fn.strlen(guides.markers.vertical) - 1 + 2 * marker_space ) end end @@ -134,13 +97,14 @@ function M.get_lines(flattened_outline_items) local final_prefix = line - local string_prefix = t_utils.table_to_str(final_prefix) + local string_prefix = table_to_str(final_prefix) - table.insert(lines, string_prefix .. node.icon .. " " .. node.name) + table.insert(lines, string_prefix .. node.icon .. " " .. node.label) local hl_start = #string_prefix local hl_end = #string_prefix + #node.icon - local hl_type = config.options.symbols[symbols.kinds[node.kind]].hl + local hl = config.options.symbols[node.kind] + local hl_type = hl and hl.hl or "@lsp.type.class" table.insert(hl_info, { node_line, hl_start, hl_end, hl_type }) node.prefix_length = #string_prefix + #node.icon + 1 @@ -152,9 +116,7 @@ function M.get_details(flattened_outline_items) local lines = {} for _, value in ipairs(flattened_outline_items) do local detail - if symbols.type_kind(value) == symbols.NodeKind.JAR then - detail = value.path - end + -- TODO table.insert(lines, detail or "") end return lines diff --git a/lua/java-deps/view.lua b/lua/java-deps/view.lua index 92ad25e..b1fdc04 100644 --- a/lua/java-deps/view.lua +++ b/lua/java-deps/view.lua @@ -1,8 +1,13 @@ local config = require("java-deps.config") + +---@class View +---@field bufnr number +---@field winnr number local View = {} +View.__index = View function View:new() - return setmetatable({ bufnr = nil, winnr = nil }, { __index = View }) + return setmetatable({ bufnr = nil, winnr = nil }, self) end ---creates the outline window and sets it up @@ -56,6 +61,9 @@ function View:close() self.winnr = nil self.bufnr = nil end +function View:open() + self:setup_view() +end function View:is_open() return self.winnr and self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) and vim.api.nvim_win_is_valid(self.winnr) diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index 0dadd46..4308835 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -47,7 +47,7 @@ M.ContainerPath = { } ---@enum TreeItemCollapsibleState -local TreeItemCollapsibleState = { +M.TreeItemCollapsibleState = { None = 0, Collapsed = 1, Expanded = 2, @@ -77,7 +77,7 @@ DataNode._hierarchicalNode = false ---@param rootNode DataNode? ---@return DataNode function DataNode:new(nodeData, parent, project, rootNode) - local data = setmetatable(ExplorerNode:new(), self) + local data = setmetatable({}, self) data._nodeData = nodeData data._parent = parent data._project = project @@ -206,7 +206,9 @@ function DataNode:loadData() end end -function DataNode:icon() end +function DataNode:icon() + return "C" +end function DataNode:kind() return self._nodeData.kind end @@ -214,11 +216,11 @@ end function DataNode:sort() table.sort(self._childrenNodes, function(a, b) ---@diagnostic disable: undefined-field - if a._nodeData.kind and a._nodeData.kind then + if a._nodeData.kind and b._nodeData.kind and a._nodeData.name and b._nodeData.name then if a._nodeData.kind == b._nodeData.kind then - return a._nodeData.name < b._nodeData.name and false or true + return a._nodeData.name < b._nodeData.name else - return a._nodeData.kind - b._nodeData.kind + return a._nodeData.kind < b._nodeData.kind end end return false @@ -300,15 +302,14 @@ function DataNode:getChildren() end self._childrenNodes = self:createChildNodeList() or {} self:sort() - return self._childrenNodes end + return self._childrenNodes else if not self._nodeData.children then local data = self:loadData() self._nodeData.children = data self._childrenNodes = self:createChildNodeList() or {} self:sort() - return self._childrenNodes end return self._childrenNodes end @@ -407,15 +408,20 @@ function DataNode:hasChildren() return self._childrenNodes and #self._childrenNodes > 0 end +function M.is_folded(tree) + return tree.collapsible == M.TreeItemCollapsibleState.Collapsed +end + function DataNode:getTreeItem() ---@type TreeItem local item = { label = self._nodeData.displayName or self._nodeData.name, - collapsible = self:hasChildren() and TreeItemCollapsibleState.Collapsed or TreeItemCollapsibleState.None, + collapsible = self:hasChildren() and M.TreeItemCollapsibleState.Collapsed or M.TreeItemCollapsibleState.None, } item.description = self:description() item.icon = self:icon() item.command = self:command() + item.data = self if self._nodeData.uri then local kind = self:kind() if diff --git a/lua/java-deps/views/data_provider.lua b/lua/java-deps/views/data_provider.lua index 3866c9a..137e570 100644 --- a/lua/java-deps/views/data_provider.lua +++ b/lua/java-deps/views/data_provider.lua @@ -1,15 +1,17 @@ -local M +local M = {} ---@class TreeItem ---@field label? string ---@field id? string ---@field icon? string ----@field level? number +---@field depth? number ---@field description? string ---@field resourceUri? string ---@field command? string ---@field collapsibleState? TreeItemCollapsibleState ----@field data? ExplorerNode +---@field isLast? boolean +---@field data? DataNode +---@field hierarchy? table local TreeItem = {} ---@param nodes DataNode[] @@ -17,9 +19,12 @@ local TreeItem = {} function M.flattenTree(nodes, _level) local level = _level or 0 local result = {} - for _, node in ipairs(nodes) do + for idx, node in ipairs(nodes) do local c = node:getTreeItem() - c.level = level + c.depth = level + if idx == #nodes then + c.isLast = true + end table.insert(result, c) if node:hasChildren() then local children = M.flattenTree(node._childrenNodes, level + 1) diff --git a/lua/java-deps/writer.lua b/lua/java-deps/writer.lua index 614425d..970e606 100644 --- a/lua/java-deps/writer.lua +++ b/lua/java-deps/writer.lua @@ -1,43 +1,36 @@ -local parser = require 'java-deps.parser' -local config = require 'java-deps.config' +local parser = require("java-deps.parser") +local config = require("java-deps.config") local M = {} local function is_buffer_outline(bufnr) local isValid = vim.api.nvim_buf_is_valid(bufnr) local name = vim.api.nvim_buf_get_name(bufnr) - local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') - return string.match(name, 'JavaProjects') ~= nil and ft == 'JavaProjects' and isValid + local ft = vim.api.nvim_buf_get_option(bufnr, "filetype") + return string.match(name, "JavaProjects") ~= nil and ft == "JavaProjects" and isValid end -local hlns = vim.api.nvim_create_namespace 'java-deps-outline-icon-highlight' +local hlns = vim.api.nvim_create_namespace("java-deps-outline-icon-highlight") function M.write_outline(bufnr, lines) if not is_buffer_outline(bufnr) then return end - vim.api.nvim_buf_set_option(bufnr, 'modifiable', true) + vim.api.nvim_buf_set_option(bufnr, "modifiable", true) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) - vim.api.nvim_buf_set_option(bufnr, 'modifiable', false) + vim.api.nvim_buf_set_option(bufnr, "modifiable", false) end function M.add_highlights(bufnr, hl_info, nodes) for _, line_hl in ipairs(hl_info) do local line, hl_start, hl_end, hl_type = unpack(line_hl) - vim.api.nvim_buf_add_highlight( - bufnr, - hlns, - hl_type, - line - 1, - hl_start, - hl_end - ) + vim.api.nvim_buf_add_highlight(bufnr, hlns, hl_type, line - 1, hl_start, hl_end) end M.add_hover_highlights(bufnr, nodes) end -local ns = vim.api.nvim_create_namespace 'java-deps-outline-virt-text' +local ns = vim.api.nvim_create_namespace("java-deps-outline-virt-text") function M.write_details(bufnr, lines) if not is_buffer_outline(bufnr) then @@ -46,9 +39,9 @@ function M.write_details(bufnr, lines) for index, value in ipairs(lines) do vim.api.nvim_buf_set_extmark(bufnr, ns, index - 1, -1, { - virt_text = { { value, 'Comment' } }, - virt_text_pos = 'eol', - hl_mode = 'combine', + virt_text = { { value, "Comment" } }, + virt_text_pos = "eol", + hl_mode = "combine", }) end end @@ -57,31 +50,9 @@ local function clear_virt_text(bufnr) vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1) end -M.add_hover_highlights = function(bufnr, nodes) - if not config.options.highlight_hovered_item then - return - end - - -- clear old highlight - ui.clear_hover_highlight(bufnr) - for _, node in ipairs(nodes) do - if not node.hovered then - goto continue - end - - if node.prefix_length then - ui.add_hover_highlight( - bufnr, - node.line_in_outline - 1, - node.prefix_length - ) - end - ::continue:: - end -end --- runs the whole writing routine where the text is cleared, new data is parsed --- and then written +---@param bufnr integer +---@param flattened_outline_items TreeItem function M.parse_and_write(bufnr, flattened_outline_items) local lines, hl_info = parser.get_lines(flattened_outline_items) M.write_outline(bufnr, lines) From 7dd617d7ee2da579bdc4eafb9a847b74353dd1b4 Mon Sep 17 00:00:00 2001 From: luokai Date: Sat, 10 Aug 2024 09:26:19 +0800 Subject: [PATCH 04/11] type --- lua/java-deps/views/data_node.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index 4308835..d58de82 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -236,11 +236,12 @@ function DataNode:revealPaths(paths) if kind == NodeKind.PackageRoot then local hierarchicalNodeData = paths[1] local children = self:getChildren() - ---@type DataNode? + ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") or hierarchicalNodeData.name == child._nodeData.name end, children) + ---@type DataNode? childNode = childNode and #childNode > 0 and childNode[1] or nil if childNode and not childNode[1]._hierarchicalNode then table.remove(paths, 1) @@ -253,11 +254,12 @@ function DataNode:revealPaths(paths) return self:revealPaths(paths) else local children = self:getChildren() - ---@type DataNode? + ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") or hierarchicalNodeData.name == child._nodeData.name end, children) + ---@type DataNode? childNode = childNode and #childNode > 0 and childNode[1] or nil return (childNode and #paths > 0) and childNode:revealPaths(paths) or nil end From b7959f8ff1926504ac688bfe86acc6f5ed4224f6 Mon Sep 17 00:00:00 2001 From: luokai Date: Sun, 11 Aug 2024 11:43:17 +0800 Subject: [PATCH 05/11] resolvePath --- lua/java-deps.lua | 28 +++------ lua/java-deps/config.lua | 1 - lua/java-deps/java/jdtls.lua | 21 ++++--- lua/java-deps/parser.lua | 5 +- lua/java-deps/views/data_node.lua | 82 +++++++++++++++++++++------ lua/java-deps/views/data_provider.lua | 71 ++++++++++++++++++++++- 6 files changed, 160 insertions(+), 48 deletions(-) diff --git a/lua/java-deps.lua b/lua/java-deps.lua index 1599c9c..4d9ffa4 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps.lua @@ -1,7 +1,6 @@ local jdtls = require("java-deps.java.jdtls") local config = require("java-deps.config") local View = require("java-deps.view") -local data_node = require("java-deps.views.data_node") local provider = require("java-deps.views.data_provider") local writer = require("java-deps.writer") @@ -14,24 +13,16 @@ local M = { code_win = nil, root_items = nil, current_node = nil, + current_path = nil, }, } -local function handle_projects(projects) - if not projects or #projects == 0 then - return - end - local project_nodes = {} - for _, project in ipairs(projects) do - if project then - local root = data_node.createNode(project) - if root then - root:getChildren() - table.insert(project_nodes, root) - end - end - end - local result = provider.flattenTree(project_nodes, 0) +local function handle_projects() + local uri = vim.uri_from_fname(jdtls.root_dir()) + local data = provider.DataProvider:new(uri, M.state.current_path) + data:revealPaths() + local result = data:flattenTree() + vim.print(vim.inspect(result)) writer.parse_and_write(M.view.bufnr, result) end @@ -46,11 +37,10 @@ end function M.open_outline() if not M.view:is_open() then M.state.code_buf = vim.api.nvim_get_current_buf() + M.state.current_path = vim.uri_from_bufnr(M.state.code_buf) M.view:open() - local uri = vim.uri_from_fname(jdtls.root_dir()) local wf = coroutine.wrap(function() - local resp = jdtls.getProjects(uri) - handle_projects(resp) + handle_projects() end) local ok, err = pcall(wf) if not ok then diff --git a/lua/java-deps/config.lua b/lua/java-deps/config.lua index feae772..04e3410 100644 --- a/lua/java-deps/config.lua +++ b/lua/java-deps/config.lua @@ -11,7 +11,6 @@ local M = { show_relative_numbers = false, preview_bg_highlight = "Pmenu", winblend = 0, - request_timeout = 3000, autofold_depth = 99, fold_markers = { "", "" }, position = "right", diff --git a/lua/java-deps/java/jdtls.lua b/lua/java-deps/java/jdtls.lua index ca991a9..c1c8803 100644 --- a/lua/java-deps/java/jdtls.lua +++ b/lua/java-deps/java/jdtls.lua @@ -7,9 +7,9 @@ local M = {} ---@return INodeData[] M.getProjects = function(params) local err, resp = lsp_command.execute_command({ - command = "java.project.list", + command = lsp_command.JAVA_PROJECT_LIST, arguments = params, - }, nil, 0) + }) if err then vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} @@ -31,11 +31,6 @@ M.getProjectUris = function() return resp or {} end --- interface IPackageDataParam { --- projectUri: string | undefined; --- [key: string]: any; --- } - ---@return INodeData[] M.getPackageData = function(params) local excludePatterns = {} @@ -84,4 +79,16 @@ M.getPackageData = function(params) return nodeData end +---@return INodeData[] +M.resolvePath = function(params) + local err, resp = lsp_command.execute_command({ + command = lsp_command.JAVA_RESOLVEPATH, + arguments = params, + }) + if err then + vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) + return {} + end + return resp or {} +end return M diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index 82e1fe7..4649419 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -59,8 +59,9 @@ function M.get_lines(flattened_outline_items) -- else add a middle marker elseif index == #line then -- add fold markers - if config.options.fold_markers and data_node.is_folded(node) then - if data_node.is_folded(node) then + local folded = data_node.is_folded(node) + if config.options.fold_markers and folded then + if folded then line[index] = config.options.fold_markers[1] else line[index] = config.options.fold_markers[2] diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index d58de82..642ab72 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -65,11 +65,13 @@ end ---@field _parent DataNode? ---@field _project DataNode? ---@field _rootNode DataNode? ----@field _hierarchicalNode boolean +---@field _hierarchicalPackageNode boolean +---@field _hierarchicalPackageRootNode boolean local DataNode = ExplorerNode:new() DataNode.__index = DataNode -DataNode._hierarchicalNode = false +DataNode._hierarchicalPackageNode = false +DataNode._hierarchicalPackageRootNode = false ---@param nodeData INodeData ---@param parent DataNode? @@ -227,14 +229,64 @@ function DataNode:sort() end) end +function DataNode:baseRevealPaths(paths) + if #paths == 0 then + return self + end + local childNodeData = table.remove(paths, 1) + ---@type DataNode[] + local children = self:getChildren() + ---@type DataNode[]? + local childNode = vim.tbl_filter(function(child) + return childNodeData.name == child._nodeData.name and childNodeData.path == child._nodeData.path + end, children) + childNode = (childNode and #childNode > 0) and childNode[1] or nil + return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode +end + +---@param uri string +---@return boolean +local function is_workspace_file(uri) + local rootPath = jdtls.root_dir() + if vim.startswith(uri, "file:/") then + local path = vim.uri_to_fname(uri) + return path == rootPath or vim.startswith(path, rootPath) + end + return false +end + ---@param paths INodeData[] function DataNode:revealPaths(paths) if #paths == 0 then return self end local kind = self:kind() - if kind == NodeKind.PackageRoot then + if kind == NodeKind.Project then + if not self._nodeData.uri then + return + end + + if is_workspace_file(self._nodeData.uri) then + return self:baseRevealPaths(paths) + end + + local childNodeData = paths[1] + ---@type DataNode[] + local children = self:getChildren() + ---@type DataNode[]? + local childNode = vim.tbl_filter(function(child) + return vim.startswith(childNodeData.name, child._nodeData.name .. ".") + or childNodeData.name == child._nodeData.name + end, children) + ---@type DataNode? + childNode = (childNode and #childNode > 0) and childNode[1] or nil + if childNode and childNode._hierarchicalPackageNode then + table.remove(paths, 1) + end + return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode + elseif kind == NodeKind.PackageRoot and self._hierarchicalPackageRootNode then local hierarchicalNodeData = paths[1] + ---@type DataNode[] local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) @@ -242,17 +294,18 @@ function DataNode:revealPaths(paths) or hierarchicalNodeData.name == child._nodeData.name end, children) ---@type DataNode? - childNode = childNode and #childNode > 0 and childNode[1] or nil - if childNode and not childNode[1]._hierarchicalNode then + childNode = (childNode and #childNode > 0) and childNode[1] or nil + if childNode and not childNode._hierarchicalPackageNode then table.remove(paths, 1) end return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode - elseif kind == NodeKind.Package then + elseif kind == NodeKind.Package and self._hierarchicalPackageNode then local hierarchicalNodeData = paths[1] if hierarchicalNodeData.name == self._nodeData.name then table.remove(paths, 1) - return self:revealPaths(paths) + return self:baseRevealPaths(paths) else + ---@type DataNode[] local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) @@ -264,14 +317,7 @@ function DataNode:revealPaths(paths) return (childNode and #paths > 0) and childNode:revealPaths(paths) or nil end else - local childNodeData = table.remove(paths, 1) - local children = self:getChildren() - ---@type DataNode? - local childNode = vim.tbl_filter(function(child) - return childNodeData.name == child._nodeData.name and childNodeData.path == child._nodeData.path - end, children) - childNode = childNode and #childNode > 0 and childNode[1] or nil - return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode + return self:baseRevealPaths(paths) end end @@ -339,14 +385,16 @@ M.createNode = function(nodeData, parent, project, rootNode) vim.notify("Package root node must have parent and project", vim.log.levels.ERROR) return nil end - return DataNode:new(nodeData, parent, project, rootNode) + local data = DataNode:new(nodeData, parent, project, rootNode) + data._hierarchicalPackageRootNode = true + return data elseif nodeData.kind == NodeKind.Package then if not parent or not project or not rootNode then vim.notify("Package node must have parent, project and root node", vim.log.levels.ERROR) return nil end local data = DataNode:new(nodeData, parent, project, rootNode) - data._hierarchicalNode = true + data._hierarchicalPackageNode = true return data elseif nodeData.kind == NodeKind.PrimaryType then if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then diff --git a/lua/java-deps/views/data_provider.lua b/lua/java-deps/views/data_provider.lua index 137e570..ecbf2ca 100644 --- a/lua/java-deps/views/data_provider.lua +++ b/lua/java-deps/views/data_provider.lua @@ -1,3 +1,5 @@ +local jdtls = require("java-deps.java.jdtls") +local data_node = require("java-deps.views.data_node") local M = {} ---@class TreeItem @@ -14,20 +16,78 @@ local M = {} ---@field hierarchy? table local TreeItem = {} +---@class DataProvider +---@field rootPath string +---@field currentPath string +---@field _rootItems DataNode[]? +local DataProvider = {} +DataProvider.__index = DataProvider + +function DataProvider:new(rootPath, currentPath) + return setmetatable({ + rootPath = rootPath, + currentPath = currentPath, + _rootItems = {}, + }, self) +end +---@return INodeData[]? +function DataProvider:getRootNodes() + return jdtls.getProjects(self.rootPath) +end + +---@param projects? INodeData[] +---@return DataNode[]? +function DataProvider:_revealPaths(projects) + if not projects or #projects == 0 then + return + end + + ---@type DataNode[] + local project_nodes = {} + for _, project in ipairs(projects) do + if project then + local root = data_node.createNode(project) + if root then + table.insert(project_nodes, root) + end + end + end + ---@type INodeData[] + local rpath = jdtls.resolvePath(self.currentPath) + ---@type INodeData + local cpath = (rpath and #rpath > 0) and table.remove(rpath, 1) or nil + for _, root in ipairs(project_nodes) do + if cpath and cpath.name == root._nodeData.name and cpath.path == root._nodeData.path then + root:revealPaths(rpath) + break + end + end + self._rootItems = project_nodes + return project_nodes +end + +---@return DataNode[]? +function DataProvider:revealPaths() + return self:_revealPaths(self:getRootNodes()) +end + ---@param nodes DataNode[] ---@return TreeItem[] -function M.flattenTree(nodes, _level) +local function _flattenTree(nodes, _level, hierarchy) local level = _level or 0 local result = {} for idx, node in ipairs(nodes) do local c = node:getTreeItem() + local _hierarchy = hierarchy or {} c.depth = level if idx == #nodes then + _hierarchy[level] = false c.isLast = true end + c.hierarchy = vim.deepcopy(_hierarchy) table.insert(result, c) if node:hasChildren() then - local children = M.flattenTree(node._childrenNodes, level + 1) + local children = _flattenTree(node._childrenNodes, level + 1, _hierarchy) for _, child in ipairs(children) do table.insert(result, child) end @@ -36,4 +96,11 @@ function M.flattenTree(nodes, _level) return result end +---@return TreeItem[] +function DataProvider:flattenTree() + return _flattenTree(self._rootItems, 0, nil) +end + +M.DataProvider = DataProvider + return M From 8ade7cefda3333b85a5bbb90930d6b7ee5e7928a Mon Sep 17 00:00:00 2001 From: luokai Date: Mon, 12 Aug 2024 08:08:25 +0800 Subject: [PATCH 06/11] update --- lua/java-deps/debog.lua | 18 +++++ lua/java-deps/java/jdtls.lua | 18 ++--- lua/java-deps/views/data_node.lua | 123 ++++++++++++++++++++++-------- 3 files changed, 118 insertions(+), 41 deletions(-) create mode 100644 lua/java-deps/debog.lua diff --git a/lua/java-deps/debog.lua b/lua/java-deps/debog.lua new file mode 100644 index 0000000..a2e03f2 --- /dev/null +++ b/lua/java-deps/debog.lua @@ -0,0 +1,18 @@ +local M = {} +local logfile = vim.fn.stdpath("cache") .. "/java—deps.log" +local write_log = function(msg) + local file = io.open(logfile, "a") + if file then + file:write(msg .. "\n") + file:close() + end +end + +M.debug = function(msg) + if type(msg) == "table" then + msg = vim.inspect(msg) + end + write_log(msg) +end + +return M diff --git a/lua/java-deps/java/jdtls.lua b/lua/java-deps/java/jdtls.lua index c1c8803..4afb953 100644 --- a/lua/java-deps/java/jdtls.lua +++ b/lua/java-deps/java/jdtls.lua @@ -33,7 +33,7 @@ end ---@return INodeData[] M.getPackageData = function(params) - local excludePatterns = {} + local excludePatterns = nil local err, resp = lsp_command.execute_command({ command = lsp_command.JAVA_GETPACKAGEDATA, arguments = params, @@ -43,18 +43,18 @@ M.getPackageData = function(params) return {} end ---@type INodeData[] - local nodeData = resp and resp or {} + local nodeDatas = resp and resp or {} -- Filter out non java resources if true then - nodeData = vim.tbl_filter(function(data) + nodeDatas = vim.tbl_filter(function(data) return data.kind ~= NodeKind.Folder and data.kind ~= NodeKind.File - end, nodeData) + end, nodeDatas) end - if excludePatterns and #nodeData > 0 then + if excludePatterns and #nodeDatas > 0 then local uriOfChildren = vim.tbl_map(function(node) return node.uri - end, nodeData) + end, nodeDatas) local urisToExclude = {} for _, pattern in pairs(excludePatterns) do @@ -68,15 +68,15 @@ M.getPackageData = function(params) end end if #urisToExclude > 0 then - nodeData = vim.tbl_filter(function(node) + nodeDatas = vim.tbl_filter(function(node) if not node.uri then return true end return not vim.tbl_contains(urisToExclude, node.uri) - end, nodeData) + end, nodeDatas) end end - return nodeData + return nodeDatas end ---@return INodeData[] diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index 642ab72..4df6fd8 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -4,6 +4,7 @@ local ExplorerNode = require("java-deps.views.explorer_node").ExplorerNode local hieararchicalPackageNodeData = require("java-deps.java.hieararchicalPackageNodeData") local M = {} +M.isHierarchicalView = true M.K_TYPE_KIND = "TypeKind" M.NATURE_ID = "NatureId" @@ -87,6 +88,41 @@ function DataNode:new(nodeData, parent, project, rootNode) return data end +---@return DataNode[] +function DataNode:createHierarchicalPackageRootNode() + local result = {} + local packageData = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + if child.kind == NodeKind.Package then + table.insert(packageData, child) + else + table.insert(result, M.createNode(child, self, self._project, self)) + end + end + if #packageData > 0 then + local data = hieararchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData) + if data and data.children then + for _, child in ipairs(data.children) do + table.insert(result, M.createNode(child, self, self._project, self)) + end + end + end + end + return result +end + +---@return DataNode[] +function DataNode:createHierarchicalPackageNode() + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self._rootNode)) + end + end + return result +end + function DataNode:createChildNodeList() local kind = self:kind() if kind == NodeKind.Workspace then @@ -106,10 +142,17 @@ function DataNode:createChildNodeList() table.insert(result, M.createNode(child, self, self, nil)) end end + if #packageData > 0 then - local data = hieararchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData) - if data and data.children then - for _, child in ipairs(data.children) do + if M.isHierarchicalView then + local data = hieararchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData) + if data and data.children then + for _, child in ipairs(data.children) do + table.insert(result, M.createNode(child, self, self, self)) + end + end + else + for _, child in ipairs(packageData) do table.insert(result, M.createNode(child, self, self, self)) end end @@ -125,21 +168,29 @@ function DataNode:createChildNodeList() end return result elseif kind == NodeKind.PackageRoot then - local result = {} - if self._nodeData.children then - for _, child in ipairs(self._nodeData.children) do - table.insert(result, M.createNode(child, self, self._project, self)) + if M.isHierarchicalView then + return self:createHierarchicalPackageRootNode() + else + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self)) + end end + return result end - return result elseif kind == NodeKind.Package then - local result = {} - if self._nodeData.children then - for _, child in ipairs(self._nodeData.children) do - table.insert(result, M.createNode(child, self, self._project, self._rootNode)) + if M.isHierarchicalView then + return self:createHierarchicalPackageNode() + else + local result = {} + if self._nodeData.children then + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, self._project, self._rootNode)) + end end + return result end - return result elseif kind == NodeKind.Folder then local result = {} if self._nodeData.children then @@ -154,7 +205,6 @@ function DataNode:createChildNodeList() return nil end end - function DataNode:loadData() local kind = self:kind() if kind == NodeKind.Workspace then @@ -176,7 +226,7 @@ function DataNode:loadData() projectUri = self._project._nodeData.uri, rootPath = self._nodeData.path, handlerIdentifier = self._nodeData.handlerIdentifier, - isHierarchicalView = true, + isHierarchicalView = M.isHierarchicalView, }) elseif kind == NodeKind.Package then return jdtls.getPackageData({ @@ -284,21 +334,26 @@ function DataNode:revealPaths(paths) table.remove(paths, 1) end return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode - elseif kind == NodeKind.PackageRoot and self._hierarchicalPackageRootNode then - local hierarchicalNodeData = paths[1] - ---@type DataNode[] - local children = self:getChildren() - ---@type DataNode[]? - local childNode = vim.tbl_filter(function(child) - return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") - or hierarchicalNodeData.name == child._nodeData.name - end, children) - ---@type DataNode? - childNode = (childNode and #childNode > 0) and childNode[1] or nil - if childNode and not childNode._hierarchicalPackageNode then - table.remove(paths, 1) + elseif kind == NodeKind.PackageRoot then + if self._hierarchicalPackageRootNode then + local hierarchicalNodeData = paths[1] + + ---@type DataNode[] + local children = self:getChildren() + ---@type DataNode[]? + local childNode = vim.tbl_filter(function(child) + return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") + or hierarchicalNodeData.name == child._nodeData.name + end, children) + ---@type DataNode? + childNode = (childNode and #childNode > 0) and childNode[1] or nil + if childNode and not childNode._hierarchicalPackageNode then + table.remove(paths, 1) + end + return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode + else + return self:baseRevealPaths(paths) end - return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode elseif kind == NodeKind.Package and self._hierarchicalPackageNode then local hierarchicalNodeData = paths[1] if hierarchicalNodeData.name == self._nodeData.name then @@ -313,7 +368,7 @@ function DataNode:revealPaths(paths) or hierarchicalNodeData.name == child._nodeData.name end, children) ---@type DataNode? - childNode = childNode and #childNode > 0 and childNode[1] or nil + childNode = (childNode and #childNode > 0) and childNode[1] or nil return (childNode and #paths > 0) and childNode:revealPaths(paths) or nil end else @@ -386,7 +441,9 @@ M.createNode = function(nodeData, parent, project, rootNode) return nil end local data = DataNode:new(nodeData, parent, project, rootNode) - data._hierarchicalPackageRootNode = true + if M.isHierarchicalView then + data._hierarchicalPackageRootNode = true + end return data elseif nodeData.kind == NodeKind.Package then if not parent or not project or not rootNode then @@ -394,7 +451,9 @@ M.createNode = function(nodeData, parent, project, rootNode) return nil end local data = DataNode:new(nodeData, parent, project, rootNode) - data._hierarchicalPackageNode = true + if M.isHierarchicalView then + data._hierarchicalPackageNode = true + end return data elseif nodeData.kind == NodeKind.PrimaryType then if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then From 6599df771b5c8d565c1e830f01ba581cdba583b4 Mon Sep 17 00:00:00 2001 From: luokai Date: Mon, 19 Aug 2024 20:06:05 +0800 Subject: [PATCH 07/11] date tree item --- lua/java-deps/config.lua | 127 ++++++-------- lua/java-deps/debog.lua | 18 -- lua/java-deps/highlight.lua | 45 +++++ lua/{java-deps.lua => java-deps/init.lua} | 30 +++- .../java/hieararchicalPackageNodeData.lua | 61 ++++--- lua/java-deps/java/jdtls.lua | 8 +- lua/java-deps/{ => java}/lsp-command.lua | 17 +- lua/java-deps/java/nodeData.lua | 49 +++++- lua/java-deps/mappings.lua | 9 + lua/java-deps/parser.lua | 15 +- lua/java-deps/view.lua | 1 + lua/java-deps/views/data_node.lua | 156 ++++++++++-------- lua/java-deps/views/data_provider.lua | 47 ++++-- lua/java-deps/views/icons.lua | 36 ++++ lua/java-deps/writer.lua | 27 +-- 15 files changed, 408 insertions(+), 238 deletions(-) delete mode 100644 lua/java-deps/debog.lua create mode 100644 lua/java-deps/highlight.lua rename lua/{java-deps.lua => java-deps/init.lua} (64%) rename lua/java-deps/{ => java}/lsp-command.lua (68%) create mode 100644 lua/java-deps/mappings.lua create mode 100644 lua/java-deps/views/icons.lua diff --git a/lua/java-deps/config.lua b/lua/java-deps/config.lua index 04e3410..a53b09f 100644 --- a/lua/java-deps/config.lua +++ b/lua/java-deps/config.lua @@ -1,95 +1,76 @@ local M = { - debug = false, - jdtls_name = "jdtls", - options = { - show_guides = true, - show_path_details = true, - auto_close = false, - width = 32, - relative_width = true, - show_numbers = false, - show_relative_numbers = false, - preview_bg_highlight = "Pmenu", - winblend = 0, - autofold_depth = 99, - fold_markers = { "", "" }, - position = "right", - wrap = false, - hierarchical_view = true, - keymaps = { -- These keymaps can be a string or a table for multiple keys - open_file = "o", - close = { "", "q" }, - show_help = "?", - toggle_preview = "K", - fold = "h", - unfold = "l", - fold_all = "W", - unfold_all = "E", - fold_reset = "R", - }, - symbols = { - Workspace = { icon = "", hl = "@text.uri" }, - Project = { icon = "", hl = "@text.uri" }, - PackageRoot = { icon = "", hl = "@text.uri" }, - Package = { icon = "", hl = "@namespace" }, - PrimaryType = { icon = "󰠱", hl = "@type" }, - CompilationUnit = { icon = "", hl = "@text.uri" }, - ClassFile = { icon = "", hl = "@text.uri" }, - Container = { icon = "󰆧", hl = "@text.uri" }, - Folder = { icon = "󰉋", hl = "@method" }, - File = { icon = "󰈙", hl = "@method" }, - - CLASS = { icon = "󰠱", hl = "@class" }, - ENUM = { icon = "", hl = "@enum" }, - INTERFACE = { icon = "", hl = "@interface" }, - JAR = { icon = "", hl = "@conditional" }, - }, - symbol_blacklist = {}, - }, + async = false, + jdtls_name = "jdtls", + options = { + show_guides = true, + show_path_details = true, + auto_close = false, + width = 32, + show_numbers = false, + show_relative_numbers = false, + preview_bg_highlight = "Pmenu", + winblend = 0, + fold_markers = { "", "" }, + position = "right", + wrap = false, + hierarchical_view = true, + keymaps = { -- These keymaps can be a string or a table for multiple keys + open_file = "o", + close = { "", "q" }, + show_help = "?", + toggle_preview = "K", + fold = "h", + unfold = "l", + fold_reset = "R", + }, + symbols = { + CLASS = { icon = "󰠱", hl = "@class" }, + ENUM = { icon = "", hl = "@enum" }, + INTERFACE = { icon = "", hl = "@interface" }, + JAR = { icon = "", hl = "@conditional" }, + }, + symbol_blacklist = {}, + }, } M.setup = function(config) - if config then - M = vim.tbl_extend("force", M, config) - end + if config then + M = vim.tbl_extend("force", M, config) + end end function M.has_numbers() - return M.options.show_numbers or M.options.show_relative_numbers + return M.options.show_numbers or M.options.show_relative_numbers end local function has_value(tab, val) - for _, value in ipairs(tab) do - if value == val then - return true - end - end + for _, value in ipairs(tab) do + if value == val then + return true + end + end - return false + return false end function M.is_symbol_blacklisted(kind) - if kind == nil then - return false - end - return has_value(M.options.symbol_blacklist, kind) + if kind == nil then + return false + end + return has_value(M.options.symbol_blacklist, kind) end function M.show_help() - print("Current keymaps:") - print(vim.inspect(M.options.keymaps)) + print("Current keymaps:") + print(vim.inspect(M.options.keymaps)) end function M.get_split_command() - if M.options.position == "left" then - return "topleft vs" - else - return "botright vs" - end + if M.options.position == "left" then + return "topleft vs" + else + return "botright vs" + end end function M.get_window_width() - if M.options.relative_width then - return math.ceil(vim.o.columns * (M.options.width / 100)) - else - return M.options.width - end + return M.options.width end return M diff --git a/lua/java-deps/debog.lua b/lua/java-deps/debog.lua deleted file mode 100644 index a2e03f2..0000000 --- a/lua/java-deps/debog.lua +++ /dev/null @@ -1,18 +0,0 @@ -local M = {} -local logfile = vim.fn.stdpath("cache") .. "/java—deps.log" -local write_log = function(msg) - local file = io.open(logfile, "a") - if file then - file:write(msg .. "\n") - file:close() - end -end - -M.debug = function(msg) - if type(msg) == "table" then - msg = vim.inspect(msg) - end - write_log(msg) -end - -return M diff --git a/lua/java-deps/highlight.lua b/lua/java-deps/highlight.lua new file mode 100644 index 0000000..9b1edf5 --- /dev/null +++ b/lua/java-deps/highlight.lua @@ -0,0 +1,45 @@ +local M = { + items = { + nsid = vim.api.nvim_create_namespace("java-deps-items"), + highlights = { + LineGuide = { link = "Comment" }, + }, + }, + vt = { + nsid = vim.api.nvim_create_namespace("java-deps-virt-text"), + highlights = { + Comment = { link = "Comment" }, + }, + }, +} + +M.init_hl = function() + local ihlf = function(hls) + for name, hl in pairs(hls.highlights) do + if vim.fn.hlexists("JavaDeps" .. name) == 0 then + vim.api.nvim_set_hl(hls.nsid, "JavaDeps" .. name, { link = hl.link }) + end + end + end + ihlf(M.items) + ihlf(M.vt) +end +M.clear_all_ns = function(bufnr) + vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1) +end + +M.clear_virt_text = function(bufnr) + vim.api.nvim_buf_clear_namespace(bufnr, M.vt.nsid, 0, -1) +end + +---@param bufnr number +---@param hl_info table +---@param nodes TreeItem[] +function M.add_icon_highlights(bufnr, hl_info, nodes) + for _, line_hl in ipairs(hl_info) do + local line, hl_start, hl_end, hl_type = unpack(line_hl) + vim.api.nvim_buf_add_highlight(bufnr, M.items.nsid, hl_type, line - 1, hl_start, hl_end) + end +end + +return M diff --git a/lua/java-deps.lua b/lua/java-deps/init.lua similarity index 64% rename from lua/java-deps.lua rename to lua/java-deps/init.lua index 4d9ffa4..dc93622 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps/init.lua @@ -3,6 +3,12 @@ local config = require("java-deps.config") local View = require("java-deps.view") local provider = require("java-deps.views.data_provider") local writer = require("java-deps.writer") +local mappings = require("java-deps.mappings") +local highlight = require("java-deps.highlight") +-- debug +vim.g.java_deps = { + debug = true, +} local M = { view = nil, @@ -22,8 +28,12 @@ local function handle_projects() local data = provider.DataProvider:new(uri, M.state.current_path) data:revealPaths() local result = data:flattenTree() - vim.print(vim.inspect(result)) + local idx, item = data:findCurrentNode(result) writer.parse_and_write(M.view.bufnr, result) + -- 设置光标位置 + if idx and item then + vim.api.nvim_win_set_cursor(M.view.winnr, { idx, 0 }) + end end function M.toggle_outline() @@ -39,12 +49,19 @@ function M.open_outline() M.state.code_buf = vim.api.nvim_get_current_buf() M.state.current_path = vim.uri_from_bufnr(M.state.code_buf) M.view:open() - local wf = coroutine.wrap(function() + mappings.init_mappings(M.view) + writer.write_outline(M.view.bufnr, { "Loading..." }) + if config.async then + local wf = coroutine.wrap(function() + handle_projects() + end) + xpcall(wf, function(err) + if err then + print(err.message or vim.inspect(err)) + end + end) + else handle_projects() - end) - local ok, err = pcall(wf) - if not ok then - print(err.message or vim.inspect(err)) end end end @@ -56,6 +73,7 @@ end function M.setup(opts) config.setup(opts) M.view = View:new() + highlight.init_hl() end return M diff --git a/lua/java-deps/java/hieararchicalPackageNodeData.lua b/lua/java-deps/java/hieararchicalPackageNodeData.lua index 20b8ee1..8f42929 100644 --- a/lua/java-deps/java/hieararchicalPackageNodeData.lua +++ b/lua/java-deps/java/hieararchicalPackageNodeData.lua @@ -6,7 +6,7 @@ local M = {} ---@class HierarchicalPackageNodeData: INodeData ---@field displayName string ---@field name string ----@field nodeData? INodeData +---@field _nodeData? INodeData ---@field children HierarchicalPackageNodeData[] local HierarchicalPackageNodeData = INodeData:new() HierarchicalPackageNodeData.__index = HierarchicalPackageNodeData @@ -28,23 +28,23 @@ function HierarchicalPackageNodeData:compressTree() local child = self.children[1] self.name = self.name .. "." .. child.displayName self.displayName = self.displayName .. "." .. child.displayName - self.children = self.children - self.nodeData = self.nodeData + self.children = child.children + self._nodeData = child._nodeData end for _, child in ipairs(self.children) do child:compressTree() end end ---@param packages string[] ----@param _nodeData INodeData -function HierarchicalPackageNodeData:addSubPackage(packages, _nodeData) +---@param nodeData INodeData +function HierarchicalPackageNodeData:addSubPackage(packages, nodeData) if #packages == 0 then - self.nodeData = _nodeData - -- TODO + self._nodeData = nodeData return end local subPackageDisplayName = table.remove(packages, 1) - local childNode = nil + ---@type HierarchicalPackageNodeData? + local childNode for _, child in ipairs(self.children) do if child.displayName == subPackageDisplayName then childNode = child @@ -52,35 +52,52 @@ function HierarchicalPackageNodeData:addSubPackage(packages, _nodeData) end end if childNode then - childNode:addSubPackage(packages, _nodeData) + childNode:addSubPackage(packages, nodeData) else local newNode = HierarchicalPackageNodeData:new(subPackageDisplayName, self.name) - newNode:addSubPackage(packages, _nodeData) + newNode:addSubPackage(packages, nodeData) table.insert(self.children, newNode) end end -function HierarchicalPackageNodeData:get_getUri() - return self.nodeData and self.nodeData.uri + +function HierarchicalPackageNodeData:isPackage() + return self._nodeData ~= nil end -function HierarchicalPackageNodeData:get_moduleName() - return self.nodeData and self.nodeData.moduleName + +function HierarchicalPackageNodeData:getDisplayName() + return self.displayName end -function HierarchicalPackageNodeData:get_path() - return self.nodeData and self.nodeData.path +function HierarchicalPackageNodeData:getName() + return self.name +end +function HierarchicalPackageNodeData:getModuleName() + return self._nodeData and self._nodeData.moduleName +end +function HierarchicalPackageNodeData:getPath() + return self._nodeData and self._nodeData.path end -function HierarchicalPackageNodeData:get_kind() - return self.nodeData and self.nodeData.kind or NodeKind.Package +function HierarchicalPackageNodeData:getHandlerIdentifier() + return self._nodeData and self._nodeData.handlerIdentifier end -function HierarchicalPackageNodeData:isPackage() - return self.nodeData ~= nil +function HierarchicalPackageNodeData:getUri() + return self._nodeData and self._nodeData.uri end -function HierarchicalPackageNodeData:handlerIdentifier() - return self.nodeData and self.nodeData.handlerIdentifier +function HierarchicalPackageNodeData:getKind() + return self._nodeData and self._nodeData.kind end + +function HierarchicalPackageNodeData:getChildren() + return self.children +end + +function HierarchicalPackageNodeData:getMetaData() + return self._nodeData and self._nodeData.metaData +end + M.HierarchicalPackageNodeData = HierarchicalPackageNodeData ---@param packageList INodeData[] diff --git a/lua/java-deps/java/jdtls.lua b/lua/java-deps/java/jdtls.lua index 4afb953..dc245fc 100644 --- a/lua/java-deps/java/jdtls.lua +++ b/lua/java-deps/java/jdtls.lua @@ -1,4 +1,4 @@ -local lsp_command = require("java-deps.lsp-command") +local lsp_command = require("java-deps.java.lsp-command") local node_data = require("java-deps.java.nodeData") local NodeKind = node_data.NodeKind local M = {} @@ -14,7 +14,7 @@ M.getProjects = function(params) vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} end - return resp or {} + return node_data.generateNodeList(resp) end M.root_dir = function() return lsp_command.get_client().root_dir @@ -43,7 +43,7 @@ M.getPackageData = function(params) return {} end ---@type INodeData[] - local nodeDatas = resp and resp or {} + local nodeDatas = node_data.generateNodeList(resp) -- Filter out non java resources if true then nodeDatas = vim.tbl_filter(function(data) @@ -89,6 +89,6 @@ M.resolvePath = function(params) vim.notify(err.message or vim.inspect(err), vim.log.levels.WARN) return {} end - return resp or {} + return node_data.generateNodeList(resp) end return M diff --git a/lua/java-deps/lsp-command.lua b/lua/java-deps/java/lsp-command.lua similarity index 68% rename from lua/java-deps/lsp-command.lua rename to lua/java-deps/java/lsp-command.lua index ddaa47d..cd4d080 100644 --- a/lua/java-deps/lsp-command.lua +++ b/lua/java-deps/java/lsp-command.lua @@ -18,7 +18,8 @@ M.get_client = function() return clients[1] end -M.execute_command = function(command, callback, bufnr) +-- 使用异步没有错误信输出 +M.execute_command_async = function(command, callback, bufnr) local client = M.get_client() if not client then return @@ -37,5 +38,19 @@ M.execute_command = function(command, callback, bufnr) return coroutine.yield() end end +M.execute_command = function(command, bufnr) + if config.async then + return M.execute_command_async(command, nil, bufnr) + end + local client = M.get_client() + if not client then + return + end + local resp = client.request_sync("workspace/executeCommand", command, 20000, bufnr) + if not resp then + return "No response" + end + return nil, resp.result +end return M diff --git a/lua/java-deps/java/nodeData.lua b/lua/java-deps/java/nodeData.lua index 9893e13..79dfe93 100644 --- a/lua/java-deps/java/nodeData.lua +++ b/lua/java-deps/java/nodeData.lua @@ -36,8 +36,53 @@ function INodeData:new() return setmetatable({}, self) end -function INodeData:print() - print(vim.inspect(self)) +function INodeData:form(resp) + return setmetatable(resp, self) +end +---@param resp table? +---@return INodeData[] +M.generateNodeList = function(resp) + if not resp then + return {} + end + local nodes = {} + for _, node in ipairs(resp) do + table.insert(nodes, INodeData:form(node)) + end + return nodes +end +function INodeData:getDisplayName() + return self.displayName +end + +function INodeData:getName() + return self.name +end +function INodeData:getModuleName() + return self.moduleName +end +function INodeData:getPath() + return self.path +end + +function INodeData:getHandlerIdentifier() + return self.handlerIdentifier +end + +function INodeData:getUri() + return self.uri +end + +function INodeData:getKind() + return self.kind +end + +function INodeData:getChildren() + return self.children +end + +function INodeData:getMetaData() + return self.metaData end M.INodeData = INodeData diff --git a/lua/java-deps/mappings.lua b/lua/java-deps/mappings.lua new file mode 100644 index 0000000..8fe5cd4 --- /dev/null +++ b/lua/java-deps/mappings.lua @@ -0,0 +1,9 @@ +local M = {} + +---@param view View +M.init_mappings = function(view) + vim.keymap.set("n", "h", function() end, { noremap = true, silent = true, buffer = view.bufnr }) + vim.keymap.set("n", "l", function() end, { noremap = true, silent = true, buffer = view.bufnr }) +end + +return M diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index 4649419..0b2011d 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -45,26 +45,21 @@ function M.get_lines(flattened_outline_items) node_line, from, to, - "JavaDespOutlineConnector", + "JavaDepsLineGuide", }) end for index, _ in ipairs(line) do -- all items start with a space (or two) if config.options.show_guides then - -- makes the guides - if index == 1 then - line[index] = " " - -- i f index is last, add a bottom marker if current item is last, - -- else add a middle marker - elseif index == #line then + if index == #line then -- add fold markers local folded = data_node.is_folded(node) if config.options.fold_markers and folded then - if folded then - line[index] = config.options.fold_markers[1] - else + if data_node.is_expanded(node) then line[index] = config.options.fold_markers[2] + else + line[index] = config.options.fold_markers[1] end add_guide_hl(running_length, running_length + vim.fn.strlen(line[index]) - 1) diff --git a/lua/java-deps/view.lua b/lua/java-deps/view.lua index b1fdc04..e4c76d1 100644 --- a/lua/java-deps/view.lua +++ b/lua/java-deps/view.lua @@ -3,6 +3,7 @@ local config = require("java-deps.config") ---@class View ---@field bufnr number ---@field winnr number +---@field data_provider DataProvider local View = {} View.__index = View diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index 4df6fd8..bfa8c5a 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -1,7 +1,9 @@ -local NodeKind = require("java-deps.java.nodeData").NodeKind +local node_data = require("java-deps.java.nodeData") +local NodeKind = node_data.NodeKind local jdtls = require("java-deps.java.jdtls") local ExplorerNode = require("java-deps.views.explorer_node").ExplorerNode local hieararchicalPackageNodeData = require("java-deps.java.hieararchicalPackageNodeData") +local icons = require("java-deps.views.icons") local M = {} M.isHierarchicalView = true @@ -94,7 +96,7 @@ function DataNode:createHierarchicalPackageRootNode() local packageData = {} if self._nodeData.children then for _, child in ipairs(self._nodeData.children) do - if child.kind == NodeKind.Package then + if child:getKind() == NodeKind.Package then table.insert(packageData, child) else table.insert(result, M.createNode(child, self, self._project, self)) @@ -104,7 +106,8 @@ function DataNode:createHierarchicalPackageRootNode() local data = hieararchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData) if data and data.children then for _, child in ipairs(data.children) do - table.insert(result, M.createNode(child, self, self._project, self)) + local node = M.createNode(child, self, self._project, self) + table.insert(result, node) end end end @@ -126,17 +129,19 @@ end function DataNode:createChildNodeList() local kind = self:kind() if kind == NodeKind.Workspace then + local result = {} if self._nodeData.children then - return vim.tbl_map(function(child) - return M.createNode(child, self, nil, nil) - end, self._nodeData.children) + for _, child in ipairs(self._nodeData.children) do + table.insert(result, M.createNode(child, self, nil, nil)) + end end + return result elseif kind == NodeKind.Project then local result = {} local packageData = {} if self._nodeData.children then for _, child in ipairs(self._nodeData.children) do - if child.kind == NodeKind.Package then + if child:getKind() == NodeKind.Package then table.insert(packageData, child) else table.insert(result, M.createNode(child, self, self, nil)) @@ -208,50 +213,50 @@ end function DataNode:loadData() local kind = self:kind() if kind == NodeKind.Workspace then - return jdtls.getProjects(self._nodeData.uri) + return jdtls.getProjects(self._nodeData:getUri()) elseif kind == NodeKind.Project then return jdtls.getPackageData({ kind = NodeKind.Project, - projectUri = self._nodeData.uri, + projectUri = self._nodeData:getUri(), }) elseif kind == NodeKind.Container then return jdtls.getPackageData({ kind = NodeKind.Container, - projectUri = self._nodeData.uri, - path = self._nodeData.path, + projectUri = self._project._nodeData:getUri(), + path = self._nodeData:getPath(), }) elseif kind == NodeKind.PackageRoot then return jdtls.getPackageData({ kind = NodeKind.PackageRoot, - projectUri = self._project._nodeData.uri, - rootPath = self._nodeData.path, - handlerIdentifier = self._nodeData.handlerIdentifier, + projectUri = self._project._nodeData:getUri(), + rootPath = self._nodeData:getPath(), + handlerIdentifier = self._nodeData:getHandlerIdentifier(), isHierarchicalView = M.isHierarchicalView, }) elseif kind == NodeKind.Package then return jdtls.getPackageData({ kind = NodeKind.Package, - projectUri = self._project._nodeData.uri, - path = self._nodeData.name, - handlerIdentifier = self._nodeData.handlerIdentifier, + projectUri = self._project._nodeData:getUri(), + path = self._nodeData:getName(), + handlerIdentifier = self._nodeData:getHandlerIdentifier(), }) elseif kind == NodeKind.Folder then return jdtls.getPackageData({ kind = NodeKind.Folder, - projectUri = self._project._nodeData.uri, - path = self._nodeData.path, - rootPath = self._rootNode and self._rootNode._nodeData.path or nil, - handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier, + projectUri = self._project._nodeData:getUri(), + path = self._nodeData:getPath(), + rootPath = self._rootNode and self._rootNode._nodeData:getPath() or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData:getHandlerIdentifier(), }) elseif kind == NodeKind.PrimaryType then return nil elseif kind == NodeKind.Folder then return jdtls.getPackageData({ kind = NodeKind.Folder, - projectUri = self._project._nodeData.uri, - path = self._nodeData.path, - rootPath = self._rootNode and self._rootNode._nodeData.path or nil, - handlerIdentifier = self._rootNode and self._rootNode._nodeData.handlerIdentifier or nil, + projectUri = self._project._nodeData:getUri(), + path = self._nodeData:getPath(), + rootPath = self._rootNode and self._rootNode._nodeData:getPath() or nil, + handlerIdentifier = self._rootNode and self._rootNode._nodeData:getHandlerIdentifier() or nil, }) else return nil @@ -259,36 +264,41 @@ function DataNode:loadData() end function DataNode:icon() - return "C" + return icons.get_icon(self) end function DataNode:kind() - return self._nodeData.kind + return self._nodeData:getKind() +end +function DataNode:typeKind() + return self._nodeData:getMetaData() and self._nodeData:getMetaData()[M.K_TYPE_KIND] or nil end function DataNode:sort() table.sort(self._childrenNodes, function(a, b) ---@diagnostic disable: undefined-field - if a._nodeData.kind and b._nodeData.kind and a._nodeData.name and b._nodeData.name then - if a._nodeData.kind == b._nodeData.kind then - return a._nodeData.name < b._nodeData.name + if a._nodeData:getKind() and b._nodeData:getKind() and a._nodeData:getName() and b._nodeData:getName() then + if a._nodeData:getKind() == b._nodeData:getKind() then + return a._nodeData:getName() < b._nodeData:getName() else - return a._nodeData.kind < b._nodeData.kind + return a._nodeData:getKind() < b._nodeData:getKind() end end return false end) end +---@param paths INodeData[] function DataNode:baseRevealPaths(paths) if #paths == 0 then return self end + ---@type DataNode[] local childNodeData = table.remove(paths, 1) ---@type DataNode[] local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) - return childNodeData.name == child._nodeData.name and childNodeData.path == child._nodeData.path + return childNodeData:getName() == child._nodeData:getName() and childNodeData:getPath() == child._nodeData:getPath() end, children) childNode = (childNode and #childNode > 0) and childNode[1] or nil return (childNode and #paths > 0) and childNode:revealPaths(paths) or childNode @@ -312,11 +322,11 @@ function DataNode:revealPaths(paths) end local kind = self:kind() if kind == NodeKind.Project then - if not self._nodeData.uri then + if not self._nodeData:getUri() then return end - if is_workspace_file(self._nodeData.uri) then + if is_workspace_file(self._nodeData:getUri()) then return self:baseRevealPaths(paths) end @@ -325,8 +335,8 @@ function DataNode:revealPaths(paths) local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) - return vim.startswith(childNodeData.name, child._nodeData.name .. ".") - or childNodeData.name == child._nodeData.name + return vim.startswith(childNodeData:getName(), child._nodeData:getName() .. ".") + or childNodeData:getName() == child._nodeData:getName() end, children) ---@type DataNode? childNode = (childNode and #childNode > 0) and childNode[1] or nil @@ -342,8 +352,8 @@ function DataNode:revealPaths(paths) local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) - return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") - or hierarchicalNodeData.name == child._nodeData.name + return vim.startswith(hierarchicalNodeData:getName(), child._nodeData:getName() .. ".") + or hierarchicalNodeData:getName() == child._nodeData:getName() end, children) ---@type DataNode? childNode = (childNode and #childNode > 0) and childNode[1] or nil @@ -356,7 +366,7 @@ function DataNode:revealPaths(paths) end elseif kind == NodeKind.Package and self._hierarchicalPackageNode then local hierarchicalNodeData = paths[1] - if hierarchicalNodeData.name == self._nodeData.name then + if hierarchicalNodeData:getName() == self._nodeData:getName() then table.remove(paths, 1) return self:baseRevealPaths(paths) else @@ -364,8 +374,8 @@ function DataNode:revealPaths(paths) local children = self:getChildren() ---@type DataNode[]? local childNode = vim.tbl_filter(function(child) - return vim.startswith(hierarchicalNodeData.name, child._nodeData.name .. ".") - or hierarchicalNodeData.name == child._nodeData.name + return vim.startswith(hierarchicalNodeData:getName(), child._nodeData:getName() .. ".") + or hierarchicalNodeData:getName() == child._nodeData:getName() end, children) ---@type DataNode? childNode = (childNode and #childNode > 0) and childNode[1] or nil @@ -398,7 +408,7 @@ function DataNode:getChildren() table.insert(self._nodeData.children, child) end self._nodeData.children = uniqBy(self._nodeData.children, function(child) - return child.path .. child.name + return child:getPath() .. child:getName() end) else self._nodeData.children = data @@ -425,17 +435,18 @@ M.DataNode = DataNode ---@param project DataNode? ---@param rootNode DataNode? M.createNode = function(nodeData, parent, project, rootNode) - if nodeData.kind == NodeKind.Workspace then + local kind = nodeData:getKind() + if kind == NodeKind.Workspace then return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.Project then + elseif kind == NodeKind.Project then return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.Container then + elseif kind == NodeKind.Container then if not parent or not project then vim.notify("Container node must have parent and project", vim.log.levels.ERROR) return nil end return DataNode:new(nodeData, parent, project, rootNode) - elseif nodeData.kind == NodeKind.PackageRoot then + elseif kind == NodeKind.PackageRoot then if not parent or not project then vim.notify("Package root node must have parent and project", vim.log.levels.ERROR) return nil @@ -445,7 +456,7 @@ M.createNode = function(nodeData, parent, project, rootNode) data._hierarchicalPackageRootNode = true end return data - elseif nodeData.kind == NodeKind.Package then + elseif kind == NodeKind.Package then if not parent or not project or not rootNode then vim.notify("Package node must have parent, project and root node", vim.log.levels.ERROR) return nil @@ -455,25 +466,21 @@ M.createNode = function(nodeData, parent, project, rootNode) data._hierarchicalPackageNode = true end return data - elseif nodeData.kind == NodeKind.PrimaryType then - if nodeData.metaData and nodeData.metaData[M.K_TYPE_KIND] then + elseif kind == NodeKind.PrimaryType then + if nodeData:getMetaData() and nodeData:getMetaData()[M.K_TYPE_KIND] then if not parent then vim.notify("Primary type node must have parent", vim.log.levels.ERROR) return nil end return DataNode:new(nodeData, parent, project, rootNode) end - elseif nodeData.kind == NodeKind.Folder then - if not parent or not project or not rootNode then - vim.notify("Folder node must have parent, project and root node", vim.log.levels.ERROR) + elseif kind == NodeKind.Folder then + if not parent or not project then + vim.notify("Folder node must have parent and project.", vim.log.levels.ERROR) return nil end return DataNode:new(nodeData, parent, project, rootNode) - elseif - nodeData.kind == NodeKind.CompilationUnit - or nodeData.kind == NodeKind.ClassFile - or nodeData.kind == NodeKind.File - then + elseif kind == NodeKind.CompilationUnit or kind == NodeKind.ClassFile or kind == NodeKind.File then if not parent then vim.notify("File node must have parent", vim.log.levels.ERROR) return nil @@ -483,7 +490,7 @@ M.createNode = function(nodeData, parent, project, rootNode) end function DataNode:isUnmanagedFolder() - local natureIds = self._nodeData.metaData and self._nodeData.metaData[M.NATURE_ID] or {} + local natureIds = self._nodeData:getMetaData() and self._nodeData:getMetaData()[M.NATURE_ID] or {} for _, natureId in ipairs(natureIds) do if natureId == M.NatureId.UnmanagedFolder then return true @@ -493,7 +500,7 @@ function DataNode:isUnmanagedFolder() end function DataNode:getContainerType() - local containerPath = self._nodeData.path or "" + local containerPath = self._nodeData:getPath() or "" if containerPath.startsWith(M.ContainerPath.JRE) then return M.ContainerType.JRE elseif containerPath.startsWith(M.ContainerPath.Maven) then @@ -513,25 +520,44 @@ function DataNode:command() --TODO end +---@return boolean function DataNode:hasChildren() - return self._childrenNodes and #self._childrenNodes > 0 + local kind = self:kind() + if + kind == NodeKind.CompilationUnit + or kind == NodeKind.ClassFile + or kind == NodeKind.File + or kind == NodeKind.PrimaryType + then + return false + end + return true end + +---@param tree TreeItem +---@return boolean function M.is_folded(tree) - return tree.collapsible == M.TreeItemCollapsibleState.Collapsed + return tree.collapsibleState == M.TreeItemCollapsibleState.Collapsed + or tree.collapsibleState == M.TreeItemCollapsibleState.Expanded +end +---@param tree TreeItem +---@return boolean +function M.is_expanded(tree) + return tree.collapsibleState == M.TreeItemCollapsibleState.Expanded end function DataNode:getTreeItem() ---@type TreeItem local item = { - label = self._nodeData.displayName or self._nodeData.name, - collapsible = self:hasChildren() and M.TreeItemCollapsibleState.Collapsed or M.TreeItemCollapsibleState.None, + label = self._nodeData:getDisplayName() or self._nodeData:getName(), + collapsibleState = self:hasChildren() and M.TreeItemCollapsibleState.Collapsed or M.TreeItemCollapsibleState.None, } item.description = self:description() item.icon = self:icon() item.command = self:command() item.data = self - if self._nodeData.uri then + if self._nodeData:getUri() then local kind = self:kind() if kind == NodeKind.Project @@ -543,7 +569,7 @@ function DataNode:getTreeItem() or kind == NodeKind.Folder or kind == NodeKind.File then - item.resourceUri = self._nodeData.uri + item.resourceUri = self._nodeData:getUri() end end return item diff --git a/lua/java-deps/views/data_provider.lua b/lua/java-deps/views/data_provider.lua index ecbf2ca..a36cd54 100644 --- a/lua/java-deps/views/data_provider.lua +++ b/lua/java-deps/views/data_provider.lua @@ -19,6 +19,7 @@ local TreeItem = {} ---@class DataProvider ---@field rootPath string ---@field currentPath string +---@field currentNodeData INodeData? ---@field _rootItems DataNode[]? local DataProvider = {} DataProvider.__index = DataProvider @@ -54,10 +55,11 @@ function DataProvider:_revealPaths(projects) end ---@type INodeData[] local rpath = jdtls.resolvePath(self.currentPath) + self.currentNodeData = rpath and #rpath > 0 and rpath[#rpath] or nil ---@type INodeData local cpath = (rpath and #rpath > 0) and table.remove(rpath, 1) or nil for _, root in ipairs(project_nodes) do - if cpath and cpath.name == root._nodeData.name and cpath.path == root._nodeData.path then + if cpath and cpath:getName() == root._nodeData:getName() and cpath:getPath() == root._nodeData:getPath() then root:revealPaths(rpath) break end @@ -72,33 +74,48 @@ function DataProvider:revealPaths() end ---@param nodes DataNode[] ----@return TreeItem[] -local function _flattenTree(nodes, _level, hierarchy) - local level = _level or 0 - local result = {} +local function _flattenTree(result, nodes, level, hierarchy) + if not nodes or #nodes == 0 then + return + end for idx, node in ipairs(nodes) do local c = node:getTreeItem() - local _hierarchy = hierarchy or {} c.depth = level if idx == #nodes then - _hierarchy[level] = false + hierarchy[level] = false c.isLast = true end - c.hierarchy = vim.deepcopy(_hierarchy) + c.hierarchy = vim.deepcopy(hierarchy) table.insert(result, c) - if node:hasChildren() then - local children = _flattenTree(node._childrenNodes, level + 1, _hierarchy) - for _, child in ipairs(children) do - table.insert(result, child) - end + if node._childrenNodes and #node._childrenNodes > 0 then + c.collapsibleState = data_node.TreeItemCollapsibleState.Expanded + _flattenTree(result, node._childrenNodes, level + 1, hierarchy) end end - return result end ---@return TreeItem[] function DataProvider:flattenTree() - return _flattenTree(self._rootItems, 0, nil) + local result = {} + _flattenTree(result, self._rootItems, 0, {}) + return result +end + +---获取当前节点位置 +---@param treeItems TreeItem[] +function DataProvider:findCurrentNode(treeItems) + if not treeItems or #treeItems == 0 then + return + end + for idx, item in ipairs(treeItems) do + if + item.data + and item.data._nodeData:getName() == self.currentNodeData:getName() + and item.data._nodeData:getPath() == self.currentNodeData:getPath() + then + return idx, item + end + end end M.DataProvider = DataProvider diff --git a/lua/java-deps/views/icons.lua b/lua/java-deps/views/icons.lua new file mode 100644 index 0000000..25761dc --- /dev/null +++ b/lua/java-deps/views/icons.lua @@ -0,0 +1,36 @@ +local node_data = require("java-deps.java.nodeData") +local NodeKind = node_data.NodeKind +local TypeKind = node_data.TypeKind +local M = { + NodeKind = { + [NodeKind.Workspace] = { icon = "", hl = "@lsp.type.class" }, + [NodeKind.Project] = { icon = "" }, + [NodeKind.PackageRoot] = { icon = "" }, + [NodeKind.Package] = { icon = "" }, + [NodeKind.PrimaryType] = { icon = "󰠱" }, + [NodeKind.CompilationUnit] = { icon = "" }, + [NodeKind.ClassFile] = { icon = "" }, + [NodeKind.Container] = { icon = "" }, + [NodeKind.Folder] = { icon = "󰉋" }, + [NodeKind.File] = { icon = "󰈙" }, + }, + TypeKind = { + [TypeKind.Class] = { icon = "󰠱" }, + [TypeKind.Interface] = { icon = "" }, + [TypeKind.Enum] = { icon = "" }, + }, +} + +---comment +---@param node DataNode +---@return string +M.get_icon = function(node) + local kind = node:kind() + if kind == node_data.NodeKind.PrimaryType then + return M.TypeKind[node:typeKind()].icon + else + return M.NodeKind[kind].icon + end +end + +return M diff --git a/lua/java-deps/writer.lua b/lua/java-deps/writer.lua index 970e606..bf8e67f 100644 --- a/lua/java-deps/writer.lua +++ b/lua/java-deps/writer.lua @@ -1,5 +1,6 @@ local parser = require("java-deps.parser") local config = require("java-deps.config") +local highlight = require("java-deps.highlight") local M = {} @@ -10,8 +11,6 @@ local function is_buffer_outline(bufnr) return string.match(name, "JavaProjects") ~= nil and ft == "JavaProjects" and isValid end -local hlns = vim.api.nvim_create_namespace("java-deps-outline-icon-highlight") - function M.write_outline(bufnr, lines) if not is_buffer_outline(bufnr) then return @@ -21,44 +20,28 @@ function M.write_outline(bufnr, lines) vim.api.nvim_buf_set_option(bufnr, "modifiable", false) end -function M.add_highlights(bufnr, hl_info, nodes) - for _, line_hl in ipairs(hl_info) do - local line, hl_start, hl_end, hl_type = unpack(line_hl) - vim.api.nvim_buf_add_highlight(bufnr, hlns, hl_type, line - 1, hl_start, hl_end) - end - - M.add_hover_highlights(bufnr, nodes) -end - -local ns = vim.api.nvim_create_namespace("java-deps-outline-virt-text") - function M.write_details(bufnr, lines) if not is_buffer_outline(bufnr) then return end for index, value in ipairs(lines) do - vim.api.nvim_buf_set_extmark(bufnr, ns, index - 1, -1, { - virt_text = { { value, "Comment" } }, + vim.api.nvim_buf_set_extmark(bufnr, highlight.vt.nsid, index - 1, -1, { + virt_text = { { value, "JavaDepsComment" } }, virt_text_pos = "eol", hl_mode = "combine", }) end end -local function clear_virt_text(bufnr) - vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1) -end - - ---@param bufnr integer ---@param flattened_outline_items TreeItem function M.parse_and_write(bufnr, flattened_outline_items) local lines, hl_info = parser.get_lines(flattened_outline_items) M.write_outline(bufnr, lines) - clear_virt_text(bufnr) - M.add_highlights(bufnr, hl_info, flattened_outline_items) + highlight.clear_virt_text(bufnr) + highlight.add_icon_highlights(bufnr, hl_info, flattened_outline_items) if config.options.show_path_details then local details = parser.get_details(flattened_outline_items) M.write_details(bufnr, details) From aa2ea0faee59012fcf327de3122c5516dcfa5c4f Mon Sep 17 00:00:00 2001 From: luokai Date: Wed, 21 Aug 2024 19:20:13 +0800 Subject: [PATCH 08/11] close/open view --- lua/java-deps/config.lua | 37 +------- lua/java-deps/highlight.lua | 15 +-- lua/java-deps/init.lua | 39 +------- lua/java-deps/java/IPackageRootNodeData.lua | 14 +++ .../java/hieararchicalPackageNodeData.lua | 7 +- lua/java-deps/java/lsp-command.lua | 3 - lua/java-deps/mappings.lua | 8 ++ lua/java-deps/parser.lua | 58 +++--------- lua/java-deps/view.lua | 90 ++++++++++++++++++ lua/java-deps/views/data_node.lua | 94 ++++++++++++++++--- lua/java-deps/views/data_provider.lua | 83 ++++++---------- lua/java-deps/views/icons.lua | 19 +++- lua/java-deps/writer.lua | 24 +---- 13 files changed, 267 insertions(+), 224 deletions(-) diff --git a/lua/java-deps/config.lua b/lua/java-deps/config.lua index a53b09f..698f341 100644 --- a/lua/java-deps/config.lua +++ b/lua/java-deps/config.lua @@ -1,11 +1,9 @@ local M = { - async = false, jdtls_name = "jdtls", options = { show_guides = true, - show_path_details = true, auto_close = false, - width = 32, + width = 40, show_numbers = false, show_relative_numbers = false, preview_bg_highlight = "Pmenu", @@ -14,22 +12,13 @@ local M = { position = "right", wrap = false, hierarchical_view = true, - keymaps = { -- These keymaps can be a string or a table for multiple keys - open_file = "o", - close = { "", "q" }, - show_help = "?", - toggle_preview = "K", - fold = "h", - unfold = "l", - fold_reset = "R", + keymaps = { + close = "q", + toggle_fold = "o", }, symbols = { - CLASS = { icon = "󰠱", hl = "@class" }, - ENUM = { icon = "", hl = "@enum" }, - INTERFACE = { icon = "", hl = "@interface" }, - JAR = { icon = "", hl = "@conditional" }, + icons = {}, }, - symbol_blacklist = {}, }, } M.setup = function(config) @@ -41,22 +30,6 @@ end function M.has_numbers() return M.options.show_numbers or M.options.show_relative_numbers end -local function has_value(tab, val) - for _, value in ipairs(tab) do - if value == val then - return true - end - end - - return false -end - -function M.is_symbol_blacklisted(kind) - if kind == nil then - return false - end - return has_value(M.options.symbol_blacklist, kind) -end function M.show_help() print("Current keymaps:") diff --git a/lua/java-deps/highlight.lua b/lua/java-deps/highlight.lua index 9b1edf5..ce2af2a 100644 --- a/lua/java-deps/highlight.lua +++ b/lua/java-deps/highlight.lua @@ -5,37 +5,26 @@ local M = { LineGuide = { link = "Comment" }, }, }, - vt = { - nsid = vim.api.nvim_create_namespace("java-deps-virt-text"), - highlights = { - Comment = { link = "Comment" }, - }, - }, } M.init_hl = function() local ihlf = function(hls) for name, hl in pairs(hls.highlights) do if vim.fn.hlexists("JavaDeps" .. name) == 0 then - vim.api.nvim_set_hl(hls.nsid, "JavaDeps" .. name, { link = hl.link }) + vim.api.nvim_set_hl(0, "JavaDeps" .. name, { link = hl.link }) end end end ihlf(M.items) - ihlf(M.vt) end M.clear_all_ns = function(bufnr) vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1) end -M.clear_virt_text = function(bufnr) - vim.api.nvim_buf_clear_namespace(bufnr, M.vt.nsid, 0, -1) -end - ---@param bufnr number ---@param hl_info table ---@param nodes TreeItem[] -function M.add_icon_highlights(bufnr, hl_info, nodes) +function M.add_item_highlights(bufnr, hl_info, nodes) for _, line_hl in ipairs(hl_info) do local line, hl_start, hl_end, hl_type = unpack(line_hl) vim.api.nvim_buf_add_highlight(bufnr, M.items.nsid, hl_type, line - 1, hl_start, hl_end) diff --git a/lua/java-deps/init.lua b/lua/java-deps/init.lua index dc93622..e579100 100644 --- a/lua/java-deps/init.lua +++ b/lua/java-deps/init.lua @@ -1,9 +1,5 @@ -local jdtls = require("java-deps.java.jdtls") local config = require("java-deps.config") local View = require("java-deps.view") -local provider = require("java-deps.views.data_provider") -local writer = require("java-deps.writer") -local mappings = require("java-deps.mappings") local highlight = require("java-deps.highlight") -- debug vim.g.java_deps = { @@ -13,29 +9,11 @@ vim.g.java_deps = { local M = { view = nil, state = { - root_uri = nil, - flattened_outline_items = {}, code_buf = nil, code_win = nil, - root_items = nil, - current_node = nil, - current_path = nil, }, } -local function handle_projects() - local uri = vim.uri_from_fname(jdtls.root_dir()) - local data = provider.DataProvider:new(uri, M.state.current_path) - data:revealPaths() - local result = data:flattenTree() - local idx, item = data:findCurrentNode(result) - writer.parse_and_write(M.view.bufnr, result) - -- 设置光标位置 - if idx and item then - vim.api.nvim_win_set_cursor(M.view.winnr, { idx, 0 }) - end -end - function M.toggle_outline() if M.view:is_open() then M.close_outline() @@ -46,23 +24,8 @@ end function M.open_outline() if not M.view:is_open() then - M.state.code_buf = vim.api.nvim_get_current_buf() - M.state.current_path = vim.uri_from_bufnr(M.state.code_buf) M.view:open() - mappings.init_mappings(M.view) - writer.write_outline(M.view.bufnr, { "Loading..." }) - if config.async then - local wf = coroutine.wrap(function() - handle_projects() - end) - xpcall(wf, function(err) - if err then - print(err.message or vim.inspect(err)) - end - end) - else - handle_projects() - end + M.view:revealPaths() end end diff --git a/lua/java-deps/java/IPackageRootNodeData.lua b/lua/java-deps/java/IPackageRootNodeData.lua index 62990a9..0c709d2 100644 --- a/lua/java-deps/java/IPackageRootNodeData.lua +++ b/lua/java-deps/java/IPackageRootNodeData.lua @@ -15,6 +15,20 @@ function IPackageRootNodeData:new() return setmetatable(INodeData:new(), self) end +---@param node INodeData +---@return IPackageRootNodeData +function IPackageRootNodeData:form(node) + return setmetatable(node, self) +end + +function IPackageRootNodeData:getEntryKind() + return self.entryKind +end + +function IPackageRootNodeData:getAttributes() + return self.attributes +end + M.IPackageRootNodeData = IPackageRootNodeData return M diff --git a/lua/java-deps/java/hieararchicalPackageNodeData.lua b/lua/java-deps/java/hieararchicalPackageNodeData.lua index 8f42929..c46a1ca 100644 --- a/lua/java-deps/java/hieararchicalPackageNodeData.lua +++ b/lua/java-deps/java/hieararchicalPackageNodeData.lua @@ -1,6 +1,7 @@ local _n = require("java-deps.java.nodeData") -local NodeKind = _n.NodeKind +local _ipkg = require("java-deps.java.IPackageRootNodeData") local INodeData = _n.INodeData +local IPackageRootNodeData = _ipkg.IPackageRootNodeData local M = {} ---@class HierarchicalPackageNodeData: INodeData @@ -98,6 +99,10 @@ function HierarchicalPackageNodeData:getMetaData() return self._nodeData and self._nodeData.metaData end +function HierarchicalPackageNodeData:getEntryKind() + return self._nodeData and IPackageRootNodeData:form(self._nodeData):getEntryKind() +end + M.HierarchicalPackageNodeData = HierarchicalPackageNodeData ---@param packageList INodeData[] diff --git a/lua/java-deps/java/lsp-command.lua b/lua/java-deps/java/lsp-command.lua index cd4d080..653cf3c 100644 --- a/lua/java-deps/java/lsp-command.lua +++ b/lua/java-deps/java/lsp-command.lua @@ -39,9 +39,6 @@ M.execute_command_async = function(command, callback, bufnr) end end M.execute_command = function(command, bufnr) - if config.async then - return M.execute_command_async(command, nil, bufnr) - end local client = M.get_client() if not client then return diff --git a/lua/java-deps/mappings.lua b/lua/java-deps/mappings.lua index 8fe5cd4..71c9483 100644 --- a/lua/java-deps/mappings.lua +++ b/lua/java-deps/mappings.lua @@ -1,9 +1,17 @@ +local config = require("java-deps.config") local M = {} ---@param view View M.init_mappings = function(view) vim.keymap.set("n", "h", function() end, { noremap = true, silent = true, buffer = view.bufnr }) vim.keymap.set("n", "l", function() end, { noremap = true, silent = true, buffer = view.bufnr }) + vim.keymap.set("n", config.options.keymaps.toggle_fold or "o", function() + view:foldToggle() + end, { noremap = true, silent = true, buffer = view.bufnr }) + + vim.keymap.set("n", config.options.keymaps.close or "q", function() + view:close() + end, { noremap = true, silent = true, buffer = view.bufnr }) end return M diff --git a/lua/java-deps/parser.lua b/lua/java-deps/parser.lua index 0b2011d..5b03c1a 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -1,5 +1,5 @@ local config = require("java-deps.config") -local data_node = require("java-deps.views.data_node") +local icons = require("java-deps.views.icons") local M = {} local function str_to_table(str) @@ -38,83 +38,51 @@ function M.get_lines(flattened_outline_items) local marker_space = config.options.fold_markers and 1 or 0 local line = str_to_table(string.rep(" ", depth + marker_space)) - local running_length = 1 - - local function add_guide_hl(from, to) - table.insert(hl_info, { - node_line, - from, - to, - "JavaDepsLineGuide", - }) - end + local folded = node:is_foldable() for index, _ in ipairs(line) do -- all items start with a space (or two) if config.options.show_guides then if index == #line then -- add fold markers - local folded = data_node.is_folded(node) if config.options.fold_markers and folded then - if data_node.is_expanded(node) then + if node:is_expanded() then line[index] = config.options.fold_markers[2] else line[index] = config.options.fold_markers[1] end - - add_guide_hl(running_length, running_length + vim.fn.strlen(line[index]) - 1) - - -- the root level has no vertical markers elseif depth > 1 then if node.isLast then line[index] = guides.markers.bottom - add_guide_hl(running_length, running_length + vim.fn.strlen(guides.markers.bottom) - 1) else line[index] = guides.markers.middle - add_guide_hl(running_length, running_length + vim.fn.strlen(guides.markers.middle) - 1) end end - -- else if the parent was not the last in its group, add a - -- vertical marker because there are items under us and we need - -- to point to those elseif not node.hierarchy[index] and depth > 1 then line[index + marker_space] = guides.markers.vertical - add_guide_hl( - running_length - 1 + 2 * marker_space, - running_length + vim.fn.strlen(guides.markers.vertical) - 1 + 2 * marker_space - ) end end line[index] = line[index] .. " " - - running_length = running_length + vim.fn.strlen(line[index]) end - local final_prefix = line + local string_prefix = "" - local string_prefix = table_to_str(final_prefix) + for _, value in ipairs(line) do + string_prefix = string_prefix .. tostring(value) + end - table.insert(lines, string_prefix .. node.icon .. " " .. node.label) + local hl_icon = icons.get_icon(node.data) + local icon = hl_icon.icon + table.insert(lines, string_prefix .. icon .. " " .. node.label) local hl_start = #string_prefix - local hl_end = #string_prefix + #node.icon - local hl = config.options.symbols[node.kind] - local hl_type = hl and hl.hl or "@lsp.type.class" + local hl_end = #string_prefix + #icon + local hl_type = hl_icon.hl or "Type" table.insert(hl_info, { node_line, hl_start, hl_end, hl_type }) - - node.prefix_length = #string_prefix + #node.icon + 1 + node.prefix_length = #string_prefix + #icon + 1 end return lines, hl_info end -function M.get_details(flattened_outline_items) - local lines = {} - for _, value in ipairs(flattened_outline_items) do - local detail - -- TODO - table.insert(lines, detail or "") - end - return lines -end return M diff --git a/lua/java-deps/view.lua b/lua/java-deps/view.lua index e4c76d1..8d88081 100644 --- a/lua/java-deps/view.lua +++ b/lua/java-deps/view.lua @@ -1,9 +1,17 @@ local config = require("java-deps.config") +local provider = require("java-deps.views.data_provider") +local writer = require("java-deps.writer") +local mappings = require("java-deps.mappings") +local jdtls = require("java-deps.java.jdtls") ---@class View ---@field bufnr number ---@field winnr number +---@field code_buf number +---@field code_win number ---@field data_provider DataProvider +---@field _flatten_tree TreeItem[] +---@field _cursor_item TreeItem? local View = {} View.__index = View @@ -61,13 +69,95 @@ function View:close() vim.api.nvim_win_close(self.winnr, true) self.winnr = nil self.bufnr = nil + self.data_provider = nil + self._flatten_tree = nil + self.code_buf = nil + self.code_win = nil end + function View:open() + self.code_buf = vim.api.nvim_get_current_buf() + self.code_win = vim.api.nvim_get_current_win() self:setup_view() + mappings.init_mappings(self) + local uri = vim.uri_from_fname(jdtls.root_dir()) + self.data_provider = provider.DataProvider:new(uri) +end + +function View:foldToggle() + local item = self:cursorNode() + if item then + item:foldToggle() + self:refresh() + if item:canOpen() then + self:open_file(item) + end + end +end + +---@return TreeItem? +function View:cursorNode() + local c = vim.api.nvim_win_get_cursor(self.winnr) + if self._flatten_tree and self._flatten_tree[c[1]] then + local item = self._flatten_tree[c[1]] + self._cursor_item = item + end + return self._cursor_item +end + +function View:revealCursor() + local idx, item = self.data_provider:findRevealNode(self._flatten_tree) + if idx and item then + vim.api.nvim_win_set_cursor(self.winnr, { idx, 0 }) + end +end + +function View:flattenTree() + self._flatten_tree = self.data_provider:flattenTree() +end + +function View:_write() + if self._flatten_tree then + writer.parse_and_write(self.bufnr, self._flatten_tree) + end +end +---refresh the outline window +function View:refresh() + self:flattenTree() + self:_write() +end + +function View:revealPaths() + writer.write_outline(self.bufnr, { "Loading..." }) + self.data_provider:revealPaths(jdtls.resolvePath(vim.uri_from_bufnr(self.code_buf))) + self:refresh() + self:revealCursor() end function View:is_open() return self.winnr and self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) and vim.api.nvim_win_is_valid(self.winnr) end +---@param node TreeItem? +function View:open_file(node) + node = node or self:cursorNode() + if not node then + return + end + -- open_file + local fname = node.resourceUri + if not fname or not node:canOpen() then + return + end + if vim.startswith(fname, "file:/") or vim.startswith(fname, "jdt:/") then + vim.fn.win_gotoid(self.code_win) + local bufnr = vim.uri_to_bufnr(fname) + vim.bo[bufnr].buflisted = true + vim.api.nvim_win_set_buf(self.code_win, bufnr) + if config.options.auto_close then + self:close() + end + end +end + return View diff --git a/lua/java-deps/views/data_node.lua b/lua/java-deps/views/data_node.lua index bfa8c5a..232cae0 100644 --- a/lua/java-deps/views/data_node.lua +++ b/lua/java-deps/views/data_node.lua @@ -70,6 +70,7 @@ end ---@field _rootNode DataNode? ---@field _hierarchicalPackageNode boolean ---@field _hierarchicalPackageRootNode boolean +---@field _collapsibleState TreeItemCollapsibleState local DataNode = ExplorerNode:new() DataNode.__index = DataNode @@ -90,6 +91,25 @@ function DataNode:new(nodeData, parent, project, rootNode) return data end +---@class TreeItem +---@field label? string +---@field id? string +---@field icon? string +---@field depth? number +---@field description? string +---@field resourceUri? string +---@field command? string +---@field isLast? boolean +---@field data? DataNode +---@field hierarchy? table +local TreeItem = {} + +TreeItem.__index = TreeItem + +function TreeItem:new() + return setmetatable({}, self) +end + ---@return DataNode[] function DataNode:createHierarchicalPackageRootNode() local result = {} @@ -320,6 +340,7 @@ function DataNode:revealPaths(paths) if #paths == 0 then return self end + self._collapsibleState = M.TreeItemCollapsibleState.Expanded local kind = self:kind() if kind == NodeKind.Project then if not self._nodeData:getUri() then @@ -534,27 +555,74 @@ function DataNode:hasChildren() return true end +---@return TreeItemCollapsibleState +function DataNode:collapsibleState() + if not self._collapsibleState then + if self:hasChildren() then + self._collapsibleState = M.TreeItemCollapsibleState.Collapsed + end + end + return self._collapsibleState +end + +function TreeItem:collapsibleState() + return self.data:collapsibleState() +end ----@param tree TreeItem ---@return boolean -function M.is_folded(tree) - return tree.collapsibleState == M.TreeItemCollapsibleState.Collapsed - or tree.collapsibleState == M.TreeItemCollapsibleState.Expanded +function TreeItem:is_foldable() + return self:collapsibleState() == M.TreeItemCollapsibleState.Collapsed + or self:collapsibleState() == M.TreeItemCollapsibleState.Expanded +end +---@return boolean +function TreeItem:is_expanded() + return self:collapsibleState() == M.TreeItemCollapsibleState.Expanded end ----@param tree TreeItem ---@return boolean -function M.is_expanded(tree) - return tree.collapsibleState == M.TreeItemCollapsibleState.Expanded +function TreeItem:is_collapsed() + return self:collapsibleState() == M.TreeItemCollapsibleState.Collapsed +end + +function TreeItem:expanded() + if not self:is_foldable() then + return + end + if self:is_expanded() then + return + end + self.data._collapsibleState = M.TreeItemCollapsibleState.Expanded + self.data:getChildren() +end + +function TreeItem:collapsed() + if not self:is_foldable() then + return + end + if self:is_collapsed() then + return + end + self.data._collapsibleState = M.TreeItemCollapsibleState.Collapsed +end + +function TreeItem:foldToggle() + if self:is_expanded() then + self:collapsed() + else + self:expanded() + end +end + +---是否是文件可以打开 +function TreeItem:canOpen() + local kind = self.data:kind() + return kind == NodeKind.PrimaryType or kind == NodeKind.ClassFile or kind == NodeKind.File end function DataNode:getTreeItem() - ---@type TreeItem - local item = { - label = self._nodeData:getDisplayName() or self._nodeData:getName(), - collapsibleState = self:hasChildren() and M.TreeItemCollapsibleState.Collapsed or M.TreeItemCollapsibleState.None, - } + local item = TreeItem:new() + item.label = self._nodeData:getDisplayName() or self._nodeData:getName() item.description = self:description() - item.icon = self:icon() + -- item.icon = self:icon() item.command = self:command() item.data = self if self._nodeData:getUri() then diff --git a/lua/java-deps/views/data_provider.lua b/lua/java-deps/views/data_provider.lua index a36cd54..7a6b595 100644 --- a/lua/java-deps/views/data_provider.lua +++ b/lua/java-deps/views/data_provider.lua @@ -2,75 +2,53 @@ local jdtls = require("java-deps.java.jdtls") local data_node = require("java-deps.views.data_node") local M = {} ----@class TreeItem ----@field label? string ----@field id? string ----@field icon? string ----@field depth? number ----@field description? string ----@field resourceUri? string ----@field command? string ----@field collapsibleState? TreeItemCollapsibleState ----@field isLast? boolean ----@field data? DataNode ----@field hierarchy? table -local TreeItem = {} - ---@class DataProvider ---@field rootPath string ----@field currentPath string ----@field currentNodeData INodeData? ----@field _rootItems DataNode[]? +---@field revealNode INodeData? +---@field _rootProjects DataNode[]? local DataProvider = {} DataProvider.__index = DataProvider -function DataProvider:new(rootPath, currentPath) +function DataProvider:new(rootPath) return setmetatable({ rootPath = rootPath, - currentPath = currentPath, - _rootItems = {}, + _rootProjects = {}, }, self) end ----@return INodeData[]? -function DataProvider:getRootNodes() - return jdtls.getProjects(self.rootPath) -end ----@param projects? INodeData[] ----@return DataNode[]? -function DataProvider:_revealPaths(projects) - if not projects or #projects == 0 then - return +---@return DataNode[] +function DataProvider:getRootProjects() + if self._rootProjects and #self._rootProjects > 0 then + return self._rootProjects end - ---@type DataNode[] - local project_nodes = {} - for _, project in ipairs(projects) do + local rootProjects = {} + for _, project in ipairs(jdtls.getProjects(self.rootPath)) do if project then local root = data_node.createNode(project) if root then - table.insert(project_nodes, root) + table.insert(rootProjects, root) end end end + self._rootProjects = rootProjects + return rootProjects +end + +function DataProvider:revealPaths(paths) ---@type INodeData[] - local rpath = jdtls.resolvePath(self.currentPath) - self.currentNodeData = rpath and #rpath > 0 and rpath[#rpath] or nil + local rpath = paths or {} + self.revealNode = rpath and #rpath > 0 and rpath[#rpath] or nil ---@type INodeData local cpath = (rpath and #rpath > 0) and table.remove(rpath, 1) or nil - for _, root in ipairs(project_nodes) do + + local projects = self:getRootProjects() + for _, root in ipairs(projects) do if cpath and cpath:getName() == root._nodeData:getName() and cpath:getPath() == root._nodeData:getPath() then root:revealPaths(rpath) break end end - self._rootItems = project_nodes - return project_nodes -end - ----@return DataNode[]? -function DataProvider:revealPaths() - return self:_revealPaths(self:getRootNodes()) end ---@param nodes DataNode[] @@ -80,38 +58,39 @@ local function _flattenTree(result, nodes, level, hierarchy) end for idx, node in ipairs(nodes) do local c = node:getTreeItem() + c.hierarchy = vim.deepcopy(hierarchy) c.depth = level if idx == #nodes then - hierarchy[level] = false + -- 如果是最后一个节点, 子节点不需要再画竖线 + c.hierarchy[level] = true c.isLast = true end - c.hierarchy = vim.deepcopy(hierarchy) table.insert(result, c) - if node._childrenNodes and #node._childrenNodes > 0 then - c.collapsibleState = data_node.TreeItemCollapsibleState.Expanded - _flattenTree(result, node._childrenNodes, level + 1, hierarchy) + if node._childrenNodes and #node._childrenNodes > 0 and c:is_expanded() then + _flattenTree(result, node._childrenNodes, level + 1, c.hierarchy) end end end ---@return TreeItem[] function DataProvider:flattenTree() + ---@type TreeItem[] local result = {} - _flattenTree(result, self._rootItems, 0, {}) + _flattenTree(result, self:getRootProjects(), 0, {}) return result end ---获取当前节点位置 ---@param treeItems TreeItem[] -function DataProvider:findCurrentNode(treeItems) +function DataProvider:findRevealNode(treeItems) if not treeItems or #treeItems == 0 then return end for idx, item in ipairs(treeItems) do if item.data - and item.data._nodeData:getName() == self.currentNodeData:getName() - and item.data._nodeData:getPath() == self.currentNodeData:getPath() + and item.data._nodeData:getName() == self.revealNode:getName() + and item.data._nodeData:getPath() == self.revealNode:getPath() then return idx, item end diff --git a/lua/java-deps/views/icons.lua b/lua/java-deps/views/icons.lua index 25761dc..706b217 100644 --- a/lua/java-deps/views/icons.lua +++ b/lua/java-deps/views/icons.lua @@ -1,9 +1,15 @@ local node_data = require("java-deps.java.nodeData") +local PackageRootKind = require("java-deps.java.IPackageRootNodeData").PackageRootKind local NodeKind = node_data.NodeKind local TypeKind = node_data.TypeKind + +---@class Icon +---@field icon string +---@field hl string? + local M = { NodeKind = { - [NodeKind.Workspace] = { icon = "", hl = "@lsp.type.class" }, + [NodeKind.Workspace] = { icon = "", hl = "Type" }, [NodeKind.Project] = { icon = "" }, [NodeKind.PackageRoot] = { icon = "" }, [NodeKind.Package] = { icon = "" }, @@ -19,17 +25,20 @@ local M = { [TypeKind.Interface] = { icon = "" }, [TypeKind.Enum] = { icon = "" }, }, + EntryKind = { + [PackageRootKind.K_SOURCE] = { icon = "" }, + [PackageRootKind.K_BINARY] = { icon = "" }, + }, } ----comment ---@param node DataNode ----@return string +---@return Icon M.get_icon = function(node) local kind = node:kind() if kind == node_data.NodeKind.PrimaryType then - return M.TypeKind[node:typeKind()].icon + return M.TypeKind[node:typeKind()] else - return M.NodeKind[kind].icon + return M.NodeKind[kind] end end diff --git a/lua/java-deps/writer.lua b/lua/java-deps/writer.lua index bf8e67f..ce12ec7 100644 --- a/lua/java-deps/writer.lua +++ b/lua/java-deps/writer.lua @@ -1,5 +1,4 @@ local parser = require("java-deps.parser") -local config = require("java-deps.config") local highlight = require("java-deps.highlight") local M = {} @@ -20,32 +19,13 @@ function M.write_outline(bufnr, lines) vim.api.nvim_buf_set_option(bufnr, "modifiable", false) end -function M.write_details(bufnr, lines) - if not is_buffer_outline(bufnr) then - return - end - - for index, value in ipairs(lines) do - vim.api.nvim_buf_set_extmark(bufnr, highlight.vt.nsid, index - 1, -1, { - virt_text = { { value, "JavaDepsComment" } }, - virt_text_pos = "eol", - hl_mode = "combine", - }) - end -end - ---@param bufnr integer ---@param flattened_outline_items TreeItem function M.parse_and_write(bufnr, flattened_outline_items) local lines, hl_info = parser.get_lines(flattened_outline_items) + highlight.clear_all_ns(bufnr) M.write_outline(bufnr, lines) - - highlight.clear_virt_text(bufnr) - highlight.add_icon_highlights(bufnr, hl_info, flattened_outline_items) - if config.options.show_path_details then - local details = parser.get_details(flattened_outline_items) - M.write_details(bufnr, details) - end + highlight.add_item_highlights(bufnr, hl_info, flattened_outline_items) end return M From a219fd1e96b31f2bbbf4223eea41de2992ce8da9 Mon Sep 17 00:00:00 2001 From: luokai Date: Thu, 22 Aug 2024 10:00:28 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E5=8E=BB=E6=8E=89=20attach=20=E6=96=B9?= =?UTF-8?q?=E5=BC=8F,=20=E6=9B=B4=E6=96=B0=E4=BD=BF=E7=94=A8=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bcd2ef5..1c6a8b0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ # Java Projects -- 使用 [symbols-outline](https://github.com/simrat39/symbols-outline.nvim) 代码实现预览 -- [vscode-java-dependency](https://github.com/Microsoft/vscode-java-dependency) 提供数据支持 - -![java-deps](https://javahello.github.io/dev/nvim-lean/images/java-deps.png) - ## 安装 [English](https://github.com/JavaHello/java-deps.nvim/issues/2) @@ -24,7 +19,7 @@ ``` -- 手动编译 `vscode-java-dependency` +- 手动编译 `vscode-java-dependency` (可选) ```sh git clone https://github.com/microsoft/vscode-java-dependency.git @@ -33,7 +28,7 @@ npm install npm run build-server ``` -- 将 `vscode-java-dependency` 编译后的 `jar` 添加到 jdtls_config["init_options"].bundles 中 +- 将 `vscode-java-dependency` 的 `jar` 包添加到 jdtls_config["init_options"].bundles 中 ```lua local jdtls_config = {} @@ -56,11 +51,10 @@ jdtls_config["init_options"] = { } ``` -- 添加 attach +- 添加命令 ```lua jdtls_config["on_attach"] = function(client, buffer) - require("java-deps").attach(client, buffer) -- 添加命令 local create_command = vim.api.nvim_buf_create_user_command create_command(buffer, "JavaProjects", require("java-deps").toggle_outline, { @@ -76,3 +70,8 @@ end :lua require('java-deps').open_outline() :lua require('java-deps').close_outline() ``` + +## 参考实现 + +- 使用 [symbols-outline](https://github.com/simrat39/symbols-outline.nvim) 代码实现预览 +- [vscode-java-dependency](https://github.com/Microsoft/vscode-java-dependency) 提供数据支持 From 4393624c5bffb8dca95f63fd69765f9e782b758b Mon Sep 17 00:00:00 2001 From: luokai Date: Thu, 22 Aug 2024 10:09:59 +0800 Subject: [PATCH 10/11] MIT License --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9b4c23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Kai + +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. From a71673382be6725be20c3ff11b0ba4d8dfafa819 Mon Sep 17 00:00:00 2001 From: luokai Date: Thu, 10 Oct 2024 17:42:43 +0800 Subject: [PATCH 11/11] fix: icon --- lua/java-deps/views/icons.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/java-deps/views/icons.lua b/lua/java-deps/views/icons.lua index 706b217..7376675 100644 --- a/lua/java-deps/views/icons.lua +++ b/lua/java-deps/views/icons.lua @@ -11,13 +11,13 @@ local M = { NodeKind = { [NodeKind.Workspace] = { icon = "", hl = "Type" }, [NodeKind.Project] = { icon = "" }, - [NodeKind.PackageRoot] = { icon = "" }, - [NodeKind.Package] = { icon = "" }, + [NodeKind.PackageRoot] = { icon = "" }, + [NodeKind.Package] = { icon = "" }, [NodeKind.PrimaryType] = { icon = "󰠱" }, [NodeKind.CompilationUnit] = { icon = "" }, [NodeKind.ClassFile] = { icon = "" }, [NodeKind.Container] = { icon = "" }, - [NodeKind.Folder] = { icon = "󰉋" }, + [NodeKind.Folder] = { icon = "" }, [NodeKind.File] = { icon = "󰈙" }, }, TypeKind = { @@ -26,8 +26,8 @@ local M = { [TypeKind.Enum] = { icon = "" }, }, EntryKind = { - [PackageRootKind.K_SOURCE] = { icon = "" }, - [PackageRootKind.K_BINARY] = { icon = "" }, + [PackageRootKind.K_SOURCE] = { icon = "" }, + [PackageRootKind.K_BINARY] = { icon = "" }, }, }