├── .github ├── CONTRIBUTING └── FUNDING.yml ├── LICENSE ├── README.md └── plugin └── chatgpt.vim /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | } 174 | 175 | max_tokens = int(vim.eval('g:chat_gpt_max_tokens')) 176 | model = str(vim.eval('g:chat_gpt_model')) 177 | temperature = float(vim.eval('g:chat_gpt_temperature')) 178 | lang = str(vim.eval('g:chat_gpt_lang')) 179 | resp = f" And respond in {lang}." if lang != 'None' else "" 180 | 181 | personas = dict(vim.eval('g:gpt_personas')) 182 | persona = str(vim.eval('g:chat_persona')) 183 | 184 | systemCtx = {"role": "system", "content": f"{personas[persona]} {resp}"} 185 | messages = [] 186 | session_id = 'gpt-persistent-session' if int(vim.eval('exists("g:chat_gpt_session_mode") ? g:chat_gpt_session_mode : 1')) == 1 else None 187 | 188 | # If session id exists and is in vim buffers 189 | if session_id: 190 | buffer = [] 191 | 192 | for b in vim.buffers: 193 | # If the buffer name matches the session id 194 | if session_id in b.name: 195 | buffer = b[:] 196 | break 197 | 198 | # Read the lines from the buffer 199 | history = "\n".join(buffer).split('\n\n>>>') 200 | history.reverse() 201 | 202 | # Adding messages to history until token limit is reached 203 | token_count = token_limits.get(model, 4097) - max_tokens - len(prompt) - len(str(systemCtx)) 204 | 205 | for line in history: 206 | if ':\n' in line: 207 | role, message = line.split(":\n") 208 | 209 | token_count -= len(message) 210 | 211 | if token_count > 0: 212 | messages.insert(0, { 213 | "role": role.lower(), 214 | "content": message 215 | }) 216 | 217 | if session_id: 218 | content = '\n\n>>>User:\n' + prompt + '\n\n>>>Assistant:\n'.replace("'", "''") 219 | 220 | vim.command("call DisplayChatGPTResponse('{0}', '', '{1}')".format(content.replace("'", "''"), session_id)) 221 | vim.command("redraw") 222 | 223 | messages.append({"role": "user", "content": prompt}) 224 | messages.insert(0, systemCtx) 225 | 226 | try: 227 | client = create_client() 228 | response = client.chat.completions.create( 229 | model=model, 230 | messages=messages, 231 | temperature=temperature, 232 | max_tokens=max_tokens, 233 | stream=True 234 | ) 235 | 236 | # Iterate through the response chunks 237 | for chunk in response: 238 | # newer Azure API responses contain empty chunks in the first streamed 239 | # response 240 | if not chunk.choices: 241 | continue 242 | 243 | chunk_session_id = session_id if session_id else chunk.id 244 | choice = chunk.choices[0] 245 | finish_reason = choice.finish_reason 246 | 247 | # Call DisplayChatGPTResponse with the finish_reason or content 248 | if finish_reason: 249 | vim.command("call DisplayChatGPTResponse('', '{0}', '{1}')".format(finish_reason.replace("'", "''"), chunk_session_id)) 250 | elif choice.delta: 251 | content = choice.delta.content 252 | vim.command("call DisplayChatGPTResponse('{0}', '', '{1}')".format(content.replace("'", "''"), chunk_session_id)) 253 | 254 | vim.command("redraw") 255 | except Exception as e: 256 | print("Error:", str(e)) 257 | 258 | chat_gpt(vim.eval('a:prompt')) 259 | EOF 260 | endfunction 261 | 262 | " Function to send highlighted code to ChatGPT 263 | function! SendHighlightedCodeToChatGPT(ask, context) abort 264 | let save_cursor = getcurpos() 265 | let [current_line, current_col] = getcurpos()[1:2] 266 | 267 | " Save the current yank register and its type 268 | let save_reg = @@ 269 | let save_regtype = getregtype('@') 270 | 271 | let [line_start, col_start] = getpos("'<")[1:2] 272 | let [line_end, col_end] = getpos("'>")[1:2] 273 | 274 | " Check if a selection is made and if current position is within the selection 275 | if (col_end - col_start > 0 || line_end - line_start > 0) && 276 | \ (current_line == line_start && current_col == col_start || 277 | \ current_line == line_end && current_col == col_end) 278 | 279 | let current_line_start = line_start 280 | let current_line_end = line_end 281 | 282 | if current_line_start == line_start && current_line_end == line_end 283 | execute 'normal! ' . line_start . 'G' . col_start . '|v' . line_end . 'G' . col_end . '|y' 284 | let yanked_text = '```' . &syntax . "\n" . @@ . "\n" . '```' 285 | else 286 | let yanked_text = '' 287 | endif 288 | else 289 | let yanked_text = '' 290 | endif 291 | 292 | let prompt = a:context . ' ' . "\n" 293 | 294 | " Include yanked_text in the prompt if it's not empty 295 | if !empty(yanked_text) 296 | let prompt .= yanked_text . "\n" 297 | endif 298 | 299 | echo a:ask 300 | if has_key(g:prompt_templates, a:ask) 301 | let prompt = g:prompt_templates[a:ask] . "\n" . prompt 302 | endif 303 | 304 | call ChatGPT(prompt) 305 | 306 | " Restore the original yank register 307 | let @@ = save_reg 308 | call setreg('@', save_reg, save_regtype) 309 | 310 | let curpos = getcurpos() 311 | call setpos("'<", curpos) 312 | call setpos("'>", curpos) 313 | call setpos('.', save_cursor) 314 | endfunction 315 | 316 | " Function to generate a commit message 317 | function! GenerateCommitMessage() 318 | " Save the current position and yank register 319 | let save_cursor = getcurpos() 320 | let save_reg = @@ 321 | let save_regtype = getregtype('@') 322 | 323 | " Yank the entire buffer into the unnamed register 324 | normal! ggVGy 325 | 326 | " Send the yanked text to ChatGPT 327 | let yanked_text = @@ 328 | 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 329 | let g:chat_gpt_session_mode = 0 330 | 331 | call ChatGPT(prompt) 332 | endfunction 333 | 334 | " Menu for ChatGPT 335 | function! s:ChatGPTMenuSink(id, choice) 336 | call popup_hide(a:id) 337 | let choices = {} 338 | 339 | for index in range(len(g:promptKeys)) 340 | let choices[index+1] = g:promptKeys[index] 341 | endfor 342 | 343 | if a:choice > 0 && a:choice <= len(g:promptKeys) 344 | call SendHighlightedCodeToChatGPT(choices[a:choice], input('Prompt > ')) 345 | endif 346 | endfunction 347 | 348 | function! s:ChatGPTMenuFilter(id, key) 349 | 350 | if a:key > 0 && a:key <= len(g:promptKeys) 351 | call s:ChatGPTMenuSink(a:id, a:key) 352 | else " No shortcut, pass to generic filter 353 | return popup_filter_menu(a:id, a:key) 354 | endif 355 | endfunction 356 | 357 | function! ChatGPTMenu() range 358 | echo a:firstline. a:lastline 359 | let menu_choices = [] 360 | 361 | for index in range(len(g:promptKeys)) 362 | call add(menu_choices, string(index + 1) . ". " . g:promptKeys[index]) 363 | endfor 364 | 365 | call popup_menu(menu_choices, #{ 366 | \ pos: 'topleft', 367 | \ line: 'cursor', 368 | \ col: 'cursor+2', 369 | \ title: ' Chat GPT ', 370 | \ highlight: 'question', 371 | \ borderchars: ['─', '│', '─', '│', '╭', '╮', '╯', '╰'], 372 | \ callback: function('s:ChatGPTMenuSink'), 373 | \ border: [], 374 | \ cursorline: 1, 375 | \ padding: [0,1,0,1], 376 | \ filter: function('s:ChatGPTMenuFilter'), 377 | \ mapping: 0, 378 | \ }) 379 | endfunction 380 | 381 | vnoremap (chatgpt-menu) :call ChatGPTMenu() 382 | 383 | function! Capitalize(str) 384 | return toupper(strpart(a:str, 0, 1)) . tolower(strpart(a:str, 1)) 385 | endfunction 386 | 387 | for i in range(len(g:promptKeys)) 388 | execute 'command! -range -nargs=? ' . Capitalize(g:promptKeys[i]) . " call SendHighlightedCodeToChatGPT('" . g:promptKeys[i] . "',)" 389 | endfor 390 | 391 | command! GenerateCommit call GenerateCommitMessage() 392 | 393 | function! SetPersona(persona) 394 | let personas = keys(g:gpt_personas) 395 | if index(personas, a:persona) != -1 396 | echo 'Persona set to: ' . a:persona 397 | let g:chat_persona = a:persona 398 | else 399 | let g:chat_persona = 'default' 400 | echo 'Persona set to default, not found ' . a:persona 401 | end 402 | endfunction 403 | 404 | 405 | command! -nargs=1 GptBe call SetPersona() 406 | --------------------------------------------------------------------------------