Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ec9922e
feat: update to latest copilot.vim/dist
AntoineGS Mar 12, 2025
061fd4f
feat: use allow changing the copilot model used
AntoineGS Mar 12, 2025
f5b9523
feat: initial support for workspaces
AntoineGS Mar 15, 2025
1c63ea5
refactor: autoformat changes, add typing for LSP, fix typo
AntoineGS Mar 15, 2025
2abd5ae
refactor: slightly improve startup error handling for better context …
AntoineGS Mar 15, 2025
11970ea
refactor: improve error message
AntoineGS Mar 15, 2025
b9ad169
fix: add missing copilot_model definition in config
AntoineGS Mar 16, 2025
6f16292
Merge branch 'gpt4o' into workspaceFolders
AntoineGS Mar 16, 2025
5d3c8aa
fix: missing copilot.lua version
AntoineGS Mar 16, 2025
827f1a1
Merge branch 'upgrade_copilot.vim' into gpt4o
AntoineGS Mar 16, 2025
c659294
Merge branch 'gpt4o' into workspaceFolders
AntoineGS Mar 16, 2025
3718ae5
refactor: resolve all Lua LSP warnings
AntoineGS Mar 16, 2025
98ef99a
feat: add support for dynamic root_dir resolving
AntoineGS Mar 16, 2025
a846a33
Merge branch 'dynamic_root_dir'
AntoineGS Mar 16, 2025
97cd020
feat: add support for dynamic root_dir resolving
AntoineGS Mar 16, 2025
4d63d59
Merge branch 'dynamic_root_dir'
AntoineGS Mar 16, 2025
9319a38
style: auto-format
AntoineGS Mar 18, 2025
863b40a
fix: add missing capability for workspaces
AntoineGS Mar 18, 2025
4026e44
Merge branch 'workspaceFolders' into warning_cleanup
AntoineGS Mar 18, 2025
e5b5400
Merge branch 'warning_cleanup' into dynamic_root_dir
AntoineGS Mar 18, 2025
ca58d30
feat: add logger and redirect existing prints/notifies through it
AntoineGS Mar 18, 2025
7b6b208
fix: :Copilot auth would not work with table.unpack, reverting
AntoineGS Mar 20, 2025
4c689d2
Merge branch 'master' into workspaceFolders
AntoineGS Mar 21, 2025
b9c3101
Merge branch 'workspaceFolders' into warning_cleanup
AntoineGS Mar 21, 2025
4007882
Merge branch 'warning_cleanup' into dynamic_root_dir
AntoineGS Mar 21, 2025
2349063
Merge branch 'dynamic_root_dir' into logger
AntoineGS Mar 21, 2025
654e6a0
fix: use root_dir instead of get_root_dir
AntoineGS Mar 21, 2025
d8a3ca7
Merge branch 'master' into dynamic_root_dir
AntoineGS Mar 21, 2025
8b13458
move a few things around
AntoineGS Mar 21, 2025
0d588b6
Merge branch 'dynamic_root_dir' into logger
AntoineGS Mar 21, 2025
3c97812
Update readme
AntoineGS Mar 21, 2025
263f445
Merge branch 'dynamic_root_dir' into logger
AntoineGS Mar 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,21 @@ require('copilot').setup({
cvs = false,
["."] = false,
},
logger = {
log_to_file = false,
file = vim.fn.stdpath("log") .. "/copilot-lua.log",
file_log_level = vim.log.levels.WARN,
print_log = true,
print_log_level = vim.log.levels.WARN,
trace_lsp = "off", -- "off" | "messages" | "verbose"
trace_lsp_progress = false,
},
copilot_node_command = 'node', -- Node.js version must be > 18.x
copilot_model = "", -- Current LSP default is gpt-35-turbo, supports gpt-4o-copilot
workspace_folders = {},
copilot_model = "", -- Current LSP default is gpt-35-turbo, supports gpt-4o-copilot
root_dir = function()
return vim.fs.dirname(vim.fs.find(".git", { upward = true })[1])
end,
server_opts_overrides = {},
})
```
Expand Down Expand Up @@ -180,6 +192,32 @@ require("copilot").setup {
}
```

### logger

When `log_to_file` is true, logs will be written to the `file` for anything of `file_log_level` or higher.
When `print_log` is true, logs will be printed to NeoVim (using `notify`) for anything of `print_log_level` or higher.
File logging is done asynchronously to minimize performance impacts, however there is still some overhead.

Log levels used are the ones defined in `vim.log`:

```lua
vim.log = {
levels = {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
OFF = 5,
},
}
```

`trace_lsp` can either be `off`, `messages` which will output the LSP messages, or `verbose` which adds additonal information to the message.
When `trace_lsp_progress` is true, LSP progress messages will also be logged.

Careful turning on all logging features as the log files may get very large over time, and are not pruned by the application.

### copilot_node_command

