Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom fill-paragraph function to respect docstrings #58

Merged
merged 1 commit into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- [#42]: Fix font locking of definitions with metadata
- [#42]: Fix indentation of definitions with metadata
- Fix semantic indentation of quoted functions
- Add custom `fill-paragraph-function` which respects docstrings similar to
`clojure-mode`.

## 0.2.2 (2024-02-16)

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ To make forms inside of `(comment ...)` forms appear as top-level forms for eval
(setq clojure-ts-toplevel-inside-comment-form t)
```

### Fill paragraph

To change the maximal line length used by `M-x prog-fill-reindent-defun` (also
bound to `M-q` by default) to reformat docstrings and comments it's possible to
customize `clojure-ts-fill-paragraph` variable (by default set to the value of
Emacs' `fill-paragraph` value).

Every new line in the docstrings is indented by
`clojure-ts-docstring-fill-prefix-width` number of spaces (set to 2 by default
which matches the `clojure-mode` settings).

## Rationale

[clojure-mode](https://github.com/clojure-emacs/clojure-mode) has served us well
Expand Down
37 changes: 37 additions & 0 deletions clojure-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ itself."
:safe #'booleanp
:package-version '(clojure-ts-mode . "0.2.1"))

(defcustom clojure-ts-docstring-fill-column fill-column
"Value of `fill-column' to use when filling a docstring."
:type 'integer
:safe #'integerp
:package-version '(clojure-ts-mode . "0.2.3"))

(defcustom clojure-ts-docstring-fill-prefix-width 2
"Width of `fill-prefix' when filling a docstring.
The default value conforms with the de facto convention for
Clojure docstrings, aligning the second line with the opening
double quotes on the third column."
:type 'integer
:safe #'integerp
:package-version '(clojure-ts-mode . "0.2.3"))

(defvar clojure-ts--debug nil
"Enables debugging messages, shows current node in mode-line.
Only intended for use at development time.")
Expand Down Expand Up @@ -863,6 +878,27 @@ forms like deftype, defrecord, reify, proxy, etc."
'(semantic fixed)
clojure-ts-indent-style)))))

(defun clojure-ts--docstring-fill-prefix ()
"The prefix string used by `clojure-ts--fill-paragraph'.
It is simply `clojure-ts-docstring-fill-prefix-width' number of spaces."
(make-string clojure-ts-docstring-fill-prefix-width ? ))

(defun clojure-ts--fill-paragraph (&optional justify)
"Like `fill-paragraph', but can handler Clojure docstrings.
If JUSTIFY is non-nil, justify as well as fill the paragraph."
(let ((current-node (treesit-node-at (point))))
(if (clojure-ts--match-docstring nil current-node nil)
(let ((fill-column (or clojure-ts-docstring-fill-column fill-column))
(fill-prefix (clojure-ts--docstring-fill-prefix))
(beg-doc (treesit-node-start current-node))
(end-doc (treesit-node-end current-node)))
(save-restriction
(narrow-to-region beg-doc end-doc)
(fill-paragraph justify)))
(or (fill-comment-paragraph justify)
(fill-paragraph justify)))
t))

(defconst clojure-ts--sexp-nodes
'("#_" ;; transpose-sexp near a discard macro moves it around.
"num_lit" "sym_lit" "kwd_lit" "nil_lit" "bool_lit"
Expand Down Expand Up @@ -963,6 +999,7 @@ See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE."
(keyword string char symbol builtin type)
(constant number quote metadata doc)
(bracket deref function regex tagged-literals)))
(setq-local fill-paragraph-function #'clojure-ts--fill-paragraph)
(when (boundp 'treesit-thing-settings) ;; Emacs 30+
(setq-local treesit-thing-settings clojure-ts--thing-settings)))

Expand Down
62 changes: 62 additions & 0 deletions test/clojure-ts-mode-fill-paragraph-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
;;; clojure-ts-mode-fill-paragraph-test.el --- -*- lexical-binding: t; -*-

;; Copyright (C) 2025 Roman Rudakov

;; This file is not part of GNU Emacs.

;; 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 <https://www.gnu.org/licenses/>.

;;; Commentary:

;; The unit test suite of CLojure TS Mode

;;; Code:

(require 'clojure-ts-mode)
(require 'buttercup)

(describe "clojure-ts--fill-paragraph"
(it "should reformat only the docstring"
(with-clojure-ts-buffer "(ns foo)

(defn hello-world
\"This is a very long docstring that should be reformatted using fill-paragraph function.\"
[]
(pringln \"Hello world\"))"
(goto-char 40)
(prog-fill-reindent-defun)
(expect (buffer-substring-no-properties (point-min) (point-max))
:to-equal
"(ns foo)

(defn hello-world
\"This is a very long docstring that should be reformatted using
fill-paragraph function.\"
[]
(pringln \"Hello world\"))")))

(it "should reformat normal comments properly"
(with-clojure-ts-buffer "(ns foo)

;; This is a very long comment that should be reformatted using fill-paragraph function."
(goto-char 20)
(prog-fill-reindent-defun)
(expect (buffer-substring-no-properties (point-min) (point-max))
:to-equal
"(ns foo)

;; This is a very long comment that should be reformatted using
;; fill-paragraph function."))))

;;; clojure-ts-mode-fill-paragraph-test.el ends here