Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add neural completion buffer #40

Merged
merged 2 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
/env
__pycache__
tags
# pyenv
.python-version
14 changes: 12 additions & 2 deletions autoload/neural.vim
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,18 @@ function! neural#Prompt(prompt) abort
return
endif

call neural#Run(a:prompt, {})
endfunction

function! neural#Run(prompt, options) abort
let l:buffer = bufnr('')
let l:moving_line = getpos('.')[1]

if has_key(a:options, 'line')
let l:moving_line = a:options.line
else
let l:moving_line = getpos('.')[1]
endif

let s:request_line = l:moving_line

if len(getline(l:moving_line)) == 0
Expand Down Expand Up @@ -322,7 +332,7 @@ function! neural#Prompt(prompt) abort
let s:current_job = l:job_id

" Tell the user something is happening, if enabled.
if g:neural.ui.echo_enabled
if g:neural.ui.echo_enabled && get(a:options, 'echo', 1)
" Echo with a 0 millisecond timer to avoid 'Press Enter to Continue'
let s:initial_timer = timer_start(0, {-> s:InitiallyInformUser(l:job_id)})

Expand Down
81 changes: 81 additions & 0 deletions autoload/neural/buffer.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
" Author: Anexon <anexon@protonmail.com>
" Description: A Neural Scratch Buffer acts as a playground for interacting with
" Neural sources directly, sending all content of the buffer to the source.

scriptencoding utf-8

call neural#config#Load()

function! s:GetOptions(options_dict_string) abort
call neural#config#Load()

" TODO: Set buffer name based on source.
let l:options = {
\ 'name': 'Neural Buffer',
\ 'create_mode': g:neural.buffer.create_mode,
\ 'wrap': g:neural.buffer.wrap,
\}

" Override default options for the buffer instance.
if !empty(a:options_dict_string)
let l:options_dict = eval(a:options_dict_string)

if has_key(l:options_dict, 'name')
let l:options.name = l:options_dict.name
endif

if has_key(l:options_dict, 'create_mode')
let l:options.create_mode = l:options_dict.create_mode
endif

if has_key(l:options_dict, 'wrap')
let l:options.wrap = l:options_dict.wrap
endif
endif

return l:options
endfunction

function! neural#buffer#CreateBuffer(options) abort
let l:buffer_options = s:GetOptions(a:options)

" TODO: Add auto incrementing buffer names instead of switching.
if bufexists(l:buffer_options.name)
execute 'buffer' bufnr(l:buffer_options.name)
else
if l:buffer_options.create_mode is# 'vertical'
vertical new
elseif l:buffer_options.create_mode is# 'horizontal'
new
else
call neural#OutputErrorMessage('Invalid create mode for Neural Buffer. Must be horizontal or vertical.')
endif

if l:buffer_options.wrap
setlocal wrap linebreak
else
setlocal nowrap nolinebreak
endif

execute 'file ' . escape(l:buffer_options.name, ' ')
setlocal filetype=neuralbuf
setlocal buftype=nofile
setlocal bufhidden=hide
setlocal noswapfile
endif

" Switch into insert mode when entering the buffer
startinsert
endfunction

function! neural#buffer#RunBuffer() abort
let l:buffer_contents = join(getline(1, '$'), "\n")
let l:options = {
\ 'line': line('$'),
\ 'echo': 0,
\}

