2525
2626; ;; Commentary:
2727
28- ; ; beginning-of-defun and end-of-defun
28+ ; ; `beginning-of-defun' and `end-of-defun'
29+ ; ;
30+ ; ; The end of a defun is just after the close curly brace.
31+ ; ;
32+ ; ; The beginning of a defun is the beginning of:
33+ ; ; - "func" keyword,
34+ ; ; - its modifiers or attributes,
35+ ; ; - comments on the same line.
36+ ; ;
37+ ; ; `swift-mode:beginning-of-defun' moves the point to the beginning of a defun
38+ ; ; that precedes (if the arg is positive) or follows (if the arg is negative)
39+ ; ; the original point and has the same or less nesting level.
40+ ; ;
41+ ; ; `swift-mode:end-of-defun' moves the point to the end of a defun
42+ ; ; that follows (if the arg is positive) or precedes (if the arg is negative)
43+ ; ; the original point and has the same or less nesting level.
2944
3045; ;; Code:
3146
3247(require 'swift-mode-lexer )
3348(require 'swift-mode-indent )
3449
50+ ;;;### autoload
51+ (defcustom swift-mode:mark-defun-preference 'containing
52+ " Preference for `swift-mode:mark-defun' for nested declarations.
53+
54+ Suppose the following code with the point located at A:
55+
56+ func outer() {
57+ func inner1() {
58+ }
59+
60+ // A
61+
62+ func inner2() {
63+ }
64+ }
65+
66+ If `swift-mode:mark-defun-preference' is `containing' , `swift-mode:mark-defun'
67+ marks the `outer' function. Likewise, it marks `inner1' if the preference is
68+ `preceding' and `inner2' if the preference is `following' ."
69+ :type '(choice (const :tag " Containing" containing)
70+ (const :tag " Preceding" preceding)
71+ (const :tag " Following" following))
72+ :group 'swift
73+ :safe 'symbolp )
74+
3575(defun swift-mode:beginning-of-defun (&optional arg )
3676 " Move backward to the beginning of a defun.
3777
38- See `beginning-of-defun' for ARG."
39- (interactive )
78+ See `beginning-of-defun' for ARG.
79+
80+ Return t if a defun is found. Return nil otherwise.
81+
82+ Push mark at previous position if this is called as a command, not repeatedly,
83+ and the region is not active."
84+ (interactive " p" )
4085 (setq arg (or arg 1 ))
41- (let (result
42- pos)
43- (if (<= 0 arg)
86+ (let ((result t )
87+ (pos (point )))
88+ (if (< 0 arg)
89+ ; ; Moving backward
4490 (progn
45- (setq pos (point ))
46- ; ; Special handling for the case that the cursor is between the
47- ; ; beginning of the defun and the open curly brace of the defun.
48- (when (< (save-excursion
49- (swift-mode:beginning-of-statement)
50- (point ))
51- (point ))
52- ; ; Searches forward { or end of a statement.
53- (while (not
54- (memq
55- (swift-mode:token:type (swift-mode:forward-token-or-list))
56- '({} implicit-\; \; } outside-of-buffer))))
57- (when (eq (char-before ) ?} )
58- (backward-list ))
59- ; ; Skips implicit ;
60- (forward-comment (point-max ))
61- (if (swift-mode:is-point-before-body-of-defun)
62- (progn
63- (swift-mode:beginning-of-statement)
64- (setq result t )
65- (setq arg (1- arg)))
66- (goto-char pos)))
67- (while (< 0 arg)
68- (setq result (swift-mode:beginning-of-defun-1
69- #'swift-mode:backward-token-or-list ))
70- (setq arg (1- arg))))
71- (while (< arg 0 )
72- ; ; If the cursor is on a defun, ensure the cursor is after the open
73- ; ; curly brace of defun.
74- (setq pos (point ))
75- (swift-mode:beginning-of-statement)
76- ; ; swift-mode:beginning-of-statement may forward the cursor if the
77- ; ; cursor is on a comment or whitespace. In that case, does not skip
78- ; ; the defun.
79- (when (<= (point ) pos)
91+ ; ; `swift-mode:beginning-of-defun-1' assumes the point is after
92+ ; ; the open curly brace of defun. So moving the point to just after
93+ ; ; the open curly brace if the current statement has one.
8094 (while (not
8195 (memq
82- (swift-mode:token:type (swift-mode:forward-token-or-list))
83- '({} } outside-of-buffer)))))
96+ (swift-mode:token:type (swift-mode:forward-token))
97+ '({ implicit-\; \; } outside-of-buffer))))
98+ (backward-char )
99+ ; ; Skips implicit-;
100+ (forward-comment (point-max ))
101+ (when (eq (char-after ) ?{ )
102+ (forward-char ))
103+
104+ (setq result (swift-mode:beginning-of-defun-1
105+ #'swift-mode:backward-token-or-list ))
106+ (when (and result (< (point ) pos))
107+ (setq arg (1- arg)))
84108
109+ (while (and result (< 0 arg))
110+ (setq result (swift-mode:beginning-of-defun-1
111+ #'swift-mode:backward-token-or-list ))
112+ (setq arg (1- arg))))
113+ ; ; Moving forward
114+ (setq result (swift-mode:beginning-of-defun-1
115+ (lambda ()
116+ (prog1 (swift-mode:forward-token-or-list)
117+ (forward-comment (point-max ))))))
118+ (when (and result (< pos (point )))
119+ (setq arg (1+ arg)))
120+ (while (and result (< arg 0 ))
121+ ; ; Skips the current statement
122+ (while (not
123+ (memq
124+ (swift-mode:token:type (swift-mode:forward-token-or-list))
125+ '({} implicit-\; \; } outside-of-buffer))))
85126 (setq result (swift-mode:beginning-of-defun-1
86127 (lambda ()
87128 (prog1 (swift-mode:forward-token-or-list)
88129 (forward-comment (point-max ))))))
89130 (setq arg (1+ arg))))
131+ (and result
132+ (eq this-command 'swift-mode:beginning-of-defun )
133+ (not (eq last-command 'swift-mode:beginning-of-defun ))
134+ (not (region-active-p ))
135+ (push-mark pos))
90136 result))
91137
92138(defun swift-mode:beginning-of-defun-1 (next-token-function )
@@ -101,7 +147,6 @@ NEXT-TOKEN-FUNCTION skips the preceding/following token."
101147 (throw 'swift-mode:found-defun t )))
102148 nil ))
103149
104-
105150(defun swift-mode:is-point-before-body-of-defun ()
106151 " Return t it the point is just before the body of a defun.
107152
@@ -132,8 +177,6 @@ Return nil otherwise."
132177 (setq previous-token (swift-mode:backward-token-or-list))
133178 (setq previous-type (swift-mode:token:type previous-token))
134179 (setq previous-text (swift-mode:token:text previous-token)))
135- (unless (bobp )
136- (swift-mode:forward-token-simple))
137180 (or (equal previous-text " init" )
138181 (member previous-text defun-keywords))))))
139182
@@ -153,7 +196,9 @@ Intended for internal use."
153196 (progn
154197 (forward-comment (- (point )))
155198 (swift-mode:beginning-of-statement))
199+ ; ; Excludes comments on previous lines.
156200 (goto-char (swift-mode:token:end parent))
201+ (forward-comment (- (point )))
157202 (setq parent (save-excursion (swift-mode:backward-token)))
158203 (forward-comment (point-max ))
159204 (swift-mode:goto-non-comment-bol)
@@ -164,22 +209,32 @@ Intended for internal use."
164209(defun swift-mode:end-of-defun (&optional arg )
165210 " Move forward to the end of a defun.
166211
167- See `end-of-defun' for ARG."
168- (interactive )
212+ See `end-of-defun' for ARG.
213+
214+ Return t if a defun is found. Return nil otherwise.
215+
216+ Push mark at previous position if this is called as a command, not repeatedly,
217+ and the region is not active."
218+ (interactive " p" )
169219 (setq arg (or arg 1 ))
170- (let (result)
220+ (let (result
221+ (pos (point )))
171222 (if (<= 0 arg)
172223 (while (< 0 arg)
173224 (setq result (swift-mode:end-of-defun-1
174- #'swift-mode:forward-token-or-list
175- ))
225+ #'swift-mode:forward-token-or-list ))
176226 (setq arg (1- arg)))
177227 (while (< arg 0 )
178228 (setq result (swift-mode:end-of-defun-1
179229 (lambda ()
180230 (prog1 (swift-mode:backward-token-or-list)
181231 (forward-comment (- (point )))))))
182232 (setq arg (1+ arg))))
233+ (and result
234+ (eq this-command 'swift-mode:end-of-defun )
235+ (not (eq last-command 'swift-mode:end-of-defun ))
236+ (not (region-active-p ))
237+ (push-mark pos))
183238 result))
184239
185240(defun swift-mode:end-of-defun-1 (next-token-function )
@@ -196,6 +251,156 @@ NEXT-TOKEN-FUNCTION skips the preceding/following token."
196251 (throw 'swift-mode:found-defun t )))
197252 nil ))
198253
254+ (defun swift-mode:mark-defun (&optional allow-extend )
255+ " Put mark at the end of defun, point at the beginning of defun.
256+
257+ If the point is between defuns, mark depend on
258+ `swift-mode:mark-defun-preference' .
259+
260+ If ALLOW-EXTEND is non-nil or called interactively, and the command is repeated
261+ or the region is active, mark the following (if the point is before the mark)
262+ or preceding (if the point is after the mark) defun. If that defun has lesser
263+ nesting level, mark the whole outer defun."
264+ (interactive (list t ))
265+ (if (and allow-extend
266+ (or
267+ (and (eq last-command this-command) (mark t ))
268+ (region-active-p )))
269+ ; ; Extends region.
270+ (let ((forward-p (<= (point ) (mark ))))
271+ (set-mark
272+ (save-excursion
273+ (goto-char (mark ))
274+ (if forward-p
275+ (swift-mode:end-of-defun)
276+ (swift-mode:beginning-of-defun))
277+ (point )))
278+ ; ; Marks the whole outer defun if it has lesser nesting level.
279+ (if forward-p
280+ (goto-char (min (point )
281+ (save-excursion
282+ (goto-char (mark ))
283+ (swift-mode:beginning-of-defun)
284+ (point ))))
285+ (goto-char (max (point )
286+ (save-excursion
287+ (goto-char (mark ))
288+ (swift-mode:end-of-defun)
289+ (point ))))))
290+ ; ; Marks new region.
291+ (let ((region
292+ (cond
293+ ((eq swift-mode:mark-defun-preference 'containing )
294+ (swift-mode:containing-defun-region))
295+ ((eq swift-mode:mark-defun-preference 'preceding )
296+ (swift-mode:preceding-defun-region))
297+ ((eq swift-mode:mark-defun-preference 'following )
298+ (swift-mode:following-defun-region)))))
299+ (if region
300+ (progn (push-mark (cdr region ) nil t )
301+ (goto-char (car region ))
302+ region )
303+ (when (called-interactively-p 'interactive )
304+ (message " No defun found " ))
305+ nil ))))
306+
307+ (defun swift-mode:following-defun-region ()
308+ " Return cons representing a region of following defun."
309+ (save-excursion
310+ (let* ((end (and (swift-mode:end-of-defun) (point )))
311+ (beginning (and end (swift-mode:beginning-of-defun) (point ))))
312+ (and beginning (cons beginning end)))))
313+
314+ (defun swift-mode:preceding-defun-region ()
315+ " Return cons representing a region of preceding defun."
316+ (save-excursion
317+ (let* ((beginning (and (swift-mode:beginning-of-defun) (point )))
318+ (end (and beginning (swift-mode:end-of-defun) (point ))))
319+ (and end (cons beginning end)))))
320+
321+ (defun swift-mode:containing-defun-region ()
322+ " Return cons representing a region of containing defun."
323+ (let* ((pos (point ))
324+ (region (swift-mode:following-defun-region))
325+ (extended (and region
326+ (swift-mode:extend-defun-region-with-spaces region ))))
327+ (cond
328+ ((and extended (<= (car extended) pos (cdr extended)))
329+ region )
330+
331+ ((progn
332+ (setq region (swift-mode:preceding-defun-region))
333+ (setq extended (swift-mode:extend-defun-region-with-spaces region ))
334+ (and extended (<= (car extended) pos (cdr extended))))
335+ region )
336+
337+ (t
338+ (catch 'swift-mode:found-defun
339+ (while (swift-mode:end-of-defun)
340+ (let ((end (point )))
341+ (save-excursion
342+ (swift-mode:beginning-of-defun)
343+ (when (<= (point ) pos end)
344+ (throw 'swift-mode:found-defun (cons (point ) end))))))
345+ (cons (point-min ) (point-max )))))))
346+
347+ (defun swift-mode:extend-defun-region-with-spaces (region )
348+ " Return REGION extended with surrounding spaces."
349+ (let ((beginning (car region ))
350+ (end (cdr region )))
351+ (save-excursion
352+ (goto-char beginning)
353+ (skip-syntax-backward " " )
354+ (setq beginning (point )))
355+ (save-excursion
356+ (goto-char end)
357+ (skip-syntax-forward " " )
358+ (setq end (point )))
359+ (cons beginning end)))
360+
361+ (defun swift-mode:narrow-to-defun (&optional include-comments )
362+ " Make text outside current defun invisible.
363+
364+ If the point is between defuns, mark depend on
365+ `swift-mode:mark-defun-preference' .
366+
367+ Preceding comments are included if INCLUDE-COMMENTS is non-nil.
368+ Interactively, the behavior depends on ‘narrow-to-defun-include-comments’."
369+ (interactive (list narrow-to-defun-include-comments))
370+ (let ((restriction (cons (point-min ) (point-max )))
371+ region
372+ extended)
373+ (save-excursion
374+ (widen )
375+
376+ (setq region
377+ (cond
378+ ((eq swift-mode:mark-defun-preference 'containing )
379+ (swift-mode:containing-defun-region))
380+ ((eq swift-mode:mark-defun-preference 'preceding )
381+ (swift-mode:preceding-defun-region))
382+ ((eq swift-mode:mark-defun-preference 'following )
383+ (swift-mode:following-defun-region))))
384+
385+ (setq extended
386+ (and region (swift-mode:extend-defun-region-with-spaces region )))
387+
388+ (when (and extended include-comments)
389+ (save-excursion
390+ (goto-char (car extended))
391+ ; ; Includes comments.
392+ (forward-comment (- (point )))
393+ ; ; Excludes spaces and line breaks.
394+ (skip-syntax-forward " >" )
395+ ; ; Includes indentation.
396+ (skip-syntax-backward " " )
397+ (setcar extended (point ))))
398+
399+ (if extended
400+ (narrow-to-region (car extended) (cdr extended))
401+ (when (called-interactively-p 'interactive )
402+ (message " No defun found " ))
403+ (narrow-to-region (car restriction) (cdr restriction))))))
199404
200405(provide 'swift-mode-beginning-of-defun )
201406
0 commit comments