diff --git a/README.md b/README.md index a4f80b3..44f0a1d 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,22 @@ - [vscode-java-dependency](https://github.com/Microsoft/vscode-java-dependency) 提供数据支持 ![java-deps](https://javahello.github.io/dev/nvim-lean/images/java-deps.png) + +## 使用说明 + +- lazy.nvim + +```lua +{ + "JavaHello/java-deps.nvim", + lazy = true, + ft = "java", + dependencies = "mfussenegger/nvim-jdtls", + config = function() + require("java-deps").setup({}) + end, + } + +-- jdtls lsp attach +require("java-deps").attach(client, buffer, root_dir) +``` diff --git a/lua/java-deps.lua b/lua/java-deps.lua index e9b8d0b..72e329b 100644 --- a/lua/java-deps.lua +++ b/lua/java-deps.lua @@ -8,355 +8,460 @@ 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").node_kind +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, - flattened_outline_items = {}, - code_buf = nil, - code_win = nil, - outline_items = nil, - }, + view = nil, + ------------------------- + -- STATE + ------------------------- + 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, + }, } 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, - }) + 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 + 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 = { outline_items = {}, flattened_outline_items = {}, code_win = 0, code_buf = 0 } + 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.outline_items) - writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items) + 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] + 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 + 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 + 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 children = {} - local response = lsp_command.get_package_data(M.state.code_buf, node) - if response == nil or type(response) ~= "table" then - return - end - for _, value in ipairs(response) do - if node_kind.CONTAINER == value.kind then - if value.entryKind == value.kind then - value.parent = node - local c = lsp_command.get_package_data(M.state.code_buf, value) - if c and c[1] then - table.insert(children, c[1]) - end - else - table.insert(children, value) - end - else - table.insert(children, value) - end - end - - local child_hir = t_utils.array_copy(node.hierarchy) - table.insert(child_hir, node.isLast) - node.children = parser.parse(children, node.depth + 1, child_hir, node) - return children - end + 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 + if node.kind == node_kind.PackageRoot then + parser.sort_result(response) + reveal_paths(response, node) + local pkg = find_pkg(M.state.current_node) + if pkg ~= nil then + for _, n in ipairs(response) do + if n.kind == node_kind.Package and node_eq(n, pkg) then + open_pkg(n) + end + end + else + open_pkgs(response[1]) + end + end + 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:") then - vim.fn.win_gotoid(M.state.code_win) - fname = string.sub(node.path, 2) - local cmd = "edit " .. fname - vim.cmd(cmd) - if config.options.auto_close then - M.close_outline() - end - end + 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() - 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 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 + 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() - 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 - end + 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 + end end function M._set_all_folded(folded, nodes) - nodes = nodes or M.state.outline_items + 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 + for _, node in ipairs(nodes) do + node.folded = folded + if node.children then + M._set_all_folded(folded, node.children) + end + end - _update_lines() + _update_lines() end function M._highlight_current_item(winnr) - local has_provider = providers.has_provider() + local has_provider = providers.has_provider() - local is_current_buffer_the_outline = M.view.bufnr == vim.api.nvim_get_current_buf() + 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 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 + 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 + -- 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 + if should_exit then + return + end - local win = winnr or vim.api.nvim_get_current_win() + local win = winnr or vim.api.nvim_get_current_win() - local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1 + local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1 - local leaf_node = nil + local leaf_node = nil - local cb = function(value) - value.hovered = nil + local cb = function(value) + value.hovered = nil - if value.line == hovered_line then - value.hovered = true - leaf_node = value - end - end + if value.line == hovered_line then + value.hovered = true + leaf_node = value + end + end - utils.items_dfs(cb, M.state.outline_items) + utils.items_dfs(cb, M.state.root_items) - _update_lines() + _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 + 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) + 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 + if response == nil or type(response) ~= "table" then + return + end - M.state.code_win = vim.api.nvim_get_current_win() + 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, - }) + 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() + setup_keymaps(M.view.bufnr) + setup_buffer_autocmd() - local items = parser.parse(response) + local items = parser.parse(response) - M.state.outline_items = items - M.state.flattened_outline_items = parser.flatten(items) + 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) + writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items) - M._highlight_current_item(M.state.code_win) + M._highlight_current_item(M.state.code_win) end function M.toggle_outline() - if M.view:is_open() then - M.close_outline() - else - M.open_outline() - end + if M.view:is_open() then + M.close_outline() + else + M.open_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 == 5 then - if value.name == node.name then - return value - end - elseif node.kind == 6 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 + 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) - end + 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) + end end function M.close_outline() - M.view:close() + M.view:close() end function M.setup(opts) - config.setup(opts) - ui.setup_highlights() + config.setup(opts) + ui.setup_highlights() - M.view = View:new() - setup_global_autocmd() + M.view = View:new() + setup_global_autocmd() end M.attach = function(client, buf, root_dir) - context.attach(client, buf, root_dir) + context.attach(client, buf, root_dir) end return M diff --git a/lua/java-deps/config.lua b/lua/java-deps/config.lua index 5cf0b2c..feae772 100644 --- a/lua/java-deps/config.lua +++ b/lua/java-deps/config.lua @@ -12,7 +12,7 @@ local M = { preview_bg_highlight = "Pmenu", winblend = 0, request_timeout = 3000, - autofold_depth = 0, + autofold_depth = 99, fold_markers = { "", "" }, position = "right", wrap = false, @@ -29,15 +29,18 @@ local M = { fold_reset = "R", }, symbols = { - WORKSPACE = { icon = "", hl = "@text.uri" }, - PROJECT = { icon = "", hl = "@text.uri" }, - CONTAINER = { icon = "", hl = "@text.uri" }, - PACKAGEROOT = { icon = "", hl = "@text.uri" }, - PACKAGE = { icon = "", hl = "@namespace" }, - PRIMARYTYPE = { icon = "ﴯ", hl = "@type" }, - FOLDER = { icon = "", hl = "@text.uri" }, - FILE = { icon = "", hl = "@text.uri" }, - CLASS = { icon = "ﴯ", hl = "@class" }, + 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" }, diff --git a/lua/java-deps/folding.lua b/lua/java-deps/folding.lua index 73f539e..9ca94b4 100644 --- a/lua/java-deps/folding.lua +++ b/lua/java-deps/folding.lua @@ -1,15 +1,15 @@ local M = {} local config = require("java-deps.config") -local node_kind = require("java-deps.symbols").node_kind +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 + 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 diff --git a/lua/java-deps/lsp-command.lua b/lua/java-deps/lsp-command.lua index 087ffb9..bacd2ca 100644 --- a/lua/java-deps/lsp-command.lua +++ b/lua/java-deps/lsp-command.lua @@ -4,88 +4,88 @@ 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) + 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) + timeout = timeout or config.options.request_timeout + local client = context.current_client + return client.request_sync(method, params, timeout, bufnr) end 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) + 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 - end - if resp.result ~= nil then - return resp.result - elseif resp.error ~= nil then - vim.notify(vim.inspect(resp), vim.log.levels.WARN) - end + 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 + end + if resp.result ~= nil then + return resp.result + elseif resp.error ~= nil then + vim.notify(vim.inspect(resp), vim.log.levels.WARN) + end end local function root_project(node) - local root = node - while root ~= nil do - if root.kind == symbols.node_kind.PROJECT then - return root - end - root = root.parent - end + local root = node + while root ~= nil do + if root.kind == symbols.NodeKind.Project then + return root + end + root = root.parent + end end M.get_package_data = function(buf, node) - local arguments = { - kind = node.kind, - } - if node.kind == symbols.node_kind.PROJECT then - arguments.projectUri = node.uri - elseif node.kind == symbols.node_kind.CONTAINER then - arguments.projectUri = root_project(node).uri - arguments.path = node.path - elseif node.kind == symbols.node_kind.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.node_kind.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 - end - return M.command_sync(buf, "java.getPackageData", arguments) + 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 + 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) + 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) + 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 32b1b2b..ce05700 100644 --- a/lua/java-deps/parser.lua +++ b/lua/java-deps/parser.lua @@ -6,184 +6,171 @@ local ui = require("java-deps.ui") local M = {} local function parse_result(result, depth, hierarchy, parent) - local ret = {} - - 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, - } - - 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 - end - end - return ret + 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 + + 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 + end + end + return ret end -local function sort_result(result) - table.sort(result, function(a, b) - if a.entryKind and b.entryKind then - if a.kind == b.kind then - return a.name:upper() < b.name:upper() - end - return a.entryKind < b.entryKind - end - if a.kind == b.kind then - if - a.kind == symbols.node_kind.PROJECT - or a.kind == symbols.node_kind.PRIMARYTYPE - or a.kind == symbols.node_kind.PACKAGEROOT - or a.kind == symbols.node_kind.PACKAGE - then - if a.name ~= b.name then - return a.name:upper() < b.name:upper() - end - end - end - return false - end) - return result +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 = sort_result(response) - return parse_result(sorted, depth, hierarchy, parent) + local sorted = M.sort_result(response) + return parse_result(sorted, depth, hierarchy, parent) end -function M.flatten(outline_items, ret, depth) - depth = depth or 1 - 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, depth + 1) - end - end - return ret +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 + end + return ret end 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 line = t_utils.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, - "JavaDespOutlineConnector", - }) - 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 - -- add fold markers - if config.options.fold_markers and folding.is_foldable(node) then - if folding.is_folded(node) then - line[index] = config.options.fold_markers[1] - else - line[index] = config.options.fold_markers[2] - 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] = ui.markers.bottom - add_guide_hl(running_length, running_length + vim.fn.strlen(ui.markers.bottom) - 1) - else - line[index] = ui.markers.middle - add_guide_hl(running_length, running_length + vim.fn.strlen(ui.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 - add_guide_hl( - running_length - 1 + 2 * marker_space, - running_length + vim.fn.strlen(ui.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 = t_utils.table_to_str(final_prefix) - - table.insert(lines, string_prefix .. node.icon .. " " .. node.name) - - local hl_start = #string_prefix - local hl_end = #string_prefix + #node.icon - local hl_type = config.options.symbols[symbols.kinds[node.kind]].hl - table.insert(hl_info, { node_line, hl_start, hl_end, hl_type }) - - node.prefix_length = #string_prefix + #node.icon + 1 - end - return lines, hl_info + 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 line = t_utils.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, + "JavaDespOutlineConnector", + }) + 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 + -- add fold markers + if config.options.fold_markers and folding.is_foldable(node) then + if folding.is_folded(node) then + line[index] = config.options.fold_markers[1] + else + line[index] = config.options.fold_markers[2] + 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] = ui.markers.bottom + add_guide_hl(running_length, running_length + vim.fn.strlen(ui.markers.bottom) - 1) + else + line[index] = ui.markers.middle + add_guide_hl(running_length, running_length + vim.fn.strlen(ui.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 + add_guide_hl( + running_length - 1 + 2 * marker_space, + running_length + vim.fn.strlen(ui.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 = t_utils.table_to_str(final_prefix) + + table.insert(lines, string_prefix .. node.icon .. " " .. node.name) + + local hl_start = #string_prefix + local hl_end = #string_prefix + #node.icon + local hl_type = config.options.symbols[symbols.kinds[node.kind]].hl + table.insert(hl_info, { node_line, hl_start, hl_end, hl_type }) + + node.prefix_length = #string_prefix + #node.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 - if symbols.type_kind(value) == symbols.node_kind.JAR then - detail = value.path - end - table.insert(lines, detail or "") - end - return lines + 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 + table.insert(lines, detail or "") + end + return lines end return M diff --git a/lua/java-deps/symbols.lua b/lua/java-deps/symbols.lua index 6024184..2e8b1aa 100644 --- a/lua/java-deps/symbols.lua +++ b/lua/java-deps/symbols.lua @@ -1,67 +1,118 @@ local config = require("java-deps.config") local M = {} -M.node_kind = { - WORKSPACE = 1, - PROJECT = 2, - CONTAINER = 3, - PACKAGEROOT = 4, - PACKAGE = 5, - PRIMARYTYPE = 6, - FOLDER = 7, - FILE = 8, - - -- metaData.TypeKind - CLASS = 11, - INTERFACE = 12, - ENUM = 13, - - JAR = 24, +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", - "CONTAINER", - "PACKAGEROOT", - "PACKAGE", - "PRIMARYTYPE", - "FOLDER", - "FILE", - - -- metaData.TypeKind - [M.node_kind.CLASS] = "CLASS", - [M.node_kind.INTERFACE] = "INTERFACE", - [M.node_kind.ENUM] = "ENUM", - - [M.node_kind.JAR] = "JAR", + "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, + 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.node_kind.JAR - end - return node.kind + 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 + local symbols = config.options.symbols - if type(node) == "string" then - return symbols[node].icon - end + if type(node) == "string" then + return symbols[node].icon + end + + local kind = M.type_kind(node) + return symbols[M.kinds[kind]].icon +end - local kind = M.type_kind(node) - return symbols[M.kinds[kind]].icon +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/view/node.lua b/lua/java-deps/view/node.lua new file mode 100644 index 0000000..fa75179 --- /dev/null +++ b/lua/java-deps/view/node.lua @@ -0,0 +1,19 @@ +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