├── plugin ├── templates │ ├── generate.txt │ └── common.txt └── bitoai.vim ├── LICENSE └── README.md /plugin/templates/generate.txt: -------------------------------------------------------------------------------- 1 | {{:prompt:}} -------------------------------------------------------------------------------- /plugin/templates/common.txt: -------------------------------------------------------------------------------- 1 | Context is provided below within contextstart and contextend 2 | contextstart 3 | {{%input%}} 4 | contextend 5 | 6 | according to the above context, {{:prompt:}} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ZhenYangze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-bitoai 2 | ---- 3 | 4 | https://user-images.githubusercontent.com/15710584/236500940-12b82393-a7f5-4eb7-8369-ebcaacc2c0cd.mp4 5 | 6 | ## Install 7 | 8 | 1. install the bito cli 9 | 10 | `https://github.com/gitbito/CLI` 11 | 12 | 2. install and setup vim-plug (plugin manager) 13 | 14 | `https://github.com/junegunn/vim-plug` 15 | 16 | 3. install vim plugin from inside vim 17 | 18 | ``` 19 | Plug 'vim-bitoai' 20 | ``` 21 | 22 | 4. change config 23 | 24 | ``` 25 | " will show in buffers list 26 | let g:bito_buffer_name_prefix = get(g:, 'bito_buffer_name_prefix', 'bito_history_') 27 | 28 | " if your bito cli is not sys command, you should change the bito path 29 | let g:vim_bito_path = get(g:, 'vim_bito_path', "bito") 30 | 31 | " can change all the result of boti ,like: "Please translate the comment into chinses", "Please write the comment in chinses 32 | let g:vim_bito_prompt_append = get(g:, 'vim_bito_prompt_append', "") 33 | ``` 34 | 35 | 36 | 37 | ## *Usage* 38 | 39 | - BitoAiGenerate 40 | - BitoAiGenerateUnit 41 | - BitoAiGenerateComment 42 | - BitoAiCheck 43 | - BitoAiCheckSecurity 44 | - BitoAiCheckStyle 45 | - BitoAiCheckPerformance 46 | - BitoAiReadable 47 | - BitoAiExplain 48 | 49 | 50 | 51 | ## Custom 52 | 53 | ``` 54 | if !exists("g:vim_bito_prompt_{command}") 55 | let g:vim_bito_prompt_{command} = "your prompt" 56 | endif 57 | 58 | " if should select code 59 | command! -range -nargs=0 BitoAi{Command} :call BitoAiSelected('{command}') 60 | ``` 61 | should replace the `{command}` with your self 62 | 63 | ## Optional Hotkeys 64 | Add these to your vimrc using 65 | `vim ~/.vimrc` 66 | 67 | ``` 68 | call plug#begin('~/.vim/plugged') 69 | Plug '~/Desktop/vim-bitoai' 70 | call plug#end() 71 | 72 | " Bito Vim Integration Key Bindings 73 | 74 | " Generate code 75 | xnoremap G :BitoAiGenerate 76 | 77 | " Generate code for a selected range in 'unit' mode 78 | xnoremap U :BitoAiGenerateUnit 79 | 80 | " Generate code comments for a selected range 81 | xnoremap C :BitoAiGenerateComment 82 | 83 | " Check code for potential issues for a selected range 84 | xnoremap K :BitoAiCheck 85 | 86 | " Check code security for a selected range 87 | xnoremap X :BitoAiCheckSecurity 88 | 89 | " Check code style for a selected range 90 | xnoremap S :BitoAiCheckStyle 91 | 92 | " Check code performance for a selected range 93 | xnoremap P :BitoAiCheckPerformance 94 | 95 | " Make code more readable for a selected range 96 | xnoremap R :BitoAiReadable 97 | 98 | " Explain 99 | xnoremap E :BitoAiExplain 100 | ``` 101 | 102 | Example Usage of HotKeys: 103 | 104 | 1. Open a file: `vim create_doc_overview.sh` 105 | 106 | 2. Press `v` to enter visual mode. 107 | 108 | 3. Highlight text using the `arrow keys` 109 | 110 | 4. With Caps Lock ON, press `E` to explain the highlighted code. 111 | -------------------------------------------------------------------------------- /plugin/bitoai.vim: -------------------------------------------------------------------------------- 1 | " --------------------------------------------------------------------------------------------------- 2 | " Ensure the plugin is only loaded once to avoid multiple invocations. 3 | " --------------------------------------------------------------------------------------------------- 4 | if exists('g:loaded_vim_bito') 5 | finish 6 | endif 7 | 8 | " --------------------------------------------------------------------------------------------------- 9 | " Register markdown as a language for treesitter in Neovim for better syntax highlighting. 10 | " --------------------------------------------------------------------------------------------------- 11 | if has('nvim') 12 | lua vim.treesitter.language.register('markdown', 'bito') 13 | endif 14 | 15 | " This variable ensures the plugin is loaded only once. 16 | 17 | " This variable ensures the plugin is loaded only once. 18 | let g:loaded_vim_bito = 1 19 | 20 | " --------------------------------------------------------------------------------------------------- 21 | " Set default configuration and paths. 22 | " --------------------------------------------------------------------------------------------------- 23 | let g:bito_buffer_name_prefix = get(g:, 'bito_buffer_name_prefix', 'bito_history_') 24 | 25 | " Get the directory path where this Vim script resides. Useful when accessing 26 | " other related files or templates in the same directory. 27 | 28 | " Get the directory path where this Vim script resides. Useful when accessing 29 | " other related files or templates in the same directory. 30 | let g:vim_bito_plugin_path = fnamemodify(expand(':p:h'), ':p') 31 | 32 | " Set default prompts for Bito interactions. These can be customized by users 33 | " in their vimrc to suit their needs. 34 | 35 | " Set default prompts for Bito interactions. These can be customized by users 36 | " in their vimrc to suit their needs. 37 | let g:vim_bito_path = get(g:, 'vim_bito_path', "bito") 38 | let g:vim_bito_prompt_append = get(g:, 'vim_bito_prompt_append', "") 39 | let g:vim_bito_prompt_generate = get(g:, 'vim_bito_prompt_generate', "Please Generate Code") 40 | let g:vim_bito_prompt_generate_unit = get(g:, 'vim_bito_prompt_generate_unit', "Please Generate Unit Test Code") 41 | let g:vim_bito_prompt_explain = get(g:, 'vim_bito_prompt_explain', "What does that code do") 42 | let g:vim_bito_prompt_generate_comment = get(g:, 'vim_bito_prompt_generate_comment', "Generate a comment for this method, explaining the parameters and output") 43 | let g:vim_bito_prompt_check_performance = get(g:, 'vim_bito_prompt_check_performance', "Check code for performance issues, explain the issues, and rewrite code if possible") 44 | let g:vim_bito_prompt_check = get(g:, 'vim_bito_prompt_check', "Identify potential issues that would find in this code, explain the issues, and rewrite code if possible") 45 | let g:vim_bito_prompt_check_security = get(g:, 'vim_bito_prompt_check_security', "Check code for security issues, explain the issues, and rewrite code if possible") 46 | let g:vim_bito_prompt_readable = get(g:, 'vim_bito_prompt_readable', "Organize the code to be more human readable") 47 | let g:vim_bito_prompt_check_style = get(g:, 'vim_bito_prompt_check_style', "Check code for style issues, explain the issues, and rewrite code if possible") 48 | 49 | " --------------------------------------------------------------------------------------------------- 50 | " Main function to generate code. It prompts the user for context and interfaces with Bito. 51 | " --------------------------------------------------------------------------------------------------- 52 | function! BitoAiGenerate() 53 | " Prompt the user for input. 54 | " Prompt the user for input. 55 | let l:input = input("Bito prompt:") 56 | 57 | " If no input is provided, show an error message and return. 58 | " If no input is provided, show an error message and return. 59 | if l:input == "" 60 | echo "Please Input Context!" 61 | return 62 | endif 63 | 64 | " Execute the Bito command with the 'generate' prompt. 65 | 66 | " Execute the Bito command with the 'generate' prompt. 67 | call BitoAiExec('generate', l:input) 68 | endfunction 69 | 70 | " --------------------------------------------------------------------------------------------------- 71 | " Utility function to work on a selected range, sending it to Bito for processing. 72 | " --------------------------------------------------------------------------------------------------- 73 | function! BitoAiSelected(prompt) 74 | " Get the start and end line numbers of the selected text. 75 | " Get the start and end line numbers of the selected text. 76 | let l:start = getpos("'<")[1] 77 | let l:end = getpos("'>")[1] 78 | 79 | " Fetch the lines from the start to the end of the selection. 80 | 81 | " Fetch the lines from the start to the end of the selection. 82 | let l:lines = getline(l:start, l:end) 83 | let l:text = join(l:lines, "\n") 84 | 85 | " If there's no selected text, simply return. 86 | " If there's no selected text, simply return. 87 | if l:text == "" 88 | return 89 | endif 90 | 91 | " Execute the Bito command using the provided prompt. 92 | 93 | " Execute the Bito command using the provided prompt. 94 | call BitoAiExec(a:prompt, l:text) 95 | endfunction 96 | 97 | " --------------------------------------------------------------------------------------------------- 98 | " Core function to interact with Bito. Manages input files, templates, and the Bito executable. 99 | " --------------------------------------------------------------------------------------------------- 100 | function! BitoAiExec(prompt, input) 101 | " Write the input to a temporary file for Bito to process. 102 | " Write the input to a temporary file for Bito to process. 103 | let l:tempFile = tempname() 104 | call writefile(split(a:input, "\n"), l:tempFile) 105 | 106 | " Load the appropriate Bito template based on the prompt. 107 | 108 | " Load the appropriate Bito template based on the prompt. 109 | let l:common_content = readfile(g:vim_bito_plugin_path . '/templates/common.txt') 110 | if (a:prompt == "generate") 111 | let l:common_content = readfile(g:vim_bito_plugin_path . '/templates/generate.txt') 112 | endif 113 | 114 | " Fetch the correct Bito prompt based on the given argument. 115 | 116 | " Fetch the correct Bito prompt based on the given argument. 117 | if exists('g:vim_bito_prompt_' . a:prompt) 118 | let l:prompt = execute('echo g:vim_bito_prompt_' . a:prompt) . ' ' . g:vim_bito_prompt_append 119 | else 120 | echomsg "Undefined variable: g:vim_bito_prompt_" . a:prompt 121 | return 122 | endif 123 | 124 | " Replace placeholders in the Bito template with the actual values. 125 | " Replace placeholders in the Bito template with the actual values. 126 | let l:replaced_content = [] 127 | for line in l:common_content 128 | let l:replaced_line = substitute(line, '{{:prompt:}}', l:prompt, '') 129 | call add(l:replaced_content, l:replaced_line) 130 | endfor 131 | 132 | " Write the replaced content to another temporary file. 133 | " Write the replaced content to another temporary file. 134 | let l:templatePath = tempname() 135 | call writefile(l:replaced_content, l:templatePath) 136 | 137 | " Run Bito using the constructed templates and the input file. 138 | " Run Bito using the constructed templates and the input file. 139 | let l:cmdList = [g:vim_bito_path, '-p', l:templatePath, '-f', l:tempFile] 140 | if has('nvim') 141 | let job = jobstart(l:cmdList, { 142 | \ 'on_stdout': 'BiAsyncCallback', 143 | \ 'on_exit': 'BitoJobExit', 144 | \ 'stdin': 'null' 145 | \ }) 146 | else 147 | let job = job_start(l:cmdList, { 148 | \ 'out_cb': 'BiAsyncCallback', 149 | \ 'exit_cb': 'BitoJobExit', 150 | \ 'in_io': 'null' 151 | \ }) 152 | endif 153 | endfunction 154 | 155 | 156 | " --------------------------------------------------------------------------------------------------- 157 | " Functions to manage the buffer for Bito outputs. 158 | " --------------------------------------------------------------------------------------------------- 159 | function! s:BitoAiFindBufferNo(job_id) 160 | let l:buf_list = tabpagebuflist() 161 | let l:buf_no = 0 162 | let s:bito_buffer_name = g:bito_buffer_name_prefix . a:job_id 163 | 164 | for buf in l:buf_list 165 | if getbufvar(buf, '&filetype') == 'bito' && bufname(buf) == s:bito_buffer_name 166 | let l:buf_no = buf 167 | break 168 | endif 169 | endfor 170 | 171 | if l:buf_no == 0 172 | exec 'vs ' . s:bito_buffer_name 173 | execute 'set filetype=bito' 174 | setlocal norelativenumber swapfile bufhidden=hide 175 | setlocal buftype=nofile 176 | let l:buf_no = bufnr("%") 177 | endif 178 | 179 | return l:buf_no 180 | endfunction 181 | 182 | let g:bito_jobs_opened = {} 183 | 184 | function! BitoJobExit(job_id, _exit_status, _event_type) 185 | " Navigate to buffer if not opened before 186 | if !has_key(g:bito_jobs_opened, a:job_id) 187 | call s:GotoBufferForJob(a:job_id) 188 | endif 189 | 190 | " Clean up 191 | unlet g:bito_jobs_opened[a:job_id] 192 | endfunction 193 | 194 | function! BiAsyncCallback(job_id, data, ...) 195 | let job_list = s:GetOrCreateJobList() 196 | let current_line_num = get(job_list, a:job_id, 1) 197 | let buf_no = s:BitoAiFindBufferNo(a:job_id) 198 | 199 | " Process data for nvim and vim 200 | if has('nvim') 201 | call s:ProcessDataForNvim(buf_no, a:data, current_line_num) 202 | else 203 | call s:ProcessDataForVim(buf_no, a:data) 204 | endif 205 | 206 | " Track data receipt for the job 207 | let g:bito_jobs_opened[a:job_id] = 1 208 | 209 | " Handle initial data receipt for the job 210 | if g:bito_jobs_opened[a:job_id] == 1 211 | call s:HighlightFirstLine(buf_no) 212 | endif 213 | endfunction 214 | 215 | function! s:GetOrCreateJobList() 216 | if !exists('g:bito_job_list') 217 | let g:bito_job_list = {} 218 | endif 219 | return g:bito_job_list 220 | endfunction 221 | 222 | function! s:GotoBufferForJob(job_id) 223 | let buf_no = s:BitoAiFindBufferNo(a:job_id) 224 | call win_gotoid(bufwinid(buf_no)) 225 | endfunction 226 | 227 | function! s:ProcessDataForNvim(buf_no, data, current_line_num) 228 | " Construct a list of lines 229 | let lines = [getbufline(a:buf_no, current_line_num)[0] . a:data[0]] 230 | call extend(lines, a:data[1:]) 231 | 232 | " Replace and append lines in buffer 233 | call setbufline(a:buf_no, current_line_num, lines) 234 | endfunction 235 | 236 | function! s:ProcessDataForVim(buf_no, data) 237 | call appendbufline(a:buf_no, '$', a:data) 238 | endfunction 239 | 240 | function! s:HighlightFirstLine(buf_no) 241 | call cursor(1, 1) 242 | if has('nvim') 243 | call nvim_buf_add_highlight(a:buf_no, -1, 'Search', 0, 0, -1) 244 | else 245 | call matchadd('Search', '^.*$', 0, -1) 246 | endif 247 | endfunction 248 | 249 | 250 | " --------------------------------------------------------------------------------------------------- 251 | " Vim commands for easy access to Bito functionalities. 252 | " --------------------------------------------------------------------------------------------------- 253 | 254 | " Define a command for generating code with Bito. 255 | " This doesn't take any arguments and is a straightforward way to prompt the user and generate code. 256 | command! -nargs=0 BitoAiGenerate :call BitoAiGenerate() 257 | 258 | " Define a command for generating code with Bito for a selected range in 'unit' mode. 259 | " This uses the range provided by the user to determine the text to use as input for Bito. 260 | 261 | " Define a command for generating code with Bito for a selected range in 'unit' mode. 262 | " This uses the range provided by the user to determine the text to use as input for Bito. 263 | command! -range -nargs=0 BitoAiGenerateUnit :call BitoAiSelected('generate_unit') 264 | 265 | " Define a command for generating code comments with Bito for a selected range. 266 | 267 | " Define a command for generating code comments with Bito for a selected range. 268 | command! -range -nargs=0 BitoAiGenerateComment :call BitoAiSelected('generate_comment') 269 | 270 | " Define a command for checking code with Bito for a selected range. 271 | 272 | " Define a command for checking code with Bito for a selected range. 273 | command! -range -nargs=0 BitoAiCheck :call BitoAiSelected('check') 274 | 275 | " Define a command for checking code security with Bito for a selected range. 276 | 277 | " Define a command for checking code security with Bito for a selected range. 278 | command! -range -nargs=0 BitoAiCheckSecurity :call BitoAiSelected('check_security') 279 | 280 | " Define a command for checking code style with Bito for a selected range. 281 | 282 | " Define a command for checking code style with Bito for a selected range. 283 | command! -range -nargs=0 BitoAiCheckStyle :call BitoAiSelected('check_style') 284 | 285 | " Define a command for checking code performance with Bito for a selected range. 286 | 287 | " Define a command for checking code performance with Bito for a selected range. 288 | command! -range -nargs=0 BitoAiCheckPerformance :call BitoAiSelected('check_performance') 289 | 290 | " Define a command for making code more readable with Bito for a selected range. 291 | 292 | " Define a command for making code more readable with Bito for a selected range. 293 | command! -range -nargs=0 BitoAiReadable :call BitoAiSelected('readable') 294 | 295 | " Define a command for explaining code with Bito for a selected range. 296 | 297 | " Define a command for explaining code with Bito for a selected range. 298 | command! -range -nargs=0 BitoAiExplain :call BitoAiSelected('explain') 299 | --------------------------------------------------------------------------------