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