├── .github ├── FUNDING.yml └── CONTRIBUTING ├── LICENSE ├── README.md └── plugin └── chatgpt.vim /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [codercooke] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | ### How to Contribute to codercooke/vim-chatgpt 2 | 3 | 1. **Fork the Repository:** 4 | - Click on the "Fork" button at the top right corner of the repository page. This will create a copy of the repository in your GitHub account. 5 | 6 | 2. **Clone the Forked Repository:** 7 | - Open your terminal and run the following command to clone the repository to your local machine: 8 | ``` 9 | git clone https://github.com/codercooke/vim-chatgpt.git 10 | ``` 11 | 12 | 3. **Create a Branch:** 13 | - Create a new branch to work on your changes: 14 | ``` 15 | git checkout -b your-feature-branch 16 | ``` 17 | 18 | 4. **Make Changes:** 19 | - Make the necessary changes in your local repository using your preferred text editor. 20 | 21 | 5. **Commit Changes:** 22 | - Commit your changes with a clear and concise message: 23 | ``` 24 | git commit -m "Description of your changes" 25 | ``` 26 | 27 | 6. **Push Changes:** 28 | - Push your changes to your forked repository on GitHub: 29 | ``` 30 | git push origin your-feature-branch 31 | ``` 32 | 33 | 7. **Open a Pull Request:** 34 | - Visit the original repository on GitHub and click on the "New Pull Request" button. 35 | - Choose your branch and provide a descriptive title and summary of your changes. 36 | - Submit the pull request. 37 | 38 | 8. **Review Process:** 39 | - We'll review your changes and provide feedback. 40 | - Make necessary adjustments based on feedback. 41 | 42 | 9. **Requirements for Code Changes:** 43 | - Ensure the code works in vanilla Vim (not just neovim). 44 | - Preserve existing features. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT Vim Plugin 2 | 3 | This Vim plugin brings the power of OpenAI's ChatGPT API into your Vim editor, enabling you to request code explanations or improvements directly within Vim. With this plugin, you can effortlessly highlight code snippets and ask ChatGPT to explain, review, or rewrite them, with the option to include additional context for better results. 4 | 5 | ## Prerequisites 6 | 7 | 1) Vim with Python3 support. 8 | 1) A ChatGPT API key from OpenAI. 9 | 10 | ## Installation 11 | Add your ChatGPT API key to your environment: 12 | https://platform.openai.com/account/api-keys 13 | 14 | ### Setup your environment 15 | To set up your environment, you can export the OPENAI_API_KEY variable in your terminal: 16 | ```bash 17 | export OPENAI_API_KEY='your-api-key-here' 18 | ``` 19 | And more useful env is proxy: 20 | ```bash 21 | export OPENAI_PROXY="http://localhost:1087" # with proxy 22 | # or 23 | export OPENAI_API_BASE='https://openai.xxx.cloud/v1' # refer: https://github.com/egoist/openai-proxy 24 | ``` 25 | 26 | Alternatively, you can add the following lines to your `.vimrc` file to set up the chatgpt plugin for Vim: 27 | ```vim 28 | let g:openai_api_key='your-api-key-here' 29 | ``` 30 | 31 | To install the chatgpt plugin, simply copy the `chatgpt.vim` file to your Vim plugin directory. If you're using [vim-pathogen](https://github.com/tpope/vim-pathogen), you can simply add the `chatgpt` directory to your `bundle` directory. 32 | 33 | Finally, to install the `openai` Python module, you can use pip: 34 | ```bash 35 | pip install openai 36 | ``` 37 | [Detailed Direction For Installation](https://github.com/CoderCookE/vim-chatgpt/issues/4#issuecomment-1704607737) 38 | 39 | Additionally, for Azure gpt user: 40 | ``` 41 | let g:api_type = 'azure' 42 | let g:chat_gpt_key = 'your_azure_chatgpt_api' 43 | let g:azure_endpoint = 'your_azure_endpoint' 44 | let g:azure_deployment = 'your_azure_deployment' 45 | let g:azure_api_version = '2023-03-15-preview' 46 | ``` 47 | 48 | ## Customization 49 | In your `.vimrc` file you set the following options 50 | 51 | ```vim 52 | let g:chat_gpt_max_tokens=2000 53 | let g:chat_gpt_model='gpt-4o' 54 | let g:chat_gpt_session_mode=0 55 | let g:chat_gpt_temperature = 0.7 56 | let g:chat_gpt_lang = 'Chinese' 57 | let g:chat_gpt_split_direction = 'vertical' 58 | let g:split_ratio=4 59 | ``` 60 | 61 | - g:chat_gpt_max_tokens: This option allows you to set the maximum number of tokens (words or characters) that the ChatGPT API will return in its response. By default, it is set to 2000 tokens. You can adjust this value based on your needs and preferences. 62 | - g:chat_gpt_model: This option allows you to specify the ChatGPT model you'd like to use. By default, it is set to 'gpt-4o' with a token limit of 4097, If you prefer to use a different model, such as {"gpt-3.5-turbo-16k": 16385, "gpt-4": 8192, "gpt-4-32k": 32768}, simply change the value to the desired model name. Note that using a different model may affect the quality of the results and API usage costs. 63 | - g:chat_gpt_session_mode: The customization allows you to maintain a persistent session with GPT, enabling a more interactive and coherent conversation with the AI model. By default, it is set to 1 which is on, 64 | - g:chat_gpt_temperature: Controls the randomness of the AI's responses. A higher temperature value (close to 1.0) will be more random, lower 0.1 will be less random, 65 | - g:chat_gpt_lang: Answer in certain langusage, such as Chinese, 66 | - g:chat_gpt_split_direction: Controls how to open splits, 'vertical' or 'horizontal'. Plugin opens horizontal splits by default. 67 | By customizing these options, you can tailor the ChatGPT Vim Plugin to better suit your specific needs and preferences. 68 | - g:split_ratio: Control the split window size. If set 4, the window size will be 1/4. 69 | 70 | ## Usage 71 | 72 | The plugin provides several commands to interact with ChatGPT: 73 | 74 | - `Ask`: Ask a question 75 | - `Rewrite`: Ask the model to rewrite a code snippet more idiomatically 76 | - `Review`: Request a code review 77 | - `Document`: Request documentation for a code snippet 78 | - `Explain`: Ask the model to explain how a code snippet works 79 | - `Test`: Ask the model to write a test for a code snippet 80 | - `Fix`: Ask the model to fix an error in a code snippet 81 | 82 | Each command takes a context as an argument, which can be any text describing the problem or question more specifically. 83 | 84 | ## Example 85 | 86 | To ask the model to review a code snippet, visually select the code and execute the `Review` command: 87 | 88 | ```vim 89 | :'<,'>Review 'Can you review this code for me?' 90 | ``` 91 | 92 | The model's response will be displayed in a new buffer. 93 | 94 | You can also use `GenerateCommit` command to generate a commit message for the current buffer. 95 | 96 | ## Customization 97 | 98 | ### Custom Personas 99 | 100 | To introduce custom personas into the system context, simply define them in your `vimrc` file: 101 | 102 | ```vim 103 | let g:chat_gpt_custom_persona = {'neptune': 'You are an expert in all things Graph databases'} 104 | ``` 105 | 106 | With the custom persona defined, you can switch to it using the following command: 107 | 108 | ```vim 109 | :GptBe neptune 110 | ``` 111 | 112 | If you try to switch to a non-existent persona, the plugin will default to the preconfigured `default` persona. 113 | 114 | You can also set a persona to be loaded by default when Vim starts, by setting it in your `vimrc`: 115 | 116 | ```vim 117 | let g:chat_persona='neptune' 118 | ``` 119 | 120 | ### Commands 121 | 122 | You can add custom prompt templates using the `chat_gpt_custom_prompts` variable. This should be a dictionary mapping prompt keys to prompt templates. 123 | 124 | For example, to add a 'debug' prompt, you could do: 125 | 126 | ```vim 127 | let g:chat_gpt_custom_prompts = {'debug': 'Can you help me debug this code?'} 128 | ``` 129 | 130 | Afterwards, you can use the `Debug` command like any other command: 131 | 132 | ```vim 133 | :'<,'>Debug 'I am encountering an issue where...' 134 | ``` 135 | 136 | ## Mappings 137 | 138 | This plugin exposes a binding to open a menu for options on a visual selecition. You can map it like this: 139 | ``` 140 | vmap 0 (chatgpt-menu) 141 | ``` 142 | 143 | ### Example usage: 144 | 1) Enter visual mode by pressing V. 145 | 1) Select the lines of code you want to explain, review, or rewrite. 146 | 1) Type `:Explain`, `:Review`, or `:Rewrite`, `:Fix`, `:Test` and press Enter. 147 | 148 | ## Notes 149 | This plugin is not affiliated with or endorsed by OpenAI. You are responsible for managing your API usage and any associated costs when using this plugin. 150 | 151 | # Keywords 152 | - Vim plugin 153 | - Chat GPT 154 | - ChatGPT 155 | - Code assistance 156 | - Programming help 157 | - Code explanations 158 | - Code review 159 | - Code documentation 160 | - Code rewrites 161 | - Test generation 162 | - Code fixes 163 | - Commit messages 164 | - OpenAI 165 | - ChatGPT API 166 | - Python module 167 | - Vim integration 168 | -------------------------------------------------------------------------------- /plugin/chatgpt.vim: -------------------------------------------------------------------------------- 1 | " ChatGPT Vim Plugin 2 | " 3 | " Ensure Python3 is available 4 | if !has('python3') 5 | echo "Python 3 support is required for ChatGPT plugin" 6 | finish 7 | endif 8 | 9 | " Set default values for Vim variables if they don't exist 10 | if !exists("g:chat_gpt_max_tokens") 11 | let g:chat_gpt_max_tokens = 2000 12 | endif 13 | 14 | if !exists("g:chat_gpt_temperature") 15 | let g:chat_gpt_temperature = 0.7 16 | endif 17 | 18 | if !exists("g:chat_gpt_model") 19 | let g:chat_gpt_model = 'gpt-4o' 20 | endif 21 | 22 | if !exists("g:chat_gpt_lang") 23 | let g:chat_gpt_lang = v:none 24 | endif 25 | 26 | if !exists("g:chat_gpt_split_direction") 27 | let g:chat_gpt_split_direction = 'horizontal' 28 | endif 29 | 30 | if !exists("g:split_ratio") 31 | let g:split_ratio = 3 32 | endif 33 | 34 | if !exists("g:chat_persona") 35 | let g:chat_persona = 'default' 36 | endif 37 | 38 | let code_wrapper_snippet = "Given the following code snippet: " 39 | let g:prompt_templates = { 40 | \ 'ask': '', 41 | \ 'rewrite': 'Can you rewrite this more idiomatically? ' . code_wrapper_snippet, 42 | \ 'review': 'Can you provide a code review? ' . code_wrapper_snippet, 43 | \ 'document': 'Return documentation following language pattern conventions. ' . code_wrapper_snippet, 44 | \ 'explain': 'Can you explain how this works? ' . code_wrapper_snippet, 45 | \ 'test': 'Can you write a test? ' . code_wrapper_snippet, 46 | \ 'fix': 'I have an error I need you to fix. ' . code_wrapper_snippet, 47 | \} 48 | 49 | if exists('g:chat_gpt_custom_prompts') 50 | call extend(g:prompt_templates, g:chat_gpt_custom_prompts) 51 | endif 52 | 53 | let g:promptKeys = keys(g:prompt_templates) 54 | 55 | let g:gpt_personas = { 56 | \ "default": 'You are a helpful expert programmer we are working together to solve complex coding challenges, and I need your help. Please make sure to wrap all code blocks in ``` annotate the programming language you are using.', 57 | \} 58 | 59 | if exists('g:chat_gpt_custom_persona') 60 | call extend(g:gpt_personas, g:chat_gpt_custom_persona) 61 | endif 62 | " 63 | " Function to show ChatGPT responses in a new buffer 64 | function! DisplayChatGPTResponse(response, finish_reason, chat_gpt_session_id) 65 | let response = a:response 66 | let finish_reason = a:finish_reason 67 | 68 | let chat_gpt_session_id = a:chat_gpt_session_id 69 | 70 | if !bufexists(chat_gpt_session_id) 71 | if g:chat_gpt_split_direction ==# 'vertical' 72 | silent execute winwidth(0)/g:split_ratio.'vnew '. chat_gpt_session_id 73 | else 74 | silent execute winheight(0)/g:split_ratio.'new '. chat_gpt_session_id 75 | endif 76 | call setbufvar(chat_gpt_session_id, '&buftype', 'nofile') 77 | call setbufvar(chat_gpt_session_id, '&bufhidden', 'hide') 78 | call setbufvar(chat_gpt_session_id, '&swapfile', 0) 79 | setlocal modifiable 80 | setlocal wrap 81 | setlocal linebreak 82 | call setbufvar(chat_gpt_session_id, '&ft', 'markdown') 83 | call setbufvar(chat_gpt_session_id, '&syntax', 'markdown') 84 | endif 85 | 86 | if bufwinnr(chat_gpt_session_id) == -1 87 | if g:chat_gpt_split_direction ==# 'vertical' 88 | execute winwidth(0)/g:split_ratio.'vsplit ' . chat_gpt_session_id 89 | else 90 | execute winheight(0)/g:split_ratio.'split ' . chat_gpt_session_id 91 | endif 92 | endif 93 | 94 | let last_lines = getbufline(chat_gpt_session_id, '$') 95 | let last_line = empty(last_lines) ? '' : last_lines[-1] 96 | 97 | let new_lines = substitute(last_line . response, '\n', '\r\n\r', 'g') 98 | let lines = split(new_lines, '\n') 99 | 100 | let clean_lines = [] 101 | for line in lines 102 | call add(clean_lines, substitute(line, '\r', '', 'g')) 103 | endfor 104 | 105 | call setbufline(chat_gpt_session_id, '$', clean_lines) 106 | 107 | execute bufwinnr(chat_gpt_session_id) . 'wincmd w' 108 | " Move the viewport to the bottom of the buffer 109 | normal! G 110 | call cursor('$', 1) 111 | 112 | if finish_reason != '' 113 | wincmd p 114 | endif 115 | endfunction 116 | 117 | " Function to interact with ChatGPT 118 | function! ChatGPT(prompt) abort 119 | python3 << EOF 120 | 121 | import sys 122 | import vim 123 | import os 124 | 125 | try: 126 | from openai import AzureOpenAI, OpenAI 127 | except ImportError: 128 | print("Error: openai module not found. Please install with Pip and ensure equality of the versions given by :!python3 -V, and :python3 import sys; print(sys.version)") 129 | raise 130 | 131 | def safe_vim_eval(expression): 132 | try: 133 | return vim.eval(expression) 134 | except vim.error: 135 | return None 136 | 137 | def create_client(): 138 | api_type = safe_vim_eval('g:api_type') 139 | api_key = os.getenv('OPENAI_API_KEY') or safe_vim_eval('g:chat_gpt_key') or safe_vim_eval('g:openai_api_key') 140 | openai_base_url = os.getenv('OPENAI_PROXY') or os.getenv('OPENAI_API_BASE') or safe_vim_eval('g:openai_base_url') 141 | 142 | if api_type == 'azure': 143 | azure_endpoint = safe_vim_eval('g:azure_endpoint') 144 | azure_api_version = safe_vim_eval('g:azure_api_version') 145 | azure_deployment = safe_vim_eval('g:azure_deployment') 146 | assert azure_endpoint and azure_api_version and azure_deployment, "azure_endpoint, azure_api_version and azure_deployment not set property, please check your settings in `vimrc` or `enviroment`." 147 | assert api_key, "api_key not set, please configure your `openai_api_key` in your `vimrc` or `enviroment`" 148 | client = AzureOpenAI( 149 | azure_endpoint=azure_endpoint, 150 | azure_deployment=azure_deployment, 151 | api_key=api_key, 152 | api_version=azure_api_version, 153 | ) 154 | else: 155 | client = OpenAI( 156 | base_url=openai_base_url, 157 | api_key=api_key, 158 | ) 159 | return client 160 | 161 | 162 | def chat_gpt(prompt): 163 | token_limits = { 164 | "gpt-3.5-turbo": 4097, 165 | "gpt-3.5-turbo-16k": 16385, 166 | "gpt-3.5-turbo-1106": 16385, 167 | "gpt-4": 8192, 168 | "gpt-4-turbo": 128000, 169 | "gpt-4-turbo-preview": 128000, 170 | "gpt-4-32k": 32768, 171 | "gpt-4o": 128000, 172 | "gpt-4o-mini": 128000, 173 | "o1": 200000, 174 | "o3": 200000, 175 | "o3-mini": 200000, 176 | "o4-mini": 200000, 177 | } 178 | 179 | max_tokens = int(vim.eval('g:chat_gpt_max_tokens')) 180 | model = str(vim.eval('g:chat_gpt_model')) 181 | temperature = float(vim.eval('g:chat_gpt_temperature')) 182 | lang = str(vim.eval('g:chat_gpt_lang')) 183 | resp = f" And respond in {lang}." if lang != 'None' else "" 184 | 185 | personas = dict(vim.eval('g:gpt_personas')) 186 | persona = str(vim.eval('g:chat_persona')) 187 | 188 | systemCtx = {"role": "system", "content": f"{personas[persona]} {resp}"} 189 | messages = [] 190 | session_id = 'gpt-persistent-session' if int(vim.eval('exists("g:chat_gpt_session_mode") ? g:chat_gpt_session_mode : 1')) == 1 else None 191 | 192 | # If session id exists and is in vim buffers 193 | if session_id: 194 | buffer = [] 195 | 196 | for b in vim.buffers: 197 | # If the buffer name matches the session id 198 | if session_id in b.name: 199 | buffer = b[:] 200 | break 201 | 202 | # Read the lines from the buffer 203 | history = "\n".join(buffer).split('\n\n>>>') 204 | history.reverse() 205 | 206 | # Adding messages to history until token limit is reached 207 | token_count = token_limits.get(model, 4097) - max_tokens - len(prompt) - len(str(systemCtx)) 208 | 209 | for line in history: 210 | if ':\n' in line: 211 | role, message = line.split(":\n") 212 | 213 | token_count -= len(message) 214 | 215 | if token_count > 0: 216 | messages.insert(0, { 217 | "role": role.lower(), 218 | "content": message 219 | }) 220 | 221 | if session_id: 222 | content = '\n\n>>>User:\n' + prompt + '\n\n>>>Assistant:\n'.replace("'", "''") 223 | 224 | vim.command("call DisplayChatGPTResponse('{0}', '', '{1}')".format(content.replace("'", "''"), session_id)) 225 | vim.command("redraw") 226 | 227 | messages.append({"role": "user", "content": prompt}) 228 | messages.insert(0, systemCtx) 229 | 230 | try: 231 | client = create_client() 232 | chat_parameters = { 233 | 'model':model, 234 | 'messages':messages, 235 | 'stream':True 236 | } 237 | if model.startswith('gpt-'): 238 | chat_parameters.update( 239 | temperature=temperature, 240 | max_tokens=max_tokens 241 | ) 242 | else: 243 | chat_parameters.update( 244 | max_completion_tokens=max_tokens 245 | ) 246 | response = client.chat.completions.create(**chat_parameters) 247 | 248 | # Iterate through the response chunks 249 | for chunk in response: 250 | # newer Azure API responses contain empty chunks in the first streamed 251 | # response 252 | if not chunk.choices: 253 | continue 254 | 255 | chunk_session_id = session_id if session_id else chunk.id 256 | choice = chunk.choices[0] 257 | finish_reason = choice.finish_reason 258 | 259 | # Call DisplayChatGPTResponse with the finish_reason or content 260 | if finish_reason: 261 | vim.command("call DisplayChatGPTResponse('', '{0}', '{1}')".format(finish_reason.replace("'", "''"), chunk_session_id)) 262 | elif choice.delta: 263 | content = choice.delta.content 264 | vim.command("call DisplayChatGPTResponse('{0}', '', '{1}')".format(content.replace("'", "''"), chunk_session_id)) 265 | 266 | vim.command("redraw") 267 | except Exception as e: 268 | print("Error:", str(e)) 269 | 270 | chat_gpt(vim.eval('a:prompt')) 271 | EOF 272 | endfunction 273 | 274 | " Function to send highlighted code to ChatGPT 275 | function! SendHighlightedCodeToChatGPT(ask, context) abort 276 | let save_cursor = getcurpos() 277 | let [current_line, current_col] = getcurpos()[1:2] 278 | 279 | " Save the current yank register and its type 280 | let save_reg = @@ 281 | let save_regtype = getregtype('@') 282 | 283 | let [line_start, col_start] = getpos("'<")[1:2] 284 | let [line_end, col_end] = getpos("'>")[1:2] 285 | 286 | " Check if a selection is made and if current position is within the selection 287 | if (col_end - col_start > 0 || line_end - line_start > 0) && 288 | \ (current_line == line_start && current_col == col_start || 289 | \ current_line == line_end && current_col == col_end) 290 | 291 | let current_line_start = line_start 292 | let current_line_end = line_end 293 | 294 | if current_line_start == line_start && current_line_end == line_end 295 | execute 'normal! ' . line_start . 'G' . col_start . '|v' . line_end . 'G' . col_end . '|y' 296 | let yanked_text = '```' . &syntax . "\n" . @@ . "\n" . '```' 297 | else 298 | let yanked_text = '' 299 | endif 300 | else 301 | let yanked_text = '' 302 | endif 303 | 304 | let prompt = a:context . ' ' . "\n" 305 | 306 | " Include yanked_text in the prompt if it's not empty 307 | if !empty(yanked_text) 308 | let prompt .= yanked_text . "\n" 309 | endif 310 | 311 | echo a:ask 312 | if has_key(g:prompt_templates, a:ask) 313 | let prompt = g:prompt_templates[a:ask] . "\n" . prompt 314 | endif 315 | 316 | call ChatGPT(prompt) 317 | 318 | " Restore the original yank register 319 | let @@ = save_reg 320 | call setreg('@', save_reg, save_regtype) 321 | 322 | let curpos = getcurpos() 323 | call setpos("'<", curpos) 324 | call setpos("'>", curpos) 325 | call setpos('.', save_cursor) 326 | endfunction 327 | 328 | " Function to generate a commit message 329 | function! GenerateCommitMessage() 330 | " Save the current position and yank register 331 | let save_cursor = getcurpos() 332 | let save_reg = @@ 333 | let save_regtype = getregtype('@') 334 | 335 | " Yank the entire buffer into the unnamed register 336 | normal! ggVGy 337 | 338 | " Send the yanked text to ChatGPT 339 | let yanked_text = @@ 340 | let prompt = 'I have the following code changes, can you write a helpful commit message, including a short title? Only respond with the commit message' . "\n" . yanked_text 341 | let g:chat_gpt_session_mode = 0 342 | 343 | call ChatGPT(prompt) 344 | endfunction 345 | 346 | " Menu for ChatGPT 347 | function! s:ChatGPTMenuSink(id, choice) 348 | call popup_hide(a:id) 349 | let choices = {} 350 | 351 | for index in range(len(g:promptKeys)) 352 | let choices[index+1] = g:promptKeys[index] 353 | endfor 354 | 355 | if a:choice > 0 && a:choice <= len(g:promptKeys) 356 | call SendHighlightedCodeToChatGPT(choices[a:choice], input('Prompt > ')) 357 | endif 358 | endfunction 359 | 360 | function! s:ChatGPTMenuFilter(id, key) 361 | 362 | if a:key > 0 && a:key <= len(g:promptKeys) 363 | call s:ChatGPTMenuSink(a:id, a:key) 364 | else " No shortcut, pass to generic filter 365 | return popup_filter_menu(a:id, a:key) 366 | endif 367 | endfunction 368 | 369 | function! ChatGPTMenu() range 370 | echo a:firstline. a:lastline 371 | let menu_choices = [] 372 | 373 | for index in range(len(g:promptKeys)) 374 | call add(menu_choices, string(index + 1) . ". " . g:promptKeys[index]) 375 | endfor 376 | 377 | call popup_menu(menu_choices, #{ 378 | \ pos: 'topleft', 379 | \ line: 'cursor', 380 | \ col: 'cursor+2', 381 | \ title: ' Chat GPT ', 382 | \ highlight: 'question', 383 | \ borderchars: ['─', '│', '─', '│', '╭', '╮', '╯', '╰'], 384 | \ callback: function('s:ChatGPTMenuSink'), 385 | \ border: [], 386 | \ cursorline: 1, 387 | \ padding: [0,1,0,1], 388 | \ filter: function('s:ChatGPTMenuFilter'), 389 | \ mapping: 0, 390 | \ }) 391 | endfunction 392 | 393 | vnoremap (chatgpt-menu) :call ChatGPTMenu() 394 | 395 | function! Capitalize(str) 396 | return toupper(strpart(a:str, 0, 1)) . tolower(strpart(a:str, 1)) 397 | endfunction 398 | 399 | for i in range(len(g:promptKeys)) 400 | execute 'command! -range -nargs=? ' . Capitalize(g:promptKeys[i]) . " call SendHighlightedCodeToChatGPT('" . g:promptKeys[i] . "',)" 401 | endfor 402 | 403 | command! GenerateCommit call GenerateCommitMessage() 404 | 405 | function! SetPersona(persona) 406 | let personas = keys(g:gpt_personas) 407 | if index(personas, a:persona) != -1 408 | echo 'Persona set to: ' . a:persona 409 | let g:chat_persona = a:persona 410 | else 411 | let g:chat_persona = 'default' 412 | echo 'Persona set to default, not found ' . a:persona 413 | end 414 | endfunction 415 | 416 | 417 | command! -nargs=1 GptBe call SetPersona() 418 | --------------------------------------------------------------------------------