From 920380ecd1224c2ba6a1a43d5eeff00e5a5489c9 Mon Sep 17 00:00:00 2001 From: He Zhizhou <2670226747@qq.com> Date: Tue, 30 Jan 2024 21:30:49 +0800 Subject: [PATCH 01/75] Fix module import error by using typing.List (#33) Co-authored-by: Cassius0924 <51365188@gitlab.com> --- rplugin/python3/copilot-plugin.py | 3 ++- rplugin/python3/copilot.py | 2 +- rplugin/python3/utilities.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py index 170b23fd..f6efe149 100644 --- a/rplugin/python3/copilot-plugin.py +++ b/rplugin/python3/copilot-plugin.py @@ -1,5 +1,6 @@ import os import time +from typing import List import copilot import dotenv @@ -31,7 +32,7 @@ def __init__(self, nvim: pynvim.Nvim): self.copilot.authenticate() @pynvim.command("CopilotChat", nargs="1") - def copilotChat(self, args: list[str]): + def copilotChat(self, args: List[str]): if self.copilot.github_token is None: self.nvim.out_write("Please authenticate with Copilot first\n") return diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index 0faea917..41a43c03 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -134,7 +134,7 @@ def ask(self, prompt: str, code: str, language: str = ""): self.chat_history.append(typings.Message(full_response, "system")) - def _get_embeddings(self, inputs: list[typings.FileExtract]): + def _get_embeddings(self, inputs: List[typings.FileExtract]): embeddings = [] url = "https://api.githubcopilot.com/embeddings" # If we have more than 18 files, we need to split them into multiple requests diff --git a/rplugin/python3/utilities.py b/rplugin/python3/utilities.py index 2c1a14a0..1f6359a4 100644 --- a/rplugin/python3/utilities.py +++ b/rplugin/python3/utilities.py @@ -1,6 +1,7 @@ import json import os import random +from typing import List import prompts import typings @@ -11,7 +12,7 @@ def random_hex(length: int = 65): def generate_request( - chat_history: list[typings.Message], + chat_history: List[typings.Message], code_excerpt: str, language: str = "", system_prompt=prompts.COPILOT_INSTRUCTIONS, @@ -48,7 +49,7 @@ def generate_request( } -def generate_embedding_request(inputs: list[typings.FileExtract]): +def generate_embedding_request(inputs: List[typings.FileExtract]): return { "input": [ f"File: `{i.filepath}`\n```{i.filepath.split('.')[-1]}\n{i.code}```" From 79cbb0ad7094e35a10d19bbd21179b78039b9a2a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:31:36 +0800 Subject: [PATCH 02/75] docs: add Cassius0924 as a contributor for code (#34) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 5 ++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index d9fb93f0..8fd9397a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -44,6 +44,15 @@ "contributions": [ "code" ] + }, + { + "login": "Cassius0924", + "name": "He Zhizhou", + "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", + "profile": "https://github.com/Cassius0924", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index 0db3e07e..da425fee 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Copilot Chat for Neovim - -[![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) > [!NOTE] @@ -154,6 +152,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Dung Duc Huynh (Kaka)
Dung Duc Huynh (Kaka)

💻 📖 Ahmed Haracic
Ahmed Haracic

💻 Trí Thiện Nguyễn
Trí Thiện Nguyễn

💻 + He Zhizhou
He Zhizhou

💻 From d0dbd4c6fb9be75ccaa591b050198d40c097f423 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Thu, 1 Feb 2024 09:55:25 +0800 Subject: [PATCH 03/75] feat: add debug flag --- README.md | 3 + lua/CopilotChat/init.lua | 6 ++ lua/CopilotChat/utils.lua | 24 +++++ lua/CopilotChat/vlog.lua | 151 ++++++++++++++++++++++++++++++ rplugin/python3/copilot-plugin.py | 7 ++ 5 files changed, 191 insertions(+) create mode 100644 lua/CopilotChat/vlog.lua diff --git a/README.md b/README.md index da425fee..b160a750 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Copilot Chat for Neovim + [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) + > [!NOTE] @@ -24,6 +26,7 @@ return { "jellydn/CopilotChat.nvim", opts = { mode = "split", -- newbuffer or split , default: newbuffer + debug = false, -- Enable or disable debug mode }, build = function() vim.defer_fn(function() diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index acb64fcb..5223da1d 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -7,13 +7,19 @@ local default_prompts = { Tests = 'Briefly how selected code works then generate unit tests.', } +_COPILOT_CHAT_GLOBAL_CONFIG = {} + -- Set up the plugin ---@param options (table | nil) -- - mode: ('newbuffer' | 'split') default: newbuffer. -- - prompts: (table?) default: default_prompts. +-- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_view_option = options and options.mode or 'newbuffer' + local debug = options and options.debug or false + _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug + -- Merge the provided prompts with the default prompts local prompts = vim.tbl_extend('force', default_prompts, options and options.prompts or {}) diff --git a/lua/CopilotChat/utils.lua b/lua/CopilotChat/utils.lua index bcae76ef..3402d5b6 100644 --- a/lua/CopilotChat/utils.lua +++ b/lua/CopilotChat/utils.lua @@ -1,5 +1,7 @@ local M = {} +local log = require('CopilotChat.vlog') + --- Create custom command ---@param cmd string The command name ---@param func function The function to execute @@ -9,4 +11,26 @@ M.create_cmd = function(cmd, func, opt) vim.api.nvim_create_user_command(cmd, func, opt) end +--- Log info +---@vararg any +M.log_info = function(...) + -- Only save log when debug is on + if not _COPILOT_CHAT_GLOBAL_CONFIG.debug then + return + end + + log.info(...) +end + +--- Log error +---@vararg any +M.log_error = function(...) + -- Only save log when debug is on + if not _COPILOT_CHAT_GLOBAL_CONFIG.debug then + return + end + + log.error(...) +end + return M diff --git a/lua/CopilotChat/vlog.lua b/lua/CopilotChat/vlog.lua new file mode 100644 index 00000000..972a4342 --- /dev/null +++ b/lua/CopilotChat/vlog.lua @@ -0,0 +1,151 @@ +-- -- log.lua +-- +-- Inspired by rxi/log.lua +-- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. + +-- User configuration section +local default_config = { + -- Name of the plugin. Prepended to log messages + plugin = 'CopilotChat.nvim', + + -- Should print the output to neovim while running + use_console = false, + + -- Should highlighting be used in console (using echohl) + highlights = true, + + -- Should write to a file + use_file = true, + + -- Any messages above this level will be logged. + level = 'trace', + + -- Level configuration + modes = { + { name = 'trace', hl = 'Comment' }, + { name = 'debug', hl = 'Comment' }, + { name = 'info', hl = 'None' }, + { name = 'warn', hl = 'WarningMsg' }, + { name = 'error', hl = 'ErrorMsg' }, + { name = 'fatal', hl = 'ErrorMsg' }, + }, + + -- Can limit the number of decimals displayed for floats + float_precision = 0.01, +} + +-- {{{ NO NEED TO CHANGE +local log = {} + +local unpack = unpack or table.unpack + +log.get_log_file = function() + return string.format('%s/%s.log', vim.fn.stdpath('state'), default_config.plugin) +end + +log.new = function(config, standalone) + config = vim.tbl_deep_extend('force', default_config, config) + + local outfile = log.get_log_file() + + local obj + if standalone then + obj = log + else + obj = {} + end + + local levels = {} + for i, v in ipairs(config.modes) do + levels[v.name] = i + end + + local round = function(x, increment) + increment = increment or 1 + x = x / increment + return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment + end + + local make_string = function(...) + local t = {} + for i = 1, select('#', ...) do + local x = select(i, ...) + + if type(x) == 'number' and config.float_precision then + x = tostring(round(x, config.float_precision)) + elseif type(x) == 'table' then + x = vim.inspect(x) + else + x = tostring(x) + end + + t[#t + 1] = x + end + return table.concat(t, ' ') + end + + local log_at_level = function(level, level_config, message_maker, ...) + -- Return early if we're below the config.level + if level < levels[config.level] then + return + end + local nameupper = level_config.name:upper() + + local msg = message_maker(...) + local info = debug.getinfo(2, 'Sl') + local lineinfo = info.short_src .. ':' .. info.currentline + + -- Output to console + if config.use_console then + local console_string = + string.format('[%-6s%s] %s: %s', nameupper, os.date('%H:%M:%S'), lineinfo, msg) + + if config.highlights and level_config.hl then + vim.cmd(string.format('echohl %s', level_config.hl)) + end + + local split_console = vim.split(console_string, '\n') + for _, v in ipairs(split_console) do + vim.cmd(string.format([[echom "[%s] %s"]], config.plugin, vim.fn.escape(v, '"'))) + end + + if config.highlights and level_config.hl then + vim.cmd('echohl NONE') + end + end + + -- Output to log file + if config.use_file then + local fp = io.open(outfile, 'a') + local str = string.format('[%-6s%s] %s: %s\n', nameupper, os.date(), lineinfo, msg) + fp:write(str) + fp:close() + end + end + + for i, x in ipairs(config.modes) do + obj[x.name] = function(...) + return log_at_level(i, x, make_string, ...) + end + + obj[('fmt_%s'):format(x.name)] = function() + return log_at_level(i, x, function(...) + local passed = { ... } + local fmt = table.remove(passed, 1) + local inspected = {} + for _, v in ipairs(passed) do + table.insert(inspected, vim.inspect(v)) + end + return string.format(fmt, unpack(inspected)) + end) + end + end +end + +log.new(default_config, true) +-- }}} +-- +return log diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py index f6efe149..1e7bc3f6 100644 --- a/rplugin/python3/copilot-plugin.py +++ b/rplugin/python3/copilot-plugin.py @@ -80,8 +80,15 @@ def copilotChat(self, args: List[str]): """ buf.append(start_separator.split("\n"), -1) + self.nvim.exec_lua( + 'require("CopilotChat.utils").log_info(...)', f"Prompt: {prompt}" + ) + # Add chat messages for token in self.copilot.ask(prompt, code, language=file_type): + self.nvim.exec_lua( + 'require("CopilotChat.utils").log_info(...)', f"Token: {token}" + ) buffer_lines = self.nvim.api.buf_get_lines(buf, 0, -1, 0) last_line_row = len(buffer_lines) - 1 last_line = buffer_lines[-1] From 282cbfcc961d1aa524db4e5c2e7f11b5dc9306e3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Feb 2024 01:55:51 +0000 Subject: [PATCH 04/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 05d1afca..ccf46045 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 January 30 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 01 ============================================================================== Table of Contents *CopilotChat-table-of-contents* @@ -41,6 +41,7 @@ LAZY.NVIM ~ "jellydn/CopilotChat.nvim", opts = { mode = "split", -- newbuffer or split , default: newbuffer + debug = false, -- Enable or disable debug mode }, build = function() vim.defer_fn(function() @@ -167,14 +168,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square 2. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 3. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif 4. *Generate tests*: https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif From f4f21647b6fe367387ff2d14b6f473dd23c29118 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:46:02 +0800 Subject: [PATCH 05/75] docs: add debug file path when enable debug mode --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b160a750..123cf504 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,10 @@ It will prompt you with instructions on your first start. If you already have `C return { { "jellydn/CopilotChat.nvim", + dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { mode = "split", -- newbuffer or split , default: newbuffer - debug = false, -- Enable or disable debug mode + debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, build = function() vim.defer_fn(function() From e4d985abd00c0adb6a4b187271f730d23d9641c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Feb 2024 12:46:33 +0000 Subject: [PATCH 06/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index ccf46045..6c820f10 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -39,9 +39,10 @@ LAZY.NVIM ~ return { { "jellydn/CopilotChat.nvim", + dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { mode = "split", -- newbuffer or split , default: newbuffer - debug = false, -- Enable or disable debug mode + debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, build = function() vim.defer_fn(function() From b851de951487026fa4f8c01bee7a14305903e829 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Thu, 1 Feb 2024 22:57:15 +0800 Subject: [PATCH 07/75] chore: sync fork Add system prompt to ask Handle bad error code after calling post --- rplugin/python3/copilot-plugin.py | 3 ++- rplugin/python3/copilot.py | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py index 1e7bc3f6..59c25594 100644 --- a/rplugin/python3/copilot-plugin.py +++ b/rplugin/python3/copilot-plugin.py @@ -84,8 +84,9 @@ def copilotChat(self, args: List[str]): 'require("CopilotChat.utils").log_info(...)', f"Prompt: {prompt}" ) + system_prompt = prompts.COPILOT_INSTRUCTIONS # Add chat messages - for token in self.copilot.ask(prompt, code, language=file_type): + for token in self.copilot.ask(system_prompt, prompt, code, language=file_type): self.nvim.exec_lua( 'require("CopilotChat.utils").log_info(...)', f"Token: {token}" ) diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index 41a43c03..db9b396d 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -5,7 +5,6 @@ from typing import Dict, List import dotenv -import prompts import requests import typings import utilities @@ -87,20 +86,15 @@ def authenticate(self): self.token = self.session.get(url, headers=headers).json() - def ask(self, prompt: str, code: str, language: str = ""): + def ask(self, system_prompt: str, prompt: str, code: str, language: str = ""): + if not self.token: + self.authenticate() # If expired, reauthenticate if self.token.get("expires_at") <= round(time.time()): self.authenticate() url = "https://api.githubcopilot.com/chat/completions" self.chat_history.append(typings.Message(prompt, "user")) - system_prompt = prompts.COPILOT_INSTRUCTIONS - if prompt == prompts.FIX_SHORTCUT: - system_prompt = prompts.COPILOT_FIX - elif prompt == prompts.TEST_SHORTCUT: - system_prompt = prompts.COPILOT_TESTS - elif prompt == prompts.EXPLAIN_SHORTCUT: - system_prompt = prompts.COPILOT_EXPLAIN data = utilities.generate_request( self.chat_history, code, language, system_prompt=system_prompt ) @@ -110,6 +104,8 @@ def ask(self, prompt: str, code: str, language: str = ""): response = self.session.post( url, headers=self._headers(), json=data, stream=True ) + if response.status_code != 200: + raise Exception(f"Error fetching response: {response}") for line in response.iter_lines(): line = line.decode("utf-8").replace("data: ", "").strip() if line.startswith("[DONE]"): From 09b6c36c6328db47eb3dc8caffd7f2263f0b8de4 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Thu, 1 Feb 2024 23:21:56 +0800 Subject: [PATCH 08/75] docs: add Discord link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 123cf504..59538b51 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > [!NOTE] -> There is a new command: `CopilotChatInPlace`, which functions similarly to ChatGPT plugin. You can find it in the [canary](https://github.com/jellydn/CopilotChat.nvim/tree/canary?tab=readme-ov-file#lazynvim) branch. +> A new command, `CopilotChatInPlace`, has been introduced. It functions like the ChatGPT plugin. You can find this feature in the [canary](https://github.com/jellydn/CopilotChat.nvim/tree/canary?tab=readme-ov-file#lazynvim) branch. To stay updated on our roadmap, please join our [Discord](https://discord.gg/vy6hJsTWaZ) community. ## Authentication From 97c2e469ee751af737ef2771c41322836d0d8c6e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Feb 2024 15:22:18 +0000 Subject: [PATCH 09/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 6c820f10..9d958f6f 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -16,10 +16,11 @@ Table of Contents *CopilotChat-table-of-contents* |CopilotChat-| - [!NOTE] There is a new command: `CopilotChatInPlace`, which functions similarly - to ChatGPT plugin. You can find it in the canary + [!NOTE] A new command, `CopilotChatInPlace`, has been introduced. It functions + like the ChatGPT plugin. You can find this feature in the canary - branch. + branch. To stay updated on our roadmap, please join our Discord + community. AUTHENTICATION *CopilotChat-copilot-chat-for-neovim-authentication* From 9722cf72c486e14f739cbb7de07700d555ad8bc8 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 07:41:01 +0800 Subject: [PATCH 10/75] chore: sync fork --- rplugin/python3/copilot.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index db9b396d..0310b931 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -105,7 +105,17 @@ def ask(self, system_prompt: str, prompt: str, code: str, language: str = ""): url, headers=self._headers(), json=data, stream=True ) if response.status_code != 200: - raise Exception(f"Error fetching response: {response}") + error_messages = { + 401: "Unauthorized. Make sure you have access to Copilot Chat.", + 500: "Internal server error. Please try again later.", + 400: "The developer of this plugin has made a mistake. Please report this issue.", + 419: "You have been rate limited. Please try again later.", + } + raise Exception( + error_messages.get( + response.status_code, f"Unknown error: {response.status_code}" + ) + ) for line in response.iter_lines(): line = line.decode("utf-8").replace("data: ", "").strip() if line.startswith("[DONE]"): From ef1fb50533c414b15d57e94c1e0a629b36c93f81 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 20:15:05 +0800 Subject: [PATCH 11/75] chore: sync fork --- README.md | 47 ++- cspell-tool.txt | 35 +- lua/CopilotChat/init.lua | 10 +- requirements.txt | 1 + rplugin/python3/copilot-agent.py | 64 ++++ rplugin/python3/copilot-plugin.py | 36 +- rplugin/python3/copilot.py | 18 +- rplugin/python3/handlers/chat_handler.py | 209 +++++++++++ .../python3/handlers/inplace_chat_handler.py | 324 ++++++++++++++++++ rplugin/python3/handlers/prompts.py | 2 + .../python3/handlers/vsplit_chat_handler.py | 38 ++ .../python3/mypynvim/core/autocmdmapper.py | 45 +++ rplugin/python3/mypynvim/core/buffer.py | 91 +++++ rplugin/python3/mypynvim/core/keymapper.py | 34 ++ rplugin/python3/mypynvim/core/nvim.py | 96 ++++++ rplugin/python3/mypynvim/core/window.py | 33 ++ .../mypynvim/ui_components/calculator.py | 78 +++++ .../python3/mypynvim/ui_components/layout.py | 211 ++++++++++++ .../python3/mypynvim/ui_components/popup.py | 166 +++++++++ .../python3/mypynvim/ui_components/types.py | 42 +++ rplugin/python3/prompts.py | 43 ++- rplugin/python3/utilities.py | 8 +- 22 files changed, 1578 insertions(+), 53 deletions(-) create mode 100644 rplugin/python3/copilot-agent.py create mode 100644 rplugin/python3/handlers/chat_handler.py create mode 100644 rplugin/python3/handlers/inplace_chat_handler.py create mode 100644 rplugin/python3/handlers/prompts.py create mode 100644 rplugin/python3/handlers/vsplit_chat_handler.py create mode 100644 rplugin/python3/mypynvim/core/autocmdmapper.py create mode 100644 rplugin/python3/mypynvim/core/buffer.py create mode 100644 rplugin/python3/mypynvim/core/keymapper.py create mode 100644 rplugin/python3/mypynvim/core/nvim.py create mode 100644 rplugin/python3/mypynvim/core/window.py create mode 100644 rplugin/python3/mypynvim/ui_components/calculator.py create mode 100644 rplugin/python3/mypynvim/ui_components/layout.py create mode 100644 rplugin/python3/mypynvim/ui_components/popup.py create mode 100644 rplugin/python3/mypynvim/ui_components/types.py diff --git a/README.md b/README.md index 59538b51..85c9e8f3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ It will prompt you with instructions on your first start. If you already have `C ### Lazy.nvim 1. `pip install python-dotenv requests pynvim==0.5.0 prompt-toolkit` -2. Put it in your lazy setup +2. `pip install tiktoken` (optional for displaying prompt token counts) +3. Put it in your lazy setup ```lua return { @@ -26,19 +27,29 @@ return { "jellydn/CopilotChat.nvim", dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { - mode = "split", -- newbuffer or split , default: newbuffer + mode = "split", -- newbuffer or split, default: newbuffer + show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, build = function() - vim.defer_fn(function() - vim.cmd("UpdateRemotePlugins") - vim.notify("CopilotChat - Updated remote plugins. Please restart Neovim.") - end, 3000) + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") end, event = "VeryLazy", keys = { { "cce", "CopilotChatExplain", desc = "CopilotChat - Explain code" }, { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, + { + "ccv", + ":CopilotChatVsplitVisual", + mode = "x", + desc = "CopilotChat - Open in vertical split", + }, + { + "ccx", + ":CopilotChatInPlace", + mode = "x", + desc = "CopilotChat - Run in-place code", + }, }, }, } @@ -75,6 +86,7 @@ You have the ability to tailor this plugin to your specific needs using the conf ```lua { debug = false, -- Enable or disable debug mode + show_help = 'yes', -- Show help text for CopilotChatInPlace prompts = { -- Set dynamic prompts for CopilotChat commands Explain = 'Explain how it works.', Tests = 'Briefly explain how the selected code works, then generate unit tests.', @@ -97,10 +109,7 @@ return { }, }, build = function() - vim.defer_fn(function() - vim.cmd("UpdateRemotePlugins") - vim.notify("CopilotChat - Updated remote plugins. Please restart Neovim.") - end, 3000) + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") end, event = "VeryLazy", keys = { @@ -112,7 +121,7 @@ return { } ``` -For further reference, you can view my [configuration](https://github.com/jellydn/lazy-nvim-ide/blob/main/lua/plugins/extras/copilot-chat.lua). +For further reference, you can view @jellydn's [configuration](https://github.com/jellydn/lazy-nvim-ide/blob/main/lua/plugins/extras/copilot-chat.lua). ### Chat with Github Copilot @@ -135,6 +144,22 @@ For further reference, you can view my [configuration](https://github.com/jellyd [![Generate tests](https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif)](https://gyazo.com/f285467d4b8d8f8fd36aa777305312ae) +### Token count & Fold + +1. Select some code using visual mode. +2. Run the command `:CopilotChatVsplitVisual` with your question. + +[![Fold Demo](https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif)](https://gyazo.com/766fb3b6ffeb697e650fc839882822a8) + +### In-place Chat Popup + +1. Select some code using visual mode. +2. Run the command `:CopilotChatInPlace` and type your prompt. For example, `What does this code do?` +3. Press `Enter` to send your question to Github Copilot. +4. Press `q` to quit. There is help text at the bottom of the screen. You can also press `?` to toggle the help text. + +[![In-place Demo](https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif)](https://gyazo.com/4a5badaa109cd483c1fc23d296325cb0) + ## Roadmap - Translation to pure Lua diff --git a/cspell-tool.txt b/cspell-tool.txt index 62c228e1..5fa35ffa 100644 --- a/cspell-tool.txt +++ b/cspell-tool.txt @@ -1,24 +1,11 @@ -newbuffer -nargs -nvim -Neovim -pynvim -jellydn -neovim -rplugin -dotenv -Nvim -getreg -getbufvar -bufnr -buftype -nofile -vnew -enew -setlocal -bufhidden -noswapfile -linebreak -fileencoding -machineid -roleplay \ No newline at end of file +ApplicationError: Unsupported NodeJS version (16.20.2); >=18 is required + at Module.run (file:///Users/huynhdung/.npm/_npx/b338be11c6678430/node_modules/cspell/dist/esm/app.mjs:17:15) + at file:///Users/huynhdung/.npm/_npx/b338be11c6678430/node_modules/cspell/bin.mjs:6:5 + at ModuleJob.run (node:internal/modules/esm/module_job:193:25) + at async Promise.all (index 0) + at async ESMLoader.import (node:internal/modules/esm/loader:530:24) + at async loadESM (node:internal/process/esm_loader:91:5) + at async handleMainPromise (node:internal/modules/run_main:65:12) { + exitCode: 1, + cause: undefined +} \ No newline at end of file diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index 5223da1d..4258f272 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -12,16 +12,18 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- Set up the plugin ---@param options (table | nil) -- - mode: ('newbuffer' | 'split') default: newbuffer. +-- - show_help: ('yes' | 'no') default: 'yes'. -- - prompts: (table?) default: default_prompts. -- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_view_option = options and options.mode or 'newbuffer' - + vim.g.copilot_chat_show_help = options and options.show_help or 'yes' local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug -- Merge the provided prompts with the default prompts local prompts = vim.tbl_extend('force', default_prompts, options and options.prompts or {}) + vim.g.copilot_chat_user_prompts = prompts -- Loop through merged table and generate commands based on keys. for key, value in pairs(prompts) do @@ -30,6 +32,12 @@ M.setup = function(options) end, { nargs = '*', range = true }) end + for key, value in pairs(prompts) do + utils.create_cmd('CC' .. key, function() + vim.cmd('CopilotChatVsplit ' .. value) + end, { nargs = '*', range = true }) + end + -- Toggle between newbuffer and split utils.create_cmd('CopilotChatToggleLayout', function() if vim.g.copilot_chat_view_option == 'newbuffer' then diff --git a/requirements.txt b/requirements.txt index 75a67aa0..510c3322 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ python-dotenv requests pynvim==0.5.0 prompt-toolkit +tiktoken diff --git a/rplugin/python3/copilot-agent.py b/rplugin/python3/copilot-agent.py new file mode 100644 index 00000000..3ac008df --- /dev/null +++ b/rplugin/python3/copilot-agent.py @@ -0,0 +1,64 @@ +import pynvim +from handlers.inplace_chat_handler import InPlaceChatHandler +from handlers.vsplit_chat_handler import VSplitChatHandler +from mypynvim.core.nvim import MyNvim + +PLUGIN_MAPPING_CMD = "CopilotChatMapping" +PLUGIN_AUTOCMD_CMD = "CopilotChatAutocmd" + + +@pynvim.plugin +class CopilotAgentPlugin(object): + def __init__(self, nvim: pynvim.Nvim): + self.nvim: MyNvim = MyNvim(nvim, PLUGIN_MAPPING_CMD, PLUGIN_AUTOCMD_CMD) + self.vsplit_chat_handler = None + self.inplace_chat_handler = None + + def init_vsplit_chat_handler(self): + if self.vsplit_chat_handler is None: + self.vsplit_chat_handler = VSplitChatHandler(self.nvim) + + @pynvim.command("CopilotChatVsplit", nargs="1") + def copilot_agent_cmd(self, args: list[str]): + self.init_vsplit_chat_handler() + if self.vsplit_chat_handler: + file_type = self.nvim.current.buffer.options["filetype"] + self.vsplit_chat_handler.vsplit() + # Get code from the unnamed register + code = self.nvim.eval("getreg('\"')") + self.vsplit_chat_handler.chat(args[0], file_type, code) + + @pynvim.command("CopilotChatVsplitVisual", nargs="1", range="") + def copilot_agent_visual_cmd(self, args: list[str], range: list[int]): + self.init_vsplit_chat_handler() + if self.vsplit_chat_handler: + file_type = self.nvim.current.buffer.options["filetype"] + code_lines = self.nvim.current.buffer[range[0] - 1 : range[1]] + code = "\n".join(code_lines) + self.vsplit_chat_handler.vsplit() + self.vsplit_chat_handler.chat(args[0], file_type, code) + + def init_inplace_chat_handler(self): + if self.inplace_chat_handler is None: + self.inplace_chat_handler = InPlaceChatHandler(self.nvim) + + # Those commands are used by the plugin, internal use only + @pynvim.command(PLUGIN_MAPPING_CMD, nargs="*") + def plugin_mapping_cmd(self, args): + bufnr, mapping = args + self.nvim.key_mapper.execute(bufnr, mapping) + + @pynvim.command(PLUGIN_AUTOCMD_CMD, nargs="*") + def plugin_autocmd_cmd(self, args): + event, id, bufnr = args + self.nvim.autocmd_mapper.execute(event, id, bufnr) + + @pynvim.command("CopilotChatInPlace", nargs="*", range="") + def inplace_cmd(self, args: list[str], range: list[int]): + self.init_inplace_chat_handler() + if self.inplace_chat_handler: + file_type = self.nvim.current.buffer.options["filetype"] + code_lines = self.nvim.current.buffer[range[0] - 1 : range[1]] + code = "\n".join(code_lines) + user_buffer = self.nvim.current.buffer + self.inplace_chat_handler.mount(code, file_type, range, user_buffer) diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py index 59c25594..9c22f018 100644 --- a/rplugin/python3/copilot-plugin.py +++ b/rplugin/python3/copilot-plugin.py @@ -55,20 +55,43 @@ def copilotChat(self, args: List[str]): # Get the view option from the command view_option = self.nvim.eval("g:copilot_chat_view_option") + buffers = self.nvim.buffers + + existing_buffer = next( + (buf for buf in buffers if os.path.basename(buf.name) == "CopilotChat"), + None, + ) + # Check if we're already in a chat buffer - if self.nvim.eval("getbufvar(bufnr(), '&buftype')") != "nofile": + if existing_buffer is None: # Create a new scratch buffer to hold the chat if view_option == "split": - self.nvim.command("vnew") + self.nvim.command("vnew CopilotChat") else: - self.nvim.command("enew") + self.nvim.command("e CopilotChat") # Set the buffer type to nofile and hide it when it's not active self.nvim.command("setlocal buftype=nofile bufhidden=hide noswapfile") # Set filetype as markdown and wrap with linebreaks self.nvim.command("setlocal filetype=markdown wrap linebreak") + buf = self.nvim.current.buffer + else: + # Use the existing buffer + buf = existing_buffer + + # Check if the buffer is already open in a window + win_id = self.nvim.funcs.bufwinnr(buf.number) + + if win_id != -1: + # If the buffer is open in a window, switch to that window + self.nvim.funcs.win_gotoid(win_id) + else: + # If the buffer is not open in a window, open it in a new split + if view_option == "split": + self.nvim.command("vsplit " + buf.name) + else: + self.nvim.command("e " + buf.name) + self.nvim.command("setlocal filetype=markdown wrap linebreak") - # Get the current buffer - buf = self.nvim.current.buffer self.nvim.api.buf_set_option(buf, "fileencoding", "utf-8") # Add start separator @@ -84,9 +107,8 @@ def copilotChat(self, args: List[str]): 'require("CopilotChat.utils").log_info(...)', f"Prompt: {prompt}" ) - system_prompt = prompts.COPILOT_INSTRUCTIONS # Add chat messages - for token in self.copilot.ask(system_prompt, prompt, code, language=file_type): + for token in self.copilot.ask(None, prompt, code, language=file_type): self.nvim.exec_lua( 'require("CopilotChat.utils").log_info(...)', f"Token: {token}" ) diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index 0310b931..ee471b64 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -5,6 +5,7 @@ from typing import Dict, List import dotenv +import prompts import requests import typings import utilities @@ -86,17 +87,26 @@ def authenticate(self): self.token = self.session.get(url, headers=headers).json() - def ask(self, system_prompt: str, prompt: str, code: str, language: str = ""): + def ask( + self, + system_prompt: str, + prompt: str, + code: str, + language: str = "", + model: str = "gpt-4", + ): if not self.token: self.authenticate() # If expired, reauthenticate if self.token.get("expires_at") <= round(time.time()): self.authenticate() + if not system_prompt: + system_prompt = prompts.COPILOT_INSTRUCTIONS url = "https://api.githubcopilot.com/chat/completions" self.chat_history.append(typings.Message(prompt, "user")) data = utilities.generate_request( - self.chat_history, code, language, system_prompt=system_prompt + self.chat_history, code, language, system_prompt=system_prompt, model=model ) full_response = "" @@ -140,7 +150,7 @@ def ask(self, system_prompt: str, prompt: str, code: str, language: str = ""): self.chat_history.append(typings.Message(full_response, "system")) - def _get_embeddings(self, inputs: List[typings.FileExtract]): + def _get_embeddings(self, inputs: list[typings.FileExtract]): embeddings = [] url = "https://api.githubcopilot.com/embeddings" # If we have more than 18 files, we need to split them into multiple requests @@ -195,7 +205,7 @@ def main(): code = get_input(session, "\n\nCode: \n") print("\n\nAI Response:") - for response in copilot.ask(user_prompt, code): + for response in copilot.ask(None, user_prompt, code): print(response, end="", flush=True) diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py new file mode 100644 index 00000000..ebcdfac6 --- /dev/null +++ b/rplugin/python3/handlers/chat_handler.py @@ -0,0 +1,209 @@ +from typing import Optional, cast + +import prompts as prompts +from copilot import Copilot +from mypynvim.core.buffer import MyBuffer +from mypynvim.core.nvim import MyNvim + + +def is_module_installed(name): + try: + __import__(name) + return True + except ImportError: + return False + + +class ChatHandler: + def __init__(self, nvim: MyNvim, buffer: MyBuffer): + self.nvim: MyNvim = nvim + self.copilot = None + self.buffer: MyBuffer = buffer + + # public + + def chat( + self, + prompt: str, + filetype: str, + code: str = "", + winnr: int = 0, + system_prompt: Optional[str] = None, + disable_start_separator: bool = False, + disable_end_separator: bool = False, + model: str = "gpt-4", + ): + if system_prompt is None: + system_prompt = self._construct_system_prompt(prompt) + + # Start the spinner + self.nvim.exec_lua('require("CopilotChat.spinner").show()') + + self.nvim.exec_lua( + 'require("CopilotChat.utils").log_info(...)', f"Chatting with {model} model" + ) + + if not disable_start_separator: + self._add_start_separator(system_prompt, prompt, code, filetype, winnr) + + self._add_chat_messages(system_prompt, prompt, code, filetype, model) + + # Stop the spinner + self.nvim.exec_lua('require("CopilotChat.spinner").hide()') + + if not disable_end_separator: + self._add_end_separator() + + # private + + def _construct_system_prompt(self, prompt: str): + system_prompt = prompts.COPILOT_INSTRUCTIONS + if prompt == prompts.FIX_SHORTCUT: + system_prompt = prompts.COPILOT_FIX + elif prompt == prompts.TEST_SHORTCUT: + system_prompt = prompts.COPILOT_TESTS + elif prompt == prompts.EXPLAIN_SHORTCUT: + system_prompt = prompts.COPILOT_EXPLAIN + return system_prompt + + def _add_start_separator( + self, + system_prompt: str, + prompt: str, + code: str, + file_type: str, + winnr: int, + ): + if is_module_installed("tiktoken"): + self._add_start_separator_with_token_count( + system_prompt, prompt, code, file_type, winnr + ) + else: + self._add_regular_start_separator( + system_prompt, prompt, code, file_type, winnr + ) + + def _add_regular_start_separator( + self, + system_prompt: str, + prompt: str, + code: str, + file_type: str, + winnr: int, + ): + if code: + code = f"\n \nCODE:\n```{file_type}\n{code}\n```" + + last_row_before = len(self.buffer.lines()) + system_prompt_height = len(system_prompt.split("\n")) + code_height = len(code.split("\n")) + + start_separator = f"""### User + +SYSTEM PROMPT: +``` +{system_prompt} +``` +{prompt}{code} + +### Copilot + +""" + self.buffer.append(start_separator.split("\n")) + + self._add_folds(code, code_height, last_row_before, system_prompt_height, winnr) + + def _add_start_separator_with_token_count( + self, + system_prompt: str, + prompt: str, + code: str, + file_type: str, + winnr: int, + ): + import tiktoken + + encoding = tiktoken.encoding_for_model("gpt-4") + + num_total_tokens = len(encoding.encode(f"{system_prompt}\n{prompt}\n{code}")) + num_system_tokens = len(encoding.encode(system_prompt)) + num_prompt_tokens = len(encoding.encode(prompt)) + num_code_tokens = len(encoding.encode(code)) + + if code: + code = f"\n \nCODE: {num_code_tokens} Tokens \n```{file_type}\n{code}\n```" + + last_row_before = len(self.buffer.lines()) + system_prompt_height = len(system_prompt.split("\n")) + code_height = len(code.split("\n")) + + start_separator = f"""### User + +SYSTEM PROMPT: {num_system_tokens} Tokens +``` +{system_prompt} +``` +{prompt}{code} + +### Copilot + +""" + self.buffer.append(start_separator.split("\n")) + + last_row_after = last_row_before + system_prompt_height + 5 + self.buffer.eol(last_row_before, f"{num_total_tokens} Total Tokens", "@float") + self.buffer.eol( + last_row_after, f"{num_prompt_tokens} Tokens", "NightflySteelBlue" + ) + + self._add_folds(code, code_height, last_row_before, system_prompt_height, winnr) + + def _add_folds( + self, + code: str, + code_height: int, + last_row_before: int, + system_prompt_height: int, + winnr: int, + ): + system_fold_start = last_row_before + 2 + system_fold_end = system_fold_start + system_prompt_height + 3 + main_command = f"{system_fold_start}, {system_fold_end} fold | normal! Gzz" + full_command = f"call win_execute({winnr}, '{main_command}')" + self.nvim.command(full_command) + + if code != "": + code_fold_start = system_fold_end + 2 + code_fold_end = code_fold_start + code_height - 1 + main_command = f"{code_fold_start}, {code_fold_end} fold | normal! G" + full_command = f"call win_execute({winnr}, '{main_command}')" + self.nvim.command(full_command) + + def _add_chat_messages( + self, system_prompt: str, prompt: str, code: str, file_type: str, model: str + ): + if self.copilot is None: + self.copilot = Copilot() + + for token in self.copilot.ask( + system_prompt, prompt, code, language=cast(str, file_type), model=model + ): + self.nvim.exec_lua( + 'require("CopilotChat.utils").log_info(...)', f"Token: {token}" + ) + buffer_lines = cast(list[str], self.buffer.lines()) + last_line_row = len(buffer_lines) - 1 + last_line_col = len(buffer_lines[-1]) + + self.nvim.api.buf_set_text( + self.buffer.number, + last_line_row, + last_line_col, + last_line_row, + last_line_col, + token.split("\n"), + ) + + def _add_end_separator(self): + end_separator = "\n---\n" + self.buffer.append(end_separator.split("\n")) diff --git a/rplugin/python3/handlers/inplace_chat_handler.py b/rplugin/python3/handlers/inplace_chat_handler.py new file mode 100644 index 00000000..500bea20 --- /dev/null +++ b/rplugin/python3/handlers/inplace_chat_handler.py @@ -0,0 +1,324 @@ +import prompts as system_prompts +from handlers.chat_handler import ChatHandler +from mypynvim.core.buffer import MyBuffer +from mypynvim.core.nvim import MyNvim +from mypynvim.ui_components.layout import Box, Layout +from mypynvim.ui_components.popup import PopUp + +from . import prompts + +# Define constants for the models +MODEL_GPT4 = "gpt-4" +MODEL_GPT35_TURBO = "gpt-3.5-turbo" + + +# TODO: change the layout, e.g: move to right side of the screen +# TODO: Abort request if the user closes the layout +class InPlaceChatHandler: + """This class handles in-place chat functionality.""" + + def __init__(self, nvim: MyNvim): + """Initialize the InPlaceChatHandler with the given nvim instance.""" + self.nvim: MyNvim = nvim + self.diff_mode: bool = False + self.model: str = MODEL_GPT4 + self.system_prompt: str = "SENIOR_DEVELOPER_PROMPT" + + # Add user prompts collection + self.user_prompts = self.nvim.eval("g:copilot_chat_user_prompts") + self.current_user_prompt = 0 + + # Initialize popups + self.original_popup = PopUp(nvim, title="Original") + self.copilot_popup = PopUp( + nvim, + title=f"Copilot ({self.model}, {self.system_prompt})", + opts={"wrap": True, "linebreak": True}, + ) + self.prompt_popup = PopUp( + nvim, title="Prompt", enter=True, padding={"left": 1, "right": 1} + ) + self.help_popup = PopUp(nvim, title="Help") + + self.popups = [ + self.original_popup, + self.copilot_popup, + self.prompt_popup, + ] + + # Initialize layout base on help text option + self.help_popup_visible = self.nvim.eval("g:copilot_chat_show_help") == "yes" + if self.help_popup_visible: + self.layout = self._create_layout() + self.popups.append(self.help_popup) + else: + self.layout = self._create_layout_without_help() + + # Initialize chat handler + self.chat_handler = ChatHandler(nvim, self.copilot_popup.buffer) + + # Set keymaps and help content + self._set_keymaps() + self._set_help_content() + + def _create_layout(self): + """Create the layout for the chat handler.""" + return Layout( + self.nvim, + Box( + [ + Box( + [ + Box([self.original_popup]), + Box([self.copilot_popup]), + ], + size=["50%", "50%"], + direction="row", + ), + Box( + [Box([self.prompt_popup]), Box([self.help_popup])], + size=["50%", "50%"], + direction="row", + ), + ], + size=["80%", "20%"], + direction="col", + ), + width="80%", + height="60%", + relative="editor", + row="50%", + col="50%", + ) + + def _create_layout_without_help(self): + """Create the layout with help for the chat handler.""" + return Layout( + self.nvim, + Box( + [ + Box( + [ + Box([self.original_popup]), + Box([self.copilot_popup]), + ], + size=["50%", "50%"], + direction="row", + ), + Box( + [Box([self.prompt_popup]), Box([])], + size=["100%", "0%"], + direction="row", + ), + ], + size=["80%", "20%"], + direction="col", + ), + width="80%", + height="60%", + relative="editor", + row="50%", + col="50%", + ) + + def mount( + self, original_code: str, filetype: str, range: list[int], user_buffer: MyBuffer + ): + """Mount the chat handler with the given parameters.""" + self.original_code = original_code + self.filetype = filetype + self.range = [range[0] - 1, range[1]] + self.user_buffer = user_buffer + + self.original_popup.buffer.lines(original_code) + self.original_popup.buffer.options["filetype"] = filetype + + self.copilot_popup.buffer.options["filetype"] = "markdown" + + self.layout.mount() + + def _replace_original_code(self): + """Replace the original code with the new code.""" + new_lines = self.copilot_popup.buffer.lines() + if new_lines[0].startswith("```"): + new_lines = new_lines[1:-1] + self.user_buffer.lines(new_lines, self.range[0], self.range[1]) + self.layout.unmount() + self.nvim.command("norm! ^") + + def _diff(self): + """Show the difference between the original code and the new code.""" + if not self.diff_mode: + self.original_popup.window.command("diffthis") + self.copilot_popup.window.command("diffthis") + self.diff_mode = True + else: + self.original_popup.window.command("diffoff") + self.diff_mode = False + + def _chat(self): + """Start a chat session.""" + self.copilot_popup.buffer.lines("") + self.copilot_popup.window.command("norm! gg") + prompt_lines = self.prompt_popup.buffer.lines() + prompt = "\n".join(prompt_lines) + self.chat_handler.chat( + prompt, + self.filetype, + self.original_code, + self.copilot_popup.window.handle, + system_prompt=system_prompts.__dict__[self.system_prompt], + disable_start_separator=True, + disable_end_separator=True, + model=self.model, + ) + + def _set_prompt(self, prompt: str): + self.prompt_popup.buffer.lines(prompt) + + def _set_user_prompt(self): + self.current_user_prompt = (self.current_user_prompt + 1) % len( + self.user_prompts + ) + prompt = list(self.user_prompts.keys())[self.current_user_prompt] + self.prompt_popup.buffer.lines(self.user_prompts[prompt]) + + def _toggle_model(self): + if self.model == MODEL_GPT4: + self.model = MODEL_GPT35_TURBO + else: + self.model = MODEL_GPT4 + self.copilot_popup.original_config.title = ( + f"Copilot ({self.model}, {self.system_prompt})" + ) + self.copilot_popup.config.title = ( + f"Copilot ({self.model}, {self.system_prompt})" + ) + self.copilot_popup.unmount() + self.copilot_popup.mount(controlled=True) + + def _toggle_system_model(self): + # Create a list of all system prompts and add the current system prompt + system_prompts = [ + "SENIOR_DEVELOPER_PROMPT", + "COPILOT_EXPLAIN", + "COPILOT_TESTS", + "COPILOT_FIX", + "COPILOT_WORKSPACE", + "TEST_SHORTCUT", + "EXPLAIN_SHORTCUT", + "FIX_SHORTCUT", + ] + + # Get the index of the current system prompt + current_system_prompt_index = system_prompts.index(self.system_prompt) + + # Set the next system prompt + self.system_prompt = system_prompts[ + (current_system_prompt_index + 1) % len(system_prompts) + ] + + self.copilot_popup.original_config.title = ( + f"Copilot ({self.model}, {self.system_prompt})" + ) + self.copilot_popup.config.title = ( + f"Copilot ({self.model}, {self.system_prompt})" + ) + self.copilot_popup.unmount() + self.copilot_popup.mount(controlled=True) + + def _set_keymaps(self): + """Set the keymaps for the chat handler.""" + self.prompt_popup.map("n", "", lambda: self._chat()) + self.prompt_popup.map("n", "", lambda: self._replace_original_code()) + self.prompt_popup.map("n", "", lambda: self._diff()) + self.prompt_popup.map("n", "", lambda cb=self._toggle_model: cb()) + self.prompt_popup.map("n", "", lambda cb=self._toggle_system_model: cb()) + + self.prompt_popup.map( + "n", "'", lambda: self._set_prompt(prompts.PROMPT_SIMPLE_DOCSTRING) + ) + self.prompt_popup.map( + "n", "s", lambda: self._set_prompt(prompts.PROMPT_SEPARATE) + ) + + self.prompt_popup.map( + "i", "", lambda: (self.nvim.feed(""), self._chat()) + ) + + self.prompt_popup.map( + "n", + "", + lambda: self._set_user_prompt(), + ) + + for i, popup in enumerate(self.popups): + popup.buffer.map("n", "q", lambda: self.layout.unmount()) + popup.buffer.map("n", "", lambda: self._clear_chat_history()) + popup.buffer.map("n", "?", lambda: self._toggle_help()) + popup.buffer.map( + "n", + "", + lambda i=i: self.popups[(i + 1) % len(self.popups)].focus(), + ) + + def _clear_chat_history(self): + """Clear the chat history in the copilot popup.""" + self.copilot_popup.buffer.lines([]) + + def _set_help_content(self): + """Set the content for the help popup.""" + help_content = [ + "Navigation:", + " : Switch focus between popups", + " q: Close layout", + " ?: Toggle help content", + "", + "Chat in Normal Mode:", + " : Submit prompt to Copilot", + " : Replace old code with new", + " : Show code differences", + " : Clear chat history", + "", + "Chat in Insert Mode:", + " : Start chat and submit prompt to Copilot", + "", + "Prompt Binding:", + " ': Set prompt to SIMPLE_DOCSTRING", + " s: Set prompt to SEPARATE", + " : Set prompt to next item in user prompts", + "", + "Model:", + " : Toggle AI model", + " : Set system prompt to next item in system prompts", + "", + "User prompts:", + ] + + for prompt in self.user_prompts: + help_content.append(f" {prompt}: {self.user_prompts[prompt]}") + + self.help_popup.buffer.lines(help_content) + + def _toggle_help(self): + """Toggle the visibility of the help popup.""" + self.layout.unmount() + if self.help_popup_visible: + self.popups = [ + self.original_popup, + self.copilot_popup, + self.prompt_popup, + ] + self.layout = self._create_layout_without_help() + else: + self.popups = [ + self.original_popup, + self.copilot_popup, + self.prompt_popup, + self.help_popup, + ] + self.layout = self._create_layout() + + self.help_popup_visible = not self.help_popup_visible + self._set_keymaps() + self.layout.mount() diff --git a/rplugin/python3/handlers/prompts.py b/rplugin/python3/handlers/prompts.py new file mode 100644 index 00000000..f12db7c7 --- /dev/null +++ b/rplugin/python3/handlers/prompts.py @@ -0,0 +1,2 @@ +PROMPT_SIMPLE_DOCSTRING = "add simple docstring to this code" +PROMPT_SEPARATE = "add comments separating the code into sections" diff --git a/rplugin/python3/handlers/vsplit_chat_handler.py b/rplugin/python3/handlers/vsplit_chat_handler.py new file mode 100644 index 00000000..c4a73736 --- /dev/null +++ b/rplugin/python3/handlers/vsplit_chat_handler.py @@ -0,0 +1,38 @@ +from handlers.chat_handler import ChatHandler +from mypynvim.core.buffer import MyBuffer +from mypynvim.core.nvim import MyNvim + + +class VSplitChatHandler(ChatHandler): + def __init__(self, nvim: MyNvim): + self.nvim: MyNvim = nvim + self.copilot = None + self.buffer: MyBuffer = MyBuffer.new( + self.nvim, + { + "filetype": "markdown", + }, + ) + + def vsplit(self): + var_key = "copilot_chat" + for window in self.nvim.windows: + try: + if window.vars[var_key]: + self.nvim.current.window = window + return + except: + pass + + self.buffer.vsplit( + { + "wrap": True, + "linebreak": True, + "conceallevel": 2, + "concealcursor": "n", + } + ) + self.nvim.current.window.vars[var_key] = True + + def chat(self, prompt: str, filetype: str, code: str = ""): + super().chat(prompt, filetype, code, self.nvim.current.window.handle) diff --git a/rplugin/python3/mypynvim/core/autocmdmapper.py b/rplugin/python3/mypynvim/core/autocmdmapper.py new file mode 100644 index 00000000..7ba731b8 --- /dev/null +++ b/rplugin/python3/mypynvim/core/autocmdmapper.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Union + +if TYPE_CHECKING: + from .nvim import MyNvim + + +class AutocmdMapper: + def __init__(self, nvim: MyNvim): + self.nvim: MyNvim = nvim + self._autocmd_callback_library = {} + + def buf_set( + self, + events: Union[str, list[str]], + id: str, + bufnr: int, + callback: Callable, + ): + if isinstance(events, str): + events = [events] + + for e in events: + if not self._autocmd_callback_library.get(e): + self._autocmd_callback_library[e] = {} + + if not self._autocmd_callback_library[e].get(id): + self._autocmd_callback_library[e][id] = {} + + self._autocmd_callback_library[e][id][str(bufnr)] = callback + + self.nvim.api.create_autocmd( + e, + { + "buffer": bufnr, + "command": f"{self.nvim._autocmd_command} {e} {id} {bufnr}", + }, + ) + + def execute(self, event: str, id: str, bufnr: int): + try: + self._autocmd_callback_library[event][id][bufnr]() + except KeyError: + self.nvim.notify(f"KeyError: {event} {id} {bufnr}") diff --git a/rplugin/python3/mypynvim/core/buffer.py b/rplugin/python3/mypynvim/core/buffer.py new file mode 100644 index 00000000..9bf1f4c0 --- /dev/null +++ b/rplugin/python3/mypynvim/core/buffer.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable, Dict, LiteralString, Union + +from pynvim.api import Buffer + +if TYPE_CHECKING: + from .nvim import MyNvim + + +class MyBuffer(Buffer): + def __init__(self, nvim: MyNvim, buffer: Buffer, opts: dict[str, Any] = {}): + self.buf: Buffer = buffer + self.nvim: MyNvim = nvim + self.namespace: int = self.nvim.api.create_namespace(str(self.buf.handle)) + self.option(opts) + + def __getattr__(self, attr): + return getattr(self.buf, attr) + + @classmethod + def new(cls, nvim: MyNvim, opts: dict[str, Any] = {}): + buffer = nvim.api.create_buf(False, True) + return cls(nvim, buffer, opts) + + # mutate methods + + def option(self, option: Union[str, Dict[str, Any]], value: Any = None): + if isinstance(option, str): + if value is None: + return self.nvim.api.buf_get_option(self.buf.handle, option) + else: + self.nvim.api.buf_set_option(self.buf.handle, option, value) + elif isinstance(option, dict): + for opt, val in option.items(): + self.nvim.api.buf_set_option(self.buf.handle, opt, val) + + def map(self, modes: Union[str, list[str]], lhs: str, rhs: Union[str, Callable]): + if isinstance(modes, str): + modes = [modes] + for mode in modes: + self.nvim.key_mapper.buf_set(self.buf.handle, mode, lhs, rhs) + + def autocmd(self, event: Union[str, list[str]], id: str, callback: Callable): + self.nvim.autocmd_mapper.buf_set(event, id, self.handle, callback) + + def var(self, name: str, value: Any = None): + if value is None: + return self.nvim.api.buf_get_var(self.buf.handle, name) + else: + self.nvim.api.buf_set_var(self.buf.handle, name, value) + + def lines( + self, + replacement: Union[str, list[str], None] = None, + start: int = 0, + end: int = -1, + ) -> list[str]: + if replacement is not None: + if isinstance(replacement, str): + replacement = replacement.split("\n") + self.nvim.api.buf_set_lines(self.buf.handle, start, end, False, replacement) + return self.nvim.api.buf_get_lines(self.buf.handle, start, end, False) + + def clear(self): + self.nvim.api.buf_set_lines(self.buf.handle, 0, -1, True, []) + + def append(self, lines: list[str] | list[LiteralString]): + self.nvim.api.buf_set_lines(self.buf.handle, -1, -1, True, lines) + + # extmark methods + + def eol(self, line: int, content: str = "", hl_group: str = "Normal"): + self.nvim.api.buf_set_extmark( + self.buf.number, + self.namespace, + line, # row + 0, # col + { + "virt_text": [[content, hl_group]], + "virt_text_pos": "eol", + }, + ) + + # mount methods + + def vsplit(self, opts: dict[str, Any] = {}): + self.nvim.api.command(f"vsplit | buffer {self.buf.handle}") + winnr = self.nvim.api.get_current_win() + for opt, val in opts.items(): + self.nvim.api.win_set_option(winnr, opt, val) diff --git a/rplugin/python3/mypynvim/core/keymapper.py b/rplugin/python3/mypynvim/core/keymapper.py new file mode 100644 index 00000000..4c93ee6c --- /dev/null +++ b/rplugin/python3/mypynvim/core/keymapper.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Union + +if TYPE_CHECKING: + from .nvim import MyNvim + + +class Keymapper: + def __init__(self, nvim: MyNvim): + self.nvim: MyNvim = nvim + self._keymap_callback_library = {} + + def buf_set( + self, + bufnr: int, + mode: str, + lhs: str, + rhs: Union[str, Callable], + ): + if callable(rhs): + escaped_lhs = lhs.replace("<", "_").replace(">", "_") + if not self._keymap_callback_library.get(str(bufnr)): + self._keymap_callback_library[str(bufnr)] = {} + self._keymap_callback_library[str(bufnr)][escaped_lhs] = rhs + rhs = f"{self.nvim._mapping_command} {bufnr} {escaped_lhs}" + + self.nvim.api.buf_set_keymap(bufnr, mode, lhs, rhs, {"noremap": True}) + + def execute(self, bufnr: int, mapping: str): + if bufnr is not None: + callback = self._keymap_callback_library[bufnr][mapping] + if callable(callback): + callback() diff --git a/rplugin/python3/mypynvim/core/nvim.py b/rplugin/python3/mypynvim/core/nvim.py new file mode 100644 index 00000000..4eeba15d --- /dev/null +++ b/rplugin/python3/mypynvim/core/nvim.py @@ -0,0 +1,96 @@ +from typing import Iterable, Union + +from pynvim import Nvim +from pynvim.api.nvim import Current + +from .autocmdmapper import AutocmdMapper +from .buffer import MyBuffer +from .keymapper import Keymapper +from .window import MyWindow + + +class MyNvim(Nvim): + def __init__(self, nvim: Nvim, mapping_command: str, autocmd_command: str): + self.nvim: Nvim = nvim + self.key_mapper = Keymapper(self) + self.autocmd_mapper = AutocmdMapper(self) + self.current = MyCurrent(self) + self._mapping_command = mapping_command + self._autocmd_command = autocmd_command + + def __getattr__(self, attr): + return getattr(self.nvim, attr) + + # native api methods + + def notify( + self, msg: Union[str, int, bool, list[str], list[int]], level: str = "info" + ): + if isinstance(msg, str): + msg = msg.split("\n") + if len(msg) == 1: + msg = f'"{msg[0]}"' + self.nvim.exec_lua(f"vim.notify(({msg}), '{level}')") + return + else: + msg = str(msg) + msg = "{" + msg[1:-1] + "}" + elif isinstance(msg, Iterable): + msg = str(msg) + msg = "{" + msg[1:-1] + "}" + else: + msg = f"{msg}" + + self.nvim.exec_lua(f"vim.notify(vim.inspect({msg}), '{level}')") + + # custom api methods + + def feed(self, keys: str, mode: str = "n"): + codes = self.nvim.api.replace_termcodes(keys, True, True, True) + self.nvim.api.feedkeys(codes, mode, False) + + def move_cursor_to_previous_window(self): + self.feed("p") + + # window methods + + @property + def windows(self) -> list[MyWindow]: + return [MyWindow(self, win) for win in self.nvim.windows] + + def win(self, winnr: int) -> MyWindow: + return MyWindow(self, self.nvim.windows[winnr]) + + # buffer methods + + @property + def buffers(self) -> list[MyBuffer]: + return [MyBuffer(self, buf) for buf in self.nvim.buffers] + + def buf(self, bufnr: int) -> MyBuffer: + return MyBuffer(self, self.nvim.buffers[bufnr]) + + +class MyCurrent(Current): + def __init__(self, mynvim: MyNvim): + self.mynvim: MyNvim = mynvim + self.nvim = mynvim.nvim + + def __getattr__(self, attr): + return getattr(self.nvim.current, attr) + + @property + def buffer(self) -> MyBuffer: + return MyBuffer(self.mynvim, self.nvim.request("nvim_get_current_buf")) + + @buffer.setter + def buffer(self, buffer: Union[MyBuffer, int]) -> None: + return self.nvim.request("nvim_set_current_buf", buffer) + + @property + def window(self) -> MyWindow: + return MyWindow(self.mynvim, self.nvim.request("nvim_get_current_win")) + + @window.setter + def window(self, window: Union[MyWindow, int]) -> None: + return self.mynvim.request("nvim_set_current_win", window) diff --git a/rplugin/python3/mypynvim/core/window.py b/rplugin/python3/mypynvim/core/window.py new file mode 100644 index 00000000..0aadfc4f --- /dev/null +++ b/rplugin/python3/mypynvim/core/window.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from pynvim.api import Window + +from .buffer import MyBuffer + +if TYPE_CHECKING: + from .nvim import MyNvim + + +class MyWindow(Window): + def __init__(self, nvim: MyNvim, window: Window): + self.win: Window = window + self.nvim: MyNvim = nvim + + def __getattr__(self, attr): + return getattr(self.win, attr) + + def command(self, command: str): + full_command = f"call win_execute({self.win.handle}, '{command}')" + self.nvim.command(full_command) + + @property + def buffer(self) -> MyBuffer: + return MyBuffer( + self.nvim, self.nvim.request("nvim_win_get_buf", self.win.handle) + ) + + @buffer.setter + def buffer(self, buffer: MyBuffer): + return self.nvim.request("nvim_win_set_buf", self.win.handle, buffer.handle) diff --git a/rplugin/python3/mypynvim/ui_components/calculator.py b/rplugin/python3/mypynvim/ui_components/calculator.py new file mode 100644 index 00000000..1f19d4ab --- /dev/null +++ b/rplugin/python3/mypynvim/ui_components/calculator.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Literal, Union + +from mypynvim.core.nvim import MyNvim + +if TYPE_CHECKING: + from .popup import PopUpConfiguration + + +@dataclass +class Calculator: + nvim: "MyNvim" + + def absolute(self, config: PopUpConfiguration) -> PopUpConfiguration: + return self._convert_relative_values_to_absolute(config) + + def center(self, config: PopUpConfiguration) -> PopUpConfiguration: + config = self._convert_relative_values_to_absolute(config) + config = self._center_configuration_row_col(config) + return config + + def _center_configuration_row_col(self, config: PopUpConfiguration): + """Centers the row and col properties based on width and height.""" + if config.relative in ["editor", "win"]: + config.row = int(config.row) - int(config.height) // 2 - 1 + config.col = int(config.col) - int(config.width) // 2 - 1 + return config + + def _convert_relative_values_to_absolute(self, config: PopUpConfiguration): + """Converts relative values to absolute values.""" + for property in ["width", "height", "row", "col"]: + current_value = getattr(config, property) + absolute_value = self._percentage_to_absolute( + current_value, config.relative, property + ) + setattr(config, property, absolute_value) + return config + + def _percentage_to_absolute( + self, + value: Union[int, str], + relative: Literal["editor", "win", "cursor"], + property: str, + ) -> int: + """ + Converts percentage string values to absolute int values. + If the value is already an int, it is returned as is. + """ + + if isinstance(value, int): + return value + + max = self._get_max_value_for_property(relative, property) + return int(int(value.rstrip("%")) / 100.0 * max) + + def _get_max_value_for_property( + self, relative: Literal["editor", "win", "cursor"], property: str + ) -> int: + """Based on relative configuration, returns the max value for a given property.""" + + def get_editor_dimensions(): + source = self.nvim.options + width, height = source["columns"], source["lines"] + return {"row": height, "col": width, "width": width, "height": height} + + def get_window_dimensions(): + source = self.nvim.current.window + width, height = source.width, source.height + return {"row": height, "col": width, "width": width, "height": height} + + max_values = { + "editor": get_editor_dimensions(), + "win": get_window_dimensions(), + "cursor": get_window_dimensions(), + } + return max_values[relative][property] diff --git a/rplugin/python3/mypynvim/ui_components/layout.py b/rplugin/python3/mypynvim/ui_components/layout.py new file mode 100644 index 00000000..dfea8c87 --- /dev/null +++ b/rplugin/python3/mypynvim/ui_components/layout.py @@ -0,0 +1,211 @@ +from dataclasses import dataclass +from typing import Callable, Literal, Optional, Union, cast + +from mypynvim.core.nvim import MyNvim + +from .calculator import Calculator +from .popup import PopUp +from .types import PopUpConfiguration, Relative + + +class Box: + width: int = 0 + height: int = 0 + row: int = 0 + col: int = 0 + relative: Relative = "editor" + + def __init__( + self, + items: Union[list["Box"], list[PopUp]], + size: list[str] = ["100%"], + direction: Literal["row", "col"] = "row", + gap: int = 0, + ): + """Initialize a Box with items, size, direction and gap.""" + self.size = size + self.direction = direction + self.items = items + self.gap = gap + + def set_base_dimensions( + self, width: int, height: int, row: int, col: int, relative: Relative + ): + """Set the base dimensions of the Box.""" + self.width = width + self.height = height + self.row = row + self.col = col + self.relative = relative + + self.last_child_row = row + self.last_child_col = col + + def mount(self): + """Mount the Box.""" + if self._has_box_items(): + for child in self.items: + cast(Box, child).mount() + elif self._has_popup_items(): + for child in self.items: + cast(PopUp, child).mount(controlled=True) + + def unmount(self): + """Unmount the Box.""" + for child in self.items: + child.unmount() + + def process(self): + """Process the Box.""" + for child in self.items: + child.set_layout(self.layout) + + self._process_size() + if self._has_box_items(): + self._process_box_items() + if self._has_popup_items(): + self._process_popup_items() + + def set_layout(self, layout: "Layout"): + self.layout = layout + + def _process_size(self): + """Compute the Box size to integers.""" + for index, size in enumerate(self.size): + if isinstance(size, str): + self.size[index] = size.rstrip("%") + + def _process_box_items(self): + """Process the Box items.""" + if self.direction == "row": + self._process_row_box_items() + elif self.direction == "col": + self._process_col_box_items() + + for child in self.items: + cast(Box, child).process() + + def _process_row_box_items(self): + """Process the row box items.""" + for index, child in enumerate(self.items): + offset = 0 if index == len(self.items) else self.gap + 2 + child_width = int(self.width / 100 * int(self.size[index])) - offset + child_col = self.last_child_col + (offset * index) + self.last_child_col = child_col + child_width - (offset * index) + cast(Box, child).set_base_dimensions( + width=child_width, + height=self.height, + row=self.row, + col=child_col, + relative=self.relative, + ) + + def _process_col_box_items(self): + """Process the column box items.""" + for index, child in enumerate(self.items): + offset = 0 if index == len(self.items) else self.gap + 2 + child_height = int(self.height / 100 * int(self.size[index])) - offset + 1 + child_row = self.last_child_row + (offset * index) + self.last_child_row = child_row + child_height - (offset * index) + cast(Box, child).set_base_dimensions( + width=self.width, + height=child_height, + row=child_row, + col=self.col, + relative=self.relative, + ) + + def _process_popup_items(self): + """Process the PopUp items.""" + for child in self.items: + cast(PopUp, child).define_controlled_configurations( + width=self.width, + height=self.height, + row=self.row, + col=self.col, + relative=self.relative, + ) + + def _has_box_items(self) -> bool: + """Check if the Box has Box items.""" + return isinstance(self.items, list) and all( + isinstance(item, Box) for item in self.items + ) + + def _has_popup_items(self) -> bool: + """Check if the Box has PopUp items.""" + return isinstance(self.items, list) and all( + isinstance(item, PopUp) for item in self.items + ) + + +@dataclass +class Layout: + nvim: MyNvim + box: Box + width: Union[int, str] + height: Union[int, str] + row: Union[int, str] + col: Union[int, str] + relative: Relative = "editor" + + last_popup: Optional[PopUp] = None + + mounting: bool = False + unmounting: bool = False + + has_been_mounted: bool = False + post_first_mount_callback: Optional[Callable] = None + + def _prepare_for_mount(self): + self._configure_popup() + self._calculate_absolute_config() + self._set_box_base_dimensions() + self.box.set_layout(self) + self.box.process() + + def _configure_popup(self): + self.config = PopUpConfiguration( + width=self.width, + height=self.height, + row=self.row, + col=self.col, + relative=self.relative, + ) + + def _calculate_absolute_config(self): + self.absolute_config = Calculator(self.nvim).center(self.config) + + def _set_box_base_dimensions(self): + self.box.set_base_dimensions( + width=int(self.absolute_config.width), + height=int(self.absolute_config.height), + row=int(self.absolute_config.row), + col=int(self.absolute_config.col), + relative=self.relative, + ) + + def mount(self): + """Mount the Layout. Focus the last popup if any.""" + self.mounting = True + self._prepare_for_mount() + self.box.mount() + self.mounting = False + + if not self.has_been_mounted: + self.has_been_mounted = True + if self.post_first_mount_callback: + self.post_first_mount_callback() + + if self.last_popup: + self.last_popup.focus() + + def unmount(self): + """Unmount the Layout.""" + self.unmounting = True + self.box.unmount() + self.unmounting = False + + def set_last_popup(self, popup: PopUp): + if not self.mounting and not self.unmounting: + self.last_popup = popup diff --git a/rplugin/python3/mypynvim/ui_components/popup.py b/rplugin/python3/mypynvim/ui_components/popup.py new file mode 100644 index 00000000..55b2c782 --- /dev/null +++ b/rplugin/python3/mypynvim/ui_components/popup.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +from copy import deepcopy +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, Unpack + +if TYPE_CHECKING: + from mypynvim.core.nvim import MyNvim + + from .layout import Layout + +if TYPE_CHECKING: + from mypynvim.core.nvim import MyNvim + +from mypynvim.core.buffer import MyBuffer +from mypynvim.core.window import MyWindow + +from .calculator import Calculator +from .types import PaddingKeys, PopUpArgs, PopUpConfiguration, Relative + + +@dataclass +class Padding: + top: int = 0 + right: int = 0 + bottom: int = 0 + left: int = 0 + + +class PopUp: + def __init__( + self, + nvim: MyNvim, + preset: Optional[PopUpConfiguration] = None, + padding: PaddingKeys = {}, + enter: bool = False, + opts={}, + **kwargs: Unpack[PopUpArgs], + ): + self.nvim: MyNvim = nvim + self.calculator: Calculator = Calculator(self.nvim) + self.enter: bool = enter + self.opts: Dict[str, Any] = opts + + # preset configuration + if preset is None: + preset = PopUpConfiguration() + for key, value in kwargs.items(): + setattr(preset, key, value) + self.original_config: PopUpConfiguration = preset + + # main window + self.buffer: MyBuffer = MyBuffer.new(self.nvim) + self._set_default_keymaps() + self._set_default_autocmds() + + # padding window + self.pd: Padding = Padding(**padding) + self.pd_buffer: MyBuffer = MyBuffer.new(self.nvim) + + def mount(self, controlled: bool = False): + """ + Mounts the PopUp. + + Resets the configuration to its original values when PopUp was instantiated. + Then computes the absolute values for the configuration. + Then mutate main window configuration & mounts the padding window if any padding is set. + Then mounts the main window. + """ + + # reset config to original + self.config: PopUpConfiguration = deepcopy(self.original_config) + self.pd_config: PopUpConfiguration = PopUpConfiguration() + + if controlled: + self._set_controlled_configurations() + else: + self._set_uncontrolled_configurations() + + # mount padding window if any padding is set + if self._has_padding(): + pd_window = self.nvim.api.open_win( + self.pd_buffer, False, self.pd_config.__dict__ + ) + self.pd_window = MyWindow(self.nvim, pd_window) + # mount main window + window = self.nvim.api.open_win(self.buffer, self.enter, self.config.__dict__) + self.window = MyWindow(self.nvim, window) + + self._set_main_window_options() + + def unmount(self): + """Unmounts the PopUp.""" + self.nvim.api.win_close(self.window, True) + if self._has_padding(): + self.nvim.api.win_close(self.pd_window, True) + + def map(self, mode: str, key: str, rhs: Union[str, Callable]): + """Maps a key to a function in the main window.""" + self.buffer.map(mode, key, rhs) + + def focus(self): + """Make the popup active.""" + self.nvim.current.window = self.window + + def set_layout(self, layout: Layout): + self.layout = layout + + def define_controlled_configurations( + self, width: int, height: int, row: int, col: int, relative: Relative = "editor" + ): + self.controlled_config = deepcopy(self.original_config) + self.controlled_config.width = width + self.controlled_config.height = height + self.controlled_config.row = row + self.controlled_config.col = col + self.controlled_config.relative = relative + + def _set_main_window_options(self): + for key, value in self.opts.items(): + self.window.options[key] = value + + def _set_controlled_configurations(self): + """Mutates the configuration of controlled PopUp.""" + self.config = self.controlled_config + if self._has_padding(): + self._mutate_configurations_for_padding() + + def _set_uncontrolled_configurations(self): + """Mutates the configuration of uncontrolled PopUp.""" + self.config = self.calculator.center(self.config) + if self._has_padding(): + self._mutate_configurations_for_padding() + + def _has_padding(self) -> bool: + return any([self.pd.top, self.pd.right, self.pd.bottom, self.pd.left]) + + def _mutate_configurations_for_padding(self): + """ + Mutates the configuration to account for padding. + + This is done by making the padding window takes place of the original window. + (Effectively replacing the original window with the padding window) + Then the original window is resized (shrinked) to fit within the padding window. + """ + + # set window config for padding window + vars(self.pd_config).update(vars(self.config)) + + # shrink content window + self.config.title = "" + self.config.width = int(self.config.width) - int(self.pd.left) - self.pd.right + self.config.height = int(self.config.height) - int(self.pd.top) - self.pd.bottom + self.config.row = int(self.config.row) + self.pd.top + 1 + self.config.col = int(self.config.col) + self.pd.left + 1 + self.config.border = "none" + + def _set_default_keymaps(self): + self.buffer.map("n", "q", lambda: self.unmount()) + + def _set_default_autocmds(self): + self.buffer.autocmd( + "BufEnter", + "update_last_popup_for_Layout", + lambda: self.layout.set_last_popup(self), + ) diff --git a/rplugin/python3/mypynvim/ui_components/types.py b/rplugin/python3/mypynvim/ui_components/types.py new file mode 100644 index 00000000..a8d2ac39 --- /dev/null +++ b/rplugin/python3/mypynvim/ui_components/types.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from typing import Dict, Literal, TypedDict, Union + +Relative = Literal["editor", "win", "cursor"] +PaddingKeys = Dict[Literal["top", "right", "bottom", "left"], int] + + +@dataclass +class PopUpConfiguration: + relative: Relative = "editor" + anchor: Literal["NW", "NE", "SW", "SE"] = "NW" + width: Union[int, str] = 40 + height: Union[int, str] = 10 + row: Union[int, str] = 0 + col: Union[int, str] = 0 + zindex: int = 500 + style: Literal["minimal"] = "minimal" + border: Union[ + list[str], + Literal["none", "single", "double", "solid", "shadow", "background"], + ] = "single" + title: str = "" + title_pos: Literal["left", "center", "right"] = "center" + noautocmd: bool = False + + +class PopUpArgs(TypedDict, total=False): + relative: Relative + anchor: Literal["NW", "NE", "SW", "SE"] + width: Union[int, str] + height: Union[int, str] + row: Union[int, str] + col: Union[int, str] + zindex: int + style: Literal["minimal"] + border: Union[ + list[str], + Literal["none", "single", "double", "solid", "shadow", "background"], + ] + title: str + title_pos: Literal["left", "center", "right"] + noautocmd: bool diff --git a/rplugin/python3/prompts.py b/rplugin/python3/prompts.py index 8cbb25e8..34a857a4 100644 --- a/rplugin/python3/prompts.py +++ b/rplugin/python3/prompts.py @@ -142,25 +142,34 @@ EXPLAIN_SHORTCUT = "Write a explanation for the code above as paragraphs of text." FIX_SHORTCUT = ( "There is a problem in this code. Rewrite the code to show it with the bug fixed." - "" ) - EMBEDDING_KEYWORDS = """You are a coding assistant who help the user answer questions about code in their workspace by providing a list of relevant keywords they can search for to answer the question. The user will provide you with potentially relevant information from the workspace. This information may be incomplete. DO NOT ask the user for additional information or clarification. DO NOT try to answer the user's question directly. + # Additional Rules + Think step by step: 1. Read the user's question to understand what they are asking about their workspace. + 2. If there are pronouns in the question, such as 'it', 'that', 'this', try to understand what they refer to by looking at the rest of the question and the conversation history. + 3. Output a precise version of question that resolves all pronouns to the nouns they stand for. Be sure to preserve the exact meaning of the question by only changing ambiguous pronouns. + 4. Then output a short markdown list of up to 8 relevant keywords that user could try searching for to answer their question. These keywords could used as file name, symbol names, abbreviations, or comments in the relevant code. Put the keywords most relevant to the question first. Do not include overly generic keywords. Do not repeat keywords. + 5. For each keyword in the markdown list of related keywords, if applicable add a comma separated list of variations after it. For example: for 'encode' possible variations include 'encoding', 'encoded', 'encoder', 'encoders'. Consider synonyms and plural forms. Do not repeat variations. + # Examples + User: Where's the code for base64 encoding? + Response: + Where's the code for base64 encoding? + - base64 encoding, base64 encoder, base64 encode - base64, base 64 - encode, encoded, encoder, encoders @@ -180,31 +189,61 @@ The user works in an IDE called Neovim which has a concept for editors with open files, integrated unit test support, an output pane that shows the output of running the code as well as an integrated terminal. The active document is the source code the user is looking at right now. You can only give one reply for each conversation turn. + Additional Rules Think step by step: + 1. Read the provided relevant workspace information (code excerpts, file names, and symbols) to understand the user's workspace. + 2. Consider how to answer the user's prompt based on the provided information and your specialized coding knowledge. Always assume that the user is asking about the code in their workspace instead of asking a general programming question. Prefer using variables, functions, types, and classes from the workspace over those from the standard library. + 3. Generate a response that clearly and accurately answers the user's question. In your response, add fully qualified links for referenced symbols (example: [`namespace.VariableName`](path/to/file.ts)) and links for files (example: [path/to/file](path/to/file.ts)) so that the user can open them. If you do not have enough information to answer the question, respond with "I'm sorry, I can't answer that question with what I currently know about your workspace". + Remember that you MUST add links for all referenced symbols from the workspace and fully qualify the symbol name in the link, for example: [`namespace.functionName`](path/to/util.ts). Remember that you MUST add links for all workspace files, for example: [path/to/file.js](path/to/file.js) + Examples: Question: What file implements base64 encoding? + Response: Base64 encoding is implemented in [src/base64.ts](src/base64.ts) as [`encode`](src/base64.ts) function. + + Question: How can I join strings with newlines? + Response: You can use the [`joinLines`](src/utils/string.ts) function from [src/utils/string.ts](src/utils/string.ts) to join multiple strings with newlines. + + Question: How do I build this project? + Response: To build this TypeScript project, run the `build` script in the [package.json](package.json) file: + ```sh npm run build ``` + + Question: How do I read a file? + Response: To read a file, you can use a [`FileReader`](src/fs/fileReader.ts) class from [src/fs/fileReader.ts](src/fs/fileReader.ts). """ +TEST_SHORTCUT = "Write a set of detailed unit test functions for the code above." +EXPLAIN_SHORTCUT = "Write a explanation for the code above as paragraphs of text." +FIX_SHORTCUT = ( + "There is a problem in this code. Rewrite the code to show it with the bug fixed." +) + +SENIOR_DEVELOPER_PROMPT = """ +You're a 10x senior developer that is an expert in programming. +Your job is to change the user's code according to their needs. +Your job is only to change / edit the code. +Your code output should keep the same level of indentation as the user's code. +You MUST add whitespace in the beginning of each line as needed to match the user's code. +""" diff --git a/rplugin/python3/utilities.py b/rplugin/python3/utilities.py index 1f6359a4..bc0540a7 100644 --- a/rplugin/python3/utilities.py +++ b/rplugin/python3/utilities.py @@ -1,7 +1,6 @@ import json import os import random -from typing import List import prompts import typings @@ -12,10 +11,11 @@ def random_hex(length: int = 65): def generate_request( - chat_history: List[typings.Message], + chat_history: list[typings.Message], code_excerpt: str, language: str = "", system_prompt=prompts.COPILOT_INSTRUCTIONS, + model="gpt-4", ): messages = [ { @@ -40,7 +40,7 @@ def generate_request( ) return { "intent": True, - "model": "gpt-4", + "model": model, "n": 1, "stream": True, "temperature": 0.1, @@ -49,7 +49,7 @@ def generate_request( } -def generate_embedding_request(inputs: List[typings.FileExtract]): +def generate_embedding_request(inputs: list[typings.FileExtract]): return { "input": [ f"File: `{i.filepath}`\n```{i.filepath.split('.')[-1]}\n{i.code}```" From a150b6cc1f5fa32c5818e927a2f03b891366ab39 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 12:15:29 +0000 Subject: [PATCH 12/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 60 +++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 9d958f6f..025172fd 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 01 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 02 ============================================================================== Table of Contents *CopilotChat-table-of-contents* @@ -34,7 +34,8 @@ INSTALLATION *CopilotChat-copilot-chat-for-neovim-installation* LAZY.NVIM ~ 1. `pip install python-dotenv requests pynvim==0.5.0 prompt-toolkit` -2. Put it in your lazy setup +2. `pip install tiktoken` (optional for displaying prompt token counts) +3. Put it in your lazy setup >lua return { @@ -42,19 +43,29 @@ LAZY.NVIM ~ "jellydn/CopilotChat.nvim", dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { - mode = "split", -- newbuffer or split , default: newbuffer + mode = "split", -- newbuffer or split, default: newbuffer + show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, build = function() - vim.defer_fn(function() - vim.cmd("UpdateRemotePlugins") - vim.notify("CopilotChat - Updated remote plugins. Please restart Neovim.") - end, 3000) + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") end, event = "VeryLazy", keys = { { "cce", "CopilotChatExplain", desc = "CopilotChat - Explain code" }, { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, + { + "ccv", + ":CopilotChatVsplitVisual", + mode = "x", + desc = "CopilotChat - Open in vertical split", + }, + { + "ccx", + ":CopilotChatInPlace", + mode = "x", + desc = "CopilotChat - Run in-place code", + }, }, }, } @@ -95,6 +106,7 @@ configuration options outlined below: >lua { debug = false, -- Enable or disable debug mode + show_help = 'yes', -- Show help text for CopilotChatInPlace prompts = { -- Set dynamic prompts for CopilotChat commands Explain = 'Explain how it works.', Tests = 'Briefly explain how the selected code works, then generate unit tests.', @@ -118,10 +130,7 @@ commands: }, }, build = function() - vim.defer_fn(function() - vim.cmd("UpdateRemotePlugins") - vim.notify("CopilotChat - Updated remote plugins. Please restart Neovim.") - end, 3000) + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") end, event = "VeryLazy", keys = { @@ -133,7 +142,7 @@ commands: } < -For further reference, you can view my configuration +For further reference, you can view @jellydn’s configuration . @@ -157,6 +166,24 @@ GENERATE TESTS ~ +TOKEN COUNT & FOLD ~ + +1. Select some code using visual mode. +2. Run the command `:CopilotChatVsplitVisual` with your question. + + + + +IN-PLACE CHAT POPUP ~ + +1. Select some code using visual mode. +2. Run the command `:CopilotChatInPlace` and type your prompt. For example, `What does this code do?` +3. Press `Enter` to send your question to Github Copilot. +4. Press `q` to quit. There is help text at the bottom of the screen. You can also press `?` to toggle the help text. + + + + ROADMAP *CopilotChat-copilot-chat-for-neovim-roadmap* - Translation to pure Lua @@ -178,9 +205,12 @@ Contributions of any kind welcome! 2. Links *CopilotChat-links* 1. *All Contributors*: https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square -2. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif -3. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif -4. *Generate tests*: https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif +2. *@jellydn*: +3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif +4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif +5. *Generate tests*: https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif +6. *Fold Demo*: https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif +7. *In-place Demo*: https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif Generated by panvimdoc From 72d8a0b950785701956d9405986a6d9b155cf1c4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 20:18:32 +0800 Subject: [PATCH 13/75] docs: add rguruprakash as a contributor for code (#44) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 5 ++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8fd9397a..cecb0676 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -53,6 +53,15 @@ "contributions": [ "code" ] + }, + { + "login": "rguruprakash", + "name": "Guruprakash Rajakkannu", + "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", + "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index 85c9e8f3..8fa96c32 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Copilot Chat for Neovim - -[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) > [!NOTE] @@ -182,6 +180,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Ahmed Haracic
Ahmed Haracic

💻 Trí Thiện Nguyễn
Trí Thiện Nguyễn

💻 He Zhizhou
He Zhizhou

💻 + Guruprakash Rajakkannu
Guruprakash Rajakkannu

💻 From 601cd4f99753e0572c62970c46a3a4449c935e53 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 20:32:07 +0800 Subject: [PATCH 14/75] chore: update dict --- cspell-tool.txt | 84 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/cspell-tool.txt b/cspell-tool.txt index 5fa35ffa..8f3c24b7 100644 --- a/cspell-tool.txt +++ b/cspell-tool.txt @@ -1,11 +1,73 @@ -ApplicationError: Unsupported NodeJS version (16.20.2); >=18 is required - at Module.run (file:///Users/huynhdung/.npm/_npx/b338be11c6678430/node_modules/cspell/dist/esm/app.mjs:17:15) - at file:///Users/huynhdung/.npm/_npx/b338be11c6678430/node_modules/cspell/bin.mjs:6:5 - at ModuleJob.run (node:internal/modules/esm/module_job:193:25) - at async Promise.all (index 0) - at async ESMLoader.import (node:internal/modules/esm/loader:530:24) - at async loadESM (node:internal/process/esm_loader:91:5) - at async handleMainPromise (node:internal/modules/run_main:65:12) { - exitCode: 1, - cause: undefined -} \ No newline at end of file +pynvim +nvim +newbuffer +nargs +Vsplit +bufexists +vlog +vararg +tjdevries +neovim +echohl +stdpath +outfile +nameupper +lineinfo +currentline +echom +Neovim +tiktoken +jellydn +zbirenbaum +rplugin +jellydn's +gptlang +Huynh +Haracic +Thiện +Nguyễn +Zhizhou +Guruprakash +Rajakkannu +vsplit +mypynvim +Nvim +AUTOCMD +Autocmd +getreg +bufnr +autocmd +dotenv +vnew +nofile +setlocal +buftype +bufhidden +noswapfile +linebreak +funcs +bufwinnr +gotoid +fileencoding +machineid +winnr +Nightfly +keymaps +diffthis +diffoff +conceallevel +concealcursor +extmark +virt +Keymapper +noremap +autocmdmapper +keymapper +termcodes +feedkeys +mynvim +autocmds +shrinked +zindex +noautocmd +roleplay \ No newline at end of file From 2ba0e59fe353c9ae9068c4324e13a9b1d7388d73 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 12:32:28 +0000 Subject: [PATCH 15/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 025172fd..9ced00a4 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -197,14 +197,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square 2. *@jellydn*: 3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif From ad341f418772110d0e35873a903d197fa642f16c Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Fri, 2 Feb 2024 21:06:20 +0800 Subject: [PATCH 16/75] Add CopilotChatVisual, CopilotChatInPlace commands (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Intergrate ziontee113/CopilotAgent.nvim (#27) * feat: add libraries from ziontee113/CopilotAgent * feat: add chat handlers from CopilotAgent * feat: add "mycopilot" module from CopilotAgent * feat: add "tools" module from CopilotAgent * feat: add copilot-agent.py * feat: conditionally check for tiktoken module * Add renamed CopilotAgent commands to README.md (#29) * ref: rename commands to prefixed with CopilotChat * docs: add renamed CopilotAgent commmands * docs: add default key mapping --------- Co-authored-by: Dung Duc Huynh (Kaka) <870029+jellydn@users.noreply.github.com> * docs: add note about the canary branch docs: fix keymap for in-place command * chore: update dict * docs: add demo videos for Fold & Inplace (#31) * chore: remove testing commands and layout component fix: remove matrix testing code * docs: remove copilot chat vspilt command * feat: add toggle layout and new split preset commands * revert: add internal comands back for inplace chat * chore: add a note about chat handler * feat: show spinner on processing * feat: show key binding on help section * docs: fix usage for InPlace command * docs: add demo for token count and demo * feat: add C-h to toggle help section * docs: add usage for InPlace command * chore: sync fork * refactor: create dedicated function for help text with layout * feat: add option for hide or show the help text on InPlace * Fix module import error by using typing.List (#33) Co-authored-by: Cassius0924 <51365188@gitlab.com> * docs: add Cassius0924 as a contributor for code (#34) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * feat: add user prompt and clear history on InPlace command * docs: fix usage for install manually * feat: add new keymap for change system prompt * feat: add debug flag * docs: add debug file path when enable debug mode * docs: notify end user to run UpdateRemotePlugins command after install plugin * chore: sync fork Add system prompt to ask Handle bad error code after calling post * chore: sync fork and remove unused * docs: add Discord link * chore: sync fork * refactor!: drop CC commands * docs: remove branch on usage --------- Co-authored-by: Trí Thiện Nguyễn <102876811+ziontee113@users.noreply.github.com> Co-authored-by: He Zhizhou <2670226747@qq.com> Co-authored-by: Cassius0924 <51365188@gitlab.com> Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- README.md | 8 +- cspell-tool.txt | 2 +- lua/CopilotChat/init.lua | 6 -- rplugin/python3/copilot-agent.py | 4 +- rplugin/python3/copilot-plugin.py | 134 ------------------------------ rplugin/python3/copilot.py | 1 - 6 files changed, 8 insertions(+), 147 deletions(-) delete mode 100644 rplugin/python3/copilot-plugin.py diff --git a/README.md b/README.md index 8fa96c32..1f3597b1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Copilot Chat for Neovim + [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) + > [!NOTE] @@ -38,7 +40,7 @@ return { { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, { "ccv", - ":CopilotChatVsplitVisual", + ":CopilotChatVisual", mode = "x", desc = "CopilotChat - Open in vertical split", }, @@ -61,7 +63,7 @@ return { 1. Put the files in the right place ``` -$ git clone https://github.com/gptlang/CopilotChat.nvim +$ git clone https://github.com/jellydn/CopilotChat.nvim $ cd CopilotChat.nvim $ cp -r --backup=nil rplugin ~/.config/nvim/ ``` @@ -145,7 +147,7 @@ For further reference, you can view @jellydn's [configuration](https://github.co ### Token count & Fold 1. Select some code using visual mode. -2. Run the command `:CopilotChatVsplitVisual` with your question. +2. Run the command `:CopilotChatVisual` with your question. [![Fold Demo](https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif)](https://gyazo.com/766fb3b6ffeb697e650fc839882822a8) diff --git a/cspell-tool.txt b/cspell-tool.txt index 8f3c24b7..98475e81 100644 --- a/cspell-tool.txt +++ b/cspell-tool.txt @@ -70,4 +70,4 @@ autocmds shrinked zindex noautocmd -roleplay \ No newline at end of file +roleplay diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index 4258f272..9c6888f6 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -32,12 +32,6 @@ M.setup = function(options) end, { nargs = '*', range = true }) end - for key, value in pairs(prompts) do - utils.create_cmd('CC' .. key, function() - vim.cmd('CopilotChatVsplit ' .. value) - end, { nargs = '*', range = true }) - end - -- Toggle between newbuffer and split utils.create_cmd('CopilotChatToggleLayout', function() if vim.g.copilot_chat_view_option == 'newbuffer' then diff --git a/rplugin/python3/copilot-agent.py b/rplugin/python3/copilot-agent.py index 3ac008df..0af6130b 100644 --- a/rplugin/python3/copilot-agent.py +++ b/rplugin/python3/copilot-agent.py @@ -18,7 +18,7 @@ def init_vsplit_chat_handler(self): if self.vsplit_chat_handler is None: self.vsplit_chat_handler = VSplitChatHandler(self.nvim) - @pynvim.command("CopilotChatVsplit", nargs="1") + @pynvim.command("CopilotChat", nargs="1") def copilot_agent_cmd(self, args: list[str]): self.init_vsplit_chat_handler() if self.vsplit_chat_handler: @@ -28,7 +28,7 @@ def copilot_agent_cmd(self, args: list[str]): code = self.nvim.eval("getreg('\"')") self.vsplit_chat_handler.chat(args[0], file_type, code) - @pynvim.command("CopilotChatVsplitVisual", nargs="1", range="") + @pynvim.command("CopilotChatVisual", nargs="1", range="") def copilot_agent_visual_cmd(self, args: list[str], range: list[int]): self.init_vsplit_chat_handler() if self.vsplit_chat_handler: diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py deleted file mode 100644 index 9c22f018..00000000 --- a/rplugin/python3/copilot-plugin.py +++ /dev/null @@ -1,134 +0,0 @@ -import os -import time -from typing import List - -import copilot -import dotenv -import prompts -import pynvim - -dotenv.load_dotenv() - - -@pynvim.plugin -class CopilotChatPlugin(object): - def __init__(self, nvim: pynvim.Nvim): - self.nvim = nvim - self.copilot = copilot.Copilot(os.getenv("COPILOT_TOKEN")) - if self.copilot.github_token is None: - req = self.copilot.request_auth() - self.nvim.out_write( - f"Please visit {req['verification_uri']} and enter the code {req['user_code']}\n" - ) - current_time = time.time() - wait_until = current_time + req["expires_in"] - while self.copilot.github_token is None: - self.copilot.poll_auth(req["device_code"]) - time.sleep(req["interval"]) - if time.time() > wait_until: - self.nvim.out_write("Timed out waiting for authentication\n") - return - self.nvim.out_write("Successfully authenticated with Copilot\n") - self.copilot.authenticate() - - @pynvim.command("CopilotChat", nargs="1") - def copilotChat(self, args: List[str]): - if self.copilot.github_token is None: - self.nvim.out_write("Please authenticate with Copilot first\n") - return - - # Start the spinner - self.nvim.exec_lua('require("CopilotChat.spinner").show()') - - prompt = " ".join(args) - if prompt == "/fix": - prompt = prompts.FIX_SHORTCUT - elif prompt == "/test": - prompt = prompts.TEST_SHORTCUT - elif prompt == "/explain": - prompt = prompts.EXPLAIN_SHORTCUT - - # Get code from the unnamed register - code = self.nvim.eval("getreg('\"')") - file_type = self.nvim.eval("expand('%')").split(".")[-1] - - # Get the view option from the command - view_option = self.nvim.eval("g:copilot_chat_view_option") - - buffers = self.nvim.buffers - - existing_buffer = next( - (buf for buf in buffers if os.path.basename(buf.name) == "CopilotChat"), - None, - ) - - # Check if we're already in a chat buffer - if existing_buffer is None: - # Create a new scratch buffer to hold the chat - if view_option == "split": - self.nvim.command("vnew CopilotChat") - else: - self.nvim.command("e CopilotChat") - # Set the buffer type to nofile and hide it when it's not active - self.nvim.command("setlocal buftype=nofile bufhidden=hide noswapfile") - # Set filetype as markdown and wrap with linebreaks - self.nvim.command("setlocal filetype=markdown wrap linebreak") - buf = self.nvim.current.buffer - else: - # Use the existing buffer - buf = existing_buffer - - # Check if the buffer is already open in a window - win_id = self.nvim.funcs.bufwinnr(buf.number) - - if win_id != -1: - # If the buffer is open in a window, switch to that window - self.nvim.funcs.win_gotoid(win_id) - else: - # If the buffer is not open in a window, open it in a new split - if view_option == "split": - self.nvim.command("vsplit " + buf.name) - else: - self.nvim.command("e " + buf.name) - self.nvim.command("setlocal filetype=markdown wrap linebreak") - - self.nvim.api.buf_set_option(buf, "fileencoding", "utf-8") - - # Add start separator - start_separator = f"""### User -{prompt} - -### Copilot - -""" - buf.append(start_separator.split("\n"), -1) - - self.nvim.exec_lua( - 'require("CopilotChat.utils").log_info(...)', f"Prompt: {prompt}" - ) - - # Add chat messages - for token in self.copilot.ask(None, prompt, code, language=file_type): - self.nvim.exec_lua( - 'require("CopilotChat.utils").log_info(...)', f"Token: {token}" - ) - buffer_lines = self.nvim.api.buf_get_lines(buf, 0, -1, 0) - last_line_row = len(buffer_lines) - 1 - last_line = buffer_lines[-1] - last_line_col = len(last_line.encode("utf-8")) - - self.nvim.api.buf_set_text( - buf, - last_line_row, - last_line_col, - last_line_row, - last_line_col, - token.split("\n"), - ) - - # Stop the spinner - self.nvim.exec_lua('require("CopilotChat.spinner").hide()') - - # Add end separator - end_separator = "\n---\n" - buf.append(end_separator.split("\n"), -1) diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index ee471b64..c1457e27 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -5,7 +5,6 @@ from typing import Dict, List import dotenv -import prompts import requests import typings import utilities From 81f1de635914125b663389b2964a94ba308fee3b Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 21:22:42 +0800 Subject: [PATCH 17/75] chore: add log to about the common issue with Ambiguous use of user-defined command --- lua/CopilotChat/init.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index 9c6888f6..ba8e3fb0 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -40,6 +40,11 @@ M.setup = function(options) vim.g.copilot_chat_view_option = 'newbuffer' end end, { nargs = '*', range = true }) + + utils.log_info( + 'Execute the ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot.' + ) + utils.log_info('If you encounter any issues, run ":healthcheck" and share the output.') end return M From 2f476b56b644127c2be74fe0048e8ae8f57b4ad3 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 21:22:53 +0800 Subject: [PATCH 18/75] docs: fix typo on step --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f3597b1..678c0ecb 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ return { } ``` -3. Run `:UpdateRemotePlugins` -4. Restart `neovim` +4. Run `:UpdateRemotePlugins` +5. Restart `neovim` ### Manual From d8aebcc04a930658a437a9813f5fb1bc0170317a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 13:23:14 +0000 Subject: [PATCH 19/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 9ced00a4..6e158903 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -56,7 +56,7 @@ LAZY.NVIM ~ { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, { "ccv", - ":CopilotChatVsplitVisual", + ":CopilotChatVisual", mode = "x", desc = "CopilotChat - Open in vertical split", }, @@ -80,7 +80,7 @@ MANUAL ~ 1. Put the files in the right place > - $ git clone https://github.com/gptlang/CopilotChat.nvim + $ git clone https://github.com/jellydn/CopilotChat.nvim $ cd CopilotChat.nvim $ cp -r --backup=nil rplugin ~/.config/nvim/ < @@ -169,7 +169,7 @@ GENERATE TESTS ~ TOKEN COUNT & FOLD ~ 1. Select some code using visual mode. -2. Run the command `:CopilotChatVsplitVisual` with your question. +2. Run the command `:CopilotChatVisual` with your question. From 6e492fa0feaf6d216ceddd1de797c4c88760db20 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 21:29:47 +0800 Subject: [PATCH 20/75] docs: remove mention about canary branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 678c0ecb..12f1712d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > [!NOTE] -> A new command, `CopilotChatInPlace`, has been introduced. It functions like the ChatGPT plugin. You can find this feature in the [canary](https://github.com/jellydn/CopilotChat.nvim/tree/canary?tab=readme-ov-file#lazynvim) branch. To stay updated on our roadmap, please join our [Discord](https://discord.gg/vy6hJsTWaZ) community. +> A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, please join our [Discord](https://discord.gg/vy6hJsTWaZ) community. ## Authentication From 707acfcfd9d147c139ebb27be49817dd7687ec0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 13:30:10 +0000 Subject: [PATCH 21/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 6e158903..b23e6759 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -16,11 +16,10 @@ Table of Contents *CopilotChat-table-of-contents* |CopilotChat-| - [!NOTE] A new command, `CopilotChatInPlace`, has been introduced. It functions - like the ChatGPT plugin. You can find this feature in the canary - - branch. To stay updated on our roadmap, please join our Discord - community. + [!NOTE] A new command, `CopilotChatInPlace` has been introduced. It functions + like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart + Neovim before starting a chat with Copilot. To stay updated on our roadmap, + please join our Discord community. AUTHENTICATION *CopilotChat-copilot-chat-for-neovim-authentication* From d8c96c3f5ac8b72a5670745d9ad39d76f4cec52b Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 21:32:50 +0800 Subject: [PATCH 22/75] chore: rename plugin --- rplugin/python3/{copilot-agent.py => copilot-plugin.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rplugin/python3/{copilot-agent.py => copilot-plugin.py} (100%) diff --git a/rplugin/python3/copilot-agent.py b/rplugin/python3/copilot-plugin.py similarity index 100% rename from rplugin/python3/copilot-agent.py rename to rplugin/python3/copilot-plugin.py From 0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 21:37:22 +0800 Subject: [PATCH 23/75] refactor!: drop new buffer mode --- README.md | 1 - lua/CopilotChat/init.lua | 15 ++------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 12f1712d..4208182d 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ return { "jellydn/CopilotChat.nvim", dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { - mode = "split", -- newbuffer or split, default: newbuffer show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index ba8e3fb0..d3058d41 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -11,12 +11,10 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- Set up the plugin ---@param options (table | nil) --- - mode: ('newbuffer' | 'split') default: newbuffer. -- - show_help: ('yes' | 'no') default: 'yes'. -- - prompts: (table?) default: default_prompts. -- - debug: (boolean?) default: false. M.setup = function(options) - vim.g.copilot_chat_view_option = options and options.mode or 'newbuffer' vim.g.copilot_chat_show_help = options and options.show_help or 'yes' local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug @@ -32,19 +30,10 @@ M.setup = function(options) end, { nargs = '*', range = true }) end - -- Toggle between newbuffer and split - utils.create_cmd('CopilotChatToggleLayout', function() - if vim.g.copilot_chat_view_option == 'newbuffer' then - vim.g.copilot_chat_view_option = 'split' - else - vim.g.copilot_chat_view_option = 'newbuffer' - end - end, { nargs = '*', range = true }) - utils.log_info( - 'Execute the ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot.' + 'Execute ":UpdateRemotePlugins" and restart Neovim before starting a chat with Copilot.' ) - utils.log_info('If you encounter any issues, run ":healthcheck" and share the output.') + utils.log_info('If issues arise, run ":healthcheck" and share the output.') end return M From 13fd3ac8a9f0dd8d6900d15b869b76fe273e66ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 13:37:47 +0000 Subject: [PATCH 24/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index b23e6759..f2c0fb2e 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -42,7 +42,6 @@ LAZY.NVIM ~ "jellydn/CopilotChat.nvim", dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { - mode = "split", -- newbuffer or split, default: newbuffer show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log }, From 1c60c70a2b951bb2e0526d6f2a60f14677aa5a84 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 22:03:08 +0800 Subject: [PATCH 25/75] docs: add receipts section --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4208182d..58c00561 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ You have the capability to expand the prompts to create more versatile commands: return { "jellydn/CopilotChat.nvim", opts = { - mode = "split", + debug = true, + show_help = "yes", prompts = { Explain = "Explain how it works.", Review = "Review the following code and provide concise suggestions.", @@ -143,7 +144,7 @@ For further reference, you can view @jellydn's [configuration](https://github.co [![Generate tests](https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif)](https://gyazo.com/f285467d4b8d8f8fd36aa777305312ae) -### Token count & Fold +### Token count & Fold with visual mode 1. Select some code using visual mode. 2. Run the command `:CopilotChatVisual` with your question. @@ -159,6 +160,75 @@ For further reference, you can view @jellydn's [configuration](https://github.co [![In-place Demo](https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif)](https://gyazo.com/4a5badaa109cd483c1fc23d296325cb0) +## Receipts + +### How to setup with `which-key.nvim` + +A special thanks to @ecosse3 for the configuration of [which-key](https://github.com/jellydn/CopilotChat.nvim/issues/30). + +```lua + { + "jellydn/CopilotChat.nvim", + event = "VeryLazy", + opts = { + prompts = { + Explain = "Explain how it works.", + Review = "Review the following code and provide concise suggestions.", + Tests = "Briefly explain how the selected code works, then generate unit tests.", + Refactor = "Refactor the code to improve clarity and readability.", + }, + }, + build = function() + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") + end, + config = function() + local present, wk = pcall(require, "which-key") + if not present then + return + end + + wk.register({ + c = { + c = { + name = "Copilot Chat", + } + } + }, { + mode = "n", + prefix = "", + silent = true, + noremap = true, + nowait = false, + }) + end, + keys = { + { "ccc", ":CopilotChat ", desc = "CopilotChat - Prompt" }, + { "cce", ":CopilotChatExplain ", desc = "CopilotChat - Explain code" }, + { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, + { "ccr", "CopilotChatReview", desc = "CopilotChat - Review code" }, + { "ccR", "CopilotChatRefactor", desc = "CopilotChat - Refactor code" }, + } + }, +``` + +### Create a simple input for CopilotChat + +Follow the example below to create a simple input for CopilotChat. + +```lua +-- Create input for CopilotChat + { + "cci", + function() + local input = vim.fn.input("Ask Copilot: ") + if input ~= "" then + vim.cmd("CopilotChat " .. input) + end + end, + desc = "CopilotChat - Ask input", + }, +``` + ## Roadmap - Translation to pure Lua From 467c55ebfa8c0c63f6665335c394a82ea04a7024 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 14:03:31 +0000 Subject: [PATCH 26/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 80 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index f2c0fb2e..73c1936c 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -7,6 +7,7 @@ Table of Contents *CopilotChat-table-of-contents* - Authentication |CopilotChat-copilot-chat-for-neovim-authentication| - Installation |CopilotChat-copilot-chat-for-neovim-installation| - Usage |CopilotChat-copilot-chat-for-neovim-usage| + - Receipts |CopilotChat-copilot-chat-for-neovim-receipts| - Roadmap |CopilotChat-copilot-chat-for-neovim-roadmap| - Contributors ✨ |CopilotChat-copilot-chat-for-neovim-contributors-✨| @@ -119,7 +120,8 @@ commands: return { "jellydn/CopilotChat.nvim", opts = { - mode = "split", + debug = true, + show_help = "yes", prompts = { Explain = "Explain how it works.", Review = "Review the following code and provide concise suggestions.", @@ -164,7 +166,7 @@ GENERATE TESTS ~ -TOKEN COUNT & FOLD ~ +TOKEN COUNT & FOLD WITH VISUAL MODE ~ 1. Select some code using visual mode. 2. Run the command `:CopilotChatVisual` with your question. @@ -182,6 +184,79 @@ IN-PLACE CHAT POPUP ~ +RECEIPTS *CopilotChat-copilot-chat-for-neovim-receipts* + + +HOW TO SETUP WITH WHICH-KEY.NVIM ~ + +A special thanks to @ecosse3 for the configuration of which-key +. + +>lua + { + "jellydn/CopilotChat.nvim", + event = "VeryLazy", + opts = { + prompts = { + Explain = "Explain how it works.", + Review = "Review the following code and provide concise suggestions.", + Tests = "Briefly explain how the selected code works, then generate unit tests.", + Refactor = "Refactor the code to improve clarity and readability.", + }, + }, + build = function() + vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") + end, + config = function() + local present, wk = pcall(require, "which-key") + if not present then + return + end + + wk.register({ + c = { + c = { + name = "Copilot Chat", + } + } + }, { + mode = "n", + prefix = "", + silent = true, + noremap = true, + nowait = false, + }) + end, + keys = { + { "ccc", ":CopilotChat ", desc = "CopilotChat - Prompt" }, + { "cce", ":CopilotChatExplain ", desc = "CopilotChat - Explain code" }, + { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, + { "ccr", "CopilotChatReview", desc = "CopilotChat - Review code" }, + { "ccR", "CopilotChatRefactor", desc = "CopilotChat - Refactor code" }, + } + }, +< + + +CREATE A SIMPLE INPUT FOR COPILOTCHAT ~ + +Follow the example below to create a simple input for CopilotChat. + +>lua + -- Create input for CopilotChat + { + "cci", + function() + local input = vim.fn.input("Ask Copilot: ") + if input ~= "" then + vim.cmd("CopilotChat " .. input) + end + end, + desc = "CopilotChat - Ask input", + }, +< + + ROADMAP *CopilotChat-copilot-chat-for-neovim-roadmap* - Translation to pure Lua @@ -209,6 +284,7 @@ Contributions of any kind welcome! 5. *Generate tests*: https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif 6. *Fold Demo*: https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif 7. *In-place Demo*: https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif +8. *@ecosse3*: Generated by panvimdoc From b68c3522d03c8ac9a332169c56e725b69a43b07c Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Fri, 2 Feb 2024 22:24:27 +0800 Subject: [PATCH 27/75] fix: remove LiteralString, use Any for fixing issue on Python 3.10 Closed #45 --- rplugin/python3/copilot-plugin.py | 2 +- rplugin/python3/mypynvim/core/buffer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rplugin/python3/copilot-plugin.py b/rplugin/python3/copilot-plugin.py index 0af6130b..ec9b4b5b 100644 --- a/rplugin/python3/copilot-plugin.py +++ b/rplugin/python3/copilot-plugin.py @@ -8,7 +8,7 @@ @pynvim.plugin -class CopilotAgentPlugin(object): +class CopilotPlugin(object): def __init__(self, nvim: pynvim.Nvim): self.nvim: MyNvim = MyNvim(nvim, PLUGIN_MAPPING_CMD, PLUGIN_AUTOCMD_CMD) self.vsplit_chat_handler = None diff --git a/rplugin/python3/mypynvim/core/buffer.py b/rplugin/python3/mypynvim/core/buffer.py index 9bf1f4c0..c11db7f7 100644 --- a/rplugin/python3/mypynvim/core/buffer.py +++ b/rplugin/python3/mypynvim/core/buffer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Dict, LiteralString, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, Union from pynvim.api import Buffer @@ -65,7 +65,7 @@ def lines( def clear(self): self.nvim.api.buf_set_lines(self.buf.handle, 0, -1, True, []) - def append(self, lines: list[str] | list[LiteralString]): + def append(self, lines: list[str] | list[Any]): self.nvim.api.buf_set_lines(self.buf.handle, -1, -1, True, lines) # extmark methods From 6e7e80f118c589a009fa1703a284ad292260e3a0 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sat, 3 Feb 2024 00:26:14 +0800 Subject: [PATCH 28/75] feat: add new keymap to get previous user prompt --- .../python3/handlers/inplace_chat_handler.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/rplugin/python3/handlers/inplace_chat_handler.py b/rplugin/python3/handlers/inplace_chat_handler.py index 500bea20..d3b36dc2 100644 --- a/rplugin/python3/handlers/inplace_chat_handler.py +++ b/rplugin/python3/handlers/inplace_chat_handler.py @@ -176,13 +176,20 @@ def _chat(self): def _set_prompt(self, prompt: str): self.prompt_popup.buffer.lines(prompt) - def _set_user_prompt(self): + def _set_next_user_prompt(self): self.current_user_prompt = (self.current_user_prompt + 1) % len( self.user_prompts ) prompt = list(self.user_prompts.keys())[self.current_user_prompt] self.prompt_popup.buffer.lines(self.user_prompts[prompt]) + def _set_previous_user_prompt(self): + self.current_user_prompt = (self.current_user_prompt - 1) % len( + self.user_prompts + ) + prompt = list(self.user_prompts.keys())[self.current_user_prompt] + self.prompt_popup.buffer.lines(self.user_prompts[prompt]) + def _toggle_model(self): if self.model == MODEL_GPT4: self.model = MODEL_GPT35_TURBO @@ -246,10 +253,16 @@ def _set_keymaps(self): "i", "", lambda: (self.nvim.feed(""), self._chat()) ) + self.prompt_popup.map( + "n", + "", + lambda: self._set_next_user_prompt(), + ) + self.prompt_popup.map( "n", "", - lambda: self._set_user_prompt(), + lambda: self._set_previous_user_prompt(), ) for i, popup in enumerate(self.popups): @@ -286,7 +299,8 @@ def _set_help_content(self): "Prompt Binding:", " ': Set prompt to SIMPLE_DOCSTRING", " s: Set prompt to SEPARATE", - " : Set prompt to next item in user prompts", + " : Get the previous user prompt", + " : Set prompt to next item in user prompts", "", "Model:", " : Toggle AI model", From f1081acd032a8bcd3630398f36e1822610f8bfdf Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Fri, 2 Feb 2024 23:25:33 +0000 Subject: [PATCH 29/75] pre-commit for standardized formatting with black and flake8 (#48) * add pre-commit hook * fix flake8 issues, duplicate code, and format everything --- .all-contributorsrc | 30 +++++-------------- .flake8 | 3 ++ .luarc.json | 7 ++--- .pre-commit-config.yaml | 17 +++++++++++ cspell.json | 13 ++------ renovate.json | 4 +-- rplugin/python3/copilot.py | 1 + .../python3/handlers/vsplit_chat_handler.py | 2 +- .../python3/mypynvim/ui_components/popup.py | 2 -- 9 files changed, 35 insertions(+), 44 deletions(-) create mode 100644 .flake8 create mode 100644 .pre-commit-config.yaml diff --git a/.all-contributorsrc b/.all-contributorsrc index cecb0676..0a5effab 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,7 +1,5 @@ { - "files": [ - "README.md" - ], + "files": ["README.md"], "imageSize": 100, "commit": false, "commitType": "docs", @@ -12,56 +10,42 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": [ - "code" - ] + "contributions": ["code"] } ], "contributorsPerLine": 7, diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..cadcae03 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +ignore = E203, E501, W503 diff --git a/.luarc.json b/.luarc.json index d213f255..04ea467f 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,6 +1,3 @@ { - "diagnostics.globals": [ - "describe", - "it" - ] -} \ No newline at end of file + "diagnostics.globals": ["describe", "it"] +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..93e78079 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +repos: + - repo: https://github.com/psf/black + rev: "23.10.0" + hooks: + - id: black + - repo: https://github.com/PyCQA/isort + rev: "5.12.0" + hooks: + - id: isort + - repo: https://github.com/PyCQA/flake8 + rev: "6.1.0" + hooks: + - id: flake8 + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "fc260393cc4ec09f8fc0a5ba4437f481c8b55dc1" + hooks: + - id: prettier diff --git a/cspell.json b/cspell.json index d7404183..656c8347 100644 --- a/cspell.json +++ b/cspell.json @@ -10,13 +10,6 @@ "addWords": true } ], - "dictionaries": [ - "cspell-tool" - ], - "ignorePaths": [ - "node_modules", - "dist", - "build", - "/cspell-tool.txt" - ] -} \ No newline at end of file + "dictionaries": ["cspell-tool"], + "ignorePaths": ["node_modules", "dist", "build", "/cspell-tool.txt"] +} diff --git a/renovate.json b/renovate.json index 5db72dd6..22a99432 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,4 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] + "extends": ["config:recommended"] } diff --git a/rplugin/python3/copilot.py b/rplugin/python3/copilot.py index c1457e27..ee471b64 100644 --- a/rplugin/python3/copilot.py +++ b/rplugin/python3/copilot.py @@ -5,6 +5,7 @@ from typing import Dict, List import dotenv +import prompts import requests import typings import utilities diff --git a/rplugin/python3/handlers/vsplit_chat_handler.py b/rplugin/python3/handlers/vsplit_chat_handler.py index c4a73736..191fd6b2 100644 --- a/rplugin/python3/handlers/vsplit_chat_handler.py +++ b/rplugin/python3/handlers/vsplit_chat_handler.py @@ -21,7 +21,7 @@ def vsplit(self): if window.vars[var_key]: self.nvim.current.window = window return - except: + except Exception: pass self.buffer.vsplit( diff --git a/rplugin/python3/mypynvim/ui_components/popup.py b/rplugin/python3/mypynvim/ui_components/popup.py index 55b2c782..9040b452 100644 --- a/rplugin/python3/mypynvim/ui_components/popup.py +++ b/rplugin/python3/mypynvim/ui_components/popup.py @@ -9,8 +9,6 @@ from .layout import Layout -if TYPE_CHECKING: - from mypynvim.core.nvim import MyNvim from mypynvim.core.buffer import MyBuffer from mypynvim.core.window import MyWindow From bc0c2c952934da11a331d2fa581922d42284120e Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sat, 3 Feb 2024 07:56:53 +0800 Subject: [PATCH 30/75] docs: add pre-commit hook usage to development --- Makefile | 12 ++++++++++-- README.md | 12 ++++++++++++ cspell-tool.txt | 25 ++++++++++--------------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index aeeba1d6..86f44cfd 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,21 @@ .PHONY: help help: - @echo "install - install vusted" - @echo "test - run test" + @echo "Available commands:" + @echo " install-cli - Install Lua and Luarocks using Homebrew" + @echo " install-pre-commit - Install pre-commit using pip" + @echo " install - Install vusted using Luarocks" + @echo " test - Run tests using vusted" .PHONY: install-cli install-cli: brew install luarocks brew install lua +.PHONY: install-pre-commit +install-pre-commit: + pip install pre-commit + pre-commit install + .PHONY: install install: luarocks install vusted diff --git a/README.md b/README.md index 58c00561..f108adca 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,18 @@ Follow the example below to create a simple input for CopilotChat. - Use vector encodings to automatically select code - Sub commands - See [issue #5](https://github.com/gptlang/CopilotChat.nvim/issues/5) +## Development + +### Installing Pre-commit Tool + +For development, you can use the provided Makefile command to install the pre-commit tool: + +```bash +make install-pre-commit +``` + +This will install the pre-commit tool and the pre-commit hooks. + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): diff --git a/cspell-tool.txt b/cspell-tool.txt index 98475e81..73288a14 100644 --- a/cspell-tool.txt +++ b/cspell-tool.txt @@ -1,8 +1,8 @@ pynvim nvim -newbuffer nargs -Vsplit +Neovim +healthcheck bufexists vlog vararg @@ -15,12 +15,15 @@ nameupper lineinfo currentline echom -Neovim tiktoken jellydn zbirenbaum rplugin jellydn's +ecosse +pcall +noremap +nowait gptlang Huynh Haracic @@ -38,21 +41,11 @@ getreg bufnr autocmd dotenv -vnew -nofile -setlocal -buftype -bufhidden -noswapfile -linebreak -funcs -bufwinnr -gotoid -fileencoding machineid winnr Nightfly keymaps +linebreak diffthis diffoff conceallevel @@ -60,7 +53,6 @@ concealcursor extmark virt Keymapper -noremap autocmdmapper keymapper termcodes @@ -71,3 +63,6 @@ shrinked zindex noautocmd roleplay +vusted +luarocks +isort \ No newline at end of file From 9a7f03ad00f8b75aea0c97d2539e835db288f208 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Feb 2024 23:57:30 +0000 Subject: [PATCH 31/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 73c1936c..2a417043 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -9,6 +9,7 @@ Table of Contents *CopilotChat-table-of-contents* - Usage |CopilotChat-copilot-chat-for-neovim-usage| - Receipts |CopilotChat-copilot-chat-for-neovim-receipts| - Roadmap |CopilotChat-copilot-chat-for-neovim-roadmap| + - Development |CopilotChat-copilot-chat-for-neovim-development| - Contributors ✨ |CopilotChat-copilot-chat-for-neovim-contributors-✨| ============================================================================== @@ -265,6 +266,21 @@ ROADMAP *CopilotChat-copilot-chat-for-neovim-roadmap* - Sub commands - See issue #5 +DEVELOPMENT *CopilotChat-copilot-chat-for-neovim-development* + + +INSTALLING PRE-COMMIT TOOL ~ + +For development, you can use the provided Makefile command to install the +pre-commit tool: + +>bash + make install-pre-commit +< + +This will install the pre-commit tool and the pre-commit hooks. + + CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key From 9709195d051e61a6f6de4f8a13a7806c96b8d134 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sat, 3 Feb 2024 08:30:53 +0800 Subject: [PATCH 32/75] docs: add debugging step after run UpdateRemotePlugins --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f108adca..46c6022c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,21 @@ return { } ``` -4. Run `:UpdateRemotePlugins` +4. Run command `:UpdateRemotePlugins`, then inspect the file `~/.local/share/nvim/rplugin.vim` for additional details. You will notice that the commands have been registered. + +For example: + +```vim +" python3 plugins +call remote#host#RegisterPlugin('python3', '/Users/huynhdung/.local/share/nvim/lazy/CopilotChat.nvim/rplugin/python3/copilot-plugin.py', [ + \ {'sync': v:false, 'name': 'CopilotChat', 'type': 'command', 'opts': {'nargs': '1'}}, + \ {'sync': v:false, 'name': 'CopilotChatVisual', 'type': 'command', 'opts': {'nargs': '1', 'range': ''}}, + \ {'sync': v:false, 'name': 'CopilotChatInPlace', 'type': 'command', 'opts': {'nargs': '*', 'range': ''}}, + \ {'sync': v:false, 'name': 'CopilotChatAutocmd', 'type': 'command', 'opts': {'nargs': '*'}}, + \ {'sync': v:false, 'name': 'CopilotChatMapping', 'type': 'command', 'opts': {'nargs': '*'}}, + \ ]) +``` + 5. Restart `neovim` ### Manual From fa9be9832879b631130b5c694f5c5015c1557b9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 3 Feb 2024 00:31:17 +0000 Subject: [PATCH 33/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 2a417043..868a9f45 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 02 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 03 ============================================================================== Table of Contents *CopilotChat-table-of-contents* @@ -71,8 +71,22 @@ LAZY.NVIM ~ } < -1. Run `:UpdateRemotePlugins` -2. Restart `neovim` +1. Run command `:UpdateRemotePlugins`, then inspect the file `~/.local/share/nvim/rplugin.vim` for additional details. You will notice that the commands have been registered. + +For example: + +>vim + " python3 plugins + call remote#host#RegisterPlugin('python3', '/Users/huynhdung/.local/share/nvim/lazy/CopilotChat.nvim/rplugin/python3/copilot-plugin.py', [ + \ {'sync': v:false, 'name': 'CopilotChat', 'type': 'command', 'opts': {'nargs': '1'}}, + \ {'sync': v:false, 'name': 'CopilotChatVisual', 'type': 'command', 'opts': {'nargs': '1', 'range': ''}}, + \ {'sync': v:false, 'name': 'CopilotChatInPlace', 'type': 'command', 'opts': {'nargs': '*', 'range': ''}}, + \ {'sync': v:false, 'name': 'CopilotChatAutocmd', 'type': 'command', 'opts': {'nargs': '*'}}, + \ {'sync': v:false, 'name': 'CopilotChatMapping', 'type': 'command', 'opts': {'nargs': '*'}}, + \ ]) +< + +1. Restart `neovim` MANUAL ~ From b341ba3d20ce89fe8aafd0b0ea256dfa7868bcbe Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sat, 3 Feb 2024 12:40:03 +0800 Subject: [PATCH 34/75] ci: add release action --- .github/workflows/release.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..9860c202 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release +on: + push: + pull_request: + +jobs: + release: + name: release + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: CopilotChat.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable From 6a17928cc142a1f15013ac4bbab297b05d94d6c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:52:38 +0800 Subject: [PATCH 35/75] chore(main): release 1.0.0 (#49) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5d0ab5b9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +## 1.0.0 (2024-02-03) + + +### ⚠ BREAKING CHANGES + +* drop new buffer mode + +### Features + +* add a note for help user to continue the chat ([8a80ee7](https://github.com/jellydn/CopilotChat.nvim/commit/8a80ee7d3f9d0dcb65b315255d629c2cd8263dac)) +* add CCExplain command ([640f361](https://github.com/jellydn/CopilotChat.nvim/commit/640f361a54be51e7c479257c374d4a26d8fcd31d)) +* add CCTests command ([b34a78f](https://github.com/jellydn/CopilotChat.nvim/commit/b34a78f05ebe65ca093e4dc4b66de9120a681f4c)) +* add configuration options for wrap and filetype ([b4c6e76](https://github.com/jellydn/CopilotChat.nvim/commit/b4c6e760232ec54d4632edef3869e1a05ec61751)) +* add CopilotChatToggleLayout ([07988b9](https://github.com/jellydn/CopilotChat.nvim/commit/07988b95a412756169016e991dabcf190a930c7e)) +* add debug flag ([d0dbd4c](https://github.com/jellydn/CopilotChat.nvim/commit/d0dbd4c6fb9be75ccaa591b050198d40c097f423)) +* add health check ([974f14f](https://github.com/jellydn/CopilotChat.nvim/commit/974f14f0d0978d858cbe0126568f30fd63262cb6)) +* add new keymap to get previous user prompt ([6e7e80f](https://github.com/jellydn/CopilotChat.nvim/commit/6e7e80f118c589a009fa1703a284ad292260e3a0)) +* set filetype to markdown and text wrapping ([9b19d51](https://github.com/jellydn/CopilotChat.nvim/commit/9b19d51deacdf5c958933e99a2e75ebe4c968a9b)) +* show chat in markdown format ([9c14152](https://github.com/jellydn/CopilotChat.nvim/commit/9c141523de12e723b1d72d95760f2daddcecd1d9)) + + +### Bug Fixes + +* **ci:** generate doc ([6287fd4](https://github.com/jellydn/CopilotChat.nvim/commit/6287fd452d83d43a739d4c7c7a5524537032fc5d)) +* Close spinner if the buffer does not exist ([#11](https://github.com/jellydn/CopilotChat.nvim/issues/11)) ([0ea238d](https://github.com/jellydn/CopilotChat.nvim/commit/0ea238d7be9c7872dd9932a56d3521531b2297db)) +* remove LiteralString, use Any for fixing issue on Python 3.10 ([b68c352](https://github.com/jellydn/CopilotChat.nvim/commit/b68c3522d03c8ac9a332169c56e725b69a43b07c)), closes [#45](https://github.com/jellydn/CopilotChat.nvim/issues/45) + + +### Reverts + +* change back to CopilotChat command ([e304f79](https://github.com/jellydn/CopilotChat.nvim/commit/e304f792a5fbba412c2a5a1f717ec7e2ab12e5b0)) + + +### Code Refactoring + +* drop new buffer mode ([0a30b7c](https://github.com/jellydn/CopilotChat.nvim/commit/0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a)) From 89b6276e995de2e05ea391a9d1045676737c93bd Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Sat, 3 Feb 2024 22:42:58 +0800 Subject: [PATCH 36/75] feat: add CopilotChatDebugInfo command (#51) * feat: add CopilotChatDebugInfo command chore: add remote plugin path to util * docs: add debug command to receipts --- README.md | 6 ++++ cspell-tool.txt | 4 ++- lua/CopilotChat/init.lua | 62 +++++++++++++++++++++++++++++++++++++++ lua/CopilotChat/utils.lua | 19 ++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 46c6022c..71812b99 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,12 @@ For further reference, you can view @jellydn's [configuration](https://github.co ## Receipts +### Debugging with `:messages` and `:CopilotChatDebugInfo` + +If you encounter any issues, you can run the command `:messages` to inspect the log. You can also run the command `:CopilotChatDebugInfo` to inspect the debug information. + +[![Debug Info](https://i.gyazo.com/bf00e700bcee1b77bcbf7b516b552521.gif)](https://gyazo.com/bf00e700bcee1b77bcbf7b516b552521) + ### How to setup with `which-key.nvim` A special thanks to @ecosse3 for the configuration of [which-key](https://github.com/jellydn/CopilotChat.nvim/issues/30). diff --git a/cspell-tool.txt b/cspell-tool.txt index 73288a14..400bb5a2 100644 --- a/cspell-tool.txt +++ b/cspell-tool.txt @@ -65,4 +65,6 @@ noautocmd roleplay vusted luarocks -isort \ No newline at end of file +isort +checkhealth +sysname \ No newline at end of file diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index d3058d41..85510023 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -30,6 +30,68 @@ M.setup = function(options) end, { nargs = '*', range = true }) end + -- Show debug info + utils.create_cmd('CopilotChatDebugInfo', function() + -- Get the log file path + local log_file_path = utils.get_log_file_path() + + -- Get the rplugin path + local rplugin_path = utils.get_remote_plugins_path() + + -- Create a popup with the log file path + local lines = { + 'CopilotChat.nvim Info:', + '- Log file path: ' .. log_file_path, + '- Rplugin path: ' .. rplugin_path, + 'If you are facing issues, run `:checkhealth CopilotChat` and share the output.', + 'There is a common issue is "Ambiguous use of user-defined command". Please check the pin issues on the repository.', + 'Press `q` to close this window.', + 'Press `?` to open the rplugin file.', + } + + local width = 0 + for _, line in ipairs(lines) do + width = math.max(width, #line) + end + local height = #lines + local opts = { + relative = 'editor', + width = width + 4, + height = height + 2, + row = (vim.o.lines - height) / 2 - 1, + col = (vim.o.columns - width) / 2, + style = 'minimal', + border = 'rounded', + } + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + vim.api.nvim_open_win(bufnr, true, opts) + + -- Bind 'q' to close the window + vim.api.nvim_buf_set_keymap( + bufnr, + 'n', + 'q', + 'close', + { noremap = true, silent = true } + ) + + -- Bind `?` to open remote plugin detail + vim.api.nvim_buf_set_keymap( + bufnr, + 'n', + '?', + -- Close the current window and open the rplugin file + 'closeedit ' + .. rplugin_path + .. '', + { noremap = true, silent = true } + ) + end, { + nargs = '*', + range = true, + }) + utils.log_info( 'Execute ":UpdateRemotePlugins" and restart Neovim before starting a chat with Copilot.' ) diff --git a/lua/CopilotChat/utils.lua b/lua/CopilotChat/utils.lua index 3402d5b6..472c411e 100644 --- a/lua/CopilotChat/utils.lua +++ b/lua/CopilotChat/utils.lua @@ -2,6 +2,25 @@ local M = {} local log = require('CopilotChat.vlog') +--- Get the log file path +---@return string +M.get_log_file_path = function() + return log.get_log_file() +end + +-- The CopilotChat.nvim is built using remote plugins. +-- This is the path to the rplugin.vim file. +-- Refer https://neovim.io/doc/user/remote_plugin.html#%3AUpdateRemotePlugins +-- @return string +M.get_remote_plugins_path = function() + local os = vim.loop.os_uname().sysname + if os == 'Linux' or os == 'Darwin' then + return '~/.local/share/nvim/rplugin.vim' + elseif os == 'Windows' then + return '~/AppData/Local/nvim/rplugin.vim' + end +end + --- Create custom command ---@param cmd string The command name ---@param func function The function to execute From b50d85612d97e0c4713d45919dbeadcb747b9b53 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 3 Feb 2024 14:43:18 +0000 Subject: [PATCH 37/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 868a9f45..0b0b8f22 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -202,6 +202,15 @@ IN-PLACE CHAT POPUP ~ RECEIPTS *CopilotChat-copilot-chat-for-neovim-receipts* +DEBUGGING WITH :MESSAGES AND :COPILOTCHATDEBUGINFO ~ + +If you encounter any issues, you can run the command `:messages` to inspect the +log. You can also run the command `:CopilotChatDebugInfo` to inspect the debug +information. + + + + HOW TO SETUP WITH WHICH-KEY.NVIM ~ A special thanks to @ecosse3 for the configuration of which-key @@ -314,7 +323,8 @@ Contributions of any kind welcome! 5. *Generate tests*: https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif 6. *Fold Demo*: https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif 7. *In-place Demo*: https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif -8. *@ecosse3*: +8. *Debug Info*: https://i.gyazo.com/bf00e700bcee1b77bcbf7b516b552521.gif +9. *@ecosse3*: Generated by panvimdoc From 0d4c217723b020241c19c9213b9a6c8dd0b8875f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:24:23 +0800 Subject: [PATCH 38/75] chore(main): release 1.1.0 (#52) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0ab5b9..e5dba741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.1.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.0.0...v1.1.0) (2024-02-04) + + +### Features + +* add CopilotChatDebugInfo command ([#51](https://github.com/jellydn/CopilotChat.nvim/issues/51)) ([89b6276](https://github.com/jellydn/CopilotChat.nvim/commit/89b6276e995de2e05ea391a9d1045676737c93bd)) + ## 1.0.0 (2024-02-03) From ca6321cb257ed4f38ec9848712fd559639f2df33 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 07:24:46 +0000 Subject: [PATCH 39/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 0b0b8f22..1ff3f1bd 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 03 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 04 ============================================================================== Table of Contents *CopilotChat-table-of-contents* From b8d0a9d0e0824ff3b643a2652202be2a51b37dbc Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:29:58 +0800 Subject: [PATCH 40/75] feat: show date time and additional information on end separator (#53) * feat: show date time and addition information on end separator * refactor: merge system prompts to one file --- rplugin/python3/handlers/chat_handler.py | 32 ++++++++++++------- .../python3/handlers/inplace_chat_handler.py | 6 ++-- rplugin/python3/handlers/prompts.py | 2 -- rplugin/python3/prompts.py | 2 ++ 4 files changed, 24 insertions(+), 18 deletions(-) delete mode 100644 rplugin/python3/handlers/prompts.py diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index ebcdfac6..75f1a3d7 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -1,6 +1,7 @@ +from datetime import datetime from typing import Optional, cast -import prompts as prompts +import prompts as system_prompts from copilot import Copilot from mypynvim.core.buffer import MyBuffer from mypynvim.core.nvim import MyNvim @@ -52,18 +53,18 @@ def chat( self.nvim.exec_lua('require("CopilotChat.spinner").hide()') if not disable_end_separator: - self._add_end_separator() + self._add_end_separator(model) # private def _construct_system_prompt(self, prompt: str): - system_prompt = prompts.COPILOT_INSTRUCTIONS - if prompt == prompts.FIX_SHORTCUT: - system_prompt = prompts.COPILOT_FIX - elif prompt == prompts.TEST_SHORTCUT: - system_prompt = prompts.COPILOT_TESTS - elif prompt == prompts.EXPLAIN_SHORTCUT: - system_prompt = prompts.COPILOT_EXPLAIN + system_prompt = system_prompts.COPILOT_INSTRUCTIONS + if prompt == system_prompts.FIX_SHORTCUT: + system_prompt = system_prompts.COPILOT_FIX + elif prompt == system_prompts.TEST_SHORTCUT: + system_prompt = system_prompts.COPILOT_TESTS + elif prompt == system_prompts.EXPLAIN_SHORTCUT: + system_prompt = system_prompts.COPILOT_EXPLAIN return system_prompt def _add_start_separator( @@ -204,6 +205,13 @@ def _add_chat_messages( token.split("\n"), ) - def _add_end_separator(self): - end_separator = "\n---\n" - self.buffer.append(end_separator.split("\n")) + def _add_end_separator(self, model: str): + current_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + model_info = f"\n#### Answer provided by Copilot (Model: `{model}`) on {current_datetime}." + additional_instructions = ( + "\n> For additional queries, please use the `CopilotChat` command." + ) + disclaimer = "\n> Please be aware that the AI's output may not always be accurate. Always cross-verify the output.\n---\n" + + end_message = model_info + additional_instructions + disclaimer + self.buffer.append(end_message.split("\n")) diff --git a/rplugin/python3/handlers/inplace_chat_handler.py b/rplugin/python3/handlers/inplace_chat_handler.py index d3b36dc2..f0d784f2 100644 --- a/rplugin/python3/handlers/inplace_chat_handler.py +++ b/rplugin/python3/handlers/inplace_chat_handler.py @@ -5,8 +5,6 @@ from mypynvim.ui_components.layout import Box, Layout from mypynvim.ui_components.popup import PopUp -from . import prompts - # Define constants for the models MODEL_GPT4 = "gpt-4" MODEL_GPT35_TURBO = "gpt-3.5-turbo" @@ -243,10 +241,10 @@ def _set_keymaps(self): self.prompt_popup.map("n", "", lambda cb=self._toggle_system_model: cb()) self.prompt_popup.map( - "n", "'", lambda: self._set_prompt(prompts.PROMPT_SIMPLE_DOCSTRING) + "n", "'", lambda: self._set_prompt(system_prompts.PROMPT_SIMPLE_DOCSTRING) ) self.prompt_popup.map( - "n", "s", lambda: self._set_prompt(prompts.PROMPT_SEPARATE) + "n", "s", lambda: self._set_prompt(system_prompts.PROMPT_SEPARATE) ) self.prompt_popup.map( diff --git a/rplugin/python3/handlers/prompts.py b/rplugin/python3/handlers/prompts.py deleted file mode 100644 index f12db7c7..00000000 --- a/rplugin/python3/handlers/prompts.py +++ /dev/null @@ -1,2 +0,0 @@ -PROMPT_SIMPLE_DOCSTRING = "add simple docstring to this code" -PROMPT_SEPARATE = "add comments separating the code into sections" diff --git a/rplugin/python3/prompts.py b/rplugin/python3/prompts.py index 34a857a4..1290cbb0 100644 --- a/rplugin/python3/prompts.py +++ b/rplugin/python3/prompts.py @@ -247,3 +247,5 @@ Your code output should keep the same level of indentation as the user's code. You MUST add whitespace in the beginning of each line as needed to match the user's code. """ +PROMPT_SIMPLE_DOCSTRING = "add simple docstring to this code" +PROMPT_SEPARATE = "add comments separating the code into sections" From 0b917f633eaef621d293f344965e9e0545be9a80 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sun, 4 Feb 2024 15:42:27 +0800 Subject: [PATCH 41/75] fix: handle get remote plugin path on Windows --- lua/CopilotChat/utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/CopilotChat/utils.lua b/lua/CopilotChat/utils.lua index 472c411e..3d83bdc3 100644 --- a/lua/CopilotChat/utils.lua +++ b/lua/CopilotChat/utils.lua @@ -16,7 +16,7 @@ M.get_remote_plugins_path = function() local os = vim.loop.os_uname().sysname if os == 'Linux' or os == 'Darwin' then return '~/.local/share/nvim/rplugin.vim' - elseif os == 'Windows' then + else return '~/AppData/Local/nvim/rplugin.vim' end end From 07cb1de2e225744fb74f6cc46fccadcd1df8466a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:59:45 +0800 Subject: [PATCH 42/75] chore(main): release 1.2.0 (#54) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5dba741..3c095c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [1.2.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.1.0...v1.2.0) (2024-02-04) + + +### Features + +* show date time and additional information on end separator ([#53](https://github.com/jellydn/CopilotChat.nvim/issues/53)) ([b8d0a9d](https://github.com/jellydn/CopilotChat.nvim/commit/b8d0a9d0e0824ff3b643a2652202be2a51b37dbc)) + + +### Bug Fixes + +* handle get remote plugin path on Windows ([0b917f6](https://github.com/jellydn/CopilotChat.nvim/commit/0b917f633eaef621d293f344965e9e0545be9a80)) + ## [1.1.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.0.0...v1.1.0) (2024-02-04) From aca9d6adc198ef7f387c101e64acde06128d0417 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:33:48 +0800 Subject: [PATCH 43/75] chore: rename to tips --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71812b99..f8beabaf 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ For further reference, you can view @jellydn's [configuration](https://github.co [![In-place Demo](https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif)](https://gyazo.com/4a5badaa109cd483c1fc23d296325cb0) -## Receipts +## Tips ### Debugging with `:messages` and `:CopilotChatDebugInfo` From 405598454408bd8998851a7bfcf39543b91c6d1c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 09:34:09 +0000 Subject: [PATCH 44/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 1ff3f1bd..c709e515 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -7,7 +7,7 @@ Table of Contents *CopilotChat-table-of-contents* - Authentication |CopilotChat-copilot-chat-for-neovim-authentication| - Installation |CopilotChat-copilot-chat-for-neovim-installation| - Usage |CopilotChat-copilot-chat-for-neovim-usage| - - Receipts |CopilotChat-copilot-chat-for-neovim-receipts| + - Tips |CopilotChat-copilot-chat-for-neovim-tips| - Roadmap |CopilotChat-copilot-chat-for-neovim-roadmap| - Development |CopilotChat-copilot-chat-for-neovim-development| - Contributors ✨ |CopilotChat-copilot-chat-for-neovim-contributors-✨| @@ -199,7 +199,7 @@ IN-PLACE CHAT POPUP ~ -RECEIPTS *CopilotChat-copilot-chat-for-neovim-receipts* +TIPS *CopilotChat-copilot-chat-for-neovim-tips* DEBUGGING WITH :MESSAGES AND :COPILOTCHATDEBUGINFO ~ From d64028b893a6be4e6ed029b530ef620a08cbd979 Mon Sep 17 00:00:00 2001 From: kristofka Date: Sun, 4 Feb 2024 15:24:29 +0100 Subject: [PATCH 45/75] Fixes #56 issue when foldmethod is not manual (#57) See issue #56 --- rplugin/python3/handlers/chat_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index 75f1a3d7..679ae9f3 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -167,6 +167,7 @@ def _add_folds( system_prompt_height: int, winnr: int, ): + self.nvim.command("set foldmethod=manual") system_fold_start = last_row_before + 2 system_fold_end = system_fold_start + system_prompt_height + 3 main_command = f"{system_fold_start}, {system_fold_end} fold | normal! Gzz" From cace00fe5fd365c45125815ac0b8b4e0b769a13e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:24:49 +0800 Subject: [PATCH 46/75] docs: add kristofka as a contributor for code (#58) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 39 ++++++++++++++++++++++++++++++++------- README.md | 5 ++--- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0a5effab..bc6c1a5a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,5 +1,7 @@ { - "files": ["README.md"], + "files": [ + "README.md" + ], "imageSize": 100, "commit": false, "commitType": "docs", @@ -10,42 +12,65 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": ["code"] + "contributions": [ + "code" + ] + }, + { + "login": "kristofka", + "name": "kristofka", + "avatar_url": "https://avatars.githubusercontent.com/u/140354?v=4", + "profile": "https://github.com/kristofka", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index f8beabaf..c35d79eb 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Copilot Chat for Neovim - -[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) > [!NOTE] @@ -284,6 +282,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Trí Thiện Nguyễn
Trí Thiện Nguyễn

💻 He Zhizhou
He Zhizhou

💻 Guruprakash Rajakkannu
Guruprakash Rajakkannu

💻 + kristofka
kristofka

💻 From 6ff5b0a2fc4d2399949506f5d32af863498b28db Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:45:23 +0000 Subject: [PATCH 47/75] merge jellydn fork --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c35d79eb..0f30fdf7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ [![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) +> [!NOTE] +> You might want to take a look at [this fork](https://github.com/jellydn/CopilotChat.nvim) which is more well maintained & is more configurable. I personally use it now as well. + > [!NOTE] > A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, please join our [Discord](https://discord.gg/vy6hJsTWaZ) community. From df5d60a200be4afb177f49df9b955ffdbbed0dc4 Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:45:53 +0000 Subject: [PATCH 48/75] remove release workflow --- .github/workflows/release.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 9860c202..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Release -on: - push: - pull_request: - -jobs: - release: - name: release - runs-on: ubuntu-latest - steps: - - uses: google-github-actions/release-please-action@v3 - id: release - with: - release-type: simple - package-name: CopilotChat.nvim - - uses: actions/checkout@v3 - - name: tag stable versions - if: ${{ steps.release.outputs.release_created }} - run: | - git config user.name github-actions[bot] - git config user.email github-actions[bot]@users.noreply.github.com - git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" - git tag -d stable || true - git push origin :stable || true - git tag -a stable -m "Last Stable Release" - git push origin stable From 3b1cfc5e80594fe1ecf0e2616ec933a9f3a7a98f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 18:46:12 +0000 Subject: [PATCH 49/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index c709e515..95c857e4 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -18,6 +18,10 @@ Table of Contents *CopilotChat-table-of-contents* |CopilotChat-| + [!NOTE] You might want to take a look at this fork + which is more well maintained & + is more configurable. I personally use it now as well. + [!NOTE] A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, @@ -309,14 +313,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square 2. *@jellydn*: 3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif From 55826c1cb915a2be51b57c1eaf29bca5c59cc8ac Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:04:08 +0000 Subject: [PATCH 50/75] put the authentication code back --- .all-contributorsrc | 34 +++++------------- CHANGELOG.md | 45 ++++++++++-------------- README.md | 2 ++ rplugin/python3/handlers/chat_handler.py | 18 +++++++++- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index bc6c1a5a..4bd80307 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,7 +1,5 @@ { - "files": [ - "README.md" - ], + "files": ["README.md"], "imageSize": 100, "commit": false, "commitType": "docs", @@ -12,65 +10,49 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "kristofka", "name": "kristofka", "avatar_url": "https://avatars.githubusercontent.com/u/140354?v=4", "profile": "https://github.com/kristofka", - "contributions": [ - "code" - ] + "contributions": ["code"] } ], "contributorsPerLine": 7, diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c095c00..20d5a900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,56 +2,49 @@ ## [1.2.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.1.0...v1.2.0) (2024-02-04) - ### Features -* show date time and additional information on end separator ([#53](https://github.com/jellydn/CopilotChat.nvim/issues/53)) ([b8d0a9d](https://github.com/jellydn/CopilotChat.nvim/commit/b8d0a9d0e0824ff3b643a2652202be2a51b37dbc)) - +- show date time and additional information on end separator ([#53](https://github.com/jellydn/CopilotChat.nvim/issues/53)) ([b8d0a9d](https://github.com/jellydn/CopilotChat.nvim/commit/b8d0a9d0e0824ff3b643a2652202be2a51b37dbc)) ### Bug Fixes -* handle get remote plugin path on Windows ([0b917f6](https://github.com/jellydn/CopilotChat.nvim/commit/0b917f633eaef621d293f344965e9e0545be9a80)) +- handle get remote plugin path on Windows ([0b917f6](https://github.com/jellydn/CopilotChat.nvim/commit/0b917f633eaef621d293f344965e9e0545be9a80)) ## [1.1.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.0.0...v1.1.0) (2024-02-04) - ### Features -* add CopilotChatDebugInfo command ([#51](https://github.com/jellydn/CopilotChat.nvim/issues/51)) ([89b6276](https://github.com/jellydn/CopilotChat.nvim/commit/89b6276e995de2e05ea391a9d1045676737c93bd)) +- add CopilotChatDebugInfo command ([#51](https://github.com/jellydn/CopilotChat.nvim/issues/51)) ([89b6276](https://github.com/jellydn/CopilotChat.nvim/commit/89b6276e995de2e05ea391a9d1045676737c93bd)) ## 1.0.0 (2024-02-03) - ### ⚠ BREAKING CHANGES -* drop new buffer mode +- drop new buffer mode ### Features -* add a note for help user to continue the chat ([8a80ee7](https://github.com/jellydn/CopilotChat.nvim/commit/8a80ee7d3f9d0dcb65b315255d629c2cd8263dac)) -* add CCExplain command ([640f361](https://github.com/jellydn/CopilotChat.nvim/commit/640f361a54be51e7c479257c374d4a26d8fcd31d)) -* add CCTests command ([b34a78f](https://github.com/jellydn/CopilotChat.nvim/commit/b34a78f05ebe65ca093e4dc4b66de9120a681f4c)) -* add configuration options for wrap and filetype ([b4c6e76](https://github.com/jellydn/CopilotChat.nvim/commit/b4c6e760232ec54d4632edef3869e1a05ec61751)) -* add CopilotChatToggleLayout ([07988b9](https://github.com/jellydn/CopilotChat.nvim/commit/07988b95a412756169016e991dabcf190a930c7e)) -* add debug flag ([d0dbd4c](https://github.com/jellydn/CopilotChat.nvim/commit/d0dbd4c6fb9be75ccaa591b050198d40c097f423)) -* add health check ([974f14f](https://github.com/jellydn/CopilotChat.nvim/commit/974f14f0d0978d858cbe0126568f30fd63262cb6)) -* add new keymap to get previous user prompt ([6e7e80f](https://github.com/jellydn/CopilotChat.nvim/commit/6e7e80f118c589a009fa1703a284ad292260e3a0)) -* set filetype to markdown and text wrapping ([9b19d51](https://github.com/jellydn/CopilotChat.nvim/commit/9b19d51deacdf5c958933e99a2e75ebe4c968a9b)) -* show chat in markdown format ([9c14152](https://github.com/jellydn/CopilotChat.nvim/commit/9c141523de12e723b1d72d95760f2daddcecd1d9)) - +- add a note for help user to continue the chat ([8a80ee7](https://github.com/jellydn/CopilotChat.nvim/commit/8a80ee7d3f9d0dcb65b315255d629c2cd8263dac)) +- add CCExplain command ([640f361](https://github.com/jellydn/CopilotChat.nvim/commit/640f361a54be51e7c479257c374d4a26d8fcd31d)) +- add CCTests command ([b34a78f](https://github.com/jellydn/CopilotChat.nvim/commit/b34a78f05ebe65ca093e4dc4b66de9120a681f4c)) +- add configuration options for wrap and filetype ([b4c6e76](https://github.com/jellydn/CopilotChat.nvim/commit/b4c6e760232ec54d4632edef3869e1a05ec61751)) +- add CopilotChatToggleLayout ([07988b9](https://github.com/jellydn/CopilotChat.nvim/commit/07988b95a412756169016e991dabcf190a930c7e)) +- add debug flag ([d0dbd4c](https://github.com/jellydn/CopilotChat.nvim/commit/d0dbd4c6fb9be75ccaa591b050198d40c097f423)) +- add health check ([974f14f](https://github.com/jellydn/CopilotChat.nvim/commit/974f14f0d0978d858cbe0126568f30fd63262cb6)) +- add new keymap to get previous user prompt ([6e7e80f](https://github.com/jellydn/CopilotChat.nvim/commit/6e7e80f118c589a009fa1703a284ad292260e3a0)) +- set filetype to markdown and text wrapping ([9b19d51](https://github.com/jellydn/CopilotChat.nvim/commit/9b19d51deacdf5c958933e99a2e75ebe4c968a9b)) +- show chat in markdown format ([9c14152](https://github.com/jellydn/CopilotChat.nvim/commit/9c141523de12e723b1d72d95760f2daddcecd1d9)) ### Bug Fixes -* **ci:** generate doc ([6287fd4](https://github.com/jellydn/CopilotChat.nvim/commit/6287fd452d83d43a739d4c7c7a5524537032fc5d)) -* Close spinner if the buffer does not exist ([#11](https://github.com/jellydn/CopilotChat.nvim/issues/11)) ([0ea238d](https://github.com/jellydn/CopilotChat.nvim/commit/0ea238d7be9c7872dd9932a56d3521531b2297db)) -* remove LiteralString, use Any for fixing issue on Python 3.10 ([b68c352](https://github.com/jellydn/CopilotChat.nvim/commit/b68c3522d03c8ac9a332169c56e725b69a43b07c)), closes [#45](https://github.com/jellydn/CopilotChat.nvim/issues/45) - +- **ci:** generate doc ([6287fd4](https://github.com/jellydn/CopilotChat.nvim/commit/6287fd452d83d43a739d4c7c7a5524537032fc5d)) +- Close spinner if the buffer does not exist ([#11](https://github.com/jellydn/CopilotChat.nvim/issues/11)) ([0ea238d](https://github.com/jellydn/CopilotChat.nvim/commit/0ea238d7be9c7872dd9932a56d3521531b2297db)) +- remove LiteralString, use Any for fixing issue on Python 3.10 ([b68c352](https://github.com/jellydn/CopilotChat.nvim/commit/b68c3522d03c8ac9a332169c56e725b69a43b07c)), closes [#45](https://github.com/jellydn/CopilotChat.nvim/issues/45) ### Reverts -* change back to CopilotChat command ([e304f79](https://github.com/jellydn/CopilotChat.nvim/commit/e304f792a5fbba412c2a5a1f717ec7e2ab12e5b0)) - +- change back to CopilotChat command ([e304f79](https://github.com/jellydn/CopilotChat.nvim/commit/e304f792a5fbba412c2a5a1f717ec7e2ab12e5b0)) ### Code Refactoring -* drop new buffer mode ([0a30b7c](https://github.com/jellydn/CopilotChat.nvim/commit/0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a)) +- drop new buffer mode ([0a30b7c](https://github.com/jellydn/CopilotChat.nvim/commit/0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a)) diff --git a/README.md b/README.md index 0f30fdf7..d2d923f2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Copilot Chat for Neovim + [![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) + > [!NOTE] diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index 679ae9f3..910e642e 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -1,3 +1,4 @@ +import time from datetime import datetime from typing import Optional, cast @@ -18,7 +19,7 @@ def is_module_installed(name): class ChatHandler: def __init__(self, nvim: MyNvim, buffer: MyBuffer): self.nvim: MyNvim = nvim - self.copilot = None + self.copilot: Copilot = None self.buffer: MyBuffer = buffer # public @@ -186,6 +187,21 @@ def _add_chat_messages( ): if self.copilot is None: self.copilot = Copilot() + if self.copilot.github_token is None: + req = self.copilot.request_auth() + self.nvim.out_write( + f"Please visit {req['verification_uri']} and enter the code {req['user_code']}\n" + ) + current_time = time.time() + wait_until = current_time + req["expires_in"] + while self.copilot.github_token is None: + self.copilot.poll_auth(req["device_code"]) + time.sleep(req["interval"]) + if time.time() > wait_until: + self.nvim.out_write("Timed out waiting for authentication\n") + return + self.nvim.out_write("Successfully authenticated with Copilot\n") + self.copilot.authenticate() for token in self.copilot.ask( system_prompt, prompt, code, language=cast(str, file_type), model=model From 1786aa81e25388cd5bf6437353bed22cd2276e4a Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:05:59 +0000 Subject: [PATCH 51/75] We don't actually have dependencies --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d2d923f2..07047001 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ It will prompt you with instructions on your first start. If you already have `C return { { "jellydn/CopilotChat.nvim", - dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log From 1877e35422a71381a9a1445ebac52d88d4e10a4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 19:06:22 +0000 Subject: [PATCH 52/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 95c857e4..89d6c082 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -46,7 +46,6 @@ LAZY.NVIM ~ return { { "jellydn/CopilotChat.nvim", - dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log From f5df49ac959fbbc71da9af7b0499d3415bd8cba9 Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:47:15 +0000 Subject: [PATCH 53/75] Restore authentication code on first start (#59) * merge jellydn fork * remove release workflow * chore(doc): auto generate docs * put the authentication code back * We don't actually have dependencies * chore(doc): auto generate docs * remove pointer to fork in preparation for PR --------- Co-authored-by: github-actions[bot] --- .all-contributorsrc | 34 +++++------------- .github/workflows/release.yml | 26 -------------- CHANGELOG.md | 45 ++++++++++-------------- README.md | 3 +- doc/CopilotChat.txt | 9 +++-- rplugin/python3/handlers/chat_handler.py | 18 +++++++++- 6 files changed, 52 insertions(+), 83 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.all-contributorsrc b/.all-contributorsrc index bc6c1a5a..4bd80307 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,7 +1,5 @@ { - "files": [ - "README.md" - ], + "files": ["README.md"], "imageSize": 100, "commit": false, "commitType": "docs", @@ -12,65 +10,49 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "kristofka", "name": "kristofka", "avatar_url": "https://avatars.githubusercontent.com/u/140354?v=4", "profile": "https://github.com/kristofka", - "contributions": [ - "code" - ] + "contributions": ["code"] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 9860c202..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Release -on: - push: - pull_request: - -jobs: - release: - name: release - runs-on: ubuntu-latest - steps: - - uses: google-github-actions/release-please-action@v3 - id: release - with: - release-type: simple - package-name: CopilotChat.nvim - - uses: actions/checkout@v3 - - name: tag stable versions - if: ${{ steps.release.outputs.release_created }} - run: | - git config user.name github-actions[bot] - git config user.email github-actions[bot]@users.noreply.github.com - git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" - git tag -d stable || true - git push origin :stable || true - git tag -a stable -m "Last Stable Release" - git push origin stable diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c095c00..20d5a900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,56 +2,49 @@ ## [1.2.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.1.0...v1.2.0) (2024-02-04) - ### Features -* show date time and additional information on end separator ([#53](https://github.com/jellydn/CopilotChat.nvim/issues/53)) ([b8d0a9d](https://github.com/jellydn/CopilotChat.nvim/commit/b8d0a9d0e0824ff3b643a2652202be2a51b37dbc)) - +- show date time and additional information on end separator ([#53](https://github.com/jellydn/CopilotChat.nvim/issues/53)) ([b8d0a9d](https://github.com/jellydn/CopilotChat.nvim/commit/b8d0a9d0e0824ff3b643a2652202be2a51b37dbc)) ### Bug Fixes -* handle get remote plugin path on Windows ([0b917f6](https://github.com/jellydn/CopilotChat.nvim/commit/0b917f633eaef621d293f344965e9e0545be9a80)) +- handle get remote plugin path on Windows ([0b917f6](https://github.com/jellydn/CopilotChat.nvim/commit/0b917f633eaef621d293f344965e9e0545be9a80)) ## [1.1.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.0.0...v1.1.0) (2024-02-04) - ### Features -* add CopilotChatDebugInfo command ([#51](https://github.com/jellydn/CopilotChat.nvim/issues/51)) ([89b6276](https://github.com/jellydn/CopilotChat.nvim/commit/89b6276e995de2e05ea391a9d1045676737c93bd)) +- add CopilotChatDebugInfo command ([#51](https://github.com/jellydn/CopilotChat.nvim/issues/51)) ([89b6276](https://github.com/jellydn/CopilotChat.nvim/commit/89b6276e995de2e05ea391a9d1045676737c93bd)) ## 1.0.0 (2024-02-03) - ### ⚠ BREAKING CHANGES -* drop new buffer mode +- drop new buffer mode ### Features -* add a note for help user to continue the chat ([8a80ee7](https://github.com/jellydn/CopilotChat.nvim/commit/8a80ee7d3f9d0dcb65b315255d629c2cd8263dac)) -* add CCExplain command ([640f361](https://github.com/jellydn/CopilotChat.nvim/commit/640f361a54be51e7c479257c374d4a26d8fcd31d)) -* add CCTests command ([b34a78f](https://github.com/jellydn/CopilotChat.nvim/commit/b34a78f05ebe65ca093e4dc4b66de9120a681f4c)) -* add configuration options for wrap and filetype ([b4c6e76](https://github.com/jellydn/CopilotChat.nvim/commit/b4c6e760232ec54d4632edef3869e1a05ec61751)) -* add CopilotChatToggleLayout ([07988b9](https://github.com/jellydn/CopilotChat.nvim/commit/07988b95a412756169016e991dabcf190a930c7e)) -* add debug flag ([d0dbd4c](https://github.com/jellydn/CopilotChat.nvim/commit/d0dbd4c6fb9be75ccaa591b050198d40c097f423)) -* add health check ([974f14f](https://github.com/jellydn/CopilotChat.nvim/commit/974f14f0d0978d858cbe0126568f30fd63262cb6)) -* add new keymap to get previous user prompt ([6e7e80f](https://github.com/jellydn/CopilotChat.nvim/commit/6e7e80f118c589a009fa1703a284ad292260e3a0)) -* set filetype to markdown and text wrapping ([9b19d51](https://github.com/jellydn/CopilotChat.nvim/commit/9b19d51deacdf5c958933e99a2e75ebe4c968a9b)) -* show chat in markdown format ([9c14152](https://github.com/jellydn/CopilotChat.nvim/commit/9c141523de12e723b1d72d95760f2daddcecd1d9)) - +- add a note for help user to continue the chat ([8a80ee7](https://github.com/jellydn/CopilotChat.nvim/commit/8a80ee7d3f9d0dcb65b315255d629c2cd8263dac)) +- add CCExplain command ([640f361](https://github.com/jellydn/CopilotChat.nvim/commit/640f361a54be51e7c479257c374d4a26d8fcd31d)) +- add CCTests command ([b34a78f](https://github.com/jellydn/CopilotChat.nvim/commit/b34a78f05ebe65ca093e4dc4b66de9120a681f4c)) +- add configuration options for wrap and filetype ([b4c6e76](https://github.com/jellydn/CopilotChat.nvim/commit/b4c6e760232ec54d4632edef3869e1a05ec61751)) +- add CopilotChatToggleLayout ([07988b9](https://github.com/jellydn/CopilotChat.nvim/commit/07988b95a412756169016e991dabcf190a930c7e)) +- add debug flag ([d0dbd4c](https://github.com/jellydn/CopilotChat.nvim/commit/d0dbd4c6fb9be75ccaa591b050198d40c097f423)) +- add health check ([974f14f](https://github.com/jellydn/CopilotChat.nvim/commit/974f14f0d0978d858cbe0126568f30fd63262cb6)) +- add new keymap to get previous user prompt ([6e7e80f](https://github.com/jellydn/CopilotChat.nvim/commit/6e7e80f118c589a009fa1703a284ad292260e3a0)) +- set filetype to markdown and text wrapping ([9b19d51](https://github.com/jellydn/CopilotChat.nvim/commit/9b19d51deacdf5c958933e99a2e75ebe4c968a9b)) +- show chat in markdown format ([9c14152](https://github.com/jellydn/CopilotChat.nvim/commit/9c141523de12e723b1d72d95760f2daddcecd1d9)) ### Bug Fixes -* **ci:** generate doc ([6287fd4](https://github.com/jellydn/CopilotChat.nvim/commit/6287fd452d83d43a739d4c7c7a5524537032fc5d)) -* Close spinner if the buffer does not exist ([#11](https://github.com/jellydn/CopilotChat.nvim/issues/11)) ([0ea238d](https://github.com/jellydn/CopilotChat.nvim/commit/0ea238d7be9c7872dd9932a56d3521531b2297db)) -* remove LiteralString, use Any for fixing issue on Python 3.10 ([b68c352](https://github.com/jellydn/CopilotChat.nvim/commit/b68c3522d03c8ac9a332169c56e725b69a43b07c)), closes [#45](https://github.com/jellydn/CopilotChat.nvim/issues/45) - +- **ci:** generate doc ([6287fd4](https://github.com/jellydn/CopilotChat.nvim/commit/6287fd452d83d43a739d4c7c7a5524537032fc5d)) +- Close spinner if the buffer does not exist ([#11](https://github.com/jellydn/CopilotChat.nvim/issues/11)) ([0ea238d](https://github.com/jellydn/CopilotChat.nvim/commit/0ea238d7be9c7872dd9932a56d3521531b2297db)) +- remove LiteralString, use Any for fixing issue on Python 3.10 ([b68c352](https://github.com/jellydn/CopilotChat.nvim/commit/b68c3522d03c8ac9a332169c56e725b69a43b07c)), closes [#45](https://github.com/jellydn/CopilotChat.nvim/issues/45) ### Reverts -* change back to CopilotChat command ([e304f79](https://github.com/jellydn/CopilotChat.nvim/commit/e304f792a5fbba412c2a5a1f717ec7e2ab12e5b0)) - +- change back to CopilotChat command ([e304f79](https://github.com/jellydn/CopilotChat.nvim/commit/e304f792a5fbba412c2a5a1f717ec7e2ab12e5b0)) ### Code Refactoring -* drop new buffer mode ([0a30b7c](https://github.com/jellydn/CopilotChat.nvim/commit/0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a)) +- drop new buffer mode ([0a30b7c](https://github.com/jellydn/CopilotChat.nvim/commit/0a30b7cfbd8b52bf8a9e4cd96dcade4995e6eb3a)) diff --git a/README.md b/README.md index c35d79eb..13d4f20e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Copilot Chat for Neovim + [![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) + > [!NOTE] @@ -23,7 +25,6 @@ It will prompt you with instructions on your first start. If you already have `C return { { "jellydn/CopilotChat.nvim", - dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index c709e515..89d6c082 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -18,6 +18,10 @@ Table of Contents *CopilotChat-table-of-contents* |CopilotChat-| + [!NOTE] You might want to take a look at this fork + which is more well maintained & + is more configurable. I personally use it now as well. + [!NOTE] A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, @@ -42,7 +46,6 @@ LAZY.NVIM ~ return { { "jellydn/CopilotChat.nvim", - dependencies = { "zbirenbaum/copilot.lua" }, -- Or { "github/copilot.vim" } opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log @@ -309,14 +312,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square 2. *@jellydn*: 3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index 679ae9f3..910e642e 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -1,3 +1,4 @@ +import time from datetime import datetime from typing import Optional, cast @@ -18,7 +19,7 @@ def is_module_installed(name): class ChatHandler: def __init__(self, nvim: MyNvim, buffer: MyBuffer): self.nvim: MyNvim = nvim - self.copilot = None + self.copilot: Copilot = None self.buffer: MyBuffer = buffer # public @@ -186,6 +187,21 @@ def _add_chat_messages( ): if self.copilot is None: self.copilot = Copilot() + if self.copilot.github_token is None: + req = self.copilot.request_auth() + self.nvim.out_write( + f"Please visit {req['verification_uri']} and enter the code {req['user_code']}\n" + ) + current_time = time.time() + wait_until = current_time + req["expires_in"] + while self.copilot.github_token is None: + self.copilot.poll_auth(req["device_code"]) + time.sleep(req["interval"]) + if time.time() > wait_until: + self.nvim.out_write("Timed out waiting for authentication\n") + return + self.nvim.out_write("Successfully authenticated with Copilot\n") + self.copilot.authenticate() for token in self.copilot.ask( system_prompt, prompt, code, language=cast(str, file_type), model=model From 7d4cb93e6c591932e5a3b215a5b65d9754bc1a80 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 19:47:35 +0000 Subject: [PATCH 54/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 89d6c082..8043f7df 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -18,10 +18,6 @@ Table of Contents *CopilotChat-table-of-contents* |CopilotChat-| - [!NOTE] You might want to take a look at this fork - which is more well maintained & - is more configurable. I personally use it now as well. - [!NOTE] A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, From 81a9d818b1369d41108c46da477e4ea5cec0a525 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Mon, 5 Feb 2024 03:54:40 +0800 Subject: [PATCH 55/75] revert(ci): add release workflow back --- .github/workflows/release.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6f3d1425 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release +on: + push: + branches: + - release + pull_request: + branches: + - main + - release + +jobs: + release: + name: release + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: CopilotChat.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable From 033406776b3ca084aacdbae18c5f864b52d64ef1 Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:51:49 +0000 Subject: [PATCH 56/75] fix Python3.10 by removing Unpack type infomation --- rplugin/python3/mypynvim/ui_components/popup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rplugin/python3/mypynvim/ui_components/popup.py b/rplugin/python3/mypynvim/ui_components/popup.py index 9040b452..cab2bac9 100644 --- a/rplugin/python3/mypynvim/ui_components/popup.py +++ b/rplugin/python3/mypynvim/ui_components/popup.py @@ -2,7 +2,7 @@ from copy import deepcopy from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, Unpack +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union if TYPE_CHECKING: from mypynvim.core.nvim import MyNvim @@ -14,7 +14,7 @@ from mypynvim.core.window import MyWindow from .calculator import Calculator -from .types import PaddingKeys, PopUpArgs, PopUpConfiguration, Relative +from .types import PaddingKeys, PopUpConfiguration, Relative @dataclass @@ -33,7 +33,7 @@ def __init__( padding: PaddingKeys = {}, enter: bool = False, opts={}, - **kwargs: Unpack[PopUpArgs], + **kwargs, ): self.nvim: MyNvim = nvim self.calculator: Calculator = Calculator(self.nvim) From 1f57cf457cdc1ca87fc2f9c9db9ef447c0165629 Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:18:26 +0000 Subject: [PATCH 57/75] add option to remove extra information: disable_extra_info --- lua/CopilotChat/init.lua | 1 + rplugin/python3/handlers/chat_handler.py | 32 +++++++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index 85510023..a5876373 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -16,6 +16,7 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_show_help = options and options.show_help or 'yes' + vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or false local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index 910e642e..3fe7b3d2 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -35,9 +35,9 @@ def chat( disable_end_separator: bool = False, model: str = "gpt-4", ): + no_annoyance = self.nvim.eval("g:copilot_chat_disable_separators") == "yes" if system_prompt is None: system_prompt = self._construct_system_prompt(prompt) - # Start the spinner self.nvim.exec_lua('require("CopilotChat.spinner").show()') @@ -46,7 +46,9 @@ def chat( ) if not disable_start_separator: - self._add_start_separator(system_prompt, prompt, code, filetype, winnr) + self._add_start_separator( + system_prompt, prompt, code, filetype, winnr, no_annoyance + ) self._add_chat_messages(system_prompt, prompt, code, filetype, model) @@ -54,7 +56,7 @@ def chat( self.nvim.exec_lua('require("CopilotChat.spinner").hide()') if not disable_end_separator: - self._add_end_separator(model) + self._add_end_separator(model, no_annoyance) # private @@ -75,14 +77,15 @@ def _add_start_separator( code: str, file_type: str, winnr: int, + no_annoyance: bool = False, ): - if is_module_installed("tiktoken"): + if is_module_installed("tiktoken") and not no_annoyance: self._add_start_separator_with_token_count( system_prompt, prompt, code, file_type, winnr ) else: self._add_regular_start_separator( - system_prompt, prompt, code, file_type, winnr + system_prompt, prompt, code, file_type, winnr, no_annoyance ) def _add_regular_start_separator( @@ -92,15 +95,17 @@ def _add_regular_start_separator( code: str, file_type: str, winnr: int, + no_annoyance: bool = False, ): - if code: + if code and not no_annoyance: code = f"\n \nCODE:\n```{file_type}\n{code}\n```" last_row_before = len(self.buffer.lines()) system_prompt_height = len(system_prompt.split("\n")) code_height = len(code.split("\n")) - start_separator = f"""### User + start_separator = ( + f"""### User SYSTEM PROMPT: ``` @@ -111,8 +116,13 @@ def _add_regular_start_separator( ### Copilot """ + if not no_annoyance + else f"### User\n{prompt}\n\n### Copilot\n\n" + ) self.buffer.append(start_separator.split("\n")) + if no_annoyance: + return self._add_folds(code, code_height, last_row_before, system_prompt_height, winnr) def _add_start_separator_with_token_count( @@ -222,13 +232,17 @@ def _add_chat_messages( token.split("\n"), ) - def _add_end_separator(self, model: str): + def _add_end_separator(self, model: str, no_annoyance: bool = False): current_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S") model_info = f"\n#### Answer provided by Copilot (Model: `{model}`) on {current_datetime}." additional_instructions = ( "\n> For additional queries, please use the `CopilotChat` command." ) - disclaimer = "\n> Please be aware that the AI's output may not always be accurate. Always cross-verify the output.\n---\n" + disclaimer = "\n> Please be aware that the AI's output may not always be accurate. Always cross-verify the output." end_message = model_info + additional_instructions + disclaimer + + if no_annoyance: + end_message = "\n" + current_datetime + "\n---\n" + self.buffer.append(end_message.split("\n")) From 0a8757fddc09f251d7bc0c5a5cafe13b4a145c07 Mon Sep 17 00:00:00 2001 From: gptlang <121417512+gptlang@users.noreply.github.com> Date: Sun, 4 Feb 2024 23:51:26 +0000 Subject: [PATCH 58/75] Fix Python 3.10 and add disable_extra_info option (#60) * fix Python3.10 by removing Unpack type infomation * add option to remove extra information: disable_extra_info --- lua/CopilotChat/init.lua | 1 + rplugin/python3/handlers/chat_handler.py | 32 +++++++++++++------ .../python3/mypynvim/ui_components/popup.py | 6 ++-- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index 85510023..a5876373 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -16,6 +16,7 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_show_help = options and options.show_help or 'yes' + vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or false local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug diff --git a/rplugin/python3/handlers/chat_handler.py b/rplugin/python3/handlers/chat_handler.py index 910e642e..3fe7b3d2 100644 --- a/rplugin/python3/handlers/chat_handler.py +++ b/rplugin/python3/handlers/chat_handler.py @@ -35,9 +35,9 @@ def chat( disable_end_separator: bool = False, model: str = "gpt-4", ): + no_annoyance = self.nvim.eval("g:copilot_chat_disable_separators") == "yes" if system_prompt is None: system_prompt = self._construct_system_prompt(prompt) - # Start the spinner self.nvim.exec_lua('require("CopilotChat.spinner").show()') @@ -46,7 +46,9 @@ def chat( ) if not disable_start_separator: - self._add_start_separator(system_prompt, prompt, code, filetype, winnr) + self._add_start_separator( + system_prompt, prompt, code, filetype, winnr, no_annoyance + ) self._add_chat_messages(system_prompt, prompt, code, filetype, model) @@ -54,7 +56,7 @@ def chat( self.nvim.exec_lua('require("CopilotChat.spinner").hide()') if not disable_end_separator: - self._add_end_separator(model) + self._add_end_separator(model, no_annoyance) # private @@ -75,14 +77,15 @@ def _add_start_separator( code: str, file_type: str, winnr: int, + no_annoyance: bool = False, ): - if is_module_installed("tiktoken"): + if is_module_installed("tiktoken") and not no_annoyance: self._add_start_separator_with_token_count( system_prompt, prompt, code, file_type, winnr ) else: self._add_regular_start_separator( - system_prompt, prompt, code, file_type, winnr + system_prompt, prompt, code, file_type, winnr, no_annoyance ) def _add_regular_start_separator( @@ -92,15 +95,17 @@ def _add_regular_start_separator( code: str, file_type: str, winnr: int, + no_annoyance: bool = False, ): - if code: + if code and not no_annoyance: code = f"\n \nCODE:\n```{file_type}\n{code}\n```" last_row_before = len(self.buffer.lines()) system_prompt_height = len(system_prompt.split("\n")) code_height = len(code.split("\n")) - start_separator = f"""### User + start_separator = ( + f"""### User SYSTEM PROMPT: ``` @@ -111,8 +116,13 @@ def _add_regular_start_separator( ### Copilot """ + if not no_annoyance + else f"### User\n{prompt}\n\n### Copilot\n\n" + ) self.buffer.append(start_separator.split("\n")) + if no_annoyance: + return self._add_folds(code, code_height, last_row_before, system_prompt_height, winnr) def _add_start_separator_with_token_count( @@ -222,13 +232,17 @@ def _add_chat_messages( token.split("\n"), ) - def _add_end_separator(self, model: str): + def _add_end_separator(self, model: str, no_annoyance: bool = False): current_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S") model_info = f"\n#### Answer provided by Copilot (Model: `{model}`) on {current_datetime}." additional_instructions = ( "\n> For additional queries, please use the `CopilotChat` command." ) - disclaimer = "\n> Please be aware that the AI's output may not always be accurate. Always cross-verify the output.\n---\n" + disclaimer = "\n> Please be aware that the AI's output may not always be accurate. Always cross-verify the output." end_message = model_info + additional_instructions + disclaimer + + if no_annoyance: + end_message = "\n" + current_datetime + "\n---\n" + self.buffer.append(end_message.split("\n")) diff --git a/rplugin/python3/mypynvim/ui_components/popup.py b/rplugin/python3/mypynvim/ui_components/popup.py index 9040b452..cab2bac9 100644 --- a/rplugin/python3/mypynvim/ui_components/popup.py +++ b/rplugin/python3/mypynvim/ui_components/popup.py @@ -2,7 +2,7 @@ from copy import deepcopy from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, Unpack +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union if TYPE_CHECKING: from mypynvim.core.nvim import MyNvim @@ -14,7 +14,7 @@ from mypynvim.core.window import MyWindow from .calculator import Calculator -from .types import PaddingKeys, PopUpArgs, PopUpConfiguration, Relative +from .types import PaddingKeys, PopUpConfiguration, Relative @dataclass @@ -33,7 +33,7 @@ def __init__( padding: PaddingKeys = {}, enter: bool = False, opts={}, - **kwargs: Unpack[PopUpArgs], + **kwargs, ): self.nvim: MyNvim = nvim self.calculator: Calculator = Calculator(self.nvim) From ef6386b5d89323c41c02d3f67357865a6cda2710 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 08:06:43 +0800 Subject: [PATCH 59/75] chore(main): release 1.2.1 (#61) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d5a900..0ccfe4ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.2.1](https://github.com/jellydn/CopilotChat.nvim/compare/v1.2.0...v1.2.1) (2024-02-05) + + +### Reverts + +* **ci:** add release workflow back ([81a9d81](https://github.com/jellydn/CopilotChat.nvim/commit/81a9d818b1369d41108c46da477e4ea5cec0a525)) + ## [1.2.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.1.0...v1.2.0) (2024-02-04) ### Features From 16c81843d6d8f732f73e119754ffb4e37f19e45a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 00:07:01 +0000 Subject: [PATCH 60/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 89d6c082..a9462e8e 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 04 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 05 ============================================================================== Table of Contents *CopilotChat-table-of-contents* From 50c8fd0e1755807a6bca5d3504b38f9f9128ff6f Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Mon, 5 Feb 2024 10:24:36 +0800 Subject: [PATCH 61/75] refactor!: disable extra info as default --- lua/CopilotChat/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index a5876373..9fe5f325 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -12,11 +12,12 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- Set up the plugin ---@param options (table | nil) -- - show_help: ('yes' | 'no') default: 'yes'. +-- - disable_extra_info: ('yes' | 'no') default: 'yes'. -- - prompts: (table?) default: default_prompts. -- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_show_help = options and options.show_help or 'yes' - vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or false + vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or 'yes' local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug From d71215b8f795a48ee9c46ab65bd90364a268c7da Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Mon, 5 Feb 2024 10:24:58 +0800 Subject: [PATCH 62/75] docs: add disable_extra_info flag on readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 07047001..b2e07617 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ return { opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log + disable_extra_info = 'no', -- Disable extra information (e.g: system prompt) in the response. }, build = function() vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") From 16c4a67f41fed21383066e2f10a6b2f41aa6b56e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 02:25:21 +0000 Subject: [PATCH 63/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index a9462e8e..d4e5a880 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -49,6 +49,7 @@ LAZY.NVIM ~ opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log + disable_extra_info = 'no', -- Disable extra information (e.g: system prompt) in the response. }, build = function() vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") From 2b850b8edd220884a264ebbbf6cf1ae17c1663d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 08:06:43 +0800 Subject: [PATCH 64/75] chore(main): release 1.2.1 (#61) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d5a900..0ccfe4ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.2.1](https://github.com/jellydn/CopilotChat.nvim/compare/v1.2.0...v1.2.1) (2024-02-05) + + +### Reverts + +* **ci:** add release workflow back ([81a9d81](https://github.com/jellydn/CopilotChat.nvim/commit/81a9d818b1369d41108c46da477e4ea5cec0a525)) + ## [1.2.0](https://github.com/jellydn/CopilotChat.nvim/compare/v1.1.0...v1.2.0) (2024-02-04) ### Features From 4694b66538605bd57cec0f9355bab8f36a1f0ccb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 00:07:01 +0000 Subject: [PATCH 65/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 89d6c082..a9462e8e 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -1,4 +1,4 @@ -*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 04 +*CopilotChat.txt* For NVIM v0.8.0 Last change: 2024 February 05 ============================================================================== Table of Contents *CopilotChat-table-of-contents* From 5c4c22d1bb13d1d927c3301840d2a0699df2b732 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Mon, 5 Feb 2024 10:24:36 +0800 Subject: [PATCH 66/75] refactor!: disable extra info as default --- lua/CopilotChat/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index a5876373..9fe5f325 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -12,11 +12,12 @@ _COPILOT_CHAT_GLOBAL_CONFIG = {} -- Set up the plugin ---@param options (table | nil) -- - show_help: ('yes' | 'no') default: 'yes'. +-- - disable_extra_info: ('yes' | 'no') default: 'yes'. -- - prompts: (table?) default: default_prompts. -- - debug: (boolean?) default: false. M.setup = function(options) vim.g.copilot_chat_show_help = options and options.show_help or 'yes' - vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or false + vim.g.copilot_chat_disable_separators = options and options.disable_extra_info or 'yes' local debug = options and options.debug or false _COPILOT_CHAT_GLOBAL_CONFIG.debug = debug From a5319e508910c4f90ca8fd91f368074eb2a00048 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Mon, 5 Feb 2024 10:24:58 +0800 Subject: [PATCH 67/75] docs: add disable_extra_info flag on readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 07047001..b2e07617 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ return { opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log + disable_extra_info = 'no', -- Disable extra information (e.g: system prompt) in the response. }, build = function() vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") From 4d596994d1df9e05a8e6715c43aa81b1798e885e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 02:25:21 +0000 Subject: [PATCH 68/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index a9462e8e..d4e5a880 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -49,6 +49,7 @@ LAZY.NVIM ~ opts = { show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log + disable_extra_info = 'no', -- Disable extra information (e.g: system prompt) in the response. }, build = function() vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") From d1e65c056035459c5e14a75e779c39db9f04f8a6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:38:44 +0800 Subject: [PATCH 69/75] docs: add PostCyberPunk as a contributor for doc (#63) --- .all-contributorsrc | 43 +++++++++++++++++++++++++++++++++++-------- README.md | 7 ++++--- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4bd80307..59f826dd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,5 +1,7 @@ { - "files": ["README.md"], + "files": [ + "README.md" + ], "imageSize": 100, "commit": false, "commitType": "docs", @@ -10,49 +12,74 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "kristofka", "name": "kristofka", "avatar_url": "https://avatars.githubusercontent.com/u/140354?v=4", "profile": "https://github.com/kristofka", - "contributions": ["code"] + "contributions": [ + "code" + ] + }, + { + "login": "PostCyberPunk", + "name": "PostCyberPunk", + "avatar_url": "https://avatars.githubusercontent.com/u/134976996?v=4", + "profile": "https://github.com/PostCyberPunk", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index b2e07617..17e12411 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Copilot Chat for Neovim - -[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-) > [!NOTE] @@ -289,6 +287,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Guruprakash Rajakkannu
Guruprakash Rajakkannu

💻 kristofka
kristofka

💻 + + PostCyberPunk
PostCyberPunk

📖 + From 0c443cb785b528fcf21ff6668cea9579fd8d9316 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 03:39:05 +0000 Subject: [PATCH 70/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index d4e5a880..4b382413 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -313,14 +313,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻PostCyberPunk📖This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square 2. *@jellydn*: 3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif From d5f26b45f6b797719833bf27f66d8dbaa4c1b8d4 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:42:16 +0800 Subject: [PATCH 71/75] docs: add same keybinds in both visual and normal mode (#62) (#64) Co-authored-by: PostCyberPunk <134976996+PostCyberPunk@users.noreply.github.com> --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 17e12411..15dc5bb9 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,36 @@ Follow the example below to create a simple input for CopilotChat. }, ``` +### Add same keybinds in both visual and normal mode + +```lua + { + "jellydn/CopilotChat.nvim", + keys = + function() + local keybinds={ + --add your custom keybinds here + } + -- change prompt and keybinds as per your need + local my_prompts = { + {prompt = "In Neovim.",desc = "Neovim",key = "n"}, + {prompt = "Help with this",desc = "Help",key = "h"}, + {prompt = "Simplify and imporve readablilty",desc = "Simplify",key = "s"}, + {prompt = "Optimize the code to improve perfomance and readablilty.",desc = "Optimize",key = "o"}, + {prompt = "Find possible errors and fix them for me",desc = "Fix",key = "f"}, + {prompt = "Explain in detail",desc = "Explain",key = "e"}, + {prompt = "Write a shell scirpt",desc = "Shell",key = "S"}, + } + -- you can change cc to your desired keybind prefix + for _,v in pairs(my_prompts) do + table.insert(keybinds,{ "cc"..v.key, ":CopilotChatVisual "..v.prompt.."", mode = "x", desc = "CopilotChat - "..v.desc }) + table.insert(keybinds,{ "cc"..v.key, "CopilotChat "..v.prompt.."", desc = "CopilotChat - "..v.desc }) + end + return keybinds + end, + }, +``` + ## Roadmap - Translation to pure Lua From ad198edd6ebb19fa3da27644d41cd0ad24ee3965 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Feb 2024 03:42:35 +0000 Subject: [PATCH 72/75] chore(doc): auto generate docs --- doc/CopilotChat.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index 4b382413..a4361c87 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -285,6 +285,37 @@ Follow the example below to create a simple input for CopilotChat. < +ADD SAME KEYBINDS IN BOTH VISUAL AND NORMAL MODE ~ + +>lua + { + "jellydn/CopilotChat.nvim", + keys = + function() + local keybinds={ + --add your custom keybinds here + } + -- change prompt and keybinds as per your need + local my_prompts = { + {prompt = "In Neovim.",desc = "Neovim",key = "n"}, + {prompt = "Help with this",desc = "Help",key = "h"}, + {prompt = "Simplify and imporve readablilty",desc = "Simplify",key = "s"}, + {prompt = "Optimize the code to improve perfomance and readablilty.",desc = "Optimize",key = "o"}, + {prompt = "Find possible errors and fix them for me",desc = "Fix",key = "f"}, + {prompt = "Explain in detail",desc = "Explain",key = "e"}, + {prompt = "Write a shell scirpt",desc = "Shell",key = "S"}, + } + -- you can change cc to your desired keybind prefix + for _,v in pairs(my_prompts) do + table.insert(keybinds,{ "cc"..v.key, ":CopilotChatVisual "..v.prompt.."", mode = "x", desc = "CopilotChat - "..v.desc }) + table.insert(keybinds,{ "cc"..v.key, "CopilotChat "..v.prompt.."", desc = "CopilotChat - "..v.desc }) + end + return keybinds + end, + }, +< + + ROADMAP *CopilotChat-copilot-chat-for-neovim-roadmap* - Translation to pure Lua From 0fa28e7f78e6caa062e5c6a19f3713ff0b0ad547 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:44:06 +0800 Subject: [PATCH 73/75] docs: add same keybinds in both visual and normal mode (#24) * Fix Python 3.10 and add disable_extra_info option (#60) * fix Python3.10 by removing Unpack type infomation * add option to remove extra information: disable_extra_info * chore(main): release 1.2.1 (#61) * chore(doc): auto generate docs * refactor!: disable extra info as default * docs: add disable_extra_info flag on readme * chore(doc): auto generate docs * docs: add PostCyberPunk as a contributor for doc (#63) * chore(doc): auto generate docs * docs: add same keybinds in both visual and normal mode (#62) (#64) Co-authored-by: PostCyberPunk <134976996+PostCyberPunk@users.noreply.github.com> * chore(doc): auto generate docs --------- Co-authored-by: gptlang <121417512+gptlang@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: PostCyberPunk <134976996+PostCyberPunk@users.noreply.github.com> --- .all-contributorsrc | 43 +++++++++++++++++++++++++++++++++++-------- README.md | 37 ++++++++++++++++++++++++++++++++++--- doc/CopilotChat.txt | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4bd80307..59f826dd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,5 +1,7 @@ { - "files": ["README.md"], + "files": [ + "README.md" + ], "imageSize": 100, "commit": false, "commitType": "docs", @@ -10,49 +12,74 @@ "name": "gptlang", "avatar_url": "https://avatars.githubusercontent.com/u/121417512?v=4", "profile": "https://github.com/gptlang", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "jellydn", "name": "Dung Duc Huynh (Kaka)", "avatar_url": "https://avatars.githubusercontent.com/u/870029?v=4", "profile": "https://productsway.com/", - "contributions": ["code", "doc"] + "contributions": [ + "code", + "doc" + ] }, { "login": "qoobes", "name": "Ahmed Haracic", "avatar_url": "https://avatars.githubusercontent.com/u/58834655?v=4", "profile": "https://qoobes.dev", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "ziontee113", "name": "Trí Thiện Nguyễn", "avatar_url": "https://avatars.githubusercontent.com/u/102876811?v=4", "profile": "https://youtube.com/@ziontee113", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "Cassius0924", "name": "He Zhizhou", "avatar_url": "https://avatars.githubusercontent.com/u/62874592?v=4", "profile": "https://github.com/Cassius0924", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "rguruprakash", "name": "Guruprakash Rajakkannu", "avatar_url": "https://avatars.githubusercontent.com/u/9963717?v=4", "profile": "https://www.linkedin.com/in/guruprakashrajakkannu/", - "contributions": ["code"] + "contributions": [ + "code" + ] }, { "login": "kristofka", "name": "kristofka", "avatar_url": "https://avatars.githubusercontent.com/u/140354?v=4", "profile": "https://github.com/kristofka", - "contributions": ["code"] + "contributions": [ + "code" + ] + }, + { + "login": "PostCyberPunk", + "name": "PostCyberPunk", + "avatar_url": "https://avatars.githubusercontent.com/u/134976996?v=4", + "profile": "https://github.com/PostCyberPunk", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index b2e07617..15dc5bb9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Copilot Chat for Neovim - -[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-) > [!NOTE] @@ -252,6 +250,36 @@ Follow the example below to create a simple input for CopilotChat. }, ``` +### Add same keybinds in both visual and normal mode + +```lua + { + "jellydn/CopilotChat.nvim", + keys = + function() + local keybinds={ + --add your custom keybinds here + } + -- change prompt and keybinds as per your need + local my_prompts = { + {prompt = "In Neovim.",desc = "Neovim",key = "n"}, + {prompt = "Help with this",desc = "Help",key = "h"}, + {prompt = "Simplify and imporve readablilty",desc = "Simplify",key = "s"}, + {prompt = "Optimize the code to improve perfomance and readablilty.",desc = "Optimize",key = "o"}, + {prompt = "Find possible errors and fix them for me",desc = "Fix",key = "f"}, + {prompt = "Explain in detail",desc = "Explain",key = "e"}, + {prompt = "Write a shell scirpt",desc = "Shell",key = "S"}, + } + -- you can change cc to your desired keybind prefix + for _,v in pairs(my_prompts) do + table.insert(keybinds,{ "cc"..v.key, ":CopilotChatVisual "..v.prompt.."", mode = "x", desc = "CopilotChat - "..v.desc }) + table.insert(keybinds,{ "cc"..v.key, "CopilotChat "..v.prompt.."", desc = "CopilotChat - "..v.desc }) + end + return keybinds + end, + }, +``` + ## Roadmap - Translation to pure Lua @@ -289,6 +317,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Guruprakash Rajakkannu
Guruprakash Rajakkannu

💻 kristofka
kristofka

💻 + + PostCyberPunk
PostCyberPunk

📖 + diff --git a/doc/CopilotChat.txt b/doc/CopilotChat.txt index d4e5a880..a4361c87 100644 --- a/doc/CopilotChat.txt +++ b/doc/CopilotChat.txt @@ -285,6 +285,37 @@ Follow the example below to create a simple input for CopilotChat. < +ADD SAME KEYBINDS IN BOTH VISUAL AND NORMAL MODE ~ + +>lua + { + "jellydn/CopilotChat.nvim", + keys = + function() + local keybinds={ + --add your custom keybinds here + } + -- change prompt and keybinds as per your need + local my_prompts = { + {prompt = "In Neovim.",desc = "Neovim",key = "n"}, + {prompt = "Help with this",desc = "Help",key = "h"}, + {prompt = "Simplify and imporve readablilty",desc = "Simplify",key = "s"}, + {prompt = "Optimize the code to improve perfomance and readablilty.",desc = "Optimize",key = "o"}, + {prompt = "Find possible errors and fix them for me",desc = "Fix",key = "f"}, + {prompt = "Explain in detail",desc = "Explain",key = "e"}, + {prompt = "Write a shell scirpt",desc = "Shell",key = "S"}, + } + -- you can change cc to your desired keybind prefix + for _,v in pairs(my_prompts) do + table.insert(keybinds,{ "cc"..v.key, ":CopilotChatVisual "..v.prompt.."", mode = "x", desc = "CopilotChat - "..v.desc }) + table.insert(keybinds,{ "cc"..v.key, "CopilotChat "..v.prompt.."", desc = "CopilotChat - "..v.desc }) + end + return keybinds + end, + }, +< + + ROADMAP *CopilotChat-copilot-chat-for-neovim-roadmap* - Translation to pure Lua @@ -313,14 +344,14 @@ CONTRIBUTORS ✨ *CopilotChat-copilot-chat-for-neovim-contributors-✨* Thanks goes to these wonderful people (emoji key ): -gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻This project follows the all-contributors +gptlang💻 📖Dung Duc Huynh (Kaka)💻 📖Ahmed Haracic💻Trí Thiện Nguyễn💻He Zhizhou💻Guruprakash Rajakkannu💻kristofka💻PostCyberPunk📖This project follows the all-contributors specification. Contributions of any kind welcome! ============================================================================== 2. Links *CopilotChat-links* -1. *All Contributors*: https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square +1. *All Contributors*: https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square 2. *@jellydn*: 3. *Chat Demo*: https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif 4. *Explain Code Demo*: https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif From adfeaf757862c864d01617626f35f518ac51c51a Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:46:50 +0800 Subject: [PATCH 74/75] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..949fd0ff --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [gptlang, jellydn] From 4c510acf31be9378730b75202ad4b3c2540259f1 Mon Sep 17 00:00:00 2001 From: "Dung Duc Huynh (Kaka)" <870029+jellydn@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:41:31 +0800 Subject: [PATCH 75/75] docs: add announcement --- README.md | 334 +----------------------------------------------------- 1 file changed, 6 insertions(+), 328 deletions(-) diff --git a/README.md b/README.md index 15dc5bb9..a6445df0 100644 --- a/README.md +++ b/README.md @@ -1,331 +1,9 @@ -# Copilot Chat for Neovim +**Project Update: My Fork Now Part of CopilotC-Nvim** - -[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-) - +I'm pleased to share that my fork has been archived and integrated into the CopilotC-Nvim organization. This move enhances collaboration, increases update frequency, and expands community support. -> [!NOTE] -> You might want to take a look at [this fork](https://github.com/jellydn/CopilotChat.nvim) which is more well maintained & is more configurable. I personally use it now as well. +**What's Next?** +- Check out the project at [CopilotChat.nvim](https://github.com/CopilotC-Nvim/CopilotChat.nvim). +- For updates and contributions, visit the new repository under CopilotC-Nvim. -> [!NOTE] -> A new command, `CopilotChatInPlace` has been introduced. It functions like the ChatGPT plugin. Please run ":UpdateRemotePlugins" command and restart Neovim before starting a chat with Copilot. To stay updated on our roadmap, please join our [Discord](https://discord.gg/vy6hJsTWaZ) community. - -## Authentication - -It will prompt you with instructions on your first start. If you already have `Copilot.vim` or `Copilot.lua`, it will work automatically. - -## Installation - -### Lazy.nvim - -1. `pip install python-dotenv requests pynvim==0.5.0 prompt-toolkit` -2. `pip install tiktoken` (optional for displaying prompt token counts) -3. Put it in your lazy setup - -```lua -return { - { - "jellydn/CopilotChat.nvim", - opts = { - show_help = "yes", -- Show help text for CopilotChatInPlace, default: yes - debug = false, -- Enable or disable debug mode, the log file will be in ~/.local/state/nvim/CopilotChat.nvim.log - disable_extra_info = 'no', -- Disable extra information (e.g: system prompt) in the response. - }, - build = function() - vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") - end, - event = "VeryLazy", - keys = { - { "cce", "CopilotChatExplain", desc = "CopilotChat - Explain code" }, - { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, - { - "ccv", - ":CopilotChatVisual", - mode = "x", - desc = "CopilotChat - Open in vertical split", - }, - { - "ccx", - ":CopilotChatInPlace", - mode = "x", - desc = "CopilotChat - Run in-place code", - }, - }, - }, -} -``` - -4. Run command `:UpdateRemotePlugins`, then inspect the file `~/.local/share/nvim/rplugin.vim` for additional details. You will notice that the commands have been registered. - -For example: - -```vim -" python3 plugins -call remote#host#RegisterPlugin('python3', '/Users/huynhdung/.local/share/nvim/lazy/CopilotChat.nvim/rplugin/python3/copilot-plugin.py', [ - \ {'sync': v:false, 'name': 'CopilotChat', 'type': 'command', 'opts': {'nargs': '1'}}, - \ {'sync': v:false, 'name': 'CopilotChatVisual', 'type': 'command', 'opts': {'nargs': '1', 'range': ''}}, - \ {'sync': v:false, 'name': 'CopilotChatInPlace', 'type': 'command', 'opts': {'nargs': '*', 'range': ''}}, - \ {'sync': v:false, 'name': 'CopilotChatAutocmd', 'type': 'command', 'opts': {'nargs': '*'}}, - \ {'sync': v:false, 'name': 'CopilotChatMapping', 'type': 'command', 'opts': {'nargs': '*'}}, - \ ]) -``` - -5. Restart `neovim` - -### Manual - -1. Put the files in the right place - -``` -$ git clone https://github.com/jellydn/CopilotChat.nvim -$ cd CopilotChat.nvim -$ cp -r --backup=nil rplugin ~/.config/nvim/ -``` - -2. Install dependencies - -``` -$ pip install -r requirements.txt -``` - -3. Open up Neovim and run `:UpdateRemotePlugins` -4. Restart Neovim - -## Usage - -### Configuration - -You have the ability to tailor this plugin to your specific needs using the configuration options outlined below: - -```lua -{ - debug = false, -- Enable or disable debug mode - show_help = 'yes', -- Show help text for CopilotChatInPlace - prompts = { -- Set dynamic prompts for CopilotChat commands - Explain = 'Explain how it works.', - Tests = 'Briefly explain how the selected code works, then generate unit tests.', - } -} -``` - -You have the capability to expand the prompts to create more versatile commands: - -```lua -return { - "jellydn/CopilotChat.nvim", - opts = { - debug = true, - show_help = "yes", - prompts = { - Explain = "Explain how it works.", - Review = "Review the following code and provide concise suggestions.", - Tests = "Briefly explain how the selected code works, then generate unit tests.", - Refactor = "Refactor the code to improve clarity and readability.", - }, - }, - build = function() - vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") - end, - event = "VeryLazy", - keys = { - { "cce", "CopilotChatExplain", desc = "CopilotChat - Explain code" }, - { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, - { "ccr", "CopilotChatReview", desc = "CopilotChat - Review code" }, - { "ccR", "CopilotChatRefactor", desc = "CopilotChat - Refactor code" }, - } -} -``` - -For further reference, you can view @jellydn's [configuration](https://github.com/jellydn/lazy-nvim-ide/blob/main/lua/plugins/extras/copilot-chat.lua). - -### Chat with Github Copilot - -1. Copy some code into the unnamed register using the `y` command. -2. Run the command `:CopilotChat` followed by your question. For example, `:CopilotChat What does this code do?` - -![Chat Demo](https://i.gyazo.com/10fbd1543380d15551791c1a6dcbcd46.gif) - -### Code Explanation - -1. Copy some code into the unnamed register using the `y` command. -2. Run the command `:CopilotChatExplain`. - -![Explain Code Demo](https://i.gyazo.com/e5031f402536a1a9d6c82b2c38d469e3.gif) - -### Generate Tests - -1. Copy some code into the unnamed register using the `y` command. -2. Run the command `:CopilotChatTests`. - -[![Generate tests](https://i.gyazo.com/f285467d4b8d8f8fd36aa777305312ae.gif)](https://gyazo.com/f285467d4b8d8f8fd36aa777305312ae) - -### Token count & Fold with visual mode - -1. Select some code using visual mode. -2. Run the command `:CopilotChatVisual` with your question. - -[![Fold Demo](https://i.gyazo.com/766fb3b6ffeb697e650fc839882822a8.gif)](https://gyazo.com/766fb3b6ffeb697e650fc839882822a8) - -### In-place Chat Popup - -1. Select some code using visual mode. -2. Run the command `:CopilotChatInPlace` and type your prompt. For example, `What does this code do?` -3. Press `Enter` to send your question to Github Copilot. -4. Press `q` to quit. There is help text at the bottom of the screen. You can also press `?` to toggle the help text. - -[![In-place Demo](https://i.gyazo.com/4a5badaa109cd483c1fc23d296325cb0.gif)](https://gyazo.com/4a5badaa109cd483c1fc23d296325cb0) - -## Tips - -### Debugging with `:messages` and `:CopilotChatDebugInfo` - -If you encounter any issues, you can run the command `:messages` to inspect the log. You can also run the command `:CopilotChatDebugInfo` to inspect the debug information. - -[![Debug Info](https://i.gyazo.com/bf00e700bcee1b77bcbf7b516b552521.gif)](https://gyazo.com/bf00e700bcee1b77bcbf7b516b552521) - -### How to setup with `which-key.nvim` - -A special thanks to @ecosse3 for the configuration of [which-key](https://github.com/jellydn/CopilotChat.nvim/issues/30). - -```lua - { - "jellydn/CopilotChat.nvim", - event = "VeryLazy", - opts = { - prompts = { - Explain = "Explain how it works.", - Review = "Review the following code and provide concise suggestions.", - Tests = "Briefly explain how the selected code works, then generate unit tests.", - Refactor = "Refactor the code to improve clarity and readability.", - }, - }, - build = function() - vim.notify("Please update the remote plugins by running ':UpdateRemotePlugins', then restart Neovim.") - end, - config = function() - local present, wk = pcall(require, "which-key") - if not present then - return - end - - wk.register({ - c = { - c = { - name = "Copilot Chat", - } - } - }, { - mode = "n", - prefix = "", - silent = true, - noremap = true, - nowait = false, - }) - end, - keys = { - { "ccc", ":CopilotChat ", desc = "CopilotChat - Prompt" }, - { "cce", ":CopilotChatExplain ", desc = "CopilotChat - Explain code" }, - { "cct", "CopilotChatTests", desc = "CopilotChat - Generate tests" }, - { "ccr", "CopilotChatReview", desc = "CopilotChat - Review code" }, - { "ccR", "CopilotChatRefactor", desc = "CopilotChat - Refactor code" }, - } - }, -``` - -### Create a simple input for CopilotChat - -Follow the example below to create a simple input for CopilotChat. - -```lua --- Create input for CopilotChat - { - "cci", - function() - local input = vim.fn.input("Ask Copilot: ") - if input ~= "" then - vim.cmd("CopilotChat " .. input) - end - end, - desc = "CopilotChat - Ask input", - }, -``` - -### Add same keybinds in both visual and normal mode - -```lua - { - "jellydn/CopilotChat.nvim", - keys = - function() - local keybinds={ - --add your custom keybinds here - } - -- change prompt and keybinds as per your need - local my_prompts = { - {prompt = "In Neovim.",desc = "Neovim",key = "n"}, - {prompt = "Help with this",desc = "Help",key = "h"}, - {prompt = "Simplify and imporve readablilty",desc = "Simplify",key = "s"}, - {prompt = "Optimize the code to improve perfomance and readablilty.",desc = "Optimize",key = "o"}, - {prompt = "Find possible errors and fix them for me",desc = "Fix",key = "f"}, - {prompt = "Explain in detail",desc = "Explain",key = "e"}, - {prompt = "Write a shell scirpt",desc = "Shell",key = "S"}, - } - -- you can change cc to your desired keybind prefix - for _,v in pairs(my_prompts) do - table.insert(keybinds,{ "cc"..v.key, ":CopilotChatVisual "..v.prompt.."", mode = "x", desc = "CopilotChat - "..v.desc }) - table.insert(keybinds,{ "cc"..v.key, "CopilotChat "..v.prompt.."", desc = "CopilotChat - "..v.desc }) - end - return keybinds - end, - }, -``` - -## Roadmap - -- Translation to pure Lua -- Tokenizer -- Use vector encodings to automatically select code -- Sub commands - See [issue #5](https://github.com/gptlang/CopilotChat.nvim/issues/5) - -## Development - -### Installing Pre-commit Tool - -For development, you can use the provided Makefile command to install the pre-commit tool: - -```bash -make install-pre-commit -``` - -This will install the pre-commit tool and the pre-commit hooks. - -## Contributors ✨ - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - - - - - - - - - - - - -
gptlang
gptlang

💻 📖
Dung Duc Huynh (Kaka)
Dung Duc Huynh (Kaka)

💻 📖
Ahmed Haracic
Ahmed Haracic

💻
Trí Thiện Nguyễn
Trí Thiện Nguyễn

💻
He Zhizhou
He Zhizhou

💻
Guruprakash Rajakkannu
Guruprakash Rajakkannu

💻
kristofka
kristofka

💻
PostCyberPunk
PostCyberPunk

📖
- - - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +Your support and contributions are appreciated as we continue to develop Copilot Chat for Neovim.