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