Skip to content

Commit

Permalink
fix merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
gptlang committed Feb 1, 2024
2 parents 9377afe + 72fa234 commit f36b90f
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 31 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copilot Chat for Neovim

<<<<<<< HEAD
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
Expand All @@ -8,6 +9,10 @@

> [!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.
=======
> [!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.
>>>>>>> main
## Authentication

Expand All @@ -18,9 +23,13 @@ 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`
<<<<<<< HEAD
2. `pip install tiktoken` (optional for displaying prompt token counts)
3. Put it in your lazy setup

=======
2. Put it in your lazy setup
>>>>>>> main
```lua
return {
{
Expand Down
22 changes: 0 additions & 22 deletions cspell.json

This file was deleted.

18 changes: 18 additions & 0 deletions rplugin/python3/copilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
import utilities
from prompt_toolkit import PromptSession
from prompt_toolkit.history import InMemoryHistory
<<<<<<< HEAD
=======
import utilities
import typings
import prompts
from typing import List, Dict
>>>>>>> main

LOGIN_HEADERS = {
"accept": "application/json",
Expand Down Expand Up @@ -91,7 +98,10 @@ def ask(self, prompt: str, code: str, language: str = ""):
# If expired, reauthenticate
if self.token.get("expires_at") <= round(time.time()):
self.authenticate()
<<<<<<< HEAD

=======
>>>>>>> main
url = "https://api.githubcopilot.com/chat/completions"
self.chat_history.append(typings.Message(prompt, "user"))
system_prompt = prompts.COPILOT_INSTRUCTIONS
Expand Down Expand Up @@ -134,15 +144,23 @@ def ask(self, prompt: str, code: str, language: str = ""):

self.chat_history.append(typings.Message(full_response, "system"))

<<<<<<< HEAD
def _get_embeddings(self, inputs: List[typings.FileExtract]):
=======
def _get_embeddings(self, inputs: list[typings.FileExtract]):
>>>>>>> main
embeddings = []
url = "https://api.githubcopilot.com/embeddings"
# If we have more than 18 files, we need to split them into multiple requests
for i in range(0, len(inputs), 18):
if i + 18 > len(inputs):
data = utilities.generate_embedding_request(inputs[i:])
else:
<<<<<<< HEAD
data = utilities.generate_embedding_request(inputs[i : i + 18])
=======
data = utilities.generate_embedding_request(inputs[i: i + 18])
>>>>>>> main
response = self.session.post(url, headers=self._headers(), json=data).json()
if "data" not in response:
raise Exception(f"Error fetching embeddings: {response}")
Expand Down
89 changes: 89 additions & 0 deletions rplugin/python3/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import os
import time

import copilot
import prompts
import dotenv
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
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]
# Check if we're already in a chat buffer
if self.nvim.eval("getbufvar(bufnr(), '&buftype')") != "nofile":
# Create a new scratch buffer to hold the chat
self.nvim.command("enew")
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")

# Get the current buffer
buf = self.nvim.current.buffer
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)

# Add chat messages
for token in self.copilot.ask(prompt, code, language=file_type):
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"),
)

# Add end separator
end_separator = "\n---\n"
buf.append(end_separator.split("\n"), -1)
30 changes: 28 additions & 2 deletions rplugin/python3/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -180,31 +189,48 @@
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).
"""
12 changes: 5 additions & 7 deletions rplugin/python3/utilities.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import json
import os
import random
from typing import List

import prompts
import typings
import random
import os
import json


def random_hex(length: int = 65):
return "".join([random.choice("0123456789abcdef") for _ in range(length)])


def generate_request(
chat_history: List[typings.Message],
chat_history: list[typings.Message],
code_excerpt: str,
language: str = "",
system_prompt=prompts.COPILOT_INSTRUCTIONS,
Expand Down Expand Up @@ -49,7 +47,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}```"
Expand Down

0 comments on commit f36b90f

Please sign in to comment.