-- Set buffer-local options for Python files vim.opt_local.expandtab = true -- Use spaces instead of tabs vim.opt_local.commentstring = '# %s' -- Python comment format vim.opt_local.shiftwidth = 4 -- Indent width vim.opt_local.tabstop = 4 -- Tab width vim.opt_local.softtabstop = 4 -- Soft tab width for alignment vim.opt_local.fileformat = 'unix' -- Use Unix line endings vim.opt_local.textwidth = 79 -- Maximum text width vim.opt_local.colorcolumn = '80' -- Highlight column 80 local dap = require('dap') local function get_python_path() local cwd = vim.fn.getcwd() if vim.env.VIRTUAL_ENV then return vim.env.VIRTUAL_ENV .. '/bin/python' -- Use virtual environment if set elseif vim.fn.executable(cwd .. '/venv/bin/python') == 1 then return cwd .. '/venv/bin/python' -- Use project-specific venv elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then return cwd .. '/.venv/bin/python' -- Use hidden project venv else return 'python' -- Fallback to system Python end end local function get_ipython_cmd() local cwd = vim.fn.getcwd() local venv = vim.env.VIRTUAL_ENV if not venv then if vim.fn.isdirectory(cwd .. '/.venv') == 1 then venv = cwd .. '/.venv' elseif vim.fn.isdirectory(cwd .. '/venv') == 1 then venv = cwd .. '/venv' end end if venv and vim.fn.executable(venv .. '/bin/ipython') == 1 then return vim.fn.shellescape(venv .. '/bin/ipython') end local py = get_python_path() if py:find('/') then py = vim.fn.shellescape(py) end return py .. ' -m IPython' end local function get_ipython_argv() local cwd = vim.fn.getcwd() local venv = vim.env.VIRTUAL_ENV if not venv then if vim.fn.isdirectory(cwd .. '/.venv') == 1 then venv = cwd .. '/.venv' elseif vim.fn.isdirectory(cwd .. '/venv') == 1 then venv = cwd .. '/venv' end end if venv and vim.fn.executable(venv .. '/bin/ipython') == 1 then return { venv .. '/bin/ipython' } end return { get_python_path(), '-m', 'IPython' } end vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('lsp_attach_disable_ruff_hover', { clear = true }), callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) if client and client.name == 'ruff' then client.server_capabilities.hoverProvider = false end end, desc = 'Disable hover capability from Ruff', }) require('dap-python').setup(get_python_path()) dap.configurations.python = { { type = 'python', request = 'launch', name = 'Launch Program', program = '${file}', console = 'integratedTerminal', }, { type = 'python', request = 'launch', name = 'Profile Memory', program = '${file}', args = { '--profile-memory' }, }, } vim.keymap.set('n', 'Rr', function() local screen_width_percentage = 25 local function find_terminal_buffer() for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do if vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].buftype == 'terminal' and vim.b[bufnr].python_repl == true then return bufnr end end return nil end local function toggle_terminal_window(bufnr) for _, win in ipairs(vim.api.nvim_list_wins()) do if vim.api.nvim_win_get_buf(win) == bufnr then vim.api.nvim_win_hide(win) return end end vim.cmd('vsplit') vim.api.nvim_win_set_buf(0, bufnr) vim.cmd.wincmd('L') vim.api.nvim_win_set_width(0, math.floor(vim.o.columns / (100 / screen_width_percentage))) end local function create_terminal() vim.cmd('vnew') vim.cmd.wincmd('L') vim.api.nvim_win_set_width(0, math.floor(vim.o.columns / (100 / screen_width_percentage))) vim.b.python_repl = true local job_id = vim.fn.termopen(get_ipython_argv()) if job_id and job_id > 0 then vim.g.slime_default_config = { jobid = job_id } end vim.cmd('startinsert') end local term_bufnr = find_terminal_buffer() if term_bufnr then local job_id = vim.b[term_bufnr].terminal_job_id if job_id and job_id > 0 then vim.g.slime_default_config = { jobid = job_id } end toggle_terminal_window(term_bufnr) else create_terminal() end end, { desc = 'Run/REPL: Toggle Python REPL' })