Skip to content

Commit

Permalink
feat: chat-gpt like experience (#5)
Browse files Browse the repository at this point in the history
* wip

* move between headings with `[` and `]`. restore previous buffer contents

* more keymaps and ability to manage multiple opened chats

* can toggle the chat window

* wip

* conversations are easier to save

* Conversations now save when toggled. Buffer input is streamed

* advisor updates

* update stylua

* update keymaps

* better toggling

* switch out curl for plenary curl. fix open chats feature

* better markdown formatting

* updates

* update tests
  • Loading branch information
olimorris authored Feb 4, 2024
1 parent 40fcae9 commit c0c3dd6
Show file tree
Hide file tree
Showing 21 changed files with 1,131 additions and 644 deletions.
5 changes: 0 additions & 5 deletions .stylua.toml

This file was deleted.

148 changes: 114 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Use the <a href="https://platform.openai.com/docs/guides/text-generation/chat-co
## :camera_flash: Screenshots

<div align="center">
<p><strong>Chat buffer</strong><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/fd1f11e7-bef4-4bbc-8c7f-0c306e5c72b8" alt="chat buffer" /></p>
<p><strong>Chat buffer</strong><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/a19c8397-a1e2-44df-98be-8a1b4d307ea7" alt="chat buffer" /></p>
<p><strong>Code author</strong><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/64cf4fa1-d5b9-43cc-8351-4d06c06c8b46" alt="code author" /></p>
<p><strong>Code advisor</strong><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/bc6181e0-85a8-4009-9cfc-f85898780bd5" alt="code advisor" /><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/cbfafcc0-87f9-43e5-8e27-f8eaaf88637d" alt="code advisor" /></p>
</div>
Expand All @@ -56,12 +56,13 @@ Use the <a href="https://platform.openai.com/docs/guides/text-generation/chat-co
"olimorris/codecompanion.nvim",
dependencies = {
"nvim-treesitter/nvim-treesitter",
"nvim-lua/plenary.nvim",
{
"stevearc/dressing.nvim", -- Optional: Improves the default Neovim UI
opts = {},
},
},
cmd = { "CodeCompanionChat", "CodeCompanionActions" },
cmd = { "CodeCompanionToggle", "CodeCompanionActions" },
config = true
}

Expand All @@ -73,19 +74,23 @@ use({
end,
requires = {
"nvim-treesitter/nvim-treesitter",
"nvim-lua/plenary.nvim",
"stevearc/dressing.nvim"
}
})
```

## :wrench: Configuration

The plugin comes with the following defaults:
> **Note**: You only need to the call the `setup` function if you wish to change any of the defaults.
<details>
<summary>Click to see the default configuration</summary>

```lua
{
api_key = "OPENAI_API_KEY", -- Your OpenAI API key
org_api_key = "OPENAI_ORG_KEY", -- Your organisation OpenAI API key
api_key = "OPENAI_API_KEY", -- Your API key
org_api_key = "OPENAI_ORG_KEY", -- Your organisation API key
base_url = "https://api.openai.com", -- The URL to use for the API requests
ai_settings = {
-- Default settings for the Completions API
Expand All @@ -105,43 +110,97 @@ The plugin comes with the following defaults:
user = nil,
},
conversations = {
auto_save = true, -- Once a conversation is created/loaded, automatically save it
save_dir = vim.fn.stdpath("data") .. "/codecompanion/conversations",
save_dir = vim.fn.stdpath("data") .. "/codecompanion/conversations", -- Path to save conversations to
},
display = {
action_palette = {
width = 95,
height = 10,
},
chat = { -- Options for the chat strategy
type = "buffer", -- buffer|float
show_settings = false, -- Show the model settings in the chat window
float = {
border = "single",
max_height = 0,
max_width = 0,
padding = 1,
},
},
win_options = {
cursorcolumn = false,
cursorline = false,
foldcolumn = "0",
linebreak = true,
list = false,
signcolumn = "no",
spell = false,
wrap = true,
},
},
display = { -- How to display `advisor` outputs
type = "popup", -- popup|split
split = "horizontal" -- horizontal|vertical
height = 0.7,
width = 0.8,
keymaps = {
["<C-c>"] = "keymaps.close", -- Close the chat buffer
["q"] = "keymaps.cancel_request", -- Cancel the currently streaming request
["gc"] = "keymaps.clear", -- Clear the contents of the chat
["ga"] = "keymaps.codeblock", -- Insert a codeblock in the chat
["gs"] = "keymaps.save_conversation", -- Save the current chat as a conversation
["]"] = "keymaps.next", -- Move to the next header in the chat
["["] = "keymaps.previous", -- Move to the previous header in the chat
},
log_level = "ERROR", -- One of: TRACE, DEBUG, ERROR
send_code = true, -- Send your code to OpenAI
show_token_count = true, -- Show the token count for the current chat
use_default_actions = true, -- The actions that appear in the action palette
log_level = "ERROR", -- TRACE|DEBUG|ERROR
send_code = true, -- Send code context to the API?
show_token_count = true, -- Show the token count for the current chat?
use_default_actions = true, -- Use the default actions in the action palette?
}
```

</details>

### Edgy.nvim Configuration

The author recommends pairing with [edgy.nvim](https://github.com/folke/edgy.nvim) for a Co-Pilot Chat-like experience:

```lua
{
"folke/edgy.nvim",
event = "VeryLazy",
init = function()
vim.opt.laststatus = 3
vim.opt.splitkeep = "screen"
end,
opts = {
right = {
{ ft = "codecompanion", title = "Code Companion Chat", size = { width = 0.45 } },
}
}
}
```

Modify these settings via the `opts` table in Lazy.nvim or by calling the `require("codecompanion").setup()` function in Packer.
### Highlight Groups

The plugin sets a number of highlights during setup:

> **Note**: The `send_code` option can prevent any visual selections from being sent to OpenAI for processing as part of any `advisor` or `author` actions
- `CodeCompanionTokens` - Virtual text showing the token count when in a chat buffer
- `CodeCompanionVirtualText` - All other virtual text in the chat buffer

## :rocket: Usage

The plugin has a number of commands:

- `CodeCompanionChat` - To open up a new chat buffer
- `CodeCompanionActions` - To open up the action selector window
- `CodeCompanionSaveConversationAs` - Saves a chat buffer as a conversation
- `CodeCompanionToggle` - Toggle a chat buffer
- `CodeCompanionActions` - To open up the action palette window

They can be assigned to keymaps with:
For an optimum workflow, the plugin author recommendeds the following keymaps:

```lua
vim.api.nvim_set_keymap("n", "<C-a>", "<cmd>CodeCompanionActions<cr>", { noremap = true, silent = true })
vim.api.nvim_set_keymap("v", "<C-a>", "<cmd>CodeCompanionActions<cr>", { noremap = true, silent = true })
vim.api.nvim_set_keymap("n", "<LocalLeader>a", "<cmd>CodeCompanionChat<cr>", { noremap = true, silent = true })
vim.api.nvim_set_keymap("n", "<LocalLeader>a", "<cmd>CodeCompanionToggle<cr>", { noremap = true, silent = true })
vim.api.nvim_set_keymap("v", "<LocalLeader>a", "<cmd>CodeCompanionToggle<cr>", { noremap = true, silent = true })
```

> **Note**: For some actions, visual mode allows your selection to be sent directly to the chat buffer or OpenAI themselves (in the case of `author` actions).
> **Note**: For some actions, visual mode allows your selection to be sent directly to the chat buffer or the API itself (in the case of `author` actions).
### The Action Palette

Expand Down Expand Up @@ -171,15 +230,27 @@ Or, if you wish to turn off the default actions, set `use_default_actions = fals

<p><img src="https://github.com/olimorris/codecompanion.nvim/assets/9512444/84d5e03a-0b48-4ffb-9ca5-e299d41171bd" alt="chat buffer" /></p>

The Chat Buffer is where you can converse with OpenAI, directly from Neovim. It behaves as a regular markdown buffer with some clever additions. When the buffer is written, autocmds trigger the sending of its content to the OpenAI API in the form of prompts. These prompts are segmented by H1 headers into `user` and `assistant` (see OpenAI's [Chat Completions API](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) for more on this). When a response is received, it is then streamed back into the buffer. The result is that you experience the feel of conversing with ChatGPT, from within Neovim.
The chat buffer is where you can converse with your GenAI API, directly from Neovim. It behaves as a regular markdown buffer with some clever additions. When the buffer is written (or "saved"), autocmds trigger the sending of its content to the API, in the form of prompts. These prompts are segmented by H1 headers: `user` and `assistant` (see OpenAI's [Chat Completions API](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) for more on this). When a response is received, it is then streamed back into the buffer. The result is that you experience the feel of conversing with GenAI, from within Neovim.

> **Note**: You can cancel a request at any point by pressing `q`.
#### Keymaps

At the very top of the Chat Buffer are the parameters which can be changed to affect the API's response back to you. You can find more detail about them by moving the cursor over them or referring to the [Chat Completions reference guide](https://platform.openai.com/docs/api-reference/chat). The parameters can be tweaked and modified throughout the conversation.
When in the chat buffer, there are number of keymaps available to you (which can be changed in the config):

Chat Buffers are not automatically saved owing to them being an `acwrite` buftype (see `:h buftype`). However, the plugin allows for this via the notion of Conversations. Simply run `:CodeCompanionSaveConversationAs` in the chat buffer you wish to save. Conversations can then be restored via the Action Palette and the _Load conversations_ actions.
- `<C-c>` - Close the buffer
- `q` - Cancel streaming from the API
- `gc` - Clear the buffer's contents
- `ga` - Add a codeblock
- `gs` - Save the chat as a conversation
- `[` - Move to the next header in the buffer
- `]` - Move to the previous header in the buffer

> **Note**: When a conversation is saved or loaded it will automatically save any changes.
#### Conversations

Chat Buffers are not automatically saved to disk, owing to them being an `acwrite` buftype (see `:h buftype`). However, the plugin allows for this via the notion of Conversations and pressing `gs` in the buffer. Conversations can then be restored via the Action Palette and the _Load conversations_ action.

#### Settings

If `display.chat.show_settings` is set to `true`, at the very top of the chat buffer will be the parameters which can be changed to affect the API's response back to you. This enables fine-tuning and parameter tweaking throughout the chat. You can find more detail about them by moving the cursor over them or referring to the [Chat Completions reference guide](https://platform.openai.com/docs/api-reference/chat) if you're using OpenAI.

### In-Built Actions

Expand All @@ -201,9 +272,9 @@ This action utilises the `author` strategy. This action can be useful for genera

#### Code advisor

As the name suggests, this action provides advice on a visual selection of code and utilises the `advisor` strategy. It uses the `display` configuration option to output the response from OpenAI into a split or a popup. Inevitably, the response back from OpenAI may lead to more questions. Pressing `c` in the advisor buffer will take the conversation to a chat buffer. Pressing `q` will close the buffer.
As the name suggests, this action provides advice on a visual selection of code and utilises the `advisor` strategy. The response from the API is output into a chat buffer which follows the `display.chat` settings in your configuration.

> **Note**: For some users, the sending of code to OpenAI may not be an option. In those instances, you can set `send_code = false` in your config.
> **Note**: For some users, the sending of code to the GenAI may not be an option. In those instances, you can set `send_code = false` in your config.
#### LSP assistant

Expand All @@ -213,13 +284,22 @@ Taken from the fantastic [Wtf.nvim](https://github.com/piersolenski/wtf.nvim) pl

### Hooks / User events

The plugin fires events at the start and the conclusion of an API request. A user can hook into these as follows:
The plugin fires the following events during its lifecycle:

- `CodeCompanionRequest` - Fired during the API request. Outputs `data.status` with a value of `started` or `finished`
- `CodeCompanionConversation` - Fired after a conversation has been saved to disk
- `CodeCompanionChat` - Fired at various points during the chat buffer. Comes with the following attributes:
- `data.action = close_buffer` - For when a chat buffer has been permanently closed
- `data.action = hide_buffer` - For when a chat buffer is now hidden
- `data.action = show_buffer` - For when a chat buffer is now visible after being hidden

Events can be hooked into as follows:

```lua
local group = vim.api.nvim_create_augroup("CodeCompanionHooks", {})

vim.api.nvim_create_autocmd({ "User" }, {
pattern = "CodeCompanion",
pattern = "CodeCompanionRequest",
group = group,
callback = function(request)
print(request.data.status) -- outputs "started" or "finished"
Expand All @@ -234,13 +314,13 @@ vim.api.nvim_create_autocmd({ "User" }, {
If you use the fantastic [Heirline.nvim](https://github.com/rebelot/heirline.nvim) plugin, consider the following snippet to display an icon in the statusline whilst CodeCompanion is speaking to the LLM:

```lua
local OpenAI = {
local GenAI = {
static = {
processing = false,
},
update = {
"User",
pattern = "CodeCompanion",
pattern = "CodeCompanionRequest",
callback = function(self, args)
self.processing = (args.data.status == "started")
vim.cmd("redrawstatus")
Expand Down
Loading

0 comments on commit c0c3dd6

Please sign in to comment.