dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

python-mode-expansions.el (5781B)


      1 ;;; python-mode-expansions.el --- python-mode-specific expansions for expand-region  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2012-2023  Free Software Foundation, Inc
      4 
      5 ;; Author: Felix Geller
      6 ;; Based on python-mode-expansions by: Ivan Andrus
      7 ;; Keywords: marking region python
      8 
      9 ;; This program is free software; you can redistribute it and/or modify
     10 ;; it under the terms of the GNU General Public License as published by
     11 ;; the Free Software Foundation, either version 3 of the License, or
     12 ;; (at your option) any later version.
     13 
     14 ;; This program is distributed in the hope that it will be useful,
     15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 ;; GNU General Public License for more details.
     18 
     19 ;; You should have received a copy of the GNU General Public License
     20 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 ;;; Commentary:
     23 
     24 ;; Commentary:
     25 ;; cf. https://github.com/magnars/expand-region.el/pull/18
     26 
     27 ;; For python-mode: https://launchpad.net/python-mode
     28 ;;  - Mark functionality taken from python-mode:
     29 ;;    - `py-mark-expression'
     30 ;;    - `py-mark-statement'
     31 ;;    - `py-mark-block'
     32 ;;    - `py-mark-class'
     33 ;;  - Additions implemented here:
     34 ;;    - `er/mark-inside-python-string'
     35 ;;    - `er/mark-outside-python-string'
     36 ;;    - `er/mark-outer-python-block'
     37 ;;  - Supports multi-line strings
     38 ;;  - Supports incremental expansion of nested blocks
     39 
     40 ;;; Code:
     41 
     42 (require 'expand-region-core)
     43 
     44 (defvar er--python-string-delimiter "'\"")
     45 
     46 (defalias 'py-goto-beyond-clause #'py-end-of-clause-bol)
     47 
     48 (declare-function py-in-string-p "python-mode")
     49 (declare-function py-beginning-of-block "python-mode")
     50 (declare-function py-end-of-block "python-mode")
     51 (declare-function py-mark-block-or-clause "python-mode")
     52 (declare-function py-end-of-clause-bol "python-mode")
     53 (defvar py-indent-offset)
     54 
     55 (defun er/mark-outside-python-string ()
     56   "Marks region outside a (possibly multi-line) Python string"
     57   (interactive)
     58   (let ((string-beginning (py-in-string-p)))
     59     (when string-beginning
     60       (goto-char string-beginning)
     61       (set-mark (point))
     62       (forward-sexp)
     63       (exchange-point-and-mark))))
     64 
     65 (defun er/mark-inside-python-string ()
     66   "Marks region inside a (possibly multi-line) Python string"
     67   (interactive)
     68   (let ((string-beginning (py-in-string-p)))
     69     (when string-beginning
     70       (goto-char string-beginning)
     71       (forward-sexp)
     72       (skip-chars-backward er--python-string-delimiter)
     73       (set-mark (point))
     74       (goto-char string-beginning)
     75       (skip-chars-forward er--python-string-delimiter))))
     76 
     77 (defun er--move-to-beginning-of-outer-python-block (start-column)
     78   "Assumes that point is in a python block that is surrounded by
     79 another that is not the entire module. Uses `py-indent-offset' to
     80 find the beginning of the surrounding block because
     81 `py-beginning-of-block-position' just looks for the previous
     82 block-starting key word syntactically."
     83   (while (> (current-column) (- start-column py-indent-offset))
     84     (forward-line -1)
     85     (py-beginning-of-block)))
     86 
     87 (defun er/mark-outer-python-block ()
     88   "Attempts to mark a surrounding block by moving to the previous
     89 line and selecting the surrounding block."
     90   (interactive)
     91   (let ((start-column (current-column)))
     92     (when (> start-column 0) ; outer block is the whole buffer
     93       (er--move-to-beginning-of-outer-python-block start-column)
     94       (let ((block-beginning (point)))
     95         (py-end-of-block)
     96         (set-mark (point))
     97         (goto-char block-beginning)))))
     98 
     99 (defun er/mark-x-python-compound-statement ()
    100   "Mark the current compound statement (if, while, for, try) and all clauses."
    101   (interactive)
    102   (let ((secondary-re
    103          (save-excursion
    104            (py-mark-block-or-clause)
    105            (cond ((looking-at "if\\|for\\|while\\|else\\|elif") "else\\|elif")
    106                  ((looking-at "try\\|except\\|finally") "except\\|finally"))))
    107         start-col)
    108     (when secondary-re
    109       (py-mark-block-or-clause)
    110       (setq start-col (current-column))
    111       (while (looking-at secondary-re)
    112         (forward-line -1) (back-to-indentation)
    113         (while (> (current-column) start-col)
    114           (forward-line -1) (back-to-indentation)))
    115       (set-mark (point))
    116       (py-end-of-clause-bol) (forward-line) (back-to-indentation)
    117       (while (and (looking-at secondary-re)
    118                   (>= (current-column) start-col))
    119         (py-end-of-clause-bol) (forward-line) (back-to-indentation))
    120       (forward-line -1) (end-of-line)
    121       (exchange-point-and-mark))))
    122 
    123 (defun er/add-python-mode-expansions ()
    124   "Adds python-mode-specific expansions for buffers in python-mode"
    125   (let ((try-expand-list-additions '(
    126                                      er/mark-inside-python-string
    127                                      er/mark-outside-python-string
    128                                      py-mark-expression
    129                                      py-mark-statement
    130                                      py-mark-block
    131                                      py-mark-def
    132                                      py-mark-clause
    133                                      er/mark-x-python-compound-statement
    134                                      er/mark-outer-python-block
    135                                      py-mark-class
    136                                      )))
    137     (set (make-local-variable 'expand-region-skip-whitespace) nil)
    138     (set (make-local-variable 'er/try-expand-list)
    139          (remove 'er/mark-inside-quotes
    140                  (remove 'er/mark-outside-quotes
    141                          (append er/try-expand-list try-expand-list-additions))))))
    142 
    143 (er/enable-mode-expansions 'python-mode #'er/add-python-mode-expansions)
    144 
    145 (provide 'python-mode-expansions)
    146 
    147 ;; python-mode-expansions.el ends here