Skip to content
Open
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
158 changes: 70 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,8 @@
<p align="center">
<img width="300" alt="Windsurf" src="windsurf.png"/>
</p>

---

[![Discord](https://img.shields.io/discord/1027685395649015980?label=community&color=5865F2&logo=discord&logoColor=FFFFFF)](https://discord.gg/3XFf78nAx5)
[![Twitter Follow](https://img.shields.io/badge/style--blue?style=social&logo=twitter&label=Follow%20%40windsurf_ai)](https://twitter.com/intent/follow?screen_name=windsurf_ai)
![License](https://img.shields.io/github/license/Exafunction/windsurf.vim)
[![Docs](https://img.shields.io/badge/Windsurf%20Docs-09B6A2)](https://docs.windsurf.com)
[![Canny Board](https://img.shields.io/badge/Feature%20Requests-6b69ff)](https://windsurf.canny.io/feature-requests/)
[![built with Windsurf](https://windsurf.com/badges/main)](https://windsurf.com?repo_name=exafunction%2Fwindsurf.vim)

[![Visual Studio](https://img.shields.io/visual-studio-marketplace/i/Codeium.codeium?label=Visual%20Studio&logo=visualstudio)](https://marketplace.visualstudio.com/items?itemName=Codeium.codeium)
[![JetBrains](https://img.shields.io/jetbrains/plugin/d/20540?label=JetBrains)](https://plugins.jetbrains.com/plugin/20540-codeium/)
[![Open VSX](https://img.shields.io/open-vsx/dt/Codeium/codeium?label=Open%20VSX)](https://open-vsx.org/extension/Codeium/codeium)
[![Google Chrome](https://img.shields.io/chrome-web-store/users/hobjkcpmjhlegmobgonaagepfckjkceh?label=Google%20Chrome&logo=googlechrome&logoColor=FFFFFF)](https://chrome.google.com/webstore/detail/codeium/hobjkcpmjhlegmobgonaagepfckjkceh)

# windsurf.vim
# Gemini.vim

_Free, ultrafast Copilot alternative for Vim and Neovim_

Windsurf autocompletes your code with AI in all major IDEs. We [launched](https://www.windsurf.com/blog/codeium-copilot-alternative-in-vim) this implementation of the Windsurf plugin for Vim and Neovim to bring this modern coding superpower to more developers. Check out our [playground](https://www.windsurf.com/playground) if you want to quickly try out Windsurf online.
Gemini autocompletes your code with AI in all major IDEs. We [launched](https://www.windsurf.com/blog/codeium-copilot-alternative-in-vim) this implementation of the Gemini plugin for Vim and Neovim to bring this modern coding superpower to more developers. Check out our [playground](https://www.windsurf.com/playground) if you want to quickly try out Gemini online.

Contributions are welcome! Feel free to submit pull requests and issues related to the plugin.

Expand All @@ -35,112 +17,112 @@ Contributions are welcome! Feel free to submit pull requests and issues related
1. Install [Vim](https://github.com/vim/vim) (at least 9.0.0185) or [Neovim](https://github.com/neovim/neovim/releases/latest) (at
least 0.6)

2. Install `Exafunction/windsurf.vim` using your vim plugin manager of
2. Install `Exafunction/gemini.vim` using your vim plugin manager of
choice, or manually. See [Installation Options](#-installation-options) below.

3. Run `:Codeium Auth` to set up the plugin and start using Windsurf.
3. Run `:Gemini Auth` to set up the plugin and start using Gemini.

You can run `:help codeium` for a full list of commands and configuration
options, or see [this guide](https://www.windsurf.com/vim_tutorial) for a quick tutorial on how to use Windsurf.
You can run `:help gemini` for a full list of commands and configuration
options, or see [this guide](https://www.gemini.com/vim_tutorial) for a quick tutorial on how to use Gemini.

## 🛠️ Configuration

For a full list of configuration options you can run `:help codeium`.
For a full list of configuration options you can run `:help gemini`.
A few of the most popular options are highlighted below.

### ⌨️ Keybindings

Windsurf provides the following functions to control suggestions:
Gemini provides the following functions to control suggestions:

| Action | Function | Default Binding |
| --------------------------- | ------------------------------ | --------------- |
| Clear current suggestion | `codeium#Clear()` | `<C-]>` |
| Next suggestion | `codeium#CycleCompletions(1)` | `<M-]>` |
| Previous suggestion | `codeium#CycleCompletions(-1)` | `<M-[>` |
| Insert suggestion | `codeium#Accept()` | `<Tab>` |
| Manually trigger suggestion | `codeium#Complete()` | `<M-Bslash>` |
| Accept word from suggestion | `codeium#AcceptNextWord()` | `<C-k>` |
| Accept line from suggestion | `codeium#AcceptNextLine()` | `<C-l>` |
| Clear current suggestion | `gemini#Clear()` | `<C-]>` |
| Next suggestion | `gemini#CycleCompletions(1)` | `<M-]>` |
| Previous suggestion | `gemini#CycleCompletions(-1)` | `<M-[>` |
| Insert suggestion | `gemini#Accept()` | `<Tab>` |
| Manually trigger suggestion | `gemini#Complete()` | `<M-Bslash>` |
| Accept word from suggestion | `gemini#AcceptNextWord()` | `<C-k>` |
| Accept line from suggestion | `gemini#AcceptNextLine()` | `<C-l>` |

Windsurf's default keybindings can be disabled by setting
Gemini's default keybindings can be disabled by setting

```vim
let g:codeium_disable_bindings = 1
let g:gemini_disable_bindings = 1
```

or in Neovim:

```lua
vim.g.codeium_disable_bindings = 1
vim.g.gemini_disable_bindings = 1
```

If you'd like to just disable the `<Tab>` binding, you can alternatively
use the `g:codeium_no_map_tab` option.
use the `g:gemini_no_map_tab` option.

If you'd like to bind the actions above to different keys, this might look something like the following in Vim:

```vim
imap <script><silent><nowait><expr> <C-g> codeium#Accept()
imap <script><silent><nowait><expr> <C-h> codeium#AcceptNextWord()
imap <script><silent><nowait><expr> <C-j> codeium#AcceptNextLine()
imap <C-;> <Cmd>call codeium#CycleCompletions(1)<CR>
imap <C-,> <Cmd>call codeium#CycleCompletions(-1)<CR>
imap <C-x> <Cmd>call codeium#Clear()<CR>
imap <script><silent><nowait><expr> <C-g> gemini#Accept()
imap <script><silent><nowait><expr> <C-h> gemini#AcceptNextWord()
imap <script><silent><nowait><expr> <C-j> gemini#AcceptNextLine()
imap <C-;> <Cmd>call gemini#CycleCompletions(1)<CR>
imap <C-,> <Cmd>call gemini#CycleCompletions(-1)<CR>
imap <C-x> <Cmd>call gemini#Clear()<CR>
```

Or in Neovim (using [wbthomason/packer.nvim](https://github.com/wbthomason/packer.nvim#specifying-plugins) or [folke/lazy.nvim](https://github.com/folke/lazy.nvim)):

```lua
-- Remove the `use` here if you're using folke/lazy.nvim.
use {
'Exafunction/windsurf.vim',
'Exafunction/gemini.vim',
config = function ()
-- Change '<C-g>' here to any keycode you like.
vim.keymap.set('i', '<C-g>', function () return vim.fn['codeium#Accept']() end, { expr = true, silent = true })
vim.keymap.set('i', '<c-;>', function() return vim.fn['codeium#CycleCompletions'](1) end, { expr = true, silent = true })
vim.keymap.set('i', '<c-,>', function() return vim.fn['codeium#CycleCompletions'](-1) end, { expr = true, silent = true })
vim.keymap.set('i', '<c-x>', function() return vim.fn['codeium#Clear']() end, { expr = true, silent = true })
vim.keymap.set('i', '<C-g>', function () return vim.fn['gemini#Accept']() end, { expr = true, silent = true })
vim.keymap.set('i', '<c-;>', function() return vim.fn['gemini#CycleCompletions'](1) end, { expr = true, silent = true })
vim.keymap.set('i', '<c-,>', function() return vim.fn['gemini#CycleCompletions'](-1) end, { expr = true, silent = true })
vim.keymap.set('i', '<c-x>', function() return vim.fn['gemini#Clear']() end, { expr = true, silent = true })
end
}
```

(Make sure that you ran `:Codeium Auth` after installation.)
(Make sure that you ran `:Gemini Auth` after installation.)

### ⛔ Disabling Windsurf
### ⛔ Disabling Gemini

Windsurf can be disabled for particular filetypes by setting the
`g:codeium_filetypes` variable in your vim config file (vimrc/init.vim):
Gemini can be disabled for particular filetypes by setting the
`g:gemini_filetypes` variable in your vim config file (vimrc/init.vim):

```vim
let g:codeium_filetypes = {
let g:gemini_filetypes = {
\ "bash": v:false,
\ "typescript": v:true,
\ }
```

Windsurf is enabled by default for most filetypes.
Gemini is enabled by default for most filetypes.

You can also _disable_ codeium by default with the `g:codeium_enabled` variable,
and enable it manually per buffer by running `:CodeiumEnable`:
You can also _disable_ gemini by default with the `g:gemini_enabled` variable,
and enable it manually per buffer by running `:GeminiEnable`:

```vim
let g:codeium_enabled = v:false
let g:gemini_enabled = v:false
```

or in Neovim:

```lua
vim.g.codeium_enabled = false
vim.g.gemini_enabled = false
```

Or you can disable codeium for _all filetypes_ with the `g:codeium_filetypes_disabled_by_default` variable,
and use the `g:codeium_filetypes` variable to selectively enable codeium for specified filetypes:
Or you can disable gemini for _all filetypes_ with the `g:gemini_filetypes_disabled_by_default` variable,
and use the `g:gemini_filetypes` variable to selectively enable gemini for specified filetypes:

```vim
" let g:codeium_enabled = v:true
let g:codeium_filetypes_disabled_by_default = v:true
" let g:gemini_enabled = v:true
let g:gemini_filetypes_disabled_by_default = v:true

let g:codeium_filetypes = {
let g:gemini_filetypes = {
\ "rust": v:true,
\ "typescript": v:true,
\ }
Expand All @@ -149,57 +131,57 @@ let g:codeium_filetypes = {
If you would like to just disable the automatic triggering of completions:

```vim
let g:codeium_manual = v:true
let g:gemini_manual = v:true

" You might want to use `CycleOrComplete()` instead of `CycleCompletions(1)`.
" This will make the forward cycling of suggestions also trigger the first
" suggestion manually.
imap <C-;> <Cmd>call codeium#CycleOrComplete()<CR>
imap <C-;> <Cmd>call gemini#CycleOrComplete()<CR>
```

To disable automatic text rendering of suggestions (the gray text that appears for a suggestion):

```vim
let g:codeium_render = v:false
let g:gemini_render = v:false
```

### Show Windsurf status in statusline
### Show Gemini status in statusline

Windsurf status can be generated by calling the `codeium#GetStatusString()` function. In
Neovim, you can use `vim.api.nvim_call_function("codeium#GetStatusString", {})` instead.
It produces a 3 char long string with Windsurf status:
Gemini status can be generated by calling the `gemini#GetStatusString()` function. In
Neovim, you can use `vim.api.nvim_call_function("gemini#GetStatusString", {})` instead.
It produces a 3 char long string with Gemini status:

- `'3/8'` - third suggestion out of 8
- `'0'` - Windsurf returned no suggestions
- `'*'` - waiting for Windsurf response
- `'0'` - Gemini returned no suggestions
- `'*'` - waiting for Gemini response

In normal mode, status shows if Windsurf is enabled or disabled by showing
In normal mode, status shows if Gemini is enabled or disabled by showing
`'ON'` or `'OFF'`.

In order to show it in status line add following line to your `.vimrc`:

```set statusline+=\{…\}%3{codeium#GetStatusString()}```
```set statusline+=\{…\}%3{gemini#GetStatusString()}```

Shorter variant without Windsurf logo:
Shorter variant without Gemini logo:

```set statusline+=%3{codeium#GetStatusString()}```
```set statusline+=%3{gemini#GetStatusString()}```

Please check `:help statusline` for further information about building statusline in VIM.

vim-airline supports Windsurf out-of-the-box since commit [3854429d](https://github.com/vim-airline/vim-airline/commit/3854429d99c8a2fb555a9837b155f33c957a2202).
vim-airline supports Gemini out-of-the-box since commit [3854429d](https://github.com/vim-airline/vim-airline/commit/3854429d99c8a2fb555a9837b155f33c957a2202).

### Launching Windsurf Chat
### Launching Gemini Chat

Calling the `codeium#Chat()` function or using the `Codeium Chat` command will enable search and indexing in the current project and launch Codeium Chat in a new browser window.
Calling the `gemini#Chat()` function or using the `Gemini Chat` command will enable search and indexing in the current project and launch Gemini Chat in a new browser window.

```vim
:call codeium#Chat()
:Codeium Chat
:call gemini#Chat()
:Gemini Chat
```

The project root is determined by looking in Vim's current working directory for some specific files or directories to be present and goes up to parent directories until one is found. This list of hints is user-configurable and the default value is:

```let g:codeium_workspace_root_hints = ['.bzr','.git','.hg','.svn','_FOSSIL_','package.json']```
```let g:gemini_workspace_root_hints = ['.bzr','.git','.hg','.svn','_FOSSIL_','package.json']```

Note that launching chat enables telemetry.

Expand All @@ -209,27 +191,27 @@ Note that launching chat enables telemetry.

```lua
{
'Exafunction/windsurf.vim',
'Exafunction/gemini.vim',
event = 'BufEnter'
}
```

### 🔌 vim-plug

```vim
Plug 'Exafunction/windsurf.vim', { 'branch': 'main' }
Plug 'Exafunction/gemini.vim', { 'branch': 'main' }
```

### 📦 Vundle

```vim
Plugin 'Exafunction/windsurf.vim'
Plugin 'Exafunction/gemini.vim'
```

### 📦 packer.nvim:

```vim
use 'Exafunction/windsurf.vim'
use 'Exafunction/gemini.vim'
```

### 💪 Manual
Expand All @@ -240,7 +222,7 @@ Run the following. On windows, you can replace `~/.vim` with
`$HOME/vimfiles`:

```bash
git clone https://github.com/Exafunction/windsurf.vim ~/.vim/pack/Exafunction/start/windsurf.vim
git clone https://github.com/Exafunction/gemini.vim ~/.vim/pack/Exafunction/start/gemini.vim
```

#### 💻 Neovim
Expand All @@ -249,5 +231,5 @@ Run the following. On windows, you can replace `~/.config` with
`$HOME/AppData/Local`:

```bash
git clone https://github.com/Exafunction/windsurf.vim ~/.config/nvim/pack/Exafunction/start/windsurf.vim
git clone https://github.com/Exafunction/gemini.vim ~/.config/nvim/pack/Exafunction/start/gemini.vim
```
33 changes: 23 additions & 10 deletions autoload/codeium.vim
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ function! s:CompletionInserter(current_completion, insert_text) abort
else
let cursor_text = "\<C-O>:exe 'go' line2byte(line('.'))+col('.')+(" . delta . ")\<CR>"
endif
call codeium#server#Request('AcceptCompletion', {'metadata': codeium#server#RequestMetadata(), 'completion_id': current_completion.completion.completionId})
return delete_range . insert_text . cursor_text
endfunction

Expand Down Expand Up @@ -115,16 +114,30 @@ function! s:HandleCompletionsResult(out, err, status) abort
let response_text = join(a:out, '')
try
let response = json_decode(response_text)
if get(response, 'code', v:null) isnot# v:null
call codeium#log#Error('Invalid response from language server')
call codeium#log#Error(response_text)
call codeium#log#Error('stderr: ' . join(a:err, ''))
call codeium#log#Exception()
let candidates = get(response, 'candidates', [])
if empty(candidates)
let b:_codeium_completions.items = []
let b:_codeium_completions.index = 0
let b:_codeium_status = 2
call s:RenderCurrentCompletion()
return
endif
let completionItems = get(response, 'completionItems', [])

let b:_codeium_completions.items = completionItems
let content = get(candidates[0], 'content', {})
let parts = get(content, 'parts', [])
if empty(parts)
let b:_codeium_completions.items = []
let b:_codeium_completions.index = 0
let b:_codeium_status = 2
call s:RenderCurrentCompletion()
return
endif
let text = get(parts[0], 'text', '')
let completionItem = {
\ 'completion': {
\ 'text': text
\ }
\ }
let b:_codeium_completions.items = [completionItem]
let b:_codeium_completions.index = 0

let b:_codeium_status = 2
Expand Down Expand Up @@ -389,7 +402,7 @@ function! codeium#Complete(...) abort

try
let b:_codeium_status = 1
let request_job = codeium#server#Request('GetCompletions', data, function('s:HandleCompletionsResult', []))
let request_job = gemini#GetCompletions(data, function('s:HandleCompletionsResult', []))

let b:_codeium_completions = {
\ 'request_data': request_data,
Expand Down
Loading