Use this field to provide the path to a specific node version such as one installed by nvm. Node.js version must be 18.x or newer.
Expand Down Expand Up @@ -228,6 +266,11 @@ workspace_folders = {

They can also be added runtime, using the command `:Copilot workspace add [folderpath]` where `[folderpath]` is the workspace folder.

### root_dir

This allows changing the function that gets the root folder, the default looks for a parent folder that contains the folder `.git`.
If none is found, it will use the current working directory.

## Commands

`copilot.lua` defines the `:Copilot` command that can perform various actions. It has completion support, so try it out.
Expand Down
3 changes: 2 additions & 1 deletion lua/copilot/auth.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local api = require("copilot.api")
local c = require("copilot.client")
local logger = require("copilot.logger")

local M = {}

Expand Down Expand Up @@ -145,7 +146,7 @@ local function find_config_path()
if vim.fn.isdirectory(config) > 0 then
return config
else
print("Error: could not find config path")
logger.error("could not find config path")
end
end
end
Expand Down
50 changes: 29 additions & 21 deletions lua/copilot/client.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local api = require("copilot.api")
local config = require("copilot.config")
local util = require("copilot.util")
local logger = require("copilot.logger")

local is_disabled = false

Expand All @@ -9,6 +10,7 @@ local M = {
id = nil,
--- @class copilot_capabilities:lsp.ClientCapabilities
--- @field copilot table<'openURL', boolean>
--- @field workspace table<'workspaceFolders', boolean>
capabilities = nil,
config = nil,
node_version = nil,
Expand All @@ -21,7 +23,8 @@ local M = {
local function store_client_id(id)
if M.id and M.id ~= id then
if vim.lsp.get_client_by_id(M.id) then
error("unexpectedly started multiple copilot servers")
logger.error("unexpectedly started multiple copilot servers")
return
end
end

Expand Down Expand Up @@ -92,7 +95,7 @@ end
---@param force? boolean
function M.buf_attach(force)
if is_disabled then
print("[Copilot] Offline")
logger.warn("copilot is disabled")
return
end

Expand All @@ -101,20 +104,23 @@ function M.buf_attach(force)
end

if not M.config then
vim.notify("[Copilot] Cannot attach: configuration not initialized", vim.log.levels.ERROR)
logger.error("cannot attach: configuration not initialized")
return
end

-- In case it has changed, we update it
M.config.root_dir = config.get_root_dir()

local ok, client_id_or_err = pcall(lsp_start, M.config)
if not ok then
vim.notify(string.format("[Copilot] Failed to start LSP client: %s", client_id_or_err), vim.log.levels.ERROR)
logger.error(string.format("failed to start LSP client: %s", client_id_or_err))
return
end

if client_id_or_err then
store_client_id(client_id_or_err)
else
vim.notify("[Copilot] LSP client failed to start (no client ID returned)", vim.log.levels.ERROR)
logger.error("LSP client failed to start (no client ID returned)")
end
end

Expand All @@ -135,21 +141,22 @@ end
---@param callback fun(client:table):nil
function M.use_client(callback)
if is_disabled then
print("[Copilot] Offline")
logger.warn("copilot is offline")
return
end

local client = M.get() --[[@as table]]

if not client then
if not M.config then
error("copilot.setup is not called yet")
logger.error("copilot.setup is not called yet")
return
end

local client_id, err = vim.lsp.start_client(M.config)

if not client_id then
error(string.format("[Copilot] Error starting LSP Client: %s", err))
logger.error(string.format("error starting LSP client: %s", err))
return
end

Expand All @@ -166,7 +173,7 @@ function M.use_client(callback)
local timer, err, _ = vim.loop.new_timer()

if not timer then
error(string.format("[Copilot] Error creating timer: %s", err))
logger.error(string.format("error creating timer: %s", err))
return
end

Expand All @@ -188,15 +195,15 @@ local function prepare_client_config(overrides)

if vim.fn.executable(node) ~= 1 then
local err = string.format("copilot_node_command(%s) is not executable", node)
vim.notify("[Copilot] " .. err, vim.log.levels.ERROR)
logger.error(err)
M.startup_error = err
return
end

local agent_path = vim.api.nvim_get_runtime_file("copilot/dist/language-server.js", false)[1]
if not agent_path or vim.fn.filereadable(agent_path) == 0 then
local err = string.format("Could not find language-server.js (bad install?) : %s", tostring(agent_path))
vim.notify("[Copilot] " .. err, vim.log.levels.ERROR)
logger.error(err)
M.startup_error = err
return
end
Expand All @@ -218,11 +225,7 @@ local function prepare_client_config(overrides)
["copilot/openURL"] = api.handlers["copilot/openURL"],
}

local root_dir = vim.loop.cwd()
if not root_dir then
root_dir = vim.fn.getcwd()
end

local root_dir = config.get_root_dir()
local workspace_folders = {
--- @type workspace_folder
{
Expand All @@ -247,6 +250,7 @@ local function prepare_client_config(overrides)
end
end

-- LSP config, not to be confused with config.lua
return vim.tbl_deep_extend("force", {
cmd = {
node,
Expand All @@ -272,11 +276,14 @@ local function prepare_client_config(overrides)
set_editor_info_params.authProvider = provider_url and {
url = provider_url,
} or nil

logger.debug("data for setEditorInfo LSP call", set_editor_info_params)
api.set_editor_info(client, set_editor_info_params, function(err)
if err then
vim.notify(string.format("[copilot] setEditorInfo failure: %s", err), vim.log.levels.ERROR)
logger.error(string.format("setEditorInfo failure: %s", err))
end
end)
logger.trace("setEditorInfo has been called")
M.initialized = true
end)
end,
Expand All @@ -299,6 +306,7 @@ local function prepare_client_config(overrides)
copilotIntegrationId = "vscode-chat",
},
workspace_folders = workspace_folders,
trace = config.get("trace") or "off",
}, overrides)
end

Expand Down Expand Up @@ -339,12 +347,12 @@ end

function M.add_workspace_folder(folder_path)
if type(folder_path) ~= "string" then
vim.notify("[Copilot] Workspace folder path must be a string", vim.log.levels.ERROR)
logger.error("workspace folder path must be a string")
return false
end

if vim.fn.isdirectory(folder_path) ~= 1 then
vim.notify("[Copilot] Invalid workspace folder: " .. folder_path, vim.log.levels.ERROR)
logger.error("invalid workspace folder: " .. folder_path)
return false
end

Expand Down Expand Up @@ -379,9 +387,9 @@ function M.add_workspace_folder(folder_path)
removed = {},
},
})
vim.notify("[Copilot] Added workspace folder: " .. folder_path, vim.log.levels.INFO)
logger.notify("added workspace folder: " .. folder_path)
else
vim.notify("[Copilot] Workspace folder added for next session: " .. folder_path, vim.log.levels.INFO)
logger.notify("workspace folder will be added on next session: " .. folder_path)
end