if &filetype is# 'neuralbuf'
call neural#Run(l:buffer_contents, l:options)
endif
endfunction
5 changes: 5 additions & 0 deletions autoload/neural/config.vim
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ let s:defaults = {
\ 'animated_sign_enabled': v:true,
\ 'echo_enabled': v:true,
\ },
\ 'buffer': {
\ 'completion_key': '<C-CR>',
\ 'create_mode': 'vertical',
\ 'wrap': v:true,
\ },
\ 'source': {
\ 'openai': {
\ 'api_key': '',
Expand Down
12 changes: 12 additions & 0 deletions ftplugin/neuralbuf.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
" Author: Anexon <anexon@protonmail.com>
" Description: Neural Buffer for interacting with neural sources directly.

call neural#config#Load()

command! -buffer -nargs=0 NeuralRun :call neural#buffer#RunBuffer()

nnoremap <silent> <buffer> <Plug>(neural_completion) :NeuralRun<CR>

" Keybindings of Neural Buffer
execute 'nnoremap ' . g:neural.buffer.completion_key . ' <Plug>(neural_completion)'
execute 'inoremap ' . g:neural.buffer.completion_key . ' <Esc><Plug>(neural_completion)'
2 changes: 0 additions & 2 deletions lua/neural.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
local next = next

-- External dependencies
local UI = {}
local AnimatedSign = {}
Expand Down
130 changes: 57 additions & 73 deletions lua/neural/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,81 +9,65 @@ local UI = {}
--- @param title string The title of the prompt.
--- @param on_submit function The function to call when the user submits the prompt.
function UI.prompt(title, on_submit)
-- TODO: Make escape keys configurable.
local exit_keys = {
{
'n',
'q',
function(_)
vim.api.nvim_command(':q')
end,
{ noremap = true },
},
{
'n',
'<ESC>',
function(_)
vim.api.nvim_command(':q')
end,
{ noremap = true },
},
{
'i',
'<ESC>',
function(_)
vim.api.nvim_command(':q')
end,
{ noremap = true },
},
{
'i',
'<C-c>',
function(_)
vim.api.nvim_command(':q')
end,
{ noremap = true },
},
}
-- TODO: Make escape keys configurable.
local exit_keys = {
{'n', 'q',
function(_)
vim.api.nvim_command(':q')
end, {noremap = true},
},
{'n', '<ESC>',
function(_)
vim.api.nvim_command(':q')
end, {noremap = true},
},
{'i', '<ESC>',
function(_)
vim.api.nvim_command(':q')
end, {noremap = true},
},
{'i', '<C-c>',
function(_)
vim.api.nvim_command(':q')
end, {noremap = true},
},
}

-- TODO: Make prompt more configurable.
local input = Input({
position = { row = '85.2%', col = '50%' },
size = {
width = '51.8%',
height = '20%',
},
relative = 'editor',
border = {
highlight = 'NeuralPromptBorder',
style = 'rounded',
text = {
top = title,
top_align = 'center',
},
},
win_options = {
winblend = 10,
winhighlight = 'Normal:Normal',
},
}, {
prompt = vim.g.neural.ui.prompt_icon .. ' ',
default_value = '',
on_close = function() end,
on_submit = function(value)
on_submit(value)
end,
})
input:mount()
-- Cleanup on lost focus & window close
input:on({ Event.BufLeave, Event.BufWinLeave }, function()
vim.schedule(function()
input:unmount()
-- TODO: Make prompt more configurable.
local input = Input({
position = {row = '85.2%', col = '50%'},
size = {
width = '51.8%',
height = '20%',
},
relative = 'editor',
border = {
highlight = 'NeuralPromptBorder',
style = 'rounded',
text = {
top = title,
top_align = 'center',
},
},
win_options = {
winblend = 10,
winhighlight = 'Normal:Normal',
},
}, {
prompt = vim.g.neural.ui.prompt_icon .. ' ',
default_value = '',
on_close = function() end,
on_submit = function(value)
on_submit(value)
end,
})
input:mount()
input:on(Event.BufLeave, function()
input:unmount()
end)
end, { once = true })
-- Define exit keys
for _, v in ipairs(exit_keys) do
input:map(unpack(v))
end
for _, v in ipairs(exit_keys) do
input:map(unpack(v))
end
end

return UI
5 changes: 5 additions & 0 deletions plugin/neural.vim
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ endif
command! -nargs=? Neural :call neural#Prompt(<q-args>)
" Stop Neural doing anything.
command! -nargs=0 NeuralStop :call neural#Stop()
" Create a completion buffer.
command! -nargs=? NeuralBuffer :call neural#buffer#CreateBuffer(<q-args>)
" Run completion on a Neural buffer.
command! -nargs=0 NeuralBufferRun :call neural#buffer#RunBuffer()
" Have Neural explain the visually selected lines.
command! -range NeuralExplain :call neural#explain#SelectedLines()

" <Plug> mappings for commands
nnoremap <silent> <Plug>(neural_prompt) :call neural#OpenPrompt()<Return>
nnoremap <silent> <Plug>(neural_stop) :call neural#Stop()<Return>
nnoremap <silent> <Plug>(neural_buffer) :call neural#buffer#CreateBuffer({})<Return>
vnoremap <silent> <Plug>(neural_explain) :NeuralExplain<Return>

" Set default keybinds for Neural unless we're told not to. We should almost
Expand Down
65 changes: 65 additions & 0 deletions test/vim/test_buffer.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
Before:
Save g:neural

runtime autoload/neural.vim

unlet! g:neural

let g:calls = []

function! neural#Run(prompt, options) abort
call add(g:calls, ['neural#Run', a:prompt, a:options])
endfunction

After:
unlet! g:calls

runtime autoload/neural/job.vim

Restore

Execute(It should create a neural buffer with default settings):
NeuralBuffer

AssertEqual bufexists('Neural Buffer'), 1
AssertEqual &filetype, 'neuralbuf'
" TODO: Assert if created with new or vertical new
AssertEqual &l:wrap, 1
AssertEqual &l:linebreak, 1

bdelete! Neural Buffer

Execute(It should create a neural buffer with arguments):
NeuralBuffer {"name": "Test Name", "create_mode": "horizontal", "wrap": v:false}

AssertEqual bufexists('Test Name'), 1
AssertEqual &filetype, 'neuralbuf'
" TODO: Assert if created with new or vertical new
AssertEqual &l:wrap, 0
AssertEqual &l:linebreak, 0

bdelete! Test Name

Given neuralbuf(A Neural buffer):
write a story
Execute(It should correctly run neural):
NeuralRun

AssertEqual
\ [
\ ['neural#Run', 'write a story', {'line': 1, 'echo': 0}],
\ ],
\ g:calls


" Plug mappings
Execute(The correct neural buffer keybinds should be configured):
redir => g:output
silent map <Plug>(neural_completion)
redir END

AssertEqual
\ [
\ 'n <Plug>(neural_completion) *@:NeuralRun<CR>',
\ ],
\ sort(split(g:output, "\n"))
13 changes: 13 additions & 0 deletions test/vim/test_config.vader
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ Execute(The default chatgpt settings should be correct):
\ },
\ get(g:neural.source, 'chatgpt')

Execute(The default neural buffer settings should be correct):
call neural#config#Load()
" call filter(g:neural.buffer, {key -> key =~ 'completion'})
"
" AssertEqual {'echo_enabled': v:true}, g:neural.ui
AssertEqual
\ {
\ 'completion_key': '<C-CR>',
\ 'create_mode': 'vertical',
\ 'wrap': v:true,
\ },
\ get(g:neural, 'buffer')

Execute(Settings should be merged correctly):
for s:i in range(2)
if s:i == 0
Expand Down
2 changes: 1 addition & 1 deletion test/vim/test_neural.vader
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Before:
Save g:neural

" Load modules so we can mock the fuctions.
runtime autoload/neural/job.vim
runtime autoload/neural.vim

unlet! g:neural
let g:job_id = 0
Expand Down
Loading