Skip to content

Commit 3c61f4c

Browse files
authored
Merge pull request #4 from realEbi/dev-kiro-cli
Feature: Dynamic Configuration and Display Window Options
2 parents bff7ad6 + 39d848e commit 3c61f4c

10 files changed

Lines changed: 297 additions & 62 deletions

File tree

README.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Install `askCode.nvim` using your favorite package manager.
2828

2929
```lua
3030
{
31-
"e3oroush/askCode",
31+
"realEbi/askCode",
3232
config = function()
3333
require("askCode").setup({
3434
-- Your configuration here
@@ -41,7 +41,7 @@ Install `askCode.nvim` using your favorite package manager.
4141

4242
```lua
4343
use {
44-
"e3oroush/askCode",
44+
"realEbi/askCode",
4545
config = function()
4646
require("askCode").setup({
4747
-- Your configuration here
@@ -60,7 +60,8 @@ require("askCode").setup({
6060
debug = false, -- Enable debug mode (default: false)
6161
quit_key = "q", -- Key to quit floating windows (default: "q")
6262
output_format = "json", -- Output format (default: "json")
63-
window = { -- Floating window configuration
63+
window = { -- Configuration for the display window
64+
type = "float", -- Type of window: "float", "vertical", or "horizontal" (default: "float")
6465
width_ratio = 0.7, -- Window width as ratio of screen width (default: 0.7)
6566
height_ratio = 0.7, -- Window height as ratio of screen height (default: 0.7)
6667
max_width = 240, -- Maximum window width in columns (default: 240)
@@ -89,6 +90,36 @@ The `:AskCode` command automatically detects whether to start a new conversation
8990

9091
**Note**: Code replacement only works in visual mode with selected text.
9192

93+
### Configuration Management
94+
95+
You can get and set configuration values at runtime using the `:AskCodeConfig` command:
96+
97+
**Get a configuration value:**
98+
```vim
99+
:AskCodeConfig agent
100+
:AskCodeConfig window.type
101+
```
102+
103+
**Set a configuration value:**
104+
```vim
105+
:AskCodeConfig agent amazonq
106+
:AskCodeConfig window.type vertical
107+
:AskCodeConfig window.width_ratio 0.8
108+
:AskCodeConfig debug true
109+
```
110+
111+
The command supports tab completion for all available configuration keys. Values are automatically converted to the appropriate type (booleans, numbers, strings).
112+
113+
**Programmatic access:**
114+
```lua
115+
-- Get config
116+
local agent = require("askCode").get_config("agent")
117+
local all_config = require("askCode").get_config()
118+
119+
-- Set config
120+
require("askCode").set_config("window.type", "horizontal")
121+
```
122+
92123
### Keybinding Examples
93124

94125
You can map the commands to keybindings for easier access:
@@ -110,7 +141,7 @@ Contributions are welcome! To get started with development:
110141
1. **Clone the repository**:
111142

112143
```sh
113-
git clone https://github.com/e3oroush/askCode.git askCode.nvim
144+
git clone https://github.com/realEbi/askCode.git askCode.nvim
114145
cd askCode.nvim
115146
```
116147

lua/askCode/agents/amazonq.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ end
1515
function M.prepare_command(prompt)
1616
-- Escape the prompt to ensure it's safely passed to the shell.
1717
local escaped_prompt = vim.fn.shellescape(prompt)
18-
return string.format("echo %s | q chat --no-interactive 2>&1", escaped_prompt)
18+
return string.format("echo %s | kiro-cli chat --no-interactive 2>&1", escaped_prompt)
1919
end
2020

2121
--- Parses the response from the AmazonQ CLI.

lua/askCode/config.lua

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ M.default = {
1414
quit_key = "q",
1515
output_format = "json",
1616
window = {
17+
type = "float", -- float, vertical, horizontal
1718
width_ratio = 0.7,
1819
height_ratio = 0.7,
1920
max_width = 240,
@@ -26,14 +27,81 @@ M.default = {
2627
---@return Config
2728
function M.merge_with_default(changes)
2829
changes = changes or {}
29-
30-
-- merge basic settings
31-
---@type Config
32-
local config = vim.tbl_deep_extend("force", M.default, changes)
33-
M.current_config = config
30+
M.current_config = vim.tbl_deep_extend("keep", vim.deepcopy(changes), M.current_config)
3431
return M.current_config
3532
end
3633

37-
M.current_config = M.default
34+
M.current_config = vim.deepcopy(M.default)
35+
36+
--- gets a config value
37+
---@param key? string dot separated key
38+
---@return any
39+
function M.get(key)
40+
if not key then
41+
return M.current_config
42+
end
43+
local parts = vim.split(key, "%.")
44+
local current = M.current_config
45+
for _, part in ipairs(parts) do
46+
if type(current) ~= "table" or current[part] == nil then
47+
return nil
48+
end
49+
current = current[part]
50+
end
51+
return current
52+
end
53+
54+
--- sets a config value
55+
---@param key string dot separated key
56+
---@param value any
57+
---@return any new value
58+
function M.set(key, value)
59+
local parts = vim.split(key, "%.")
60+
if type(value) == "string" then
61+
if value == "true" then
62+
value = true
63+
elseif value == "false" then
64+
value = false
65+
elseif tonumber(value) then
66+
value = tonumber(value)
67+
end
68+
end
69+
70+
local t = {}
71+
local current = t
72+
for i, part in ipairs(parts) do
73+
if i == #parts then
74+
current[part] = value
75+
else
76+
current[part] = {}
77+
current = current[part]
78+
end
79+
end
80+
M.merge_with_default(t)
81+
return M.get(key)
82+
end
83+
84+
--- resets config to default
85+
function M.reset_config()
86+
M.current_config = vim.deepcopy(M.default)
87+
end
88+
89+
--- gets all config keys as dot-separated strings
90+
---@return string[]
91+
function M.get_all_keys()
92+
local keys = {}
93+
local function traverse(tbl, prefix)
94+
for key, value in pairs(tbl) do
95+
local full_key = prefix == "" and key or prefix .. "." .. key
96+
if type(value) == "table" then
97+
traverse(value, full_key)
98+
else
99+
table.insert(keys, full_key)
100+
end
101+
end
102+
end
103+
traverse(M.default, "")
104+
return keys
105+
end
38106

39107
return M

lua/askCode/init.lua

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ function M.setup(cfg)
1919
config.merge_with_default(cfg)
2020
end
2121

22+
--- Gets a config value
23+
---@param key? string dot separated key (e.g., "window.type"). If nil, returns entire config
24+
---@return any
25+
function M.get_config(key)
26+
return config.get(key)
27+
end
28+
29+
--- Sets a config value
30+
---@param key string dot separated key (e.g., "window.type")
31+
---@param value any the value to set
32+
---@return any the new value
33+
function M.set_config(key, value)
34+
return config.set(key, value)
35+
end
36+
2237
--- Constructs a prompt for the AI assistant based on question, mode, and conversation history
2338
--- @param question string The user's question or request
2439
--- @param mode string The current editor mode (visual, normal, etc.)
@@ -83,7 +98,7 @@ function M.ask(question, mode)
8398
end
8499

85100
local command = agent.prepare_command(full_prompt)
86-
state.win_id, state.buf_id = ui.show_in_float("Loading...", on_close)
101+
state.win_id, state.buf_id = ui.show_window("Loading...", on_close)
87102

88103
local response_lines = {}
89104
local on_stdout = function(_, data)
@@ -114,7 +129,7 @@ function M.ask(question, mode)
114129
utils.append_file(state.history_file, agent_response)
115130

116131
state.display_content = "AGENT: " .. response
117-
ui.update_float(state.win_id, state.buf_id, state.display_content)
132+
ui.update_window(state.win_id, state.buf_id, state.display_content)
118133
end
119134

120135
runner.run_command(
@@ -174,7 +189,7 @@ function M.follow_up(question)
174189
local new_display_part = string.format("\n\n---\n\nUSER: %s\n\nAGENT: %s", question, response)
175190
state.display_content = state.display_content .. new_display_part
176191

177-
ui.update_float(state.win_id, state.buf_id, state.display_content, cursor_position)
192+
ui.update_window(state.win_id, state.buf_id, state.display_content, cursor_position)
178193
end
179194

180195
runner.run_command({ "sh", "-c", command }, on_stdout, { on_exit = on_exit, stdout_buffered = true })
@@ -224,7 +239,7 @@ function M.ask_replace(question, mode)
224239

225240
local command = agent.prepare_command(full_prompt)
226241
state.display_content = "Press Q to apply replacement, q to cancel\n\n"
227-
state.win_id, state.buf_id = ui.show_in_float(state.display_content, on_close, true, on_apply)
242+
state.win_id, state.buf_id = ui.show_window(state.display_content, on_close, true, on_apply)
228243

229244
local response_lines = {}
230245
local on_stdout = function(_, data)
@@ -245,7 +260,7 @@ function M.ask_replace(question, mode)
245260
end
246261

247262
state.display_content = state.display_content .. response
248-
ui.update_float(state.win_id, state.buf_id, state.display_content, nil, true)
263+
ui.update_window(state.win_id, state.buf_id, state.display_content, nil, true)
249264
end
250265

251266
runner.run_command({ "sh", "-c", command }, on_stdout, { on_exit = on_exit, stdout_buffered = true })

lua/askCode/ui.lua

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,60 @@
11
local config = require("askCode.config")
22
local M = {}
33

4-
--- Creates a floating window to show content.
4+
--- Creates a window to show content.
55
--- @param content string The content to display.
66
--- @param on_close function A callback to execute when the window is closed.
77
--- @param editable boolean|nil Whether the window should be editable (default: false).
88
--- @param on_apply function|nil Callback for apply action (Q key) - receives edited content.
99
--- @return number, number The window ID and buffer ID.
10-
function M.show_in_float(content, on_close, editable, on_apply)
10+
function M.show_window(content, on_close, editable, on_apply)
1111
local window_config = config.current_config.window
12+
local window_type = window_config.type or "float"
1213

13-
local width = math.floor(vim.o.columns * window_config.width_ratio)
14-
if width > window_config.max_width then
15-
width = window_config.max_width
16-
end
17-
18-
local height = math.floor(vim.o.lines * window_config.height_ratio)
19-
if height > window_config.max_height then
20-
height = window_config.max_height
21-
end
22-
23-
local win_opts = {
24-
relative = "cursor",
25-
width = width,
26-
height = height,
27-
row = 1,
28-
col = 0,
29-
style = "minimal",
30-
border = "rounded",
31-
}
3214
local buf_id = vim.api.nvim_create_buf(false, true)
3315
vim.api.nvim_set_option_value("filetype", "markdown", { buf = buf_id })
3416

35-
local win_id = vim.api.nvim_open_win(buf_id, true, win_opts)
17+
local win_id
18+
if window_type == "vertical" then
19+
vim.api.nvim_command("vsplit")
20+
win_id = vim.api.nvim_get_current_win()
21+
vim.api.nvim_win_set_buf(win_id, buf_id)
22+
local width = math.floor(vim.o.columns * window_config.width_ratio)
23+
if width > window_config.max_width then
24+
width = window_config.max_width
25+
end
26+
vim.api.nvim_win_set_width(win_id, width)
27+
elseif window_type == "horizontal" then
28+
vim.api.nvim_command("split")
29+
win_id = vim.api.nvim_get_current_win()
30+
vim.api.nvim_win_set_buf(win_id, buf_id)
31+
local height = math.floor(vim.o.lines * window_config.height_ratio)
32+
if height > window_config.max_height then
33+
height = window_config.max_height
34+
end
35+
vim.api.nvim_win_set_height(win_id, height)
36+
else -- float is the default
37+
local width = math.floor(vim.o.columns * window_config.width_ratio)
38+
if width > window_config.max_width then
39+
width = window_config.max_width
40+
end
41+
42+
local height = math.floor(vim.o.lines * window_config.height_ratio)
43+
if height > window_config.max_height then
44+
height = window_config.max_height
45+
end
46+
47+
local win_opts = {
48+
relative = "cursor",
49+
width = width,
50+
height = height,
51+
row = 1,
52+
col = 0,
53+
style = "minimal",
54+
border = "rounded",
55+
}
56+
win_id = vim.api.nvim_open_win(buf_id, true, win_opts)
57+
end
3658

3759
-- Prepare content with instructions if editable
3860
-- Set content
@@ -77,13 +99,13 @@ function M.show_in_float(content, on_close, editable, on_apply)
7799
return win_id, buf_id
78100
end
79101

80-
--- Updates the content of a floating window.
102+
--- Updates the content of a window.
81103
--- @param win_id number The ID of the window to update.
82104
--- @param buf_id number The ID of the buffer to update.
83105
--- @param content string The new content.
84106
--- @param replacement? boolean if the buffer is for replacement command
85107
--- @param cursor_line number|nil Optional line number to position cursor (defaults to end).
86-
function M.update_float(win_id, buf_id, content, cursor_line, replacement)
108+
function M.update_window(win_id, buf_id, content, cursor_line, replacement)
87109
vim.schedule(function()
88110
if not (buf_id and vim.api.nvim_buf_is_valid(buf_id)) then
89111
return

plugin/askCode.lua

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,37 @@ vim.api.nvim_create_user_command("AskCodeReplace", function(opts)
3838
end
3939
end, { range = true, nargs = "?" })
4040

41+
vim.api.nvim_create_user_command("AskCodeConfig", function(opts)
42+
local args = vim.split(opts.args, "%s+")
43+
local key = args[1]
44+
local value = args[2]
45+
46+
if not key or key == "" then
47+
vim.notify("Usage: AskCodeConfig <key> [value]", vim.log.levels.ERROR)
48+
return
49+
end
50+
51+
if value then
52+
local new_value = require("askCode").set_config(key, value)
53+
vim.notify(string.format("Set %s = %s", key, vim.inspect(new_value)), vim.log.levels.INFO)
54+
else
55+
local current_value = require("askCode").get_config(key)
56+
vim.notify(string.format("%s = %s", key, vim.inspect(current_value)), vim.log.levels.INFO)
57+
end
58+
end, {
59+
nargs = "+",
60+
complete = function(arg_lead, cmd_line, cursor_pos)
61+
local args = vim.split(cmd_line, "%s+")
62+
if #args <= 2 then
63+
local keys = require("askCode.config").get_all_keys()
64+
return vim.tbl_filter(function(key)
65+
return vim.startswith(key, arg_lead)
66+
end, keys)
67+
end
68+
return {}
69+
end,
70+
})
71+
4172
vim.keymap.set(
4273
"v",
4374
"<Plug>(AskCodeExplain)",

tests/test_amazonq.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ T["prepare_command"] = new_set()
3232

3333
T["prepare_command"]["should format command correctly"] = function()
3434
local command = child.lua_get([[Agent.prepare_command("test prompt")]])
35-
eq(command, "echo 'test prompt' | q chat --no-interactive 2>&1")
35+
eq(command, "echo 'test prompt' | kiro-cli chat --no-interactive 2>&1")
3636
end
3737

3838
T["prepare_command"]["should escape special characters"] = function()
3939
local command = child.lua_get([[Agent.prepare_command("test 'quoted' prompt")]])
40-
eq(command, "echo 'test '\\''quoted'\\'' prompt' | q chat --no-interactive 2>&1")
40+
eq(command, "echo 'test '\\''quoted'\\'' prompt' | kiro-cli chat --no-interactive 2>&1")
4141
end
4242

4343
-- Test parse_response function

0 commit comments

Comments
 (0)