Skip to content

Latest commit

 

History

History
201 lines (165 loc) · 7.86 KB

File metadata and controls

201 lines (165 loc) · 7.86 KB

Helper functions

;; -*- lexical-binding: t; -*-
    (defun display-python-instance ()
      "Display the currently running Python instance in another window"
      (interactive)
      (if (eq (python-shell-get-buffer) nil)
          (python-shell-get-or-create-process)
        (display-buffer (python-shell-get-buffer) t)))

    (defun my-python-eval-line (vis)
      "Send the current line to the inferior ESS process.
    Arg has same meaning as for `ess-eval-region'."
      (interactive "P")
      (save-excursion
        (end-of-line)
        (let ((end (point)))
          (beginning-of-line)
          (princ (concat "Loading line:...") t)
          (conor-python-shell-send-region (point) end))))


    (defun python-shell-send-line (&optional arg)
      "This function will send the line that the point is on to the active python interpreter."
      (interactive "p")
      (save-excursion (move-beginning-of-line arg)
                      (setq start (point))
                      (move-end-of-line arg)
                      (setq end (point))
                      (python-shell-send-region start end)))



    (defun python-shell-send-block (&optional arg)
      (interactive "p")
      (save-excursion (backward-paragraph)
                      (setq start (point))
                      (forward-paragraph)
                      (setq end (point))
                      (python-shell-send-region start end)))

    (fset 'descend-python-dict "\M-b\M-f\C-f\C-f\['")


    (defun conor-python-shell-send-string (string &optional process msg)
      "Send STRING to inferior Python PROCESS.
    When MSG is non-nil messages the first line of STRING."
      (interactive "sPython command: ")
      (let ((process (or process (python-shell-get-or-create-process)))
            (lines (split-string string "\n" t)))
        (when msg
          (message (format "Sent: %s..." (nth 0 lines))))
        (if (> (length lines) 1)
            (let* ((temp-file-name (make-temp-file "py"))
                   (file-name (or (buffer-file-name) temp-file-name)))
              (with-temp-file temp-file-name
                (insert string)
                (delete-trailing-whitespace))
              (python-shell-send-file file-name process temp-file-name))
          (comint-send-string process string)
          (when (or (not (string-match "\n$" string))
                    (string-match "\n[ \t].*\n?$" string))
            (comint-send-string process "\n")))))

    (defun conor-python-shell-send-region (start end)
      "Send the region delimited by START and END to inferior Python process."
      (interactive "r")
      (let ((deactivate-mark nil))
        (conor-python-shell-send-string (buffer-substring start end) nil t)))

    (defun my-add-to-multiple-hooks (func hooks)
      (mapc (lambda (hook)
              (add-hook hook func))
            hooks))

uv

(require 'treesit)
  (add-to-list 'treesit-language-source-alist '(toml "https://github.com/tree-sitter-grammars/tree-sitter-toml"))
  (unless (treesit-language-available-p 'toml)
    (treesit-install-language-grammar 'toml))

(use-package uv
  :straight (uv :type git :host github :repo "johannes-mueller/uv.el")
  :bind ((:map python-mode-map ("C-c s" . uv))
         (:map python-ts-mode-map ("C-c s" . uv))))

(defun uv-activate ()
  "Activate Python environment managed by uv based on current project directory.
Looks for .venv directory in project root and activates the Python interpreter."
  (interactive)
  (let* ((project-root (project-root (project-current t)))
         (venv-path (expand-file-name ".venv" project-root))
         (python-path (expand-file-name
                       (if (eq system-type 'windows-nt)
                           "Scripts/python.exe"
                         "bin/python")
                       venv-path)))
    (if (file-exists-p python-path)
        (progn
          ;; Set Python interpreter path
          (setq-local python-shell-interpreter python-path)

          ;; Update exec-path to include the venv's bin directory
          (let ((venv-bin-dir (file-name-directory python-path)))
            (setq-local exec-path (cons venv-bin-dir
                                  (remove venv-bin-dir exec-path))))

          ;; Update PATH environment variable
          (setenv "PATH" (concat (file-name-directory python-path)
                                 path-separator
                                 (getenv "PATH")))

          ;; Update VIRTUAL_ENV environment variable
          (setenv "VIRTUAL_ENV" venv-path)

          ;; Remove PYTHONHOME if it exists
          (setenv "PYTHONHOME" nil)

          (message "Activated UV Python environment at %s" venv-path))
      (error "No UV Python environment found in %s" project-root))))

Pytest

(use-package python-pytest
  :bind ((:map python-mode-map ("C-c t" . python-pytest-dispatch))
         (:map python-ts-mode-map ("C-c t" . python-pytest-dispatch))))

Python Mode

(use-package py-isort)
(use-package sphinx-doc
  :config
  (setq sphinx-doc-include-types t))

(setq-default tab-width 4
              ;; Flycheck executables (kept for non-LSP fallback)
              flycheck-python-pylint-executable "/Users/conornash/.local/bin/pylint"
              flycheck-python-mypy-executable "/Users/conornash/.local/bin/mypy"
              ;; Note: flake8, pyright, ruff now handled by Eglot via ty + ruff LSP
              python-indent-offset 4
              python-shell-interpreter "ipython"
              python-shell-interpreter-args "--simple-prompt -i --"
              python-shell-prompt-detect-failure-warning t
              python-shell-completion-native t
              python-shell-prompt-regexp "In \\[[0-9]+\\]: "
              python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
              python-shell-completion-native-disabled-interpreters '("pypy" "ipython" "jupyter")
              python-shell-process-environment
              '("PYTHONIOENCODING='utf-8'"
                "LANG=en_US.UTF-8"
                "LC_ALL=en_US.UTF-8"
                "LC_LANG=en_US.UTF-8"))

(defun my-python-mode-setup ()
  "Configure Python mode for development with Eglot LSP (ty + ruff)."
  (rainbow-delimiters-mode 1)
  (rainbow-mode 1)
  (smartscan-mode 1)
  (turn-on-smartparens-strict-mode)
  (require 'smartparens-python)

  ;; Use Flymake with Eglot (not Flycheck) for LSP-based diagnostics
  (flymake-mode 1)
  (flycheck-mode -1)

  ;; Start Eglot for LSP support (ty + ruff via rass)
  (eglot-ensure)

  (sphinx-doc-mode 1)
  (add-hook 'after-save-hook #'delete-trailing-whitespace nil t)

  (guide-key/add-local-guide-key-sequence "C-c")

  ;; Keybindings (use local-set-key to work with both python-mode and python-ts-mode)
  (local-set-key (kbd "<M-up>") 'move-text-up)
  (local-set-key (kbd "<M-down>") 'move-text-down)
  (local-set-key (kbd "M-\\") 'display-python-instance)
  (local-set-key (kbd "C-|") 'eval-at-cursor)
  (local-set-key (kbd "C-\\") 'my-python-eval-line)
  (local-set-key (kbd "C-S-a") 'python-nav-beginning-of-statement)
  (local-set-key (kbd "C-a") 'beginning-of-visual-line)
  (local-set-key (kbd "C-S-e") 'python-nav-end-of-statement)
  (local-set-key (kbd "C-e") 'end-of-visual-line)
  (local-unset-key (kbd "C-c C-d"))
  (local-set-key (kbd "C-c C-r") 'conor-python-shell-send-region))

;; Apply to both python-mode and python-ts-mode
(add-hook 'python-mode-hook #'my-python-mode-setup)
(add-hook 'python-ts-mode-hook #'my-python-mode-setup)