diff options
| author | ThePrimeAgain <theprimeagain@theprimeagain.com> | 2026-01-14 18:32:30 -0700 |
|---|---|---|
| committer | ThePrimeAgain <theprimeagain@theprimeagain.com> | 2026-01-14 18:32:30 -0700 |
| commit | 6c88a9537ae829cd8a4b92312e115c311e6beb42 (patch) | |
| tree | d2b1796d453cbbd7374ed740fa50a1be411362b1 /lua/99/editor | |
| parent | 701b4c34a1e3de34327d21cc6f51c79b49d26b51 (diff) | |
| download | a4-6c88a9537ae829cd8a4b92312e115c311e6beb42.tar.xz a4-6c88a9537ae829cd8a4b92312e115c311e6beb42.zip | |
fuzzy_find: for agents stuff
Diffstat (limited to 'lua/99/editor')
| -rw-r--r-- | lua/99/editor/init.lua | 4 | ||||
| -rw-r--r-- | lua/99/editor/lsp.lua | 776 | ||||
| -rw-r--r-- | lua/99/editor/treesitter.lua | 330 |
3 files changed, 551 insertions, 559 deletions
diff --git a/lua/99/editor/init.lua b/lua/99/editor/init.lua index 70c24a3..ff0e8ab 100644 --- a/lua/99/editor/init.lua +++ b/lua/99/editor/init.lua @@ -1,4 +1,4 @@ return { - treesitter = require("99.editor.treesitter"), - -- lsp = require("99.editor.lsp"), + treesitter = require("99.editor.treesitter"), + -- lsp = require("99.editor.lsp"), } diff --git a/lua/99/editor/lsp.lua b/lua/99/editor/lsp.lua index 7f8e7d3..caf4b94 100644 --- a/lua/99/editor/lsp.lua +++ b/lua/99/editor/lsp.lua @@ -23,8 +23,8 @@ --- @param node _99.treesitter.Node The treesitter node to convert --- @return LspPosition The LSP-compatible position (0-based line and character) local function ts_node_to_lsp_position(node) - local start_row, start_col, _, _ = node:range() - return { line = start_row, character = start_col } + local start_row, start_col, _, _ = node:range() + return { line = start_row, character = start_col } end --- Makes an LSP textDocument/definition request for a given position. @@ -33,17 +33,17 @@ end --- @param position LspPosition The position in the document to get definitions for --- @param cb fun(res: LspDefinitionResult[] | nil): nil Callback receiving the definition results local function get_lsp_definitions(buffer, position, cb) - local params = vim.lsp.util.make_position_params() - params.position = position + local params = vim.lsp.util.make_position_params() + params.position = position - vim.lsp.buf_request( - buffer, - "textDocument/definition", - params, - function(_, result, _, _) - cb(result) - end - ) + vim.lsp.buf_request( + buffer, + "textDocument/definition", + params, + function(_, result, _, _) + cb(result) + end + ) end --- Resolves a Lua require path to an absolute file path using Neovim's runtime. @@ -51,22 +51,22 @@ end --- @param require_path string The Lua require path (e.g., "99.logger.logger") --- @return string|nil The absolute file path, or nil if it can't be resolved local function resolve_require_path(require_path) - local relative_path = "lua/" .. require_path:gsub("%.", "/") .. ".lua" - local results = vim.api.nvim_get_runtime_file(relative_path, false) + local relative_path = "lua/" .. require_path:gsub("%.", "/") .. ".lua" + local results = vim.api.nvim_get_runtime_file(relative_path, false) - if results and #results > 0 then - return results[1] - end + if results and #results > 0 then + return results[1] + end - -- Also try init.lua for module directories - local init_path = "lua/" .. require_path:gsub("%.", "/") .. "/init.lua" - results = vim.api.nvim_get_runtime_file(init_path, false) + -- Also try init.lua for module directories + local init_path = "lua/" .. require_path:gsub("%.", "/") .. "/init.lua" + results = vim.api.nvim_get_runtime_file(init_path, false) - if results and #results > 0 then - return results[1] - end + if results and #results > 0 then + return results[1] + end - return nil + return nil end --- Ensures a buffer is loaded and has LSP attached, then calls the callback. @@ -74,23 +74,23 @@ end --- @param filepath string The file path to load --- @param cb fun(bufnr: number|nil, err: string|nil): nil Callback with buffer number or error local function ensure_buffer_with_lsp(filepath, cb) - local bufnr = vim.fn.bufnr(filepath) - if bufnr == -1 then - bufnr = vim.fn.bufadd(filepath) - end + local bufnr = vim.fn.bufnr(filepath) + if bufnr == -1 then + bufnr = vim.fn.bufadd(filepath) + end - if not vim.api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end + if not vim.api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end - vim.schedule(function() - local clients = vim.lsp.get_clients({ bufnr = bufnr }) - if #clients == 0 then - cb(nil, "No LSP client attached to buffer for: " .. filepath) - return - end - cb(bufnr, nil) - end) + vim.schedule(function() + local clients = vim.lsp.get_clients({ bufnr = bufnr }) + if #clients == 0 then + cb(nil, "No LSP client attached to buffer for: " .. filepath) + return + end + cb(bufnr, nil) + end) end --- Makes an LSP textDocument/hover request for a given position. @@ -99,23 +99,23 @@ end --- @param position LspPosition The position to hover at --- @param cb fun(result: table|nil, err: string|nil): nil Callback with hover result local function get_lsp_hover(bufnr, position, cb) - local params = { - textDocument = { uri = vim.uri_from_bufnr(bufnr) }, - position = position, - } + local params = { + textDocument = { uri = vim.uri_from_bufnr(bufnr) }, + position = position, + } - vim.lsp.buf_request( - bufnr, - "textDocument/hover", - params, - function(err, result, _, _) - if err then - cb(nil, vim.inspect(err)) - return - end - cb(result, nil) - end - ) + vim.lsp.buf_request( + bufnr, + "textDocument/hover", + params, + function(err, result, _, _) + if err then + cb(nil, vim.inspect(err)) + return + end + cb(result, nil) + end + ) end --- Finds the return statement in a Lua file and extracts the exported keys. @@ -123,51 +123,51 @@ end --- @param bufnr number The buffer number --- @return { name: string, line: number, col: number }[] List of exported names with positions local function find_export_keys(bufnr) - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) - local exports = {} + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local exports = {} - -- Find the last return statement - local return_line_idx = nil - for i = #lines, 1, -1 do - if lines[i]:match("^%s*return%s+") then - return_line_idx = i - break - end + -- Find the last return statement + local return_line_idx = nil + for i = #lines, 1, -1 do + if lines[i]:match("^%s*return%s+") then + return_line_idx = i + break end + end - if not return_line_idx then - return exports - end + if not return_line_idx then + return exports + end - -- Check if it's a simple `return M` style - local simple_return = - lines[return_line_idx]:match("^%s*return%s+([%w_]+)%s*$") - if simple_return then - local col = lines[return_line_idx]:find(simple_return) + -- Check if it's a simple `return M` style + local simple_return = + lines[return_line_idx]:match("^%s*return%s+([%w_]+)%s*$") + if simple_return then + local col = lines[return_line_idx]:find(simple_return) + table.insert(exports, { + name = simple_return, + line = return_line_idx - 1, + col = col - 1, + }) + return exports + end + + -- Parse `return { Key = Value, ... }` style + for i = return_line_idx, #lines do + local line = lines[i] + for key, col_start in line:gmatch("()([%w_]+)%s*=") do + key, col_start = col_start, key + if key ~= "" and not key:match("^%d") then table.insert(exports, { - name = simple_return, - line = return_line_idx - 1, - col = col - 1, + name = key, + line = i - 1, + col = col_start - 1, }) - return exports - end - - -- Parse `return { Key = Value, ... }` style - for i = return_line_idx, #lines do - local line = lines[i] - for key, col_start in line:gmatch("()([%w_]+)%s*=") do - key, col_start = col_start, key - if key ~= "" and not key:match("^%d") then - table.insert(exports, { - name = key, - line = i - 1, - col = col_start - 1, - }) - end - end + end end + end - return exports + return exports end --- Gets the hover information for each exported symbol using LSP. @@ -176,59 +176,55 @@ end --- @param export_keys { name: string, line: number, col: number }[] The export positions --- @param cb fun(results: table<string, string>): nil Callback with name -> hover info map local function get_exports_hover_info(bufnr, export_keys, cb) - if #export_keys == 0 then - cb({}) - return - end + if #export_keys == 0 then + cb({}) + return + end - local results = {} - local pending = #export_keys + local results = {} + local pending = #export_keys - for _, export in ipairs(export_keys) do - local line_text = vim.api.nvim_buf_get_lines( - bufnr, - export.line, - export.line + 1, - false - )[1] + for _, export in ipairs(export_keys) do + local line_text = + vim.api.nvim_buf_get_lines(bufnr, export.line, export.line + 1, false)[1] - local pattern = export.name .. "%s*=%s*()" - local value_start = line_text:match(pattern) - local hover_col = value_start and (value_start - 1) or export.col - local position = { line = export.line, character = hover_col } + local pattern = export.name .. "%s*=%s*()" + local value_start = line_text:match(pattern) + local hover_col = value_start and (value_start - 1) or export.col + local position = { line = export.line, character = hover_col } - get_lsp_hover(bufnr, position, function(result, _) - if result and result.contents then - local content = result.contents - if type(content) == "table" then - if content.value then - results[export.name] = content.value - elseif content.kind == "markdown" then - results[export.name] = content.value - else - local parts = {} - for _, part in ipairs(content) do - if type(part) == "string" then - table.insert(parts, part) - elseif part.value then - table.insert(parts, part.value) - end - end - results[export.name] = table.concat(parts, "\n") - end - else - results[export.name] = tostring(content) - end - else - results[export.name] = "unknown" + get_lsp_hover(bufnr, position, function(result, _) + if result and result.contents then + local content = result.contents + if type(content) == "table" then + if content.value then + results[export.name] = content.value + elseif content.kind == "markdown" then + results[export.name] = content.value + else + local parts = {} + for _, part in ipairs(content) do + if type(part) == "string" then + table.insert(parts, part) + elseif part.value then + table.insert(parts, part.value) + end end + results[export.name] = table.concat(parts, "\n") + end + else + results[export.name] = tostring(content) + end + else + results[export.name] = "unknown" + end - pending = pending - 1 - if pending == 0 then - cb(results) - end - end) - end + pending = pending - 1 + if pending == 0 then + cb(results) + end + end) + end end --- Finds all method/field definitions for a class in the source file. @@ -237,32 +233,32 @@ end --- @param class_name string The name of the class (e.g., "Lsp") --- @return { name: string, line: number, col: number }[] List of member positions local function find_class_member_positions(file_lines, class_name) - local members = {} + local members = {} - for i, line in ipairs(file_lines) do - local method_name = - line:match("^%s*function%s+" .. class_name .. "[%.:]([%w_]+)%s*%(") - if method_name then - local col = line:find(method_name, 1, true) - table.insert(members, { - name = method_name, - line = i - 1, - col = col and (col - 1) or 0, - }) - end + for i, line in ipairs(file_lines) do + local method_name = + line:match("^%s*function%s+" .. class_name .. "[%.:]([%w_]+)%s*%(") + if method_name then + local col = line:find(method_name, 1, true) + table.insert(members, { + name = method_name, + line = i - 1, + col = col and (col - 1) or 0, + }) + end - local field_name = line:match("^%s*" .. class_name .. "%.([%w_]+)%s*=") - if field_name and not line:match("^%s*function") then - local col = line:find(field_name, 1, true) - table.insert(members, { - name = field_name, - line = i - 1, - col = col and (col - 1) or 0, - }) - end + local field_name = line:match("^%s*" .. class_name .. "%.([%w_]+)%s*=") + if field_name and not line:match("^%s*function") then + local col = line:find(field_name, 1, true) + table.insert(members, { + name = field_name, + line = i - 1, + col = col and (col - 1) or 0, + }) end + end - return members + return members end --- Gets hover information for each class member using LSP. @@ -271,52 +267,52 @@ end --- @param member_positions { name: string, line: number, col: number }[] Member positions --- @param cb fun(results: table<string, string>): nil Callback with name -> type info map local function get_class_members_hover(bufnr, member_positions, cb) - if #member_positions == 0 then - cb({}) - return - end + if #member_positions == 0 then + cb({}) + return + end - local results = {} - local pending = #member_positions + local results = {} + local pending = #member_positions - for _, member in ipairs(member_positions) do - local position = { line = member.line, character = member.col } + for _, member in ipairs(member_positions) do + local position = { line = member.line, character = member.col } - get_lsp_hover(bufnr, position, function(result, _) - local hover_text = "unknown" + get_lsp_hover(bufnr, position, function(result, _) + local hover_text = "unknown" - if result and result.contents then - local content = result.contents + if result and result.contents then + local content = result.contents - if type(content) == "table" then - if content.value then - hover_text = content.value - elseif content.kind then - hover_text = content.value or "" - else - local parts = {} - for _, part in ipairs(content) do - if type(part) == "string" then - table.insert(parts, part) - elseif part.value then - table.insert(parts, part.value) - end - end - hover_text = table.concat(parts, "\n") - end - else - hover_text = tostring(content) - end + if type(content) == "table" then + if content.value then + hover_text = content.value + elseif content.kind then + hover_text = content.value or "" + else + local parts = {} + for _, part in ipairs(content) do + if type(part) == "string" then + table.insert(parts, part) + elseif part.value then + table.insert(parts, part.value) + end end + hover_text = table.concat(parts, "\n") + end + else + hover_text = tostring(content) + end + end - results[member.name] = hover_text + results[member.name] = hover_text - pending = pending - 1 - if pending == 0 then - cb(results) - end - end) - end + pending = pending - 1 + if pending == 0 then + cb(results) + end + end) + end end --- Removes markdown fencing and cleans hover output. @@ -324,24 +320,24 @@ end --- @param hover_text string The raw hover text from LSP --- @return string The cleaned type information local function format_hover_output(hover_text) - if not hover_text or hover_text == "unknown" then - return "unknown" - end + if not hover_text or hover_text == "unknown" then + return "unknown" + end - local lines = {} + local lines = {} - for line in hover_text:gmatch("[^\n]+") do - if not line:match("^```") then - local cleaned = line - cleaned = cleaned:gsub("^local%s+", "") - cleaned = cleaned:gsub("^[%w_]+:%s*", "") - if cleaned ~= "" then - table.insert(lines, cleaned) - end - end + for line in hover_text:gmatch("[^\n]+") do + if not line:match("^```") then + local cleaned = line + cleaned = cleaned:gsub("^local%s+", "") + cleaned = cleaned:gsub("^[%w_]+:%s*", "") + if cleaned ~= "" then + table.insert(lines, cleaned) + end end + end - return table.concat(lines, "\n") + return table.concat(lines, "\n") end --- Formats a function hover result into TypeScript-style signature. @@ -349,21 +345,21 @@ end --- @param hover_text string The hover text from LSP --- @return string The formatted signature like "(a: number, b: string): boolean" local function format_function_signature(hover_text) - local clean = hover_text:gsub("```%w*\n?", ""):gsub("```", "") - clean = clean:gsub("^%s*", ""):gsub("%s*$", "") + local clean = hover_text:gsub("```%w*\n?", ""):gsub("```", "") + clean = clean:gsub("^%s*", ""):gsub("%s*$", "") - local params, ret = - clean:match("function%s*[%w_%.%:]*%((.-)%)%s*:%s*([^\n]+)") + local params, ret = + clean:match("function%s*[%w_%.%:]*%((.-)%)%s*:%s*([^\n]+)") + if params then + return string.format("(%s): %s", params, ret or "nil") + else + params = clean:match("function%s*[%w_%.%:]*%((.-)%)") if params then - return string.format("(%s): %s", params, ret or "nil") - else - params = clean:match("function%s*[%w_%.%:]*%((.-)%)") - if params then - return string.format("(%s): nil", params) - end + return string.format("(%s): nil", params) end + end - return clean + return clean end --- Extracts all enum values from source (not truncated like hover). @@ -372,34 +368,34 @@ end --- @param symbol_name string The name of the enum symbol --- @return string[] Array of enum entries like "Key = value" local function expand_enum_values(file_lines, symbol_name) - local values = {} - - for i, line in ipairs(file_lines) do - if - line:match("local%s+" .. symbol_name .. "%s*=") - or line:match(symbol_name .. "%s*=%s*{") - then - local j = i - while j <= #file_lines do - local enum_line = file_lines[j] + local values = {} - if enum_line:match("^%s*}") then - break - end + for i, line in ipairs(file_lines) do + if + line:match("local%s+" .. symbol_name .. "%s*=") + or line:match(symbol_name .. "%s*=%s*{") + then + local j = i + while j <= #file_lines do + local enum_line = file_lines[j] - local key, value = enum_line:match("^%s*([%w_]+)%s*=%s*([^,]+)") - if key and value then - value = value:match("^%s*(.-)%s*,?%s*$") - table.insert(values, key .. " = " .. value) - end + if enum_line:match("^%s*}") then + break + end - j = j + 1 - end - break + local key, value = enum_line:match("^%s*([%w_]+)%s*=%s*([^,]+)") + if key and value then + value = value:match("^%s*(.-)%s*,?%s*$") + table.insert(values, key .. " = " .. value) end + + j = j + 1 + end + break end + end - return values + return values end -------------------------------------------------------------------------------- @@ -416,9 +412,9 @@ Lsp.__index = Lsp --- @param config _99.Options The configuration options --- @return Lsp A new Lsp instance function Lsp.new(config) - return setmetatable({ - config = config, - }, Lsp) + return setmetatable({ + config = config, + }, Lsp) end -------------------------------------------------------------------------------- @@ -431,97 +427,97 @@ end --- @param require_path string The Lua require path (e.g., "99", "99.logger.logger") --- @param cb fun(result: string, err: string|nil): nil Callback with formatted string or error function Lsp.stringify_module_exports(require_path, cb) - local resolved_path = resolve_require_path(require_path) + local resolved_path = resolve_require_path(require_path) - if not resolved_path then - cb( - "", - "Could not resolve module path: " - .. require_path - .. ". The module may not be in runtimepath." - ) - return - end + if not resolved_path then + cb( + "", + "Could not resolve module path: " + .. require_path + .. ". The module may not be in runtimepath." + ) + return + end - local uri = vim.uri_from_fname(resolved_path) + local uri = vim.uri_from_fname(resolved_path) - ensure_buffer_with_lsp(resolved_path, function(bufnr, err) - if err then - cb("", err) - return - end + ensure_buffer_with_lsp(resolved_path, function(bufnr, err) + if err then + cb("", err) + return + end - local export_keys = find_export_keys(bufnr) + local export_keys = find_export_keys(bufnr) - if #export_keys == 0 then - cb("", "No exports found in return statement") - return - end + if #export_keys == 0 then + cb("", "No exports found in return statement") + return + end - get_exports_hover_info(bufnr, export_keys, function(hover_results) - local file_lines = vim.fn.readfile(resolved_path) + get_exports_hover_info(bufnr, export_keys, function(hover_results) + local file_lines = vim.fn.readfile(resolved_path) - -- Collect classes that need member expansion - local classes_to_expand = {} - for _, export in ipairs(export_keys) do - local hover = hover_results[export.name] or "unknown" - local is_class = hover:match("__index") ~= nil - or hover:match(":%s*[%w_]+%s*{") ~= nil + -- Collect classes that need member expansion + local classes_to_expand = {} + for _, export in ipairs(export_keys) do + local hover = hover_results[export.name] or "unknown" + local is_class = hover:match("__index") ~= nil + or hover:match(":%s*[%w_]+%s*{") ~= nil - if is_class then - local member_positions = - find_class_member_positions(file_lines, export.name) - if #member_positions > 0 then - table.insert(classes_to_expand, { - name = export.name, - positions = member_positions, - }) - end - end - end + if is_class then + local member_positions = + find_class_member_positions(file_lines, export.name) + if #member_positions > 0 then + table.insert(classes_to_expand, { + name = export.name, + positions = member_positions, + }) + end + end + end - -- If no classes, format immediately - if #classes_to_expand == 0 then - local result = Lsp._format_exports( - require_path, - uri, - export_keys, - hover_results, - file_lines, - {} - ) - cb(result, nil) - return - end + -- If no classes, format immediately + if #classes_to_expand == 0 then + local result = Lsp._format_exports( + require_path, + uri, + export_keys, + hover_results, + file_lines, + {} + ) + cb(result, nil) + return + end - -- Get hover for class members - local pending = #classes_to_expand - local all_member_hovers = {} + -- Get hover for class members + local pending = #classes_to_expand + local all_member_hovers = {} - for _, class_info in ipairs(classes_to_expand) do - get_class_members_hover( - bufnr, - class_info.positions, - function(member_hovers) - all_member_hovers[class_info.name] = member_hovers - pending = pending - 1 + for _, class_info in ipairs(classes_to_expand) do + get_class_members_hover( + bufnr, + class_info.positions, + function(member_hovers) + all_member_hovers[class_info.name] = member_hovers + pending = pending - 1 - if pending == 0 then - local result = Lsp._format_exports( - require_path, - uri, - export_keys, - hover_results, - file_lines, - all_member_hovers - ) - cb(result, nil) - end - end - ) + if pending == 0 then + local result = Lsp._format_exports( + require_path, + uri, + export_keys, + hover_results, + file_lines, + all_member_hovers + ) + cb(result, nil) end - end) + end + ) + end end) + end) end --- Internal function to format exports into a string. @@ -534,88 +530,84 @@ end --- @param class_member_hovers table<string, table<string, string>> Class name -> member hovers --- @return string The formatted export string function Lsp._format_exports( - module_path, - uri, - export_keys, - hover_results, - file_lines, - class_member_hovers + module_path, + uri, + export_keys, + hover_results, + file_lines, + class_member_hovers ) - local out = {} + local out = {} - table.insert(out, "Module: " .. module_path) - table.insert(out, "URI: " .. uri) - table.insert(out, string.rep("-", 60)) + table.insert(out, "Module: " .. module_path) + table.insert(out, "URI: " .. uri) + table.insert(out, string.rep("-", 60)) - for _, export in ipairs(export_keys) do - table.insert(out, "") - - local hover = hover_results[export.name] or "unknown" + for _, export in ipairs(export_keys) do + table.insert(out, "") - local is_enum = hover:match("enum%s+") ~= nil - local is_class = hover:match("__index") ~= nil - or hover:match(":%s*[%w_]+%s*{") ~= nil + local hover = hover_results[export.name] or "unknown" - if is_enum then - local values = expand_enum_values(file_lines, export.name) - if #values > 0 then - table.insert(out, export.name .. " = {") - for _, v in ipairs(values) do - table.insert(out, " " .. v) - end - table.insert(out, "}") - else - table.insert( - out, - export.name .. ": " .. format_hover_output(hover) - ) - end - elseif is_class then - local member_hovers = class_member_hovers[export.name] or {} - table.insert(out, export.name .. " {") + local is_enum = hover:match("enum%s+") ~= nil + local is_class = hover:match("__index") ~= nil + or hover:match(":%s*[%w_]+%s*{") ~= nil - -- Extract fields from class hover - local class_fields = {} - for line in hover:gmatch("[^\n]+") do - local field_name, field_type = - line:match("^%s*([%w_]+):%s*([^,}]+)") - if field_name and field_type then - field_type = field_type:match("^%s*(.-)%s*,?$") - if field_type ~= "function" then - class_fields[field_name] = field_type - end - end - end + if is_enum then + local values = expand_enum_values(file_lines, export.name) + if #values > 0 then + table.insert(out, export.name .. " = {") + for _, v in ipairs(values) do + table.insert(out, " " .. v) + end + table.insert(out, "}") + else + table.insert(out, export.name .. ": " .. format_hover_output(hover)) + end + elseif is_class then + local member_hovers = class_member_hovers[export.name] or {} + table.insert(out, export.name .. " {") - -- Print fields - for field_name, field_type in pairs(class_fields) do - if field_name ~= "__index" then - table.insert(out, " " .. field_name .. ": " .. field_type) - end - end + -- Extract fields from class hover + local class_fields = {} + for line in hover:gmatch("[^\n]+") do + local field_name, field_type = line:match("^%s*([%w_]+):%s*([^,}]+)") + if field_name and field_type then + field_type = field_type:match("^%s*(.-)%s*,?$") + if field_type ~= "function" then + class_fields[field_name] = field_type + end + end + end - -- Print methods with full signatures - for method_name, method_hover in pairs(member_hovers) do - if method_name ~= "__index" then - local sig = format_function_signature(method_hover) - table.insert(out, " " .. method_name .. sig) - end - end + -- Print fields + for field_name, field_type in pairs(class_fields) do + if field_name ~= "__index" then + table.insert(out, " " .. field_name .. ": " .. field_type) + end + end - table.insert(out, "}") - else - local formatted = format_hover_output(hover) - table.insert(out, export.name .. ": " .. formatted) + -- Print methods with full signatures + for method_name, method_hover in pairs(member_hovers) do + if method_name ~= "__index" then + local sig = format_function_signature(method_hover) + table.insert(out, " " .. method_name .. sig) end + end + + table.insert(out, "}") + else + local formatted = format_hover_output(hover) + table.insert(out, export.name .. ": " .. formatted) end + end - return table.concat(out, "\n") + return table.concat(out, "\n") end Lsp.stringify_module_exports("99.editor.lsp", function(res) - print(res) + print(res) end) return { - Lsp = Lsp, + Lsp = Lsp, } diff --git a/lua/99/editor/treesitter.lua b/lua/99/editor/treesitter.lua index b758266..d219ffb 100644 --- a/lua/99/editor/treesitter.lua +++ b/lua/99/editor/treesitter.lua @@ -22,69 +22,69 @@ local fn_call_query = "99-fn-call" --- @param buffer number ---@param lang string local function tree_root(buffer, lang) - -- Load the parser and the query. - local ok, parser = pcall(vim.treesitter.get_parser, buffer, lang) - if not ok then - return nil - end + -- Load the parser and the query. + local ok, parser = pcall(vim.treesitter.get_parser, buffer, lang) + if not ok then + return nil + end - local tree = parser:parse()[1] - return tree:root() + local tree = parser:parse()[1] + return tree:root() end --- @param context _99.RequestContext --- @param cursor _99.Point --- @return _99.treesitter.TSNode | nil function M.fn_call(context, cursor) - local buffer = context.buffer - local lang = context.file_type - local logger = context.logger:set_area("treesitter") - local root = tree_root(buffer, lang) - if not root then - Logger:error( - "unable to find treeroot, this should never happen", - "buffer", - buffer, - "lang", - lang - ) - return nil - end + local buffer = context.buffer + local lang = context.file_type + local logger = context.logger:set_area("treesitter") + local root = tree_root(buffer, lang) + if not root then + Logger:error( + "unable to find treeroot, this should never happen", + "buffer", + buffer, + "lang", + lang + ) + return nil + end - local ok, query = pcall(vim.treesitter.query.get, lang, fn_call_query) - if not ok or query == nil then - logger:error( - "unable to get the fn_call_query", - "lang", - lang, - "buffer", - buffer, - "ok", - type(ok), - "query", - type(query) - ) - return nil - end + local ok, query = pcall(vim.treesitter.query.get, lang, fn_call_query) + if not ok or query == nil then + logger:error( + "unable to get the fn_call_query", + "lang", + lang, + "buffer", + buffer, + "ok", + type(ok), + "query", + type(query) + ) + return nil + end - --- likely something that needs to be done with treesitter#get_node - local found = nil - for _, match, _ in query:iter_matches(root, buffer, 0, -1, { all = true }) do - for _, nodes in pairs(match) do - for _, node in ipairs(nodes) do - local range = Range:from_ts_node(node, buffer) - if range:contains(cursor) then - found = node - goto end_of_loops - end - end + --- likely something that needs to be done with treesitter#get_node + local found = nil + for _, match, _ in query:iter_matches(root, buffer, 0, -1, { all = true }) do + for _, nodes in pairs(match) do + for _, node in ipairs(nodes) do + local range = Range:from_ts_node(node, buffer) + if range:contains(cursor) then + found = node + goto end_of_loops end + end end - ::end_of_loops:: + end + ::end_of_loops:: - logger:debug("treesitter#fn_call", "found", found ~= nil) + logger:debug("treesitter#fn_call", "found", found ~= nil) - return found + return found end --- @class _99.treesitter.Function @@ -99,7 +99,7 @@ Function.__index = Function --- to replace at the exact function begin / end --- @param replace_with string[] function Function:replace_text(replace_with) - self.function_range:replace_text(replace_with) + self.function_range:replace_text(replace_with) end --- @param ts_node _99.treesitter.TSNode @@ -107,149 +107,149 @@ end ---@param context _99.RequestContext ---@return _99.treesitter.Function function Function.from_ts_node(ts_node, cursor, context) - local ok, query = - pcall(vim.treesitter.query.get, context.file_type, function_query) - local logger = context.logger:set_area("Function") - if not ok or query == nil then - logger:fatal("not query or not ok") - error("failed") - end + local ok, query = + pcall(vim.treesitter.query.get, context.file_type, function_query) + local logger = context.logger:set_area("Function") + if not ok or query == nil then + logger:fatal("not query or not ok") + error("failed") + end - local func = {} - for id, node, _ in - query:iter_captures(ts_node, context.buffer, 0, -1, { all = true }) - do - local range = Range:from_ts_node(node, context.buffer) - local name = query.captures[id] - if range:contains(cursor) then - if name == "context.function" then - func.function_node = node - func.function_range = range - elseif name == "context.body" then - func.body_node = node - func.body_range = range - end - end + local func = {} + for id, node, _ in + query:iter_captures(ts_node, context.buffer, 0, -1, { all = true }) + do + local range = Range:from_ts_node(node, context.buffer) + local name = query.captures[id] + if range:contains(cursor) then + if name == "context.function" then + func.function_node = node + func.function_range = range + elseif name == "context.body" then + func.body_node = node + func.body_range = range + end end + end - --- NOTE: not all functions have bodies... (lua: local function foo() end) - logger:assert(func.function_node ~= nil, "function_node not found") - logger:assert(func.function_range ~= nil, "function_range not found") + --- NOTE: not all functions have bodies... (lua: local function foo() end) + logger:assert(func.function_node ~= nil, "function_node not found") + logger:assert(func.function_range ~= nil, "function_range not found") - return setmetatable(func, Function) + return setmetatable(func, Function) end --- @param context _99.RequestContext --- @param cursor _99.Point --- @return _99.treesitter.Function? function M.containing_function(context, cursor) - local buffer = context.buffer - local lang = context.file_type - local logger = context and context.logger:set_area("treesitter") or Logger - - logger:error("loading lang", "buffer", buffer, "lang", lang) - local root = tree_root(buffer, lang) - if not root then - logger:debug("LSP: could not find tree root") - return nil - end + local buffer = context.buffer + local lang = context.file_type + local logger = context and context.logger:set_area("treesitter") or Logger - local ok, query = pcall(vim.treesitter.query.get, lang, function_query) - if not ok or query == nil then - logger:debug( - "LSP: not ok or query", - "query", - vim.inspect(query), - "lang", - lang, - "ok", - vim.inspect(ok) - ) - return nil - end - - --- @type _99.Range - local found_range = nil - --- @type _99.treesitter.TSNode - local found_node = nil - for id, node, _ in query:iter_captures(root, buffer, 0, -1, { all = true }) do - local range = Range:from_ts_node(node, buffer) - local name = query.captures[id] - if name == "context.function" and range:contains(cursor) then - if not found_range then - found_range = range - found_node = node - elseif found_range:area() > range:area() then - found_range = range - found_node = node - end - end - end + logger:error("loading lang", "buffer", buffer, "lang", lang) + local root = tree_root(buffer, lang) + if not root then + logger:debug("LSP: could not find tree root") + return nil + end + local ok, query = pcall(vim.treesitter.query.get, lang, function_query) + if not ok or query == nil then logger:debug( - "treesitter#containing_function", - "found_range", - found_range and found_range:to_string() or "found_range is nil" + "LSP: not ok or query", + "query", + vim.inspect(query), + "lang", + lang, + "ok", + vim.inspect(ok) ) + return nil + end - if not found_range then - return nil + --- @type _99.Range + local found_range = nil + --- @type _99.treesitter.TSNode + local found_node = nil + for id, node, _ in query:iter_captures(root, buffer, 0, -1, { all = true }) do + local range = Range:from_ts_node(node, buffer) + local name = query.captures[id] + if name == "context.function" and range:contains(cursor) then + if not found_range then + found_range = range + found_node = node + elseif found_range:area() > range:area() then + found_range = range + found_node = node + end end - logger:assert( - found_node, - "INVARIANT: found_range is not nil but found node is" - ) + end - ok, query = pcall(vim.treesitter.query.get, lang, function_query) - if not ok or query == nil then - logger:fatal("INVARIANT: found_range ", "range", found_range:to_text()) - return - end + logger:debug( + "treesitter#containing_function", + "found_range", + found_range and found_range:to_string() or "found_range is nil" + ) + + if not found_range then + return nil + end + logger:assert( + found_node, + "INVARIANT: found_range is not nil but found node is" + ) + + ok, query = pcall(vim.treesitter.query.get, lang, function_query) + if not ok or query == nil then + logger:fatal("INVARIANT: found_range ", "range", found_range:to_text()) + return + end - --- TODO: we need some language specific things here. - --- that is because comments above the function needs to considered - return Function.from_ts_node(found_node, cursor, context) + --- TODO: we need some language specific things here. + --- that is because comments above the function needs to considered + return Function.from_ts_node(found_node, cursor, context) end --- @param buffer number --- @return _99.treesitter.Node[] function M.imports(buffer) - Logger:assert(false, "not implemented yet", "id", 69420) - local lang = vim.bo[buffer].ft - local root = tree_root(buffer, lang) - if not root then - Logger:debug("imports: could not find tree root") - return {} - end + Logger:assert(false, "not implemented yet", "id", 69420) + local lang = vim.bo[buffer].ft + local root = tree_root(buffer, lang) + if not root then + Logger:debug("imports: could not find tree root") + return {} + end - local ok, query = pcall(vim.treesitter.query.get, lang, imports_query) + local ok, query = pcall(vim.treesitter.query.get, lang, imports_query) - if not ok or query == nil then - Logger:debug( - "imports: not ok or query", - "query", - vim.inspect(query), - "lang", - lang, - "ok", - vim.inspect(ok) - ) - return {} - end + if not ok or query == nil then + Logger:debug( + "imports: not ok or query", + "query", + vim.inspect(query), + "lang", + lang, + "ok", + vim.inspect(ok) + ) + return {} + end - local imports = {} - for _, match, _ in query:iter_matches(root, buffer, 0, -1, { all = true }) do - for id, nodes in pairs(match) do - local name = query.captures[id] - if name == "import.name" then - for _, node in ipairs(nodes) do - table.insert(imports, node) - end - end + local imports = {} + for _, match, _ in query:iter_matches(root, buffer, 0, -1, { all = true }) do + for id, nodes in pairs(match) do + local name = query.captures[id] + if name == "import.name" then + for _, node in ipairs(nodes) do + table.insert(imports, node) end + end end + end - return imports + return imports end return M |
