diff options
| author | theprimeagain <the.primeagen@gmail.com> | 2026-02-26 07:11:49 -0700 |
|---|---|---|
| committer | theprimeagain <the.primeagen@gmail.com> | 2026-02-26 07:11:49 -0700 |
| commit | cbc24f940f49b28da62642bca10206f3933bec77 (patch) | |
| tree | d9429175bdb3320501358380ff91159a1bb4a30e | |
| parent | 9be01a1a50a61635ea7169e01c85fab41638ad66 (diff) | |
| download | a4-cbc24f940f49b28da62642bca10206f3933bec77.tar.xz a4-cbc24f940f49b28da62642bca10206f3933bec77.zip | |
tutorial
| -rw-r--r-- | .agents/skills/vim.diagnostics/SKILL.md | 83 | ||||
| -rw-r--r-- | .agents/skills/vim.lsp/SKILL.md | 114 | ||||
| -rw-r--r-- | .agents/skills/vim.treesitter/SKILL.md | 90 | ||||
| -rw-r--r-- | .agents/skills/vim/SKILL.md | 734 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | lua/99/geo.lua | 8 | ||||
| -rw-r--r-- | lua/99/init.lua | 253 | ||||
| -rw-r--r-- | lua/99/ops/init.lua | 2 | ||||
| -rw-r--r-- | lua/99/ops/search.lua | 4 | ||||
| -rw-r--r-- | lua/99/ops/tutorial.lua | 3 | ||||
| -rw-r--r-- | lua/99/ops/vibe.lua | 4 | ||||
| -rw-r--r-- | lua/99/prompt-settings.lua | 5 | ||||
| -rw-r--r-- | lua/99/prompt.lua | 3 | ||||
| -rw-r--r-- | lua/99/state.lua | 10 | ||||
| -rw-r--r-- | lua/99/test/geo_spec.lua | 22 | ||||
| -rw-r--r-- | lua/99/test/open_spec.lua | 99 | ||||
| -rw-r--r-- | lua/99/test/qfix_navigation_spec.lua | 70 | ||||
| -rw-r--r-- | lua/99/test/utils_spec.lua | 19 | ||||
| -rw-r--r-- | lua/99/test/vibe_search_spec.lua | 56 | ||||
| -rw-r--r-- | lua/99/utils.lua | 15 | ||||
| -rw-r--r-- | lua/99/window/init.lua | 39 |
21 files changed, 1283 insertions, 363 deletions
diff --git a/.agents/skills/vim.diagnostics/SKILL.md b/.agents/skills/vim.diagnostics/SKILL.md new file mode 100644 index 0000000..463f7e2 --- /dev/null +++ b/.agents/skills/vim.diagnostics/SKILL.md @@ -0,0 +1,83 @@ +# Neovim Diagnostics API Reference + +This document contains function definitions for Neovim's diagnostics Lua API. +Use this as a reference when working with diagnostics in Neovim Lua. + +--- + +## diagnostic + +vim.diagnostic APIs, types, and helpers. + +```lua +function get_qf_id_for_title(title) +function __newindex(t, name, handler) +function __index(t, bufnr) +function callback() +function to_severity(severity) +function severity_predicate(severity) +function filter_by_severity(severity, diagnostics) +function count_sources(bufnr) +function prefix_source(diagnostics) +function reformat_diagnostics(format, diagnostics) +function enabled_value(option, namespace) +function resolve_optional_value(option, value, namespace, bufnr) +function get_resolved_options(opts, namespace, bufnr) +function make_highlight_map(base_name) +function diagnostic_lines(diagnostics) +function diagnostics_at_cursor(diagnostics) +function set_diagnostic_cache(namespace, bufnr, diagnostics) +function restore_extmarks(bufnr, last) +function save_extmarks(namespace, bufnr) +function on_lines(_, _, _, _, _, last) +function on_detach() +function severity_to_extmark_priority(priority, opts) +function make_augroup_key(namespace, bufnr) +function execute_scheduled_display(namespace, bufnr) +function schedule_display(namespace, bufnr, args) +function clear_scheduled_display(namespace, bufnr) +function get_diagnostics(bufnr, opts, clamp) +function __index(t, k) +function add(b, d) +function add_all_diags(buf, diags) +function set_list(loclist, opts) +function filter_highest(diagnostics) +function next_diagnostic(search_forward, opts) +function sort_diagnostics(a, b) +function is_next(d) +function goto_diagnostic(diagnostic, opts) +function M.config(opts, namespace) +function M.set(namespace, bufnr, diagnostics, opts) +function M.get_namespace(namespace) +function M.get_namespaces() +function M.get(bufnr, opts) +function M.count(bufnr, opts) +function M.get_prev(opts) +function M.get_prev_pos(opts) +function M.goto_prev(opts) +function M.get_next(opts) +function M.get_next_pos(opts) +function M.jump(opts) +function M.goto_next(opts) +function show(namespace, bufnr, diagnostics, opts) +function hide(namespace, bufnr) +function render_virtual_text(namespace, bufnr, diagnostics, opts) +function distance_between_cols(bufnr, lnum, start_col, end_col) +function render_virtual_lines(namespace, bufnr, diagnostics) +function format_virtual_lines(diagnostic) +function M._get_virt_text_chunks(line_diags, opts) +function M.hide(namespace, bufnr) +function M.is_enabled(filter) +function M.is_disabled(bufnr, namespace) +function M.show(namespace, bufnr, diagnostics, opts) +function M.open_float(opts, ...) +function M.reset(namespace, bufnr) +function M.setqflist(opts) +function M.setloclist(opts) +function M.disable(bufnr, namespace) +function M.enable(enable, filter) +function __index() +function M.match(str, pat, groups, severity_map, defaults) +function M.toqflist(diagnostics) +function M.fromqflist(list) +``` diff --git a/.agents/skills/vim.lsp/SKILL.md b/.agents/skills/vim.lsp/SKILL.md new file mode 100644 index 0000000..17aae57 --- /dev/null +++ b/.agents/skills/vim.lsp/SKILL.md @@ -0,0 +1,114 @@ +# Neovim LSP API Reference + +This document contains function definitions from Neovim's LSP help docs. +Use this as a reference when working with LSP in Neovim Lua. + +--- + +## lsp + +Functions extracted from `lsp.txt`. + +```lua +vim.lsp.buf_attach_client({bufnr}, {client_id}) +vim.lsp.buf_detach_client({bufnr}, {client_id}) +vim.lsp.buf_is_attached({bufnr}, {client_id}) +vim.lsp.buf_notify({bufnr}, {method}, {params}) +vim.lsp.buf_request_all({bufnr}, {method}, {params}, {handler}) +vim.lsp.buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) +vim.lsp.client_is_stopped({client_id}) +vim.lsp.config({name}, {cfg}) +vim.lsp.enable({name}, {enable}) +vim.lsp.foldclose({kind}, {winid}) +vim.lsp.foldexpr({lnum}) +vim.lsp.foldtext() +vim.lsp.formatexpr({opts}) +vim.lsp.get_buffers_by_client_id({client_id}) +vim.lsp.get_client_by_id({client_id}) +vim.lsp.get_clients({filter}) +vim.lsp.get_log_path() +vim.lsp.is_enabled({name}) +vim.lsp.omnifunc({findstart}, {base}) +vim.lsp.set_log_level({level}) +vim.lsp.start({config}, {opts}) +vim.lsp.status() +vim.lsp.stop_client({client_id}, {force}) +vim.lsp.tagfunc({pattern}, {flags}) +vim.lsp.buf.add_workspace_folder({workspace_folder}) +vim.lsp.buf.clear_references() +vim.lsp.buf.code_action({opts}) +vim.lsp.buf.declaration({opts}) +vim.lsp.buf.definition({opts}) +vim.lsp.buf.document_highlight() +vim.lsp.buf.document_symbol({opts}) +vim.lsp.buf.format({opts}) +vim.lsp.buf.hover({config}) +vim.lsp.buf.implementation({opts}) +vim.lsp.buf.incoming_calls() +vim.lsp.buf.list_workspace_folders() +vim.lsp.buf.outgoing_calls() +vim.lsp.buf.references({context}, {opts}) +vim.lsp.buf.remove_workspace_folder({workspace_folder}) +vim.lsp.buf.rename({new_name}, {opts}) +vim.lsp.buf.signature_help({config}) +vim.lsp.buf.type_definition({opts}) +vim.lsp.buf.typehierarchy({kind}) +vim.lsp.buf.workspace_symbol({query}, {opts}) +vim.lsp.diagnostic.from({diagnostics}) +vim.lsp.diagnostic.get_namespace({client_id}, {is_pull}) +vim.lsp.diagnostic.on_diagnostic({error}, {result}, {ctx}) +vim.lsp.diagnostic.on_publish_diagnostics({_}, {params}, {ctx}) +vim.lsp.codelens.clear({client_id}, {bufnr}) +vim.lsp.codelens.display({lenses}, {bufnr}, {client_id}) +vim.lsp.codelens.get({bufnr}) +vim.lsp.codelens.on_codelens({err}, {result}, {ctx}) +vim.lsp.codelens.refresh({opts}) +vim.lsp.codelens.run() +vim.lsp.codelens.save({lenses}, {bufnr}, {client_id}) +vim.lsp.completion.enable({enable}, {client_id}, {bufnr}, {opts}) +vim.lsp.completion.get({opts}) +vim.lsp.inlay_hint.enable({enable}, {filter}) +vim.lsp.inlay_hint.get({filter}) +vim.lsp.inlay_hint.is_enabled({filter}) +vim.lsp.semantic_tokens.force_refresh({bufnr}) +vim.lsp.semantic_tokens.get_at_pos({bufnr}, {row}, {col}) +vim.lsp.semantic_tokens.highlight_token({token}, {bufnr}, {client_id}, {hl_group}, {opts}) +vim.lsp.semantic_tokens.start({bufnr}, {client_id}, {opts}) +vim.lsp.semantic_tokens.stop({bufnr}, {client_id}) +vim.lsp.util.apply_text_document_edit({text_document_edit}, {index}, {position_encoding}) +vim.lsp.util.apply_text_edits({text_edits}, {bufnr}, {position_encoding}) +vim.lsp.util.apply_workspace_edit({workspace_edit}, {position_encoding}) +vim.lsp.util.buf_clear_references({bufnr}) +vim.lsp.util.buf_highlight_references({bufnr}, {references}, {position_encoding}) +vim.lsp.util.character_offset({buf}, {row}, {col}, {offset_encoding}) +vim.lsp.util.convert_input_to_markdown_lines({input}, {contents}) +vim.lsp.util.convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) +vim.lsp.util.get_effective_tabstop({bufnr}) +vim.lsp.util.locations_to_items({locations}, {position_encoding}) +vim.lsp.util.make_floating_popup_options({width}, {height}, {opts}) +vim.lsp.util.make_formatting_params({options}) +vim.lsp.util.make_given_range_params({start_pos}, {end_pos}, {bufnr}, {position_encoding}) +vim.lsp.util.make_position_params({window}, {position_encoding}) +vim.lsp.util.make_range_params({window}, {position_encoding}) +vim.lsp.util.make_text_document_params({bufnr}) +vim.lsp.util.make_workspace_params({added}, {removed}) +vim.lsp.util.open_floating_preview({contents}, {syntax}, {opts}) +vim.lsp.util.preview_location({location}, {opts}) +vim.lsp.util.rename({old_fname}, {new_fname}, {opts}) +vim.lsp.util.show_document({location}, {position_encoding}, {opts}) +vim.lsp.util.stylize_markdown({bufnr}, {contents}, {opts}) +vim.lsp.util.symbols_to_items({symbols}, {bufnr}, {position_encoding}) +vim.lsp.log.get_filename() +vim.lsp.log.get_level() +vim.lsp.log.set_format_func({handle}) +vim.lsp.log.set_level({level}) +vim.lsp.log.should_log({level}) +vim.lsp.rpc.connect({host_or_path}, {port}) +vim.lsp.rpc.format_rpc_error({err}) +vim.lsp.rpc.notify({method}, {params}) +vim.lsp.rpc.request({method}, {params}, {callback}, {notify_reply_callback}) +vim.lsp.rpc.rpc_response_error({code}, {message}, {data}) +vim.lsp.rpc.start({cmd}, {dispatchers}, {extra_spawn_params}) +vim.lsp.protocol.make_client_capabilities() +vim.lsp.protocol.resolve_capabilities({server_capabilities}) +``` diff --git a/.agents/skills/vim.treesitter/SKILL.md b/.agents/skills/vim.treesitter/SKILL.md new file mode 100644 index 0000000..6a3da81 --- /dev/null +++ b/.agents/skills/vim.treesitter/SKILL.md @@ -0,0 +1,90 @@ +# Neovim Treesitter API Reference + +This document contains type stubs and API references for Neovim's treesitter Lua API. +Use this as a reference when working with treesitter in Neovim Lua. + +--- + +## tsnode + +TSNode methods - represents a specific element in a parsed syntax tree. Use these methods to navigate and inspect nodes. + +```lua +function TSNode:parent() end +function TSNode:next_sibling() end +function TSNode:prev_sibling() end +function TSNode:next_named_sibling() end +function TSNode:prev_named_sibling() end +function TSNode:iter_children() end +function TSNode:field(name) end +function TSNode:child_count() end +function TSNode:child(index) end +function TSNode:named_child_count() end +function TSNode:named_children() end +function TSNode:__has_ancestor(node_types) end +function TSNode:named_child(index) end +function TSNode:child_with_descendant(descendant) end +function TSNode:start() end +function TSNode:end_() end +function TSNode:range(include_bytes) end +function TSNode:type() end +function TSNode:symbol() end +function TSNode:named() end +function TSNode:missing() end +function TSNode:extra() end +function TSNode:has_changes() end +function TSNode:has_error() end +function TSNode:sexpr() end +function TSNode:id() end +function TSNode:tree() end +function TSNode:descendant_for_range(start_row, start_col, end_row, end_col) end +function TSNode:named_descendant_for_range(start_row, start_col, end_row, end_col) end +function TSNode:equal(node) end +function TSNode:byte_length() end +``` + +--- + +## tstree + +TSTree methods - represents the parsed contents of a buffer. + +```lua +function TSTree:root() end +function TSTree:edit(start_byte, end_byte_old, end_byte_new, start_row, start_col, end_row_old, end_col_old, end_row_new, end_col_new) end +function TSTree:copy() end +function TSTree:included_ranges(include_bytes) end +function TSTree:included_ranges(include_bytes) end +``` + +--- + +## tsquery + +TSQuery methods - for working with treesitter queries. + +```lua +function TSQuery:inspect() end +function TSQuery:disable_capture(capture_name) end +function TSQuery:disable_pattern(pattern_index) end +``` + +--- + +## misc + +Various treesitter types including TSParser, TSQueryMatch, TSQueryCursor, and TSLangInfo. + +```lua +vim._ts_inspect_language = function(lang) end +vim._ts_get_language_version = function() end +vim._ts_add_language_from_object = function(path, lang, symbol_name) end +vim._ts_add_language_from_wasm = function(path, lang) end +vim._ts_get_minimum_language_version = function() end +vim._ts_parse_query = function(lang, query) end +vim._create_ts_parser = function(lang) end +function TSQueryMatch:info() end +function TSQueryCursor:next_capture() end +function TSQueryCursor:next_match() end +function vim._create_ts_querycursor(node, query, start, stop, opts) end +``` diff --git a/.agents/skills/vim/SKILL.md b/.agents/skills/vim/SKILL.md new file mode 100644 index 0000000..9bef4fb --- /dev/null +++ b/.agents/skills/vim/SKILL.md @@ -0,0 +1,734 @@ +# Neovim Lua API Reference + +This document contains type stubs and API references for Neovim's Lua API. +Use this as a reference when writing Neovim plugins or configurations in Lua. + +--- + +## api + +The following are type stubs for all the functions available on `vim.api.*`. Prefer these functions where possible. + +```lua +vim.api = {} +vim.api.nvim__buf_debug_extmarks(buffer, keys, dot) +vim.api.nvim__buf_stats(buffer) +vim.api.nvim__complete_set(index, opts) +vim.api.nvim__get_lib_dir() +vim.api.nvim__get_runtime(pat, all, opts) +vim.api.nvim__id(obj) +vim.api.nvim__id_array(arr) +vim.api.nvim__id_dict(dct) +vim.api.nvim__id_float(flt) +vim.api.nvim__inspect_cell(grid, row, col) +vim.api.nvim__invalidate_glyph_cache() +vim.api.nvim__ns_get(ns_id) +vim.api.nvim__ns_set(ns_id, opts) +vim.api.nvim__redraw(opts) +vim.api.nvim__runtime_inspect() +vim.api.nvim__screenshot(path) +vim.api.nvim__stats() +vim.api.nvim__unpack(str) +vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) +vim.api.nvim_buf_attach(buffer, send_buffer, opts) +vim.api.nvim_buf_call(buffer, fun) +vim.api.nvim_buf_clear_highlight(buffer, ns_id, line_start, line_end) +vim.api.nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) +vim.api.nvim_buf_create_user_command(buffer, name, command, opts) +vim.api.nvim_buf_del_extmark(buffer, ns_id, id) +vim.api.nvim_buf_del_keymap(buffer, mode, lhs) +vim.api.nvim_buf_del_mark(buffer, name) +vim.api.nvim_buf_del_user_command(buffer, name) +vim.api.nvim_buf_del_var(buffer, name) +vim.api.nvim_buf_delete(buffer, opts) +vim.api.nvim_buf_get_changedtick(buffer) +vim.api.nvim_buf_get_commands(buffer, opts) +vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) +vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) +vim.api.nvim_buf_get_keymap(buffer, mode) +vim.api.nvim_buf_get_lines(buffer, start, end_, strict_indexing) +vim.api.nvim_buf_get_mark(buffer, name) +vim.api.nvim_buf_get_name(buffer) +vim.api.nvim_buf_get_number(buffer) +vim.api.nvim_buf_get_offset(buffer, index) +vim.api.nvim_buf_get_option(buffer, name) +vim.api.nvim_buf_get_text(buffer, start_row, start_col, end_row, end_col, opts) +vim.api.nvim_buf_get_var(buffer, name) +vim.api.nvim_buf_is_loaded(buffer) +vim.api.nvim_buf_is_valid(buffer) +vim.api.nvim_buf_line_count(buffer) +vim.api.nvim_buf_set_extmark(buffer, ns_id, line, col, opts) +vim.api.nvim_buf_set_keymap(buffer, mode, lhs, rhs, opts) +vim.api.nvim_buf_set_lines(buffer, start, end_, strict_indexing, replacement) +vim.api.nvim_buf_set_mark(buffer, name, line, col, opts) +vim.api.nvim_buf_set_name(buffer, name) +vim.api.nvim_buf_set_option(buffer, name, value) +vim.api.nvim_buf_set_text(buffer, start_row, start_col, end_row, end_col, replacement) +vim.api.nvim_buf_set_var(buffer, name, value) +vim.api.nvim_buf_set_virtual_text(buffer, src_id, line, chunks, opts) +vim.api.nvim_call_dict_function(dict, fn, args) +vim.api.nvim_call_function(fn, args) +vim.api.nvim_chan_send(chan, data) +vim.api.nvim_clear_autocmds(opts) +vim.api.nvim_cmd(cmd, opts) +vim.api.nvim_command(command) +vim.api.nvim_command_output(command) +vim.api.nvim_create_augroup(name, opts) +vim.api.nvim_create_autocmd(event, opts) +vim.api.nvim_create_buf(listed, scratch) +vim.api.nvim_create_namespace(name) +vim.api.nvim_create_user_command(name, command, opts) +vim.api.nvim_del_augroup_by_id(id) +vim.api.nvim_del_augroup_by_name(name) +vim.api.nvim_del_autocmd(id) +vim.api.nvim_del_current_line() +vim.api.nvim_del_keymap(mode, lhs) +vim.api.nvim_del_mark(name) +vim.api.nvim_del_user_command(name) +vim.api.nvim_del_var(name) +vim.api.nvim_echo(chunks, history, opts) +vim.api.nvim_err_write(str) +vim.api.nvim_err_writeln(str) +vim.api.nvim_eval(expr) +vim.api.nvim_eval_statusline(str, opts) +vim.api.nvim_exec(src, output) +vim.api.nvim_exec2(src, opts) +vim.api.nvim_exec_autocmds(event, opts) +vim.api.nvim_feedkeys(keys, mode, escape_ks) +vim.api.nvim_get_all_options_info() +vim.api.nvim_get_autocmds(opts) +vim.api.nvim_get_chan_info(chan) +vim.api.nvim_get_color_by_name(name) +vim.api.nvim_get_color_map() +vim.api.nvim_get_commands(opts) +vim.api.nvim_get_context(opts) +vim.api.nvim_get_current_buf() +vim.api.nvim_get_current_line() +vim.api.nvim_get_current_tabpage() +vim.api.nvim_get_current_win() +vim.api.nvim_get_hl(ns_id, opts) +vim.api.nvim_get_hl_by_id(hl_id, rgb) +vim.api.nvim_get_hl_by_name(name, rgb) +vim.api.nvim_get_hl_id_by_name(name) +vim.api.nvim_get_hl_ns(opts) +vim.api.nvim_get_keymap(mode) +vim.api.nvim_get_mark(name, opts) +vim.api.nvim_get_mode() +vim.api.nvim_get_namespaces() +vim.api.nvim_get_option(name) +vim.api.nvim_get_option_info(name) +vim.api.nvim_get_option_info2(name, opts) +vim.api.nvim_get_option_value(name, opts) +vim.api.nvim_get_proc(pid) +vim.api.nvim_get_proc_children(pid) +vim.api.nvim_get_runtime_file(name, all) +vim.api.nvim_get_var(name) +vim.api.nvim_get_vvar(name) +vim.api.nvim_input(keys) +vim.api.nvim_input_mouse(button, action, modifier, grid, row, col) +vim.api.nvim_list_bufs() +vim.api.nvim_list_chans() +vim.api.nvim_list_runtime_paths() +vim.api.nvim_list_tabpages() +vim.api.nvim_list_uis() +vim.api.nvim_list_wins() +vim.api.nvim_load_context(dict) +vim.api.nvim_notify(msg, log_level, opts) +vim.api.nvim_open_term(buffer, opts) +vim.api.nvim_open_win(buffer, enter, config) +vim.api.nvim_out_write(str) +vim.api.nvim_parse_cmd(str, opts) +vim.api.nvim_parse_expression(expr, flags, highlight) +vim.api.nvim_paste(data, crlf, phase) +vim.api.nvim_put(lines, type, after, follow) +vim.api.nvim_replace_termcodes(str, from_part, do_lt, special) +vim.api.nvim_select_popupmenu_item(item, insert, finish, opts) +vim.api.nvim_set_current_buf(buffer) +vim.api.nvim_set_current_dir(dir) +vim.api.nvim_set_current_line(line) +vim.api.nvim_set_current_tabpage(tabpage) +vim.api.nvim_set_current_win(window) +vim.api.nvim_set_decoration_provider(ns_id, opts) +vim.api.nvim_set_hl(ns_id, name, val) +vim.api.nvim_set_hl_ns(ns_id) +vim.api.nvim_set_hl_ns_fast(ns_id) +vim.api.nvim_set_keymap(mode, lhs, rhs, opts) +vim.api.nvim_set_option(name, value) +vim.api.nvim_set_option_value(name, value, opts) +vim.api.nvim_set_var(name, value) +vim.api.nvim_set_vvar(name, value) +vim.api.nvim_strwidth(text) +vim.api.nvim_tabpage_del_var(tabpage, name) +vim.api.nvim_tabpage_get_number(tabpage) +vim.api.nvim_tabpage_get_var(tabpage, name) +vim.api.nvim_tabpage_get_win(tabpage) +vim.api.nvim_tabpage_is_valid(tabpage) +vim.api.nvim_tabpage_list_wins(tabpage) +vim.api.nvim_tabpage_set_var(tabpage, name, value) +vim.api.nvim_tabpage_set_win(tabpage, win) +vim.api.nvim_win_call(window, fun) +vim.api.nvim_win_close(window, force) +vim.api.nvim_win_del_var(window, name) +vim.api.nvim_win_get_buf(window) +vim.api.nvim_win_get_config(window) +vim.api.nvim_win_get_cursor(window) +vim.api.nvim_win_get_height(window) +vim.api.nvim_win_get_number(window) +vim.api.nvim_win_get_option(window, name) +vim.api.nvim_win_get_position(window) +vim.api.nvim_win_get_tabpage(window) +vim.api.nvim_win_get_var(window, name) +vim.api.nvim_win_get_width(window) +vim.api.nvim_win_hide(window) +vim.api.nvim_win_is_valid(window) +vim.api.nvim_win_set_buf(window, buffer) +vim.api.nvim_win_set_config(window, config) +vim.api.nvim_win_set_cursor(window, pos) +vim.api.nvim_win_set_height(window, height) +vim.api.nvim_win_set_hl_ns(window, ns_id) +vim.api.nvim_win_set_option(window, name, value) +vim.api.nvim_win_set_var(window, name, value) +vim.api.nvim_win_set_width(window, width) +vim.api.nvim_win_text_height(window, opts) +``` + +--- + +## builtin + +Various APIs that are provided by Neovim, that are unique to the Lua API. + +```lua +vim.NIL = ... +function vim.in_fast_event() end +function vim.empty_dict() end +function vim.rpcnotify(channel, method, ...) end +function vim.rpcrequest(channel, method, ...) end +function vim.stricmp(a, b) end +function vim.str_utf_pos(str) end +function vim.str_utf_start(str, index) end +function vim.str_utf_end(str, index) end +function vim.iconv(str, from, to, opts) end +function vim.schedule(fn) end +function vim.wait(time, callback, interval, fast_only) end +function vim.ui_attach(ns, options, callback) end +function vim.ui_detach(ns) end +``` + +--- + +## api_keysets + +The following describe various types that are used in Neovim's API. + +```lua + +``` + +--- + +## api_keysets_extra + +Additional types that are used in Neovim's API. + +```lua + +``` + +--- + +## builtin_types + +Various types used by Neovim's builtin APIs. + +```lua + +``` + +--- + +## vimfn + +Functions available from Neovim's vimscript APIs. They are available via `vim.fn.*`. + +```lua +vim.fn.abs(expr) +vim.fn.acos(expr) +vim.fn.add(object, expr) +vim.fn['and'] = function(expr, expr1) +vim.fn.api_info() +vim.fn.append(lnum, text) +vim.fn.appendbufline(buf, lnum, text) +vim.fn.argc(winid) +vim.fn.argidx() +vim.fn.arglistid(winnr, tabnr) +vim.fn.argv(nr, winid) +vim.fn.asin(expr) +vim.fn.assert_beeps(cmd) +vim.fn.assert_equal(expected, actual, msg) +vim.fn.assert_equalfile(fname_one, fname_two) +vim.fn.assert_exception(error, msg) +vim.fn.assert_fails(cmd, error, msg, lnum, context) +vim.fn.assert_false(actual, msg) +vim.fn.assert_inrange(lower, upper, actual, msg) +vim.fn.assert_match(pattern, actual, msg) +vim.fn.assert_nobeep(cmd) +vim.fn.assert_notequal(expected, actual, msg) +vim.fn.assert_notmatch(pattern, actual, msg) +vim.fn.assert_report(msg) +vim.fn.assert_true(actual, msg) +vim.fn.atan(expr) +vim.fn.atan2(expr1, expr2) +vim.fn.blob2list(blob) +vim.fn.browse(save, title, initdir, default) +vim.fn.browsedir(title, initdir) +vim.fn.bufadd(name) +vim.fn.bufexists(buf) +vim.fn.buffer_exists(...) +vim.fn.buffer_name(...) +vim.fn.buffer_number(...) +vim.fn.buflisted(buf) +vim.fn.bufload(buf) +vim.fn.bufloaded(buf) +vim.fn.bufname(buf) +vim.fn.bufnr(buf, create) +vim.fn.bufwinid(buf) +vim.fn.bufwinnr(buf) +vim.fn.byte2line(byte) +vim.fn.byteidx(expr, nr, utf16) +vim.fn.byteidxcomp(expr, nr, utf16) +vim.fn.call(func, arglist, dict) +vim.fn.ceil(expr) +vim.fn.chanclose(id, stream) +vim.fn.changenr() +vim.fn.chansend(id, data) +vim.fn.char2nr(string, utf8) +vim.fn.charclass(string) +vim.fn.charcol(expr, winid) +vim.fn.charidx(string, idx, countcc, utf16) +vim.fn.chdir(dir) +vim.fn.cindent(lnum) +vim.fn.clearmatches(win) +vim.fn.col(expr, winid) +vim.fn.complete(startcol, matches) +vim.fn.complete_add(expr) +vim.fn.complete_check() +vim.fn.complete_info(what) +vim.fn.confirm(msg, choices, default, type) +vim.fn.copy(expr) +vim.fn.cos(expr) +vim.fn.cosh(expr) +vim.fn.count(comp, expr, ic, start) +vim.fn.ctxget(index) +vim.fn.ctxpop() +vim.fn.ctxpush(types) +vim.fn.ctxset(context, index) +vim.fn.ctxsize() +vim.fn.cursor(lnum, col, off) +vim.fn.cursor(list) +vim.fn.debugbreak(pid) +vim.fn.deepcopy(expr, noref) +vim.fn.delete(fname, flags) +vim.fn.deletebufline(buf, first, last) +vim.fn.dictwatcheradd(dict, pattern, callback) +vim.fn.dictwatcherdel(dict, pattern, callback) +vim.fn.did_filetype() +vim.fn.diff_filler(lnum) +vim.fn.diff_hlID(lnum, col) +vim.fn.digraph_get(chars) +vim.fn.digraph_getlist(listall) +vim.fn.digraph_set(chars, digraph) +vim.fn.digraph_setlist(digraphlist) +vim.fn.empty(expr) +vim.fn.environ() +vim.fn.escape(string, chars) +vim.fn.eval(string) +vim.fn.eventhandler() +vim.fn.executable(expr) +vim.fn.execute(command, silent) +vim.fn.exepath(expr) +vim.fn.exists(expr) +vim.fn.exp(expr) +vim.fn.expand(string, nosuf, list) +vim.fn.expand(string, nosuf, list) +vim.fn.expandcmd(string, options) +vim.fn.extend(expr1, expr2, expr3) +vim.fn.extendnew(expr1, expr2, expr3) +vim.fn.feedkeys(string, mode) +vim.fn.file_readable(file) +vim.fn.filecopy(from, to) +vim.fn.filereadable(file) +vim.fn.filewritable(file) +vim.fn.filter(expr1, expr2) +vim.fn.finddir(name, path, count) +vim.fn.findfile(name, path, count) +vim.fn.flatten(list, maxdepth) +vim.fn.flattennew(list, maxdepth) +vim.fn.float2nr(expr) +vim.fn.floor(expr) +vim.fn.fmod(expr1, expr2) +vim.fn.fnameescape(string) +vim.fn.fnamemodify(fname, mods) +vim.fn.foldclosed(lnum) +vim.fn.foldclosedend(lnum) +vim.fn.foldlevel(lnum) +vim.fn.foldtext() +vim.fn.foldtextresult(lnum) +vim.fn.foreach(expr1, expr2) +vim.fn.fullcommand(name) +vim.fn.funcref(name, arglist, dict) +vim.fn['function'] = function(name, arglist, dict) +vim.fn.garbagecollect(atexit) +vim.fn.get(list, idx, default) +vim.fn.get(blob, idx, default) +vim.fn.get(dict, key, default) +vim.fn.get(func, what) +vim.fn.getbufinfo(buf) +vim.fn.getbufinfo(dict) +vim.fn.getbufline(buf, lnum, end_) +vim.fn.getbufoneline(buf, lnum) +vim.fn.getbufvar(buf, varname, def) +vim.fn.getcellwidths() +vim.fn.getchangelist(buf) +vim.fn.getchar(expr, opts) +vim.fn.getcharmod() +vim.fn.getcharpos(expr) +vim.fn.getcharsearch() +vim.fn.getcharstr(expr, opts) +vim.fn.getcmdcomplpat() +vim.fn.getcmdcompltype() +vim.fn.getcmdline() +vim.fn.getcmdpos() +vim.fn.getcmdprompt() +vim.fn.getcmdscreenpos() +vim.fn.getcmdtype() +vim.fn.getcmdwintype() +vim.fn.getcompletion(pat, type, filtered) +vim.fn.getcurpos(winid) +vim.fn.getcursorcharpos(winid) +vim.fn.getcwd(winnr, tabnr) +vim.fn.getenv(name) +vim.fn.getfontname(name) +vim.fn.getfperm(fname) +vim.fn.getfsize(fname) +vim.fn.getftime(fname) +vim.fn.getftype(fname) +vim.fn.getjumplist(winnr, tabnr) +vim.fn.getline(lnum, end_) +vim.fn.getline(lnum, end_) +vim.fn.getloclist(nr, what) +vim.fn.getmarklist(buf) +vim.fn.getmatches(win) +vim.fn.getmousepos() +vim.fn.getpid() +vim.fn.getpos(expr) +vim.fn.getqflist(what) +vim.fn.getreg(regname, list) +vim.fn.getreg(regname, list) +vim.fn.getreginfo(regname) +vim.fn.getregion(pos1, pos2, opts) +vim.fn.getregionpos(pos1, pos2, opts) +vim.fn.getregtype(regname) +vim.fn.getscriptinfo(opts) +vim.fn.getstacktrace() +vim.fn.gettabinfo(tabnr) +vim.fn.gettabvar(tabnr, varname, def) +vim.fn.gettabwinvar(tabnr, winnr, varname, def) +vim.fn.gettagstack(winnr) +vim.fn.gettext(text) +vim.fn.getwininfo(winid) +vim.fn.getwinpos(timeout) +vim.fn.getwinposx() +vim.fn.getwinposy() +vim.fn.getwinvar(winnr, varname, def) +vim.fn.glob(expr, nosuf, list, alllinks) +vim.fn.glob2regpat(string) +vim.fn.globpath(path, expr, nosuf, list, allinks) +vim.fn.has(feature) +vim.fn.has_key(dict, key) +vim.fn.haslocaldir(winnr, tabnr) +vim.fn.hasmapto(what, mode, abbr) +vim.fn.highlightID(name) +vim.fn.highlight_exists(name) +vim.fn.histadd(history, item) +vim.fn.histdel(history, item) +vim.fn.histget(history, index) +vim.fn.histnr(history) +vim.fn.hlID(name) +vim.fn.hlexists(name) +vim.fn.hostname() +vim.fn.iconv(string, from, to) +vim.fn.id(expr) +vim.fn.indent(lnum) +vim.fn.index(object, expr, start, ic) +vim.fn.indexof(object, expr, opts) +vim.fn.input(prompt, text, completion) +vim.fn.input(opts) +vim.fn.inputdialog(...) +vim.fn.inputlist(textlist) +vim.fn.inputrestore() +vim.fn.inputsave() +vim.fn.inputsecret(prompt, text) +vim.fn.insert(object, item, idx) +vim.fn.interrupt() +vim.fn.invert(expr) +vim.fn.isabsolutepath(path) +vim.fn.isdirectory(directory) +vim.fn.isinf(expr) +vim.fn.islocked(expr) +vim.fn.isnan(expr) +vim.fn.items(dict) +vim.fn.jobclose(...) +vim.fn.jobpid(job) +vim.fn.jobresize(job, width, height) +vim.fn.jobsend(...) +vim.fn.jobstart(cmd, opts) +vim.fn.jobstop(id) +vim.fn.jobwait(jobs, timeout) +vim.fn.join(list, sep) +vim.fn.json_decode(expr) +vim.fn.json_encode(expr) +vim.fn.keys(dict) +vim.fn.keytrans(string) +vim.fn.last_buffer_nr() +vim.fn.len(expr) +vim.fn.libcall(libname, funcname, argument) +vim.fn.libcallnr(libname, funcname, argument) +vim.fn.line(expr, winid) +vim.fn.line2byte(lnum) +vim.fn.lispindent(lnum) +vim.fn.list2blob(list) +vim.fn.list2str(list, utf8) +vim.fn.localtime() +vim.fn.log(expr) +vim.fn.log10(expr) +vim.fn.map(expr1, expr2) +vim.fn.maparg(name, mode, abbr, dict) +vim.fn.maparg(name, mode, abbr, dict) +vim.fn.mapcheck(name, mode, abbr) +vim.fn.maplist(abbr) +vim.fn.mapnew(expr1, expr2) +vim.fn.mapset(mode, abbr, dict) +vim.fn.mapset(dict) +vim.fn.match(expr, pat, start, count) +vim.fn.matchadd(group, pattern, priority, id, dict) +vim.fn.matchaddpos(group, pos, priority, id, dict) +vim.fn.matcharg(nr) +vim.fn.matchbufline(buf, pat, lnum, end_, dict) +vim.fn.matchdelete(id, win) +vim.fn.matchend(expr, pat, start, count) +vim.fn.matchfuzzy(list, str, dict) +vim.fn.matchfuzzypos(list, str, dict) +vim.fn.matchlist(expr, pat, start, count) +vim.fn.matchstr(expr, pat, start, count) +vim.fn.matchstrlist(list, pat, dict) +vim.fn.matchstrpos(expr, pat, start, count) +vim.fn.max(expr) +vim.fn.menu_get(path, modes) +vim.fn.menu_info(name, mode) +vim.fn.min(expr) +vim.fn.mkdir(name, flags, prot) +vim.fn.mode(expr) +vim.fn.msgpackdump(list, type) +vim.fn.msgpackparse(data) +vim.fn.nextnonblank(lnum) +vim.fn.nr2char(expr, utf8) +vim.fn['or'] = function(expr, expr1) +vim.fn.pathshorten(path, len) +vim.fn.perleval(expr) +vim.fn.pow(x, y) +vim.fn.prevnonblank(lnum) +vim.fn.printf(fmt, expr1) +vim.fn.prompt_getprompt(buf) +vim.fn.prompt_setcallback(buf, expr) +vim.fn.prompt_setinterrupt(buf, expr) +vim.fn.prompt_setprompt(buf, text) +vim.fn.pum_getpos() +vim.fn.pumvisible() +vim.fn.py3eval(expr) +vim.fn.pyeval(expr) +vim.fn.pyxeval(expr) +vim.fn.rand(expr) +vim.fn.range(expr, max, stride) +vim.fn.readblob(fname, offset, size) +vim.fn.readdir(directory, expr) +vim.fn.readfile(fname, type, max) +vim.fn.reduce(object, func, initial) +vim.fn.reg_executing() +vim.fn.reg_recorded() +vim.fn.reg_recording() +vim.fn.reltime() +vim.fn.reltime(start) +vim.fn.reltime(start, end_) +vim.fn.reltimefloat(time) +vim.fn.reltimestr(time) +vim.fn.remove(list, idx) +vim.fn.remove(list, idx, end_) +vim.fn.remove(blob, idx) +vim.fn.remove(blob, idx, end_) +vim.fn.remove(dict, key) +vim.fn.rename(from, to) +vim.fn['repeat'] = function(expr, count) +vim.fn.resolve(filename) +vim.fn.reverse(object) +vim.fn.round(expr) +vim.fn.rpcnotify(channel, event, ...) +vim.fn.rpcrequest(channel, method, ...) +vim.fn.rpcstart(prog, argv) +vim.fn.rpcstop(...) +vim.fn.rubyeval(expr) +vim.fn.screenattr(row, col) +vim.fn.screenchar(row, col) +vim.fn.screenchars(row, col) +vim.fn.screencol() +vim.fn.screenpos(winid, lnum, col) +vim.fn.screenrow() +vim.fn.screenstring(row, col) +vim.fn.search(pattern, flags, stopline, timeout, skip) +vim.fn.searchcount(options) +vim.fn.searchdecl(name, global, thisblock) +vim.fn.searchpair(start, middle, end_, flags, skip, stopline, timeout) +vim.fn.searchpairpos(start, middle, end_, flags, skip, stopline, timeout) +vim.fn.searchpos(pattern, flags, stopline, timeout, skip) +vim.fn.serverlist() +vim.fn.serverstart(address) +vim.fn.serverstop(address) +vim.fn.setbufline(buf, lnum, text) +vim.fn.setbufvar(buf, varname, val) +vim.fn.setcellwidths(list) +vim.fn.setcharpos(expr, list) +vim.fn.setcharsearch(dict) +vim.fn.setcmdline(str, pos) +vim.fn.setcmdpos(pos) +vim.fn.setcursorcharpos(lnum, col, off) +vim.fn.setcursorcharpos(list) +vim.fn.setenv(name, val) +vim.fn.setfperm(fname, mode) +vim.fn.setline(lnum, text) +vim.fn.setloclist(nr, list, action, what) +vim.fn.setmatches(list, win) +vim.fn.setpos(expr, list) +vim.fn.setqflist(list, action, what) +vim.fn.setreg(regname, value, options) +vim.fn.settabvar(tabnr, varname, val) +vim.fn.settabwinvar(tabnr, winnr, varname, val) +vim.fn.settagstack(nr, dict, action) +vim.fn.setwinvar(nr, varname, val) +vim.fn.sha256(string) +vim.fn.shellescape(string, special) +vim.fn.shiftwidth(col) +vim.fn.sign_define(name, dict) +vim.fn.sign_define(list) +vim.fn.sign_getdefined(name) +vim.fn.sign_getplaced(buf, dict) +vim.fn.sign_jump(id, group, buf) +vim.fn.sign_place(id, group, name, buf, dict) +vim.fn.sign_placelist(list) +vim.fn.sign_undefine(name) +vim.fn.sign_undefine(list) +vim.fn.sign_unplace(group, dict) +vim.fn.sign_unplacelist(list) +vim.fn.simplify(filename) +vim.fn.sin(expr) +vim.fn.sinh(expr) +vim.fn.slice(expr, start, end_) +vim.fn.sockconnect(mode, address, opts) +vim.fn.sort(list, how, dict) +vim.fn.soundfold(word) +vim.fn.spellbadword(sentence) +vim.fn.spellsuggest(word, max, capital) +vim.fn.split(string, pattern, keepempty) +vim.fn.sqrt(expr) +vim.fn.srand(expr) +vim.fn.state(what) +vim.fn.stdioopen(opts) +vim.fn.stdpath(what) +vim.fn.stdpath(what) +vim.fn.stdpath(what) +vim.fn.str2float(string, quoted) +vim.fn.str2list(string, utf8) +vim.fn.str2nr(string, base) +vim.fn.strcharlen(string) +vim.fn.strcharpart(src, start, len, skipcc) +vim.fn.strchars(string, skipcc) +vim.fn.strdisplaywidth(string, col) +vim.fn.strftime(format, time) +vim.fn.strgetchar(str, index) +vim.fn.stridx(haystack, needle, start) +vim.fn.string(expr) +vim.fn.strlen(string) +vim.fn.strpart(src, start, len, chars) +vim.fn.strptime(format, timestring) +vim.fn.strridx(haystack, needle, start) +vim.fn.strtrans(string) +vim.fn.strutf16len(string, countcc) +vim.fn.strwidth(string) +vim.fn.submatch(nr, list) +vim.fn.submatch(nr, list) +vim.fn.substitute(string, pat, sub, flags) +vim.fn.swapfilelist() +vim.fn.swapinfo(fname) +vim.fn.swapname(buf) +vim.fn.synID(lnum, col, trans) +vim.fn.synIDattr(synID, what, mode) +vim.fn.synIDtrans(synID) +vim.fn.synconcealed(lnum, col) +vim.fn.synstack(lnum, col) +vim.fn.system(cmd, input) +vim.fn.systemlist(cmd, input, keepempty) +vim.fn.tabpagebuflist(arg) +vim.fn.tabpagenr(arg) +vim.fn.tabpagewinnr(tabarg, arg) +vim.fn.tagfiles() +vim.fn.taglist(expr, filename) +vim.fn.tan(expr) +vim.fn.tanh(expr) +vim.fn.tempname() +vim.fn.termopen(cmd, opts) +vim.fn.timer_info(id) +vim.fn.timer_pause(timer, paused) +vim.fn.timer_start(time, callback, options) +vim.fn.timer_stop(timer) +vim.fn.timer_stopall() +vim.fn.tolower(expr) +vim.fn.toupper(expr) +vim.fn.tr(src, fromstr, tostr) +vim.fn.trim(text, mask, dir) +vim.fn.trunc(expr) +vim.fn.type(expr) +vim.fn.undofile(name) +vim.fn.undotree(buf) +vim.fn.uniq(list, func, dict) +vim.fn.utf16idx(string, idx, countcc, charidx) +vim.fn.values(dict) +vim.fn.virtcol(expr, list, winid) +vim.fn.virtcol2col(winid, lnum, col) +vim.fn.visualmode(expr) +vim.fn.wait(timeout, condition, interval) +vim.fn.wildmenumode() +vim.fn.win_execute(id, command, silent) +vim.fn.win_findbuf(bufnr) +vim.fn.win_getid(win, tab) +vim.fn.win_gettype(nr) +vim.fn.win_gotoid(expr) +vim.fn.win_id2tabwin(expr) +vim.fn.win_id2win(expr) +vim.fn.win_move_separator(nr, offset) +vim.fn.win_move_statusline(nr, offset) +vim.fn.win_screenpos(nr) +vim.fn.win_splitmove(nr, target, options) +vim.fn.winbufnr(nr) +vim.fn.wincol() +vim.fn.windowsversion() +vim.fn.winheight(nr) +vim.fn.winlayout(tabnr) +vim.fn.winline() +vim.fn.winnr(arg) +vim.fn.winrestcmd() +vim.fn.winrestview(dict) +vim.fn.winsaveview() +vim.fn.winwidth(nr) +vim.fn.wordcount() +vim.fn.writefile(object, fname, flags) +vim.fn.xor(expr, expr1) +``` @@ -143,7 +143,8 @@ See search for more details | --- | --- | --- | | `setup` | `fun(opts?: _99.Options): nil` | - | | `search` | `fun(opts: _99.ops.SearchOpts): _99.TraceID` | - | -| `vibe_search` | `fun(opts?: _99.ops.Opts): _99.TraceID \| nil` | - | +| `vibe` | `fun(opts?: _99.ops.Opts): _99.TraceID \| nil` | - | +| `open` | `fun(): nil` | - | | `visual` | `fun(opts: _99.ops.Opts): _99.TraceID` | - | | `view_logs` | `fun(): nil` | - | | `stop_all_requests` | `fun(): nil` | - | @@ -161,8 +162,14 @@ way you want it to. Performs a search across your project with the prompt you provide and return out a list of locations with notes that will be put into your quick fix list. -#### vibe_search -Select a previous search, edit it, then pass it to vibe. +#### vibe +No description. + +#### open +Opens a selection window for you to select the last interaction to open +and display its contents in a way that makes sense for its type. For +search and vibe, it will open the qfix window. For tutorial, it will open +the tutorial window. #### visual takes your current selection and sends that along with the prompt provided and replaces diff --git a/lua/99/geo.lua b/lua/99/geo.lua index 48e60ad..01c8f71 100644 --- a/lua/99/geo.lua +++ b/lua/99/geo.lua @@ -204,6 +204,14 @@ function Point.from_mark(mark) }, Point) end +--- Returns the line in the editor that this point is on +--- @param buffer number +function Point:line(buffer) + local row, _ = self:to_vim() + local lines = vim.api.nvim_buf_get_lines(buffer, row, row + 1, false) + return lines[1] +end + function Point.zero() return Point.from_0_based(0, 0) end diff --git a/lua/99/init.lua b/lua/99/init.lua index 52ba23b..f623db2 100644 --- a/lua/99/init.lua +++ b/lua/99/init.lua @@ -1,3 +1,6 @@ +--- TODO: I would like to clean up this file. I will probably need to create a +--- task for me to do in the future to make this a bit more clean and only have +--- stuff that makes sense for the api to be in here... but for now.. ia m sorry local Logger = require("99.logger.logger") local Level = require("99.logger.level") local ops = require("99.ops") @@ -186,9 +189,12 @@ local _99_state --- @field search fun(opts: _99.ops.SearchOpts): _99.TraceID --- Performs a search across your project with the prompt you provide and return out a list of --- locations with notes that will be put into your quick fix list. ---- @field vibe_search fun(opts?: _99.ops.Opts): _99.TraceID | nil ---- Select a previous search, edit it, then pass it to vibe. --- @field vibe fun(opts?: _99.ops.Opts): _99.TraceID | nil +--- @field open fun(): nil +--- Opens a selection window for you to select the last interaction to open +--- and display its contents in a way that makes sense for its type. For +--- search and vibe, it will open the qfix window. For tutorial, it will open +--- the tutorial window. --- @field visual fun(opts: _99.ops.Opts): _99.TraceID --- takes your current selection and sends that along with the prompt provided and replaces --- your visual selection with the results @@ -265,56 +271,59 @@ function _99.info() Window.display_centered_message(info) end ---- @param tutorials _99.Prompt.Data.Tutorial[] ---- @return string[] -local function tutorial_to_string(tutorials) - local out = {} - for _, t in ipairs(tutorials) do - table.insert(out, string.format("%d: %s", t.xid, t.tutorial[1])) - end - return out -end - ---- @param xid number | nil ---- @param opts? _99.window.SplitWindowOpts -function _99.open_tutorial(xid, opts) - opts = opts or { split_direction = "vertical" } - if xid == nil then - --- @type _99.Prompt.Data.Tutorial[] - local tutorials = _99_state:request_by_type("tutorial") - if #tutorials == 0 then - print("no tutorials available") - return - elseif #tutorials == 1 then - local data = tutorials[1] - assert(data, "tutorial is malformed") - Window.create_split(data.tutorial, data.buffer, opts) - return - else - --- TODO: Complete this task when i work through tutorials - error([[not implemented. right now tutorials are not sccrollable. -This is a later change required. I want a next/prev tutorial navigation -much like qfix list. then i to have a capture input style window where you -can press enter -]]) - end - return - end - - --- @type _99.Prompt | nil - local context = _99_state.__request_by_id[xid] - assert(context, "could not find request") - assert(context.state == "success", "tutorial found had a non success state") +-- elseif #tutorials == 1 then +-- local data = tutorials[1] +-- assert(data, "tutorial is malformed") +-- Window.create_split(data.tutorial, data.buffer, opts) +-- return +--- @param context _99.Prompt +function _99.open_tutorial(context) local tutorial = context:tutorial_data() - Window.create_split(tutorial.tutorial, tutorial.buffer, opts) + Window.create_split(tutorial.tutorial, tutorial.buffer) end ---- @param path string -function _99:rule_from_path(path) - _ = self - path = expand(path) --[[ @as string]] - return Agents.get_rule_by_path(_99_state.rules, path) +function _99.open() + local requests = _99_state:requests() + local str_requests = {} + for _, r in ipairs(requests) do + table.insert(str_requests, r:summary()) + end + for i = 1, #requests do + str_requests[i] = string.format("%d: %s", i, requests[i]:summary()) + end + Window.capture_select_input("99", { + content = str_requests, + cb = function(success, result) + if not success or result == "" then + return + end + + local idx = tonumber(vim.fn.matchstr(result, "^\\d\\+")) + local r = requests[idx] + if not r then + print( + "somehow we have had a successful callback, but no request context... i honestly have no idea how we got here" + ) + return + end + assert( + r:valid(), + "request is not valid... not sure how we got here. please report bug and this word: " + .. vim.inspect(r.operation) + ) + if r.operation == "visual" then + --- TODO: this is its own work item for being able to have a global mark + --- section in which i keep track of marks for the lifetime of the + --- editor and when you close the editor, then it should lose them + print("visual not supported: i will figure this out... at some point") + elseif r.operation == "search" or r.operation == "vibe" then + _99.open_qfix_for_request(r) + elseif r.operation == "tutorial" then + _99.open_tutorial(r) + end + end, + }) end --- @param opts? _99.ops.Opts @@ -345,55 +354,12 @@ function _99.search(opts) return context.xid end ---- @param opts? _99.ops.Opts ---- @return _99.TraceID | nil -function _99.vibe_search(opts) - local searches = _99_state:request_by_type("search") - if #searches == 0 then - error("no previous search results") - return nil - end - - local o = process_opts(opts) - local lines = {} - for i, context in ipairs(searches) do - table.insert(lines, string.format("%d: %s", i, context:summary())) - end - - Window.capture_select_input("Select Search", { - content = lines, - cb = function(ok, result) - if not ok then - return - end - - local idx = tonumber(string.match(result, "^(%d+)%:")) - local selected = idx and searches[idx] or nil - if not selected then - print("failed to select search result") - return - end - - local selected_data = selected:search_data() - local context = Prompt.vibe(_99_state) - capture_prompt( - ops.vibe, - "Vibe", - context, - o, - vim.split(selected_data.response, "\n") - ) - end, - }) - - return nil -end - --- @param opts _99.ops.Opts function _99.tutorial(opts) opts = process_opts(opts) local context = Prompt.tutorial(_99_state) if opts.additional_prompt then + context.user_prompt = opts.additional_prompt ops.tutorial(context, opts) else capture_prompt(ops.tutorial, "Tutorial", context, opts) @@ -446,57 +412,8 @@ function _99.next_request_logs() Window.display_full_screen_message(logs[_99_state.__view_log_idx]) end ---- @class _99.QFixEntry ---- @field filename string ---- @field lnum number ---- @field col number ---- @field text string - ---- @class _99.QFixOpts ---- @field operation? "search" | "vibe" - ---- @param opts? _99.QFixOpts ---- @return "search" | "vibe" -local function qfix_operation(opts) - local operation = (opts and opts.operation) or "search" - assert( - operation == "search" or operation == "vibe", - "opts.operation must be either 'search' or 'vibe'" - ) - return operation -end - ---- @param operation "search" | "vibe" ---- @return _99.Prompt[] -local function qfix_requests(operation) - local out = {} --[[ @as _99.Prompt[] ]] - for _, request in ipairs(_99_state.__request_history) do - if request.operation == operation then - local ok, items = pcall(request.qfix_data, request) - if ok and #items > 0 then - table.insert(out, request) - end - end - end - return out -end - ---- @param operation "search" | "vibe" ---- @param index number ---- @return _99.Prompt | nil -local function qfix_request_at(operation, index) - local requests = qfix_requests(operation) - if #requests == 0 then - return nil - end - - local idx = math.max(1, math.min(index, #requests)) - _99_state.__qfix_history_idx[operation] = idx - return requests[idx] -end - --- @param request _99.Prompt -local function open_qfix_request(request) +function _99.open_qfix_for_request(request) local items = request:qfix_data() if #items == 0 then print("there are no quickfix items to show") @@ -522,58 +439,6 @@ function _99.clear_all_marks() _99_state.__active_marks = {} end ---- @param opts? _99.QFixOpts -function _99.qfix_top(opts) - local operation = qfix_operation(opts) - local requests = qfix_requests(operation) - if #requests == 0 then - print("there are no quickfix items to show") - return - end - - local request = qfix_request_at(operation, #requests) - assert(request, "failed to retrieve qfix request") - open_qfix_request(request) -end - ---- @param opts? _99.QFixOpts -function _99.qfix_older(opts) - local operation = qfix_operation(opts) - local requests = qfix_requests(operation) - if #requests == 0 then - print("there are no quickfix items to show") - return - end - - local current = _99_state.__qfix_history_idx[operation] - if current == 0 then - current = #requests - end - - local request = qfix_request_at(operation, current - 1) - assert(request, "failed to retrieve older qfix request") - open_qfix_request(request) -end - ---- @param opts? _99.QFixOpts -function _99.qfix_newer(opts) - local operation = qfix_operation(opts) - local requests = qfix_requests(operation) - if #requests == 0 then - print("there are no quickfix items to show") - return - end - - local current = _99_state.__qfix_history_idx[operation] - if current == 0 then - current = #requests - end - - local request = qfix_request_at(operation, current + 1) - assert(request, "failed to retrieve newer qfix request") - open_qfix_request(request) -end - function _99.clear_previous_requests() _99_state:clear_history() end diff --git a/lua/99/ops/init.lua b/lua/99/ops/init.lua index d45b1a5..5cb7a74 100644 --- a/lua/99/ops/init.lua +++ b/lua/99/ops/init.lua @@ -35,5 +35,5 @@ return { search = require("99.ops.search"), tutorial = require("99.ops.tutorial"), over_range = require("99.ops.over-range"), - vibe = require("99.ops.vibe") + vibe = require("99.ops.vibe"), } diff --git a/lua/99/ops/search.lua b/lua/99/ops/search.lua index dc7520f..0b19bb4 100644 --- a/lua/99/ops/search.lua +++ b/lua/99/ops/search.lua @@ -17,9 +17,7 @@ local function create_search_locations(context, response) } if #qf_list > 0 then - require("99").qfix_top({ - operation = "search", - }) + require("99").open_qfix_for_request(context) else vim.notify("No search results found", vim.log.levels.INFO) end diff --git a/lua/99/ops/tutorial.lua b/lua/99/ops/tutorial.lua index 9889801..4b9a9b8 100644 --- a/lua/99/ops/tutorial.lua +++ b/lua/99/ops/tutorial.lua @@ -54,8 +54,7 @@ local function tutorial(context, opts) response or "no response provided" ) elseif status == "success" then - local data = open_tutorial(context, response) - context._99:open_tutorial(data) + open_tutorial(context, response) end end)) end diff --git a/lua/99/ops/vibe.lua b/lua/99/ops/vibe.lua index 3ba15c5..0e52307 100644 --- a/lua/99/ops/vibe.lua +++ b/lua/99/ops/vibe.lua @@ -17,9 +17,7 @@ local function finish_vibe(context, response) } if #qf_list > 0 then - require("99").qfix_top({ - operation = "vibe", - }) + require("99").open_qfix_for_request(context) else vim.notify("No search results found", vim.log.levels.INFO) end diff --git a/lua/99/prompt-settings.lua b/lua/99/prompt-settings.lua index c7e87dd..fb219db 100644 --- a/lua/99/prompt-settings.lua +++ b/lua/99/prompt-settings.lua @@ -174,10 +174,7 @@ local prompt_settings = { --- @param tmp_file string --- @return string tmp_file_location = function(tmp_file) - return string.format( - "<TEMP_FILE>%s</TEMP_FILE>", - tmp_file - ) + return string.format("<TEMP_FILE>%s</TEMP_FILE>", tmp_file) end, --- @return string diff --git a/lua/99/prompt.lua b/lua/99/prompt.lua index fbb8265..429ad7b 100644 --- a/lua/99/prompt.lua +++ b/lua/99/prompt.lua @@ -158,7 +158,8 @@ end --- @return string function Prompt:summary() - return string.format("%s: %s", self.operation, self.user_prompt) + local prompt_str = utils.split_with_count(self.user_prompt, 8) + return string.format("%s: %s", self.operation, table.concat(prompt_str, " ")) end --- @param _99 _99.State diff --git a/lua/99/state.lua b/lua/99/state.lua index 397e30f..180c1a9 100644 --- a/lua/99/state.lua +++ b/lua/99/state.lua @@ -38,7 +38,6 @@ end --- @field __request_by_id table<number, _99.Prompt> --- @field __active_marks _99.Mark[] --- @field __tmp_dir string | nil ---- @field __qfix_history_idx table<"search" | "vibe", number> local State = {} State.__index = State @@ -56,10 +55,6 @@ local function create() __view_log_idx = 1, __request_history = {}, __request_by_id = {}, - __qfix_history_idx = { - search = 0, - vibe = 0, - }, tmp_dir = nil, } end @@ -121,6 +116,11 @@ function State:completed_prompts() return count end +--- @return _99.Prompt[] +function State:requests() + return self.__request_history +end + function State:clear_history() local keep = {} for _, entry in ipairs(self.__request_history) do diff --git a/lua/99/test/geo_spec.lua b/lua/99/test/geo_spec.lua index ed67b6f..fbbfedc 100644 --- a/lua/99/test/geo_spec.lua +++ b/lua/99/test/geo_spec.lua @@ -5,6 +5,28 @@ local Range = geo.Range local test_utils = require("99.test.test_utils") local eq = assert.are.same +describe("Point", function() + local buffer + + before_each(function() + buffer = test_utils.create_file({ + "first line", + "middle line", + "last line", + }, "lua", 1, 0) + end) + + after_each(function() + test_utils.clean_files() + end) + + it("line returns first, middle, and last line", function() + eq("first line", Point:from_1_based(1, 1):line(buffer)) + eq("middle line", Point:from_1_based(2, 1):line(buffer)) + eq("last line", Point:from_1_based(3, 1):line(buffer)) + end) +end) + describe("Range", function() local buffer diff --git a/lua/99/test/open_spec.lua b/lua/99/test/open_spec.lua new file mode 100644 index 0000000..a65d8e8 --- /dev/null +++ b/lua/99/test/open_spec.lua @@ -0,0 +1,99 @@ +-- luacheck: globals describe it assert before_each after_each +local _99 = require("99") +local Window = require("99.window") +local test_utils = require("99.test.test_utils") +local QFixHelpers = require("99.ops.qfix-helpers") +local eq = assert.are.same + +local function some_window_has(str) + local wins = vim.api.nvim_list_wins() + + for _, winid in ipairs(wins) do + local bufnr = vim.api.nvim_win_get_buf(winid) + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local content = table.concat(lines, "\n") + if str == content then + return + end + end + assert( + false, + "unable to find buffer with the str contents from an active window" + ) +end + +local function qfix_items() + local items = vim.fn.getqflist() + local out = {} + for _, item in ipairs(items) do + table.insert(out, { + filename = vim.api.nvim_buf_get_name(item.bufnr), + col = item.col, + lnum = item.lnum, + text = item.text, + }) + end + return out +end + +describe("open", function() + local provider + local previous_capture_select_input + + before_each(function() + provider = test_utils.TestProvider.new() + _99.setup(test_utils.get_test_setup_options({}, provider)) + + previous_capture_select_input = Window.capture_select_input + end) + + after_each(function() + Window.capture_select_input = previous_capture_select_input + end) + + --- @param term "search" | "tutorial" | "vibe" + ---@param result_str any + local function op(term, result_str) + _99[term]({ additional_prompt = result_str }) + local out = result_str + provider:resolve("success", out) + test_utils.next_frame() + return out + end + + local function search() + return op("search", "/tmp/foo.lua:1:1,search note") + end + + local function vibe() + return op("vibe", "/tmp/bar.lua:2:2,search bar note") + end + + local function tutorial() + return op("tutorial", "here is a large tutorial") + end + + local function select_content(idx) + Window.capture_select_input = function(_, opts) + opts.cb(true, opts.content[idx]) + end + end + + it("selects a previous search and passes edited output to vibe", function() + local s = search() + local v = vibe() + local t = tutorial() + + select_content(1) + _99.open() + eq(QFixHelpers.create_qfix_entries(s), qfix_items()) + + select_content(2) + _99.open() + eq(QFixHelpers.create_qfix_entries(v), qfix_items()) + + select_content(3) + _99.open() + some_window_has(t) + end) +end) diff --git a/lua/99/test/qfix_navigation_spec.lua b/lua/99/test/qfix_navigation_spec.lua deleted file mode 100644 index f079a9e..0000000 --- a/lua/99/test/qfix_navigation_spec.lua +++ /dev/null @@ -1,70 +0,0 @@ --- luacheck: globals describe it assert before_each -local _99 = require("99") -local test_utils = require("99.test.test_utils") -local eq = assert.are.same - -local content = { - "local value = 1", - "return value", -} - -local function qfix_first_text() - local items = vim.fn.getqflist() - local first = items[1] - assert(first, "expected a quickfix item") - return first.text -end - -describe("qfix navigation", function() - local provider - local results = {} - - before_each(function() - provider = test_utils.test_setup(content, 1, 0) - end) - - it("navigates older and newer search quickfix results", function() - _99.search({ additional_prompt = "search one" }) - provider:resolve("success", "/tmp/one.lua:1:1,1,first search") - test_utils.next_frame() - - _99.search({ additional_prompt = "search two" }) - provider:resolve("success", "/tmp/two.lua:1:1,1,second search") - test_utils.next_frame() - - _99.search({ additional_prompt = "search three" }) - provider:resolve("success", "/tmp/three.lua:1:1,1,third search") - test_utils.next_frame() - - _99.qfix_older({ operation = "search" }) - eq("second search", qfix_first_text()) - - _99.qfix_older({ operation = "search" }) - eq("first search", qfix_first_text()) - - _99.qfix_newer({ operation = "search" }) - eq("second search", qfix_first_text()) - - _99.qfix_top({ operation = "search" }) - eq("third search", qfix_first_text()) - end) - - it("navigates vibe quickfix history through vibe results", function() - _99.vibe({ additional_prompt = "vibe one" }) - provider:resolve("success", "/tmp/a.lua:1:1,1,first vibe") - test_utils.next_frame() - - _99.vibe({ additional_prompt = "vibe two" }) - provider:resolve("success", "/tmp/b.lua:1:1,1,second vibe") - test_utils.next_frame() - - _99.qfix_top({ operation = "vibe" }) - eq("second vibe", qfix_first_text()) - - _99.qfix_older({ operation = "vibe" }) - eq("first vibe", qfix_first_text()) - - _99.qfix_newer({ operation = "vibe" }) - eq("second vibe", qfix_first_text()) - end) -end) diff --git a/lua/99/test/utils_spec.lua b/lua/99/test/utils_spec.lua new file mode 100644 index 0000000..516f902 --- /dev/null +++ b/lua/99/test/utils_spec.lua @@ -0,0 +1,19 @@ +-- luacheck: globals describe it assert +local Utils = require("99.utils") +local eq = assert.are.same + +describe("utils", function() + it("split_with_count handles 0, 1, 5, and N + 2 words", function() + local str = "alpha\tbeta gamma\ndelta epsilon zeta eta" + local words = vim.split(str, "%s+", { trimempty = true }) + local n = #words + + eq({}, Utils.split_with_count(str, 0)) + eq({ "alpha" }, Utils.split_with_count(str, 1)) + eq( + { "alpha", "beta", "gamma", "delta", "epsilon" }, + Utils.split_with_count(str, 5) + ) + eq(words, Utils.split_with_count(str, n + 2)) + end) +end) diff --git a/lua/99/test/vibe_search_spec.lua b/lua/99/test/vibe_search_spec.lua deleted file mode 100644 index f3f13ae..0000000 --- a/lua/99/test/vibe_search_spec.lua +++ /dev/null @@ -1,56 +0,0 @@ --- luacheck: globals describe it assert before_each after_each -local _99 = require("99") -local Window = require("99.window") -local ops = require("99.ops") -local test_utils = require("99.test.test_utils") -local eq = assert.are.same - -describe("vibe_search", function() - local provider - local previous_capture_input - local previous_capture_select_input - local previous_vibe - - before_each(function() - provider = test_utils.TestProvider.new() - _99.setup(test_utils.get_test_setup_options({}, provider)) - - previous_capture_input = Window.capture_input - previous_capture_select_input = Window.capture_select_input - previous_vibe = ops.vibe - end) - - after_each(function() - Window.capture_input = previous_capture_input - Window.capture_select_input = previous_capture_select_input - ops.vibe = previous_vibe - end) - - it("selects a previous search and passes edited output to vibe", function() - _99.search({ - additional_prompt = "find error handling", - }) - provider:resolve("success", "/tmp/foo.lua:1:1,search note") - test_utils.next_frame() - - local selected_content - Window.capture_select_input = function(_, opts) - opts.cb(true, opts.content[1]) - end - - Window.capture_input = function(_, opts) - selected_content = opts.content - opts.cb(true, "extra vibe context") - end - - local vibe_prompt - ops.vibe = function(_, opts) - vibe_prompt = opts.additional_prompt - end - - _99.vibe_search() - - eq({ "/tmp/foo.lua:1:1,search note" }, selected_content) - eq("extra vibe context", vibe_prompt) - end) -end) diff --git a/lua/99/utils.lua b/lua/99/utils.lua index 56c49a5..e15e178 100644 --- a/lua/99/utils.lua +++ b/lua/99/utils.lua @@ -1,5 +1,20 @@ local M = {} +--- @param str string +---@param word_count number +---@return string[] +function M.split_with_count(str, word_count) + local out = {} + local words = vim.split(str, "%s+", { trimempty = true }) + + local count = math.min(word_count, #words) + for i = 1, count do + table.insert(out, words[i]) + end + + return out +end + function M.copy(t) assert(type(t) == "table", "passed in non table into table") local out = {} diff --git a/lua/99/window/init.lua b/lua/99/window/init.lua index 47f6f1c..a3a02bb 100644 --- a/lua/99/window/init.lua +++ b/lua/99/window/init.lua @@ -1,15 +1,17 @@ --- TODO: I need to refactor a lot of this file --- it really sucks local Agents = require("99.extensions.agents") +local Point = require("99.geo").Point --- @class _99.window.Module --- @field active_windows _99.window.Window[] local M = { active_windows = {}, } + local nsid = vim.api.nvim_create_namespace("99.window.error") -local nvim_win_is_valid = vim.api.nvim_win_is_valid -local nvim_buf_is_valid = vim.api.nvim_buf_is_valid +local win_valid = vim.api.nvim_win_is_valid +local buf_valid = vim.api.nvim_buf_is_valid --- @class _99.window.Config --- @field width number @@ -199,10 +201,10 @@ end --- @param window _99.window.Window local function window_close(window) - if nvim_win_is_valid(window.win_id) then + if win_valid(window.win_id) then vim.api.nvim_win_close(window.win_id, true) end - if nvim_buf_is_valid(window.buf_id) then + if buf_valid(window.buf_id) then vim.api.nvim_buf_delete(window.buf_id, { force = true }) end end @@ -210,7 +212,7 @@ end --- @param window _99.window.Window --- @return boolean function M.valid(window) - return nvim_win_is_valid(window.win_id) and nvim_buf_is_valid(window.buf_id) + return win_valid(window.win_id) and buf_valid(window.buf_id) end --- @param text string @@ -228,7 +230,7 @@ function M.display_cancellation_message(text) }) vim.defer_fn(function() - if nvim_win_is_valid(window.win_id) then + if win_valid(window.win_id) then M.clear_active_popups() end end, 5000) @@ -293,7 +295,7 @@ local function highlight_rules_found(win, rules, group) local rule_nsid = vim.api.nvim_create_namespace("99.window.rules") local function check_and_highlight_rules() - if not nvim_win_is_valid(win.win_id) then + if not win_valid(win.win_id) then return end @@ -382,7 +384,7 @@ function M.capture_input(name, opts) group = group, buffer = win.buf_id, callback = function() - if nvim_win_is_valid(win.win_id) then + if win_valid(win.win_id) then vim.api.nvim_set_current_win(win.win_id) else M.clear_active_popups() @@ -394,7 +396,7 @@ function M.capture_input(name, opts) group = group, buffer = win.buf_id, callback = function() - if not nvim_win_is_valid(win.win_id) then + if not win_valid(win.win_id) then return end local lines = vim.api.nvim_buf_get_lines(win.buf_id, 0, -1, false) @@ -408,7 +410,7 @@ function M.capture_input(name, opts) group = group, buffer = win.buf_id, callback = function() - if not nvim_win_is_valid(win.win_id) then + if not win_valid(win.win_id) then return end vim.api.nvim_del_augroup_by_id(group) @@ -419,7 +421,7 @@ function M.capture_input(name, opts) group = group, pattern = tostring(win.win_id), callback = function() - if not nvim_win_is_valid(win.win_id) then + if not win_valid(win.win_id) then return end M.clear_active_popups() @@ -471,19 +473,14 @@ function M.capture_select_input(name, opts) }) vim.keymap.set("n", "<CR>", function() - if not nvim_win_is_valid(win.win_id) then + if not win_valid(win.win_id) then return end - local cursor = vim.api.nvim_win_get_cursor(win.win_id) - local line = vim.api.nvim_buf_get_lines( - win.buf_id, - cursor[1] - 1, - cursor[1], - false - )[1] or "" + local point = Point:from_cursor() + local line = point:line(win.buf_id) M.clear_active_popups() - opts.cb(true, line) + opts.cb(true, line or "") end, { buffer = win.buf_id, nowait = true }) end @@ -599,7 +596,7 @@ function M.create_split(content, buffer, opts) local win_id = vim.api.nvim_get_current_win() local buf_id = buffer - if not buf_id or not nvim_buf_is_valid(buf_id) then + if not buf_id or not buf_valid(buf_id) then buf_id = vim.api.nvim_create_buf(false, false) vim.api.nvim_buf_set_lines( buf_id, |
