Skip to content

Commit 26f1754

Browse files
authored
feat(config)!: support model option and improved UI behavior (#8)
* chore(ci): removed old branch ref * feat(config): added ability to define model and fixed borders - `model` opt is now passed to the `-m` flag of the Codex CLI - Single border style no longer duplicates "rounded" * chore(tests): moved files to avoid running full Plenary suite * chore(readme): updated README.md with new config documentation * fix(ui)!: resolved multiple buffer open/close issues - BREAKING CHANGE: Buffer toggle is now <q> instead of <Esc> This avoids conflict with the Codex CLI's newer interrupt sequence (<Esc><Esc>) - Buffer now backgrounds smoothly with no delay * feat(tests): added model option tests
1 parent d3b137e commit 26f1754

17 files changed

Lines changed: 355 additions & 102 deletions

.github/workflows/ci.yml

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ name: CI
33

44
on:
55
push:
6-
branches: [main, codecov-dev]
6+
branches: [main]
77
pull_request:
88
branches: [main]
99

10+
workflow_dispatch:
11+
1012
jobs:
1113
test:
1214
name: Neovim ${{ matrix.neovim }}
@@ -19,6 +21,17 @@ jobs:
1921
- name: Checkout plugin
2022
uses: actions/checkout@v4
2123

24+
- name: Setup Node.js
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: '20'
28+
29+
- name: Setup package managers with Corepack
30+
run: |
31+
corepack enable
32+
corepack prepare pnpm@latest --activate
33+
corepack prepare yarn@stable --activate
34+
2235
- name: Install Neovim ${{ matrix.neovim }}
2336
run: |
2437
set -euo pipefail
@@ -57,31 +70,62 @@ jobs:
5770
set -euo pipefail
5871
sudo apt-get update
5972
sudo apt-get install -y lua5.1 liblua5.1-0-dev luarocks
60-
luarocks --lua-version=5.1 --local install luacheck
61-
luarocks --lua-version=5.1 --local install luacov
62-
luarocks --lua-version=5.1 --local install luacov-reporter-lcov
63-
echo "$HOME/.luarocks/bin" >> $GITHUB_PATH
73+
make install-deps
74+
75+
- name: Add PM global bin paths
76+
run: |
77+
echo "$(npm bin -g)" >> $GITHUB_PATH
78+
echo "$(pnpm bin -g)" >> $GITHUB_PATH
79+
echo "$(yarn global bin)" >> $GITHUB_PATH
6480
6581
- name: Run tests with coverage
6682
run: |
67-
eval "$(luarocks --lua-version=5.1 path)"
68-
luarocks --lua-version=5.1 list # debug
69-
nvim --headless -u tests/minimal_init.lua -c "luafile tests/run_cov.lua"
70-
71-
luacov -r lcov > lcov.info
72-
sed -i 's|SF:.*/codex.nvim/codex.nvim/|SF:|g' lcov.info
73-
head -n 10 lcov.info # debug
74-
75-
# Debug output
76-
echo "=== first 20 lines of lcov.info ==="
77-
head -n 20 lcov.info
78-
79-
echo "=== all source-file entries ==="
80-
grep '^SF:' lcov.info | sed -e 's/^SF://g' | sort | uniq | head -n 10
81-
83+
make coverage
84+
85+
- name: Run luacov-lcov manually to generate lcov.info
86+
run: |
87+
eval "$(luarocks --lua-version=5.1 path --bin)"
88+
echo "LuaRocks PATH: $PATH"
89+
which luacov || echo "luacov still not found"
90+
luacov -t LcovReporter > lcov.info
91+
ls -l lcov.info
92+
93+
8294
- name: Upload code coverage
8395
uses: codecov/codecov-action@v4
8496
with:
8597
files: lcov.info # <-- new file
8698
disable_search: true
8799
token: ${{ secrets.CODECOV_TOKEN }}
100+
101+
release:
102+
name: Semantic Release
103+
runs-on: ubuntu-latest
104+
needs: Run tests with coverage
105+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
106+
107+
steps:
108+
- name: Checkout repo
109+
uses: actions/checkout@v4
110+
111+
- name: Setup Node.js
112+
uses: actions/setup-node@v4
113+
with:
114+
node-version: '20'
115+
116+
- name: Install semantic-release and plugins
117+
run: |
118+
npm install --no-save \
119+
semantic-release \
120+
@semantic-release/commit-analyzer \
121+
@semantic-release/release-notes-generator \
122+
@semantic-release/changelog \
123+
@semantic-release/github
124+
125+
- name: Run semantic-release
126+
env:
127+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
run: |
129+
npx semantic-release
130+
131+

.luacov

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ return {
55

66
-- collect coverage only for plugin source
77
include = {
8-
"lua/",
8+
"lua/codex",
99
},
1010

1111
-- ignore test helpers and specs

.luacovrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[general]
2+
statsfile = "luacov.stats.out"
3+
reportfile = "luacov.report.out"
4+
5+
[lcovreport]
6+
output = "lcov.info"
7+
include = "lua/codex/"

README.md

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
## A Neovim plugin integrating the open-sourced Codex CLI (`codex`).
55
> Latest version: ![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/johnseth97/codex.nvim?sort=semver)
66
7+
Note: As of v1.0.0, <Esc> no longer closes the Codex window. Press q to close, and <Esc><Esc> to safely interrupt model generation without resetting context.
8+
79
### Features:
810
- ✅ Toggle Codex floating window with `:CodexToggle`
911
- ✅ Optional keymap mapping via `setup` call
@@ -31,27 +33,35 @@ export OPENAI_API_KEY=your_api_key
3133
return {
3234
'johnseth97/codex.nvim',
3335
lazy = true,
36+
cmd = { 'Codex', 'CodexToggle' }, -- Optional: Load only on command execution
3437
keys = {
3538
{
36-
'<leader>cc',
39+
'<leader>cc', -- Change this to your preferred keybinding
3740
function() require('codex').toggle() end,
3841
desc = 'Toggle Codex popup',
3942
},
4043
},
4144
opts = {
42-
keymaps = {}, -- disable internal mapping
43-
border = 'rounded', -- or 'double'
44-
width = 0.8,
45-
height = 0.8,
46-
autoinstall = true,
45+
keymaps = {}, -- Disable internal default keymap (<leader>cc -> :CodexToggle)
46+
border = 'rounded', -- Options: 'single', 'double', or 'rounded'
47+
width = 0.8, -- Width of the floating window (0.0 to 1.0)
48+
height = 0.8, -- Height of the floating window (0.0 to 1.0)
49+
model = nil, -- Optional: pass a string to use a specific model (e.g., 'o3-mini')
50+
autoinstall = true, -- Automatically install the Codex CLI if not found
4751
},
48-
}
49-
```
52+
}```
5053

5154
### Usage:
5255
- Call `:Codex` (or `:CodexToggle`) to open or close the Codex popup.
5356
-- Map your own keybindings via the `keymaps.toggle` setting.
5457
- Add the following code to show backgrounded Codex window in lualine:
58+
5559
```lua
5660
require('codex').status() -- drop in to your lualine sections
5761
```
62+
63+
### Configuration:
64+
- All plugin configurations can be seen in the `opts` table of the plugin setup, as shown in the installation section.
65+
66+
- **For deeper customization, please refer to the [Codex CLI documentation](https://github.com/openai/codex?tab=readme-ov-file#full-configuration-example) full configuration example. These features change quickly as Codex CLI is in active beta development.*
67+

codex-test.log

Whitespace-only changes.

lua/codex/init.lua

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ local config = {
1010
width = 0.8,
1111
height = 0.8,
1212
cmd = 'codex',
13+
model = nil, -- Default to the latest model
1314
autoinstall = true,
1415
}
1516

@@ -37,13 +38,13 @@ local function open_window()
3738

3839
local styles = {
3940
single = {
40-
{ '', 'FloatBorder' },
41+
{ '', 'FloatBorder' },
4142
{ '', 'FloatBorder' },
42-
{ '', 'FloatBorder' },
43+
{ '', 'FloatBorder' },
4344
{ '', 'FloatBorder' },
44-
{ '', 'FloatBorder' },
45+
{ '', 'FloatBorder' },
4546
{ '', 'FloatBorder' },
46-
{ '', 'FloatBorder' },
47+
{ '', 'FloatBorder' },
4748
{ '', 'FloatBorder' },
4849
},
4950
double = {
@@ -83,6 +84,16 @@ local function open_window()
8384
end
8485

8586
function M.open()
87+
local function create_clean_buf()
88+
local buf = vim.api.nvim_create_buf(false, false)
89+
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'hide')
90+
vim.api.nvim_buf_set_option(buf, 'swapfile', false)
91+
vim.api.nvim_buf_set_option(buf, 'filetype', 'codex')
92+
vim.api.nvim_buf_set_keymap(buf, 't', 'q', [[<C-\><C-n><cmd>lua require('codex').close()<CR>]], { noremap = true, silent = true })
93+
vim.api.nvim_buf_set_keymap(buf, 'n', 'q', [[<cmd>lua require('codex').close()<CR>]], { noremap = true, silent = true })
94+
return buf
95+
end
96+
8697
if state.win and vim.api.nvim_win_is_valid(state.win) then
8798
vim.api.nvim_set_current_win(state.win)
8899
return
@@ -98,7 +109,7 @@ function M.open()
98109
else
99110
-- Show failure message *after* buffer is created
100111
if not state.buf or not vim.api.nvim_buf_is_valid(state.buf) then
101-
state.buf = vim.api.nvim_create_buf(false, false)
112+
state.buf = create_clean_buf()
102113
end
103114
vim.api.nvim_buf_set_lines(state.buf, 0, -1, false, {
104115
'Autoinstall cancelled or failed.',
@@ -128,32 +139,37 @@ function M.open()
128139
end
129140
end
130141

131-
-- At this point, CLI is available: safe to setup buffer and window
132-
if not state.buf or not vim.api.nvim_buf_is_valid(state.buf) then
133-
state.buf = vim.api.nvim_create_buf(false, false)
134-
vim.api.nvim_buf_set_option(state.buf, 'bufhidden', 'hide')
135-
vim.api.nvim_buf_set_option(state.buf, 'swapfile', false)
136-
vim.api.nvim_buf_set_option(state.buf, 'filetype', 'codex')
137-
vim.api.nvim_buf_set_keymap(state.buf, 't', '<Esc>', [[<C-\><C-n><cmd>lua require('codex').close()<CR>]], { noremap = true, silent = true })
138-
vim.api.nvim_buf_set_keymap(state.buf, 'n', '<Esc>', [[<cmd>lua require('codex').close()<CR>]], { noremap = true, silent = true })
142+
local function is_buf_reusable(buf)
143+
return type(buf) == 'number' and vim.api.nvim_buf_is_valid(buf)
144+
end
145+
146+
if not is_buf_reusable(state.buf) then
147+
state.buf = create_clean_buf()
139148
end
140149

141150
open_window()
142151

143152
if not state.job then
144-
state.job = vim.fn.termopen(config.cmd, {
153+
local cmd_args = type(config.cmd) == 'string' and { config.cmd } or vim.deepcopy(config.cmd)
154+
if config.model then
155+
table.insert(cmd_args, '-m')
156+
table.insert(cmd_args, config.model)
157+
end
158+
159+
state.job = vim.fn.termopen(cmd_args, {
145160
cwd = vim.loop.cwd(),
146161
on_exit = function()
147162
state.job = nil
148163
end,
149164
})
150165
end
151166
end
167+
152168
function M.close()
153169
if state.win and vim.api.nvim_win_is_valid(state.win) then
154170
vim.api.nvim_win_close(state.win, true)
155-
state.win = nil
156171
end
172+
state.win = nil
157173
end
158174

159175
function M.toggle()

lua/codex/installer.lua

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
-- lua/codex/installer.lua
22
local state = require 'codex.state'
33
local M = {}
4+
M.__test_ignore_path_check = false -- used in tests to skip path checks
45

56
local install_cmds = {
67
npm = 'npm install -g @openai/codex',
@@ -115,7 +116,7 @@ function M.run_install(pm, on_success)
115116
on_exit = function(_, code)
116117
if code == 0 then
117118
vim.notify('[codex.nvim] codex CLI installed successfully via ' .. pm, vim.log.levels.INFO)
118-
if vim.fn.executable 'codex' == 0 then
119+
if not M.__test_ignore_path_check and vim.fn.executable 'codex' == 0 then
119120
local fallback = fallback_instructions[pm]
120121
if fallback then
121122
vim.notify('[codex.nvim] CLI not yet available on PATH.\n' .. fallback, vim.log.levels.WARN)
@@ -127,7 +128,13 @@ function M.run_install(pm, on_success)
127128
vim.schedule(on_success)
128129
end
129130
else
130-
vim.notify('[codex.nvim] Installation failed via ' .. pm, vim.log.levels.ERROR)
131+
if not M.__test_ignore_path_check then
132+
vim.notify('[codex.nvim] Installation failed via ' .. pm, vim.log.levels.ERROR)
133+
134+
vim.schedule(function()
135+
vim.cmd 'cquit 1'
136+
end)
137+
end
131138
end
132139
state.job = nil
133140
end,

makefile

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,40 @@
44
# make coverage - run tests + generate coverage (luacov + lcov.info)
55

66
# Force correct Lua version for Neovim (Lua 5.1)
7-
LUAROCKS_ENV = eval "$(luarocks --lua-version=5.1 path)"
87

98
# Headless Neovim test runner
109
NVIM_TEST_CMD = nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/"
1110

1211
.PHONY: test coverage clean
1312

1413
test:
15-
$(LUAROCKS_ENV) && $(NVIM_TEST_CMD)
14+
@bash -c 'eval "$$(luarocks --lua-version=5.1 path)" && \
15+
nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/"'
1616

1717
coverage:
18-
$(LUAROCKS_ENV) && nvim --headless -u tests/minimal_init.lua -c "luafile tests/run_cov.lua"
19-
ls -lh luacov.stats.out
20-
$(LUAROCKS_ENV) && luacov -t LcovReporter
21-
@echo "Generated coverage report: lcov.info"
18+
@bash -c 'eval "$$(luarocks --lua-version=5.1 path --bin)" && \
19+
nvim --headless -u tests/minimal_init.lua -c "luafile tests/run_cov.lua" || exit 0 && \
20+
if [ -f luacov.stats.out ]; then \
21+
echo "::group::Coverage"; \
22+
luacov -t LcovReporter > lcov.info; \
23+
echo "::endgroup::"; \
24+
else \
25+
echo "luacov.stats.out not found, skipping coverage report."; \
26+
fi'
2227

2328
clean:
2429
rm -f luacov.stats.out lcov.info
2530
@echo "Cleaned coverage artifacts"
2631

2732
install-deps:
28-
luarocks --lua-version=5.1 install luacov || true
29-
git clone https://github.com/nvim-lua/plenary.nvim tests/plenary.nvim || true
33+
luarocks --lua-version=5.1 install --local luacov
34+
luarocks --lua-version=5.1 install --local luacov-reporter-lcov
35+
luarocks --lua-version=5.1 install --local luacheck
36+
if [ ! -d ~/.local/share/nvim/site/pack/test/start/plenary.nvim ]; then \
37+
echo "Installing plenary.nvim dependency..."; \
38+
mkdir -p ~/.local/share/nvim/site/pack/test/start; \
39+
git clone https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/test/start/plenary.nvim || true; \
40+
else \
41+
echo "plenary.nvim already installed."; \
42+
fi
43+

tests/minimal_init.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
vim.cmd 'set rtp+=.'
22
vim.cmd 'set rtp+=./plenary.nvim' -- if using as a submodule or symlinked
3-
require 'plugin.codex' -- triggers plugin/gh_dash.lua
4-
vim.opt.runtimepath:append '~/.local/share/nvim/lazy/plenary.nvim/'
3+
pcall(require, 'plugin.codex') -- triggers plugin/gh_dash.lua
4+
vim.opt.runtimepath:append(vim.fn.getcwd())
5+
vim.opt.runtimepath:append(vim.fn.stdpath 'data' .. '/site/pack/deps/start/plenary.nvim')

tests/plenary.nvim

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)