Skip to content

Commit 0d1ef0e

Browse files
woolsweatertaku0
authored andcommitted
Add specialized comment fill function
This incorporates certain Swift comment markup elements into comment filling, allowing `fill-paragraph` to nicely format most Swift doc comments. A new variable allows the user to configure their comment fill column differently from the code fill column, defaulting to 80. The implementation does not (yet) preserve indented lists or recognize fenced code blocks.
1 parent 4777c40 commit 0d1ef0e

File tree

6 files changed

+456
-3
lines changed

6 files changed

+456
-3
lines changed

swift-mode-fill.el

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
;;; swift-mode-fill.el --- Major-mode for Apple's Swift programming language, paragraph filling. -*- lexical-binding: t -*-
2+
3+
;; Copyright (C) 2022 Josh Caswell
4+
5+
;; Authors: Josh Caswell (https://github.com/woolsweater)
6+
7+
;; This file is not part of GNU Emacs.
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+
;; Routines for paragraph filling
25+
26+
;;; Code:
27+
28+
(require 'rx)
29+
(require 'swift-mode-lexer)
30+
31+
(defcustom swift-mode:comment-fill-column 80
32+
"Fill column for comment wrapping in Swift code.
33+
34+
This may be different than the fill column for the rest of the source. See
35+
also `swift-mode:comment-fill-function'."
36+
:type 'integer
37+
:group 'swift
38+
:safe 'integerp)
39+
40+
(make-variable-buffer-local 'swift-mode:comment-fill-column)
41+
42+
(defconst swift-mode:doc-comment-annotation-re
43+
(let ((bullet '(any ?- ?+ ?*))
44+
(any-spacing '(* blank))
45+
(identifier '(+ (syntax word))))
46+
(rx-to-string
47+
`(seq
48+
;; Explicitly include indentation here, for `forward-paragraph'
49+
;; search (see usage of 'parstart' there)
50+
,any-spacing
51+
,bullet ,any-spacing ,identifier
52+
;; For '- parameter foo:'
53+
(? ,any-spacing ,identifier)
54+
,any-spacing ?:)))
55+
"Regex to match Swift documentation comment markup labels, like '- remark:'.
56+
57+
This is used by `swift-mode:comment-fill-function' to extend
58+
`paragraph-start' such that the built-in fill functions recognize
59+
these elements as the beginnings of their own paragraphs.")
60+
61+
(defsubst swift-mode:-find-slash-comment-edges (slashes)
62+
"Helper for `swift-mode:comment-fill-function' handling '///' style.
63+
64+
Search backwards and forwards for contiguous lines that
65+
open (after any indentation) with SLASHES. Return the buffer
66+
locations for the beginning and end of the comment contents. For
67+
this style of comment, the content beginning is after the first
68+
delimiter; the end is the end of the last contiguous line found.
69+
70+
Point may be anywhere in the comment when this is called."
71+
(let ((orig-point (point))
72+
start end)
73+
(back-to-indentation)
74+
(while (and (not (bobp))
75+
(looking-at-p slashes))
76+
(forward-line -1)
77+
(back-to-indentation))
78+
(setq start (progn
79+
(search-forward slashes nil t)
80+
(point)))
81+
(goto-char orig-point)
82+
(while (and (not (eobp))
83+
(looking-at-p slashes))
84+
(forward-line 1)
85+
(unless (eobp)
86+
(back-to-indentation)))
87+
(setq end (if (progn
88+
(back-to-indentation)
89+
(looking-at-p slashes))
90+
;; Edge case: comment is last thing in buffer with no trailing
91+
;; newline.
92+
(point-max)
93+
(forward-line -1)
94+
(move-end-of-line 1)
95+
(point)))
96+
(cons start end)))
97+
98+
(defsubst swift-mode:-fix-up-star-comment-edges ()
99+
"Helper for `swift-mode:comment-fill-function' handling '/**' style.
100+
101+
Ensure each delimiter is on its own line, then return the buffer
102+
locations for the beginning and end of the comment
103+
contents (excluding the delimiters).
104+
105+
Point is assumed to be just after the opening delimiter and its
106+
trailing whitespace (if any) when this is called."
107+
(when (not (looking-at-p "\n"))
108+
(delete-horizontal-space)
109+
(insert-and-inherit "\n")
110+
(indent-according-to-mode))
111+
112+
(let ((start (point))
113+
(end (progn (re-search-forward "\\*+/")
114+
(match-beginning 0))))
115+
(goto-char end)
116+
(skip-chars-backward " \t")
117+
(if (bolp)
118+
(setq end (- (point) 1))
119+
(insert-and-inherit "\n")
120+
(indent-according-to-mode))
121+
(cons start end)))
122+
123+
(defun swift-mode:comment-fill-function (justify)
124+
"Handle comment filling in Swift code.
125+
126+
Delegates to `fill-region' with `fill-column' bound to the value of
127+
`swift-mode:comment-fill-column' so that comments can be wrapped at
128+
different width than the rest of the source source. JUSTIFY is as the
129+
argument of the same name in `fill-region'.
130+
131+
The function determines which style of comment is at or around
132+
point and does preliminary cleanup as needed (the built-in fill
133+
functions do not handle the '/**' style of comment particularly
134+
well)."
135+
;; TODO A leading star on an empty line screws up paragraph calculation.
136+
;; TODO Recognize fenced code blocks.
137+
;; TODO Handle trailing comments.
138+
(let ((chunk (swift-mode:chunk-after)))
139+
(if (not (or (swift-mode:chunk:comment-p chunk)
140+
(looking-at-p comment-start-skip)))
141+
;; If not in a comment, just let `fill-paragraph' try to handle it
142+
nil
143+
(save-match-data
144+
(save-excursion
145+
;; Move to opening delimiter if not already there.
146+
(let ((start (swift-mode:chunk:start chunk)))
147+
(when start
148+
(goto-char start)))
149+
(skip-syntax-forward " ")
150+
151+
;; We want these two bound to their special values when delegating to
152+
;; `fill-region'.
153+
(let ((fill-column swift-mode:comment-fill-column)
154+
(paragraph-start (concat
155+
swift-mode:doc-comment-annotation-re
156+
"\\|"
157+
paragraph-start)))
158+
(cond
159+
160+
;; Slash-style comment
161+
((looking-at "/\\{2,\\}")
162+
(let* ((slashes (match-string-no-properties 0))
163+
(edges (swift-mode:-find-slash-comment-edges slashes))
164+
(start (car edges))
165+
(end (cdr edges)))
166+
;; Factor the comment markers into paragraph recognition
167+
(let ((paragraph-start (concat "[[:blank:]]*" slashes
168+
"\\(?:" paragraph-start "\\)"))
169+
(paragraph-separate (concat "[[:blank:]]*" slashes
170+
"\\(?:" paragraph-separate
171+
"\\)")))
172+
(fill-region start end justify :preserve-spaces)
173+
(indent-region start end))))
174+
175+
;; Star-style comment
176+
((re-search-forward "/\\*\\{2,\\} *" nil t)
177+
(let* ((edges (swift-mode:-fix-up-star-comment-edges))
178+
(start (car edges))
179+
(end (cdr edges)))
180+
(fill-region start end justify :preserve-spaces)
181+
(indent-region start end))))))
182+
183+
;; Make sure `fill-paragraph' does not undo our work.
184+
t))))
185+
186+
(provide 'swift-mode-fill)
187+
188+
;;; swift-mode-fill.el ends here

swift-mode.el

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
(require 'swift-mode-lexer)
3838
(require 'swift-mode-indent)
39+
(require 'swift-mode-fill)
3940
(require 'swift-mode-font-lock)
4041
(require 'swift-mode-beginning-of-defun)
4142
(require 'swift-mode-repl)
@@ -147,7 +148,7 @@ Signal `scan-error' if it hits opening parentheses."
147148

148149
;;;###autoload
149150
(defsubst swift-mode:add-supported-extension-for-speedbar ()
150-
"Register .swfit to speedbar."
151+
"Register .swift to speedbar."
151152
(if (fboundp 'speedbar-add-supported-extension)
152153
(speedbar-add-supported-extension ".swift")
153154
(add-hook 'speedbar-load-hook
@@ -193,6 +194,7 @@ Signal `scan-error' if it hits opening parentheses."
193194
(setq-local fill-indent-according-to-mode t)
194195
(setq-local comment-multi-line t)
195196
(setq-local comment-line-break-function #'swift-mode:indent-new-comment-line)
197+
(setq-local fill-paragraph-function #'swift-mode:comment-fill-function)
196198

197199
(setq-local parse-sexp-lookup-properties t)
198200
(add-hook 'syntax-propertize-extend-region-functions

test/swift-files/fill/comment.swift

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// swift-mode:test:case-begin (Slash style doc comment)
2+
3+
/// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
4+
/// tempor incididunt ut labore et dolore magna aliqua. Ut
5+
///
6+
/// - parameter foo: enim ad minim veniam, quis nostrud exercitation ullamco
7+
/// laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
8+
/// reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
9+
/// pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
10+
/// officia deserunt mollit anim id est laborum.
11+
/// - parameter bar: enim ad minim veniam, quis nostrud exercitation ullamco
12+
/// - returns: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
13+
/// eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
14+
/// veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
15+
/// commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit
16+
/// esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
17+
/// cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
18+
/// est laborum.
19+
///
20+
/// - warning: reprehenderit in voluptate velit esse
21+
22+
// swift-mode:test:case-input-begin
23+
24+
/// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
25+
/// dolore magna aliqua. Ut
26+
///
27+
/// - parameter foo: enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
28+
/// in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
29+
/// - parameter bar: enim ad minim veniam, quis nostrud exercitation ullamco
30+
/// - returns: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
31+
///
32+
/// - warning: reprehenderit in voluptate velit esse
33+
34+
// swift-mode:test:case-end (Slash style doc comment)
35+
36+
// swift-mode:test:eval (setq swift-mode:comment-fill-column 96)
37+
// swift-mode:test:case-begin (Slash line comment)
38+
39+
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut
40+
// labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
41+
42+
// swift-mode:test:case-input-begin
43+
44+
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
45+
// aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
46+
47+
// swift-mode:test:case-end
48+
49+
// swift-mode:test:eval (setq swift-mode:comment-fill-column 80)
50+
// swift-mode:test:case-begin (Star style doc comment)
51+
52+
/**
53+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
54+
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
55+
nostrud exercitation ullamco
56+
57+
- parameter foo: laboris nisi ut
58+
- parameter bar: aliquip ex ea commodo consequat. Duis aute irure dolor in
59+
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
60+
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
61+
officia deserunt mollit anim id est laborum.
62+
- parameter baz: enim ad minim veniam, quis nostrud exercitation ullamco
63+
64+
- returns: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
65+
eiusmod tempor incididunt ut labore et dolore magna aliqua.
66+
67+
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
68+
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
69+
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
70+
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim
71+
id est laborum.
72+
73+
- important: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
74+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
75+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
76+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
77+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
78+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
79+
*/
80+
81+
// swift-mode:test:case-input-begin
82+
83+
/**
84+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
85+
enim ad minim veniam, quis nostrud exercitation ullamco
86+
87+
- parameter foo: laboris nisi ut
88+
- parameter bar: aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
89+
in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
90+
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim
91+
id est laborum.
92+
- parameter baz: enim ad minim veniam, quis nostrud exercitation ullamco
93+
94+
- returns: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore
95+
et dolore magna aliqua.
96+
97+
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
98+
99+
- important: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
100+
*/
101+
102+
// swift-mode:test:case-end (Star style doc comment)
103+
104+
// swift-mode:test:case-begin (Star comment, single line)
105+
106+
/**
107+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
108+
incididunt ut
109+
*/
110+
111+
// swift-mode:test:case-input-begin
112+
113+
/** Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut */
114+
115+
// swift-mode:test:case-end (Star comment, single line)

0 commit comments

Comments
 (0)