Skip to content

Instantly share code, notes, and snippets.

@bluss
Last active November 6, 2024 18:25
Show Gist options
  • Save bluss/761455e123fbdbf481be9ec0b0bdce58 to your computer and use it in GitHub Desktop.
Save bluss/761455e123fbdbf481be9ec0b0bdce58 to your computer and use it in GitHub Desktop.
-- This file configures basedpyright for nvim-lspconfig
-- This is not the complete lspconfig configuration - lazy.nvim
-- configuration merging gathers multiple files for the complete lspconfig configuration.
-- global function to silence this thing
function SilenceBasedpyright()
local function filter_diagnostics(diagnostic)
if diagnostic.source == 'basedpyright' then
return false
end
return true
end
vim.lsp.handlers['textDocument/publishDiagnostics'] = vim.lsp.with(
function(_, result, ctx, config)
result.diagnostics = vim.tbl_filter(filter_diagnostics, result.diagnostics)
vim.lsp.diagnostic.on_publish_diagnostics(_, result, ctx, config)
end,
{}
)
end
if vim.fn.executable('basedpyright') <= 0 then
return {}
end
local function get_root(fname)
local root_func = require("lspconfig.util").root_pattern({"pyproject.toml", ".git"})
local ret = root_func(fname) or vim.fn.getcwd()
return ret
end
return {
{
"neovim/nvim-lspconfig",
opts = {
servers = {
basedpyright = {
mason = false,
cmd = { "basedpyright-langserver", "--stdio" },
filetypes = { 'python' },
single_file_support = true,
root_dir = function(fname) return get_root(fname) end,
init_options = {
diagnostic = {
enable = true,
},
},
settings = {
python = {
},
basedpyright = {
analysis = { typeCheckingMode = "standard" },
},
},
on_init = function(client, _)
client.server_capabilities.semanticTokensProvider = nil; -- turn off semantic tokens
local root_func = require("lspconfig.util").root_pattern("pyproject.toml")
if root_func(vim.fn.getcwd()) then
-- nothing
else
print("No pyproject: basedpyright linter disabled")
SilenceBasedpyright()
end
end,
on_new_config = function(new_config, new_root_dir)
-- configure basedpyright's python.pythonPath to be the python executable path for the environment
-- vim.print("new root: " .. tostring(new_root_dir))
local root_func = require("lspconfig.util").root_pattern("pyproject.toml")
if root_func(new_root_dir) then
--
local success, py_path = pcall(function()
local pysupport = require("python_support")
return pysupport.get_local_python_location(new_root_dir)
end)
if success and py_path then
new_config.settings.python.pythonPath = py_path
-- note, one could set extraPaths instead, but those are counted as 'in' the project
end
if not success then
vim.notify("Error: " .. tostring(py_path), vim.log.levels.WARN)
end
else
-- pass
end
end,
},
},
},
},
}
-- helper functions for python
local M = {}
-- python-preference system causes it to find system python outside projects,
-- but inside a project it still picks the current project's venv python
local _uv_command = {"uv", "run", "-q", "--no-sync", "--python-preference=system", "python"}
local _uv_find = {"uv", "python", "find", "-q", "--python-preference=system"}
-- Get local python (python in virtualenv with preference, else system python)
---@return table: python command arguments
function M.get_local_python_cmd()
if vim.fn.executable('uv') > 0 then
return vim.list_slice(_uv_command) -- copy
else
return {"python3"}
end
end
local function get_python_finder_cmd()
return vim.list_extend(M.get_local_python_cmd(), {"-c", "import sys; import json; print(json.dumps(sys.executable))"})
end
local function _system_get_output(cmd, cwd)
local proc = vim.system(cmd, {cwd=cwd, timeout=15000, text=false, env={UV_NO_ENV_FILE = "1"}})
local ret = proc:wait()
if ret.stderr then
vim.notify(ret.stderr, vim.log.levels.WARN)
end
return ret.stdout
end
function M.get_local_python_location(cwd)
local cmd = get_python_finder_cmd()
if #cmd > 1 then
local out = _system_get_output(cmd, cwd)
local decoded = vim.json.decode(out)
return vim.fn.trim(decoded)
else
return cmd[1]
end
end
local function nonempty(elt)
if elt and #elt > 0 then return true else return false end
end
-- Get local python's sys.path
function M.get_py_path(cwd)
local cmd = vim.list_extend(M.get_local_python_cmd(), {"-c", "import json; import sys; print(json.dumps(sys.path))"})
local out = _system_get_output(cmd, cwd)
local decoded_list = vim.json.decode(out)
local non_empty = vim.tbl_filter(nonempty, decoded_list or {})
local ret = vim.tbl_filter(function(elt) return vim.fn.isdirectory(elt) > 0 end, non_empty)
return ret
end
return M
local M = {}
-- live grep in the listed paths
---@param paths table: list of directories
---@param opts table?
function M.live_grep_paths(paths, opts)
opts = opts or {}
require("telescope.builtin").live_grep(vim.tbl_deep_extend("force", {
prompt_title = "Live Grep Paths",
search_dirs = paths,
}, opts))
end
local shorten_path_opts = {path_display={ shorten = { len = 2, exclude = {-1, -2, -3 }}}}
---@param str string
---@param pfx string
---@return boolean
local function has_prefix(str, pfx)
local len = #pfx
return str:sub(1, len) == pfx
end
---@param str string
---@param pfx string
---@param sub string
---@return string
local function sub_prefix(str, pfx, sub)
local plen = #pfx
return sub .. str:sub(plen + 1)
end
local function table_is_empty(table_to_check)
return table_to_check == nil or next(table_to_check) == nil
end
---@param opt1 table?
---@param opt2 table?
---@return table
local function combine_opts(opt1, opt2)
if opt1 == nil or table_is_empty(opt1) then
return opt2 or {}
elseif opt2 == nil or table_is_empty(opt2) then
return opt1 or {}
end
return vim.tbl_deep_extend("force", opt1, opt2)
end
---@param opts table
function M.sub_homedir_path_display_func(opts)
local tutils = require("telescope.utils")
local homedir = vim.fn.expand("~")
---@param inner_opts table
---@param path string
return function(inner_opts, path)
if has_prefix(path, homedir) then
path = sub_prefix(path, homedir, "~")
end
local copts
if opts.path_display then
copts = combine_opts(inner_opts, opts)
else
copts = opts
end
return tutils.transform_path(copts, path)
end
end
function M.live_grep_pypath()
local py_paths = require("python_support").get_py_path()
M.live_grep_paths(py_paths, {
prompt_title = "Live Grep Python Env",
-- shorten home directory to ~
path_display = M.sub_homedir_path_display_func(shorten_path_opts),
additional_args = { "-u" } })
end
function M.find_files_pypath(opts)
local py_paths = require("python_support").get_py_path()
local og = (combine_opts({
prompt_title = "Find Files in Python Env",
hidden = true,
search_dirs = py_paths,
-- shorten home directory to ~
path_display = M.sub_homedir_path_display_func(shorten_path_opts),
find_command = {"rg", "--color=never", "--files", "-g*.py", "-u"}
}, opts))
require("telescope.builtin").find_files(og)
end
return M
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment