|
" retoor <retoor@molodetz.nl>
|
|
" Self-contained config for the ppy container. No external plugins or managers:
|
|
" it works out of the box with the stock vim, and the AI edit feature uses only
|
|
" curl and the PRAVDA_* gateway env that every instance is launched with.
|
|
|
|
set nocompatible
|
|
filetype plugin indent on
|
|
syntax on
|
|
|
|
set encoding=utf-8
|
|
set fileencoding=utf-8
|
|
set termencoding=utf-8
|
|
set mouse=a
|
|
set backspace=indent,eol,start
|
|
set autoindent
|
|
set smartindent
|
|
set tabstop=4
|
|
set shiftwidth=4
|
|
set expandtab
|
|
set number
|
|
set showmatch
|
|
set showtabline=2
|
|
set laststatus=2
|
|
set hidden
|
|
set incsearch
|
|
set hlsearch
|
|
set wildmenu
|
|
set ttimeoutlen=50
|
|
|
|
if has('clipboard')
|
|
set clipboard=unnamedplus
|
|
endif
|
|
|
|
if !isdirectory(expand('~/.vim/undo'))
|
|
call mkdir(expand('~/.vim/undo'), 'p')
|
|
endif
|
|
set undofile
|
|
set undodir=~/.vim/undo
|
|
|
|
set statusline=%f\ %h%m%r\ %=\ [%{&filetype}]\ [%l,%c]\ %p%%
|
|
highlight StatusLine cterm=bold ctermfg=15 ctermbg=24
|
|
highlight StatusLineNC cterm=none ctermfg=250 ctermbg=236
|
|
highlight ErrorMsg cterm=bold ctermfg=red ctermbg=none
|
|
|
|
let mapleader = ","
|
|
|
|
inoremap <C-n> <ESC>:tabnext<CR>
|
|
inoremap <C-p> <ESC>:tabprevious<CR>
|
|
nnoremap <C-n> :tabnext<CR>
|
|
nnoremap <C-p> :tabprevious<CR>
|
|
nnoremap <Tab> :tabnext<CR>
|
|
nnoremap <C-Tab> :tabprevious<CR>
|
|
nnoremap <C-e> :tabnew<Space>
|
|
inoremap <C-e> <ESC>:tabnew<Space>
|
|
|
|
if has('autocmd')
|
|
autocmd BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
|
endif
|
|
|
|
function! s:GetVisualSelection() abort
|
|
let [l:line_start, l:col_start] = [line("'<"), col("'<")]
|
|
let [l:line_end, l:col_end] = [line("'>"), col("'>")]
|
|
let l:lines = getline(l:line_start, l:line_end)
|
|
if empty(l:lines)
|
|
return ''
|
|
endif
|
|
let l:lines[-1] = l:lines[-1][: l:col_end - (l:line_start == l:line_end ? 1 : 2)]
|
|
let l:lines[0] = l:lines[0][l:col_start - 1 :]
|
|
return join(l:lines, "\n")
|
|
endfunction
|
|
|
|
function! s:GatewayUrl() abort
|
|
let l:base = substitute($PRAVDA_OPENAI_URL, '/\+$', '', '')
|
|
if empty(l:base)
|
|
return 'https://openai.app.molodetz.nl/v1/chat/completions'
|
|
elseif l:base =~# '/chat/completions$'
|
|
return l:base
|
|
endif
|
|
return l:base . '/chat/completions'
|
|
endfunction
|
|
|
|
function! AiEditSelection() abort
|
|
let l:instruction = input('AI instruction: ')
|
|
if empty(l:instruction)
|
|
echo 'Cancelled.'
|
|
return
|
|
endif
|
|
let l:orig = s:GetVisualSelection()
|
|
if empty(l:orig)
|
|
echo 'No selection.'
|
|
return
|
|
endif
|
|
let l:prompt = l:instruction . "\n\nHere is the text:\n" . l:orig
|
|
\ . "\n\nOutput only the transformed text. No explanations, markdown, or code blocks."
|
|
let l:api_key = !empty($PRAVDA_API_KEY) ? $PRAVDA_API_KEY : $DEEPSEEK_API_KEY
|
|
let l:json = '{"model":"deepseek-chat","messages":[{"role":"user","content":' . json_encode(l:prompt) . '}]}'
|
|
let l:cmd = 'curl -sS -X POST ' . shellescape(s:GatewayUrl())
|
|
\ . ' -H ' . shellescape('Authorization: Bearer ' . l:api_key)
|
|
\ . ' -H ' . shellescape('Content-Type: application/json')
|
|
\ . ' -d ' . shellescape(l:json)
|
|
let l:reply = system(l:cmd)
|
|
if v:shell_error
|
|
echohl ErrorMsg | echom 'AI request failed' | echohl None
|
|
return
|
|
endif
|
|
let l:text = matchstr(l:reply, '"content":\s*"\zs\(.\{-}\)\ze"\s*[,}]')
|
|
if empty(l:text)
|
|
echohl ErrorMsg | echom 'No content in AI response' | echohl None
|
|
return
|
|
endif
|
|
let l:text = substitute(l:text, '\\n', "\n", 'g')
|
|
let l:text = substitute(l:text, '\\"', '"', 'g')
|
|
let l:text = substitute(l:text, '\\t', "\t", 'g')
|
|
normal! gv
|
|
normal! c
|
|
call feedkeys(l:text, 'n')
|
|
endfunction
|
|
|
|
xnoremap <silent> <Leader>a :<C-u>call AiEditSelection()<CR>
|