dotemacs

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

yaml-mode-expansions.el (7393B)


      1 ;;; yaml-mode-expansions.el --- expansions for yaml mode  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2021-2023  Free Software Foundation, Inc.
      4 
      5 ;; Author: Aaron Gonzales
      6 ;; Keywords: marking region yaml YAML expand
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 ;;
     23 ;;  - Additions implemented here:
     24 ;;    - er/mark-yaml-key-value
     25 ;;    - er/mark-yaml-list-item
     26 ;;    - er/mark-yaml-block
     27 ;;    - er/mark-yaml-outer-block
     28 ;;    - er/mark-yaml-inner-block
     29 
     30 
     31 ;;; Code:
     32 
     33 (require 'expand-region-core)
     34 
     35 (defconst yaml-indent 2)
     36 
     37 (unless (fboundp 'yaml-indent-offset)
     38   (defalias 'yaml-indent-offset #'yaml-indent))
     39 
     40 (defvar er--yaml-key-value-regex
     41   (rx (one-or-more
     42        (any "0-9A-Za-z"))
     43       ":"
     44       (zero-or-more " ")
     45       (one-or-more
     46        (any "0-9A-Za-z" " '_-"))))
     47 
     48 (defvar er--yaml-list-item-regex
     49   (rx (seq "- "
     50            (one-or-more
     51             (any "0-9A-Za-z" "\"':=_-")))))
     52 
     53 (defvar er--yaml-block-regex
     54   (rx (seq (zero-or-more
     55             (any " -"))
     56            (one-or-more
     57             (any "0-9A-Za-z" " '_-"))
     58            ":\n")))
     59 
     60 (defun er--get-regex-indentation-level (regex)
     61   "Return the indentation level of the code with respect to the REGEX passed."
     62   (when (looking-at regex)
     63     ;; Block start means that the next level is deeper.
     64     (+ (current-indentation) yaml-indent-offset) ;FIXME: Unused?
     65     ;; Assuming we're inside the block that we want to mark
     66     (current-indentation)))
     67 
     68 (defun er/mark-yaml-line-base (regex)
     69   "Mark line of yaml file based on simple REGEX."
     70   (back-to-indentation)
     71   (when (looking-at regex)
     72     (set-mark (line-end-position))))
     73 
     74 (defun er/mark-yaml-block-static-base (regex)
     75   "Mark yaml block based on REGEX passed."
     76   ;; go bac to indentation so always can get regexp
     77   (back-to-indentation)
     78   ;; make sure the cursor is set inside the block
     79   ;; mark point at this higher code block
     80   (set-mark (point))
     81   ;; save level of this blocks indentation
     82   (let ((block-indentation (current-indentation)))
     83     (forward-line 1)
     84     (while (and
     85             ;; No need to go beyond the end of the buffer. Can't use
     86             ;; eobp as the loop places the point at the beginning of
     87             ;; line, but eob might be at the end of the line.
     88             (not (= (point-max) (line-end-position)))
     89             ;; Proceed if: indentation is too deep
     90             (or (> (current-indentation) block-indentation)
     91                 ;; Looking at an empty line
     92                 (looking-at (rx line-start (* whitespace) line-end))
     93                 ;; We're not looking at the start of a YAML block
     94                 ;; and the indent is deeper than the block's indent
     95                 (and (not (looking-at regex))
     96                      (> (current-indentation) block-indentation))))
     97       (forward-line 1)
     98       (back-to-indentation))
     99     ;; Find the end of the block by skipping comments backwards
    100     (python-util-forward-comment -1)
    101     (exchange-point-and-mark))
    102   (back-to-indentation))
    103 
    104 (defun er/mark-yaml-block-base (regex &optional next-indent-level)
    105   "Mark yaml block based on REGEX passed.
    106 NEXT-INDENT-LEVEL can be used to search outer blocks when necessary."
    107   ;; go bac to indentation so always can get regexp
    108   (back-to-indentation)
    109   ;; make sure the cursor is set inside the block
    110   (let ((next-indent-level
    111          (or
    112           ;; Use the given level
    113           next-indent-level
    114           ;; used to mark current block
    115           (er--get-regex-indentation-level regex))))
    116     ;; if true then at start of block and wanna mark itself
    117     ;; else were are inside the block already and will mark it)))
    118     ;; move up the code unti a parent code block is reached
    119     (while (and (>= (current-indentation) next-indent-level)
    120                 (not (eq (current-indentation) 0)))
    121       (re-search-backward regex (point-min) t)
    122       (back-to-indentation))
    123     ;; mark point at this higher code block
    124     (set-mark (point))
    125     ;; save level of this blocks indentation
    126     (let ((block-indentation (current-indentation)))
    127       (forward-line 1)
    128       (while (and
    129               ;; No need to go beyond the end of the buffer. Can't use
    130               ;; eobp as the loop places the point at the beginning of
    131               ;; line, but eob might be at the end of the line.
    132               (not (= (point-max) (line-end-position)))
    133               ;; Proceed if: indentation is too deep
    134               (or (> (current-indentation) block-indentation)
    135                   ;; Looking at an empty line
    136                   (looking-at (rx line-start (* whitespace) line-end))
    137                   ;; We're not looking at the start of a YAML block
    138                   ;; and the indent is deeper than the block's indent
    139                   (and (not (looking-at regex))
    140                        (> (current-indentation) block-indentation))))
    141         (forward-line 1)
    142         (back-to-indentation))
    143       ;; Find the end of the block by skipping comments backwards
    144       (python-util-forward-comment -1)
    145       (exchange-point-and-mark)))
    146   (back-to-indentation))
    147 
    148 (defun er/mark-yaml-key-value ()
    149   "Mark a yaml key-value pair."
    150   (interactive)
    151   (er/mark-yaml-line-base er--yaml-key-value-regex))
    152 
    153 (defun er/mark-yaml-list-item ()
    154   "Mark a yaml list item."
    155   (interactive)
    156   (er/mark-yaml-line-base er--yaml-list-item-regex))
    157 
    158 (defun er/mark-yaml-inner-block ()
    159   "Mark the yaml contents of the block at point.
    160 Command that wraps `er/mark-yaml-block-base'."
    161   (interactive)
    162   (er/mark-yaml-block-base er--yaml-block-regex (current-indentation))
    163   (forward-line)
    164   (back-to-indentation))
    165 
    166 (defun er/mark-yaml-block ()
    167   "Mark the yaml block that point is currently at the top of.
    168 Command that wraps `er/mark-yaml-block-base'."
    169   (interactive)
    170   (er/mark-yaml-block-static-base er--yaml-block-regex))
    171 
    172 (defun er/mark-yaml-outer-block ()
    173   "Mark the outer yaml block that surrounds the block around point.
    174 Command that wraps `er/mark-yaml-block-base'."
    175   (interactive)
    176   (er/mark-yaml-block-base er--yaml-block-regex (current-indentation)))
    177 
    178 (defun er/add-yaml-mode-expansions ()
    179   "Add yaml-mode-specific expansions for buffers in yaml-mode."
    180   (let ((try-expand-list-additions '(er/mark-symbol
    181                                      er/mark-outside-quotes
    182                                      er/mark-yaml-list-item
    183                                      er/mark-yaml-key-value
    184                                      er/mark-yaml-block
    185                                      er/mark-yaml-outer-block
    186                                      er/mark-yaml-inner-block)))
    187     (set (make-local-variable 'expand-region-skip-whitespace) nil)
    188     (set (make-local-variable 'er/try-expand-list) try-expand-list-additions)))
    189 
    190 (er/enable-mode-expansions 'yaml-mode #'er/add-yaml-mode-expansions)
    191 
    192 (provide 'yaml-mode-expansions)
    193 
    194 ;;; yaml-mode-expansions.el ends here