return true
Expand Down
48 changes: 45 additions & 3 deletions lua/copilot/config.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local logger = require("copilot.logger")

---@class copilot_config
local default_config = {
---@class copilot_config_panel
Expand Down Expand Up @@ -33,6 +35,17 @@ local default_config = {
dismiss = "<C-]>",
},
},
---@class copilot_config_logging
logger = {
log_to_file = false,
file = vim.fn.stdpath("log") .. "/copilot-lua.log",
file_log_level = vim.log.levels.WARN,
print_log = true,
print_log_level = vim.log.levels.WARN,
---@type string<'off'|'messages'|'verbose'>
trace_lsp = "off",
trace_lsp_progress = false,
},
---@deprecated
ft_disable = nil,
---@type table<string, boolean>
Expand All @@ -45,15 +58,20 @@ local default_config = {
server_opts_overrides = {},
---@type string|nil
copilot_model = nil,
---@type function|string
root_dir = function()
return vim.fs.dirname(vim.fs.find(".git", { upward = true })[1])
end,
}

local mod = {
---@type copilot_config
config = nil,
}

function mod.setup(opts)
if mod.config then
vim.notify("[Copilot] config is already set", vim.log.levels.WARN)
logger.warn("config is already set")
return mod.config
end

Expand All @@ -76,7 +94,8 @@ end
---@param key? string
function mod.get(key)
if not mod.config then
error("[Copilot] not initialized")
logger.error("not initialized")
return
end

if key then
Expand All @@ -90,10 +109,33 @@ end
---@param value any
function mod.set(key, value)
if not mod.config then
error("[Copilot] not initialized")
logger.error("not initialized")
return
end

mod.config[key] = value
end

function mod.get_root_dir()
if not mod.config then
error("[Copilot] not initialized")
end

local config_root_dir = mod.config.root_dir
local root_dir --[[@as string]]

if type(config_root_dir) == "function" then
root_dir = config_root_dir()
else
root_dir = config_root_dir
end

if not root_dir or root_dir == "" then
root_dir = "."
end

root_dir = vim.fn.fnamemodify(root_dir, ":p:h")
return root_dir
end

return mod
7 changes: 7 additions & 0 deletions lua/copilot/init.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local M = { setup_done = false }
local config = require("copilot.config")
local highlight = require("copilot.highlight")
local logger = require("copilot.logger")
local client = require("copilot.client")

local create_cmds = function()
vim.api.nvim_create_user_command("CopilotDetach", function()
Expand Down Expand Up @@ -39,6 +41,11 @@ M.setup = function(opts)
end

require("copilot.command").enable()
logger.setup(conf.logger)

logger.debug("active plugin config:", config)
-- logged here to ensure the logger is setup
logger.debug("active LSP config (may change runtime):", client.config)

M.setup_done = true
end
Expand Down
Loading