Skip to content

Commit 95430b3

Browse files
committed
Add support for different indent strategies.
Allow different line indentation strategies to be used. Set the default strategy to `declaration` so that indentation is recomputed at the declaration level once valid syntax is available. This can help recover from incorrect indentation due to previously syntactically invalid states of the buffer.
1 parent e066efc commit 95430b3

File tree

4 files changed

+149
-20
lines changed

4 files changed

+149
-20
lines changed

README.org

+30-7
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,17 @@ levels to match your own style.
213213
Continuation indentation for partial expressions (i.e., terms,
214214
concatenation, etc).
215215

216-
It should be noted that the settings above are defined in terms of
217-
each other, so that by customizing the standard indentation offset
218-
~gpr-ts-mode-indent-offset~, the other indentation offsets will be
219-
adjusted accordingly. In other words, those settings either use the
220-
same value or a value derived from it. Therefore, customizing the
216+
- User Option: gpr-ts-mode-indent-strategy ::
217+
Indentation strategy to use, typically when =RET= or =TAB= are pressed
218+
during editing. Historically, this would drive a line-based
219+
indentation, however more complex indentation strategies can be used
220+
due to the availability of the syntax tree.
221+
222+
It should be noted that the offsets defined above are described in
223+
terms of each other, so that by customizing the standard indentation
224+
offset ~gpr-ts-mode-indent-offset~, the other indentation offsets will
225+
be adjusted accordingly. In other words, those settings either use
226+
the same value or a value derived from it. Therefore, customizing the
221227
base value will have an affect on the remaining values as well. If
222228
this is not the desired outcome, the other offsets should be
223229
customized as well.
@@ -227,8 +233,8 @@ indentation will align stacked items under each other. This applies
227233
to lists, function call parameters, import lists, discrete choice
228234
lists in case constructions, etc.
229235

230-
Indentation rules used in gpr-ts-mode (as in all tree-sitter based
231-
modes) are based on the syntax of the language. When what currently
236+
Indentation rules used in ~gpr-ts-mode~, as in all tree-sitter based
237+
modes, are based on the syntax of the language. When what currently
232238
exists in the buffer is not syntactically correct, the in-memory
233239
syntax tree will contain errors, since the buffer doesn't adhere to
234240
the grammar rules of the language (i.e., it contains syntax errors).
@@ -248,6 +254,23 @@ declarations, case statements, etc.) are highly recommended. Not only
248254
can this help keep your buffer closer to a syntactically correct
249255
state, you also benefit from the productivity gains as well.
250256

257+
The indentation strategy can help recover from previously incorrect
258+
indentation that has occurred while the buffer was in a syntactically
259+
invalid state. The default ~declaration~ setting for
260+
=gpr-ts-mode-indent-strategy= will re-indent at the declaration level
261+
once the declaration is in a syntactically valid state. When invalid
262+
syntax exists within a declaration, this strategy reverts back to
263+
"best effort" line-based indentation. Once the syntax becomes valid,
264+
indentation will be applied at the declaration level.
265+
266+
If none of the existing indentation strategies are sufficient, a
267+
custom strategy can be created and used. In order to create a
268+
strategy, a new strategy symbol should be specified in
269+
=gpr-ts-mode-indent-strategy=, and an implementation of
270+
=gpr-ts-mode-indent= should be created, specializing on the new strategy
271+
symbol name. Refer to existing instances of this function to
272+
understand how current strategy functions are implemented.
273+
251274
* Navigation
252275

253276
The following specialized navigation functions exist and are applied

doc/gpr-ts-mode.texi

+31-7
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,18 @@ Continuation indentation for partial expressions (i.e., terms,
297297
concatenation, etc).
298298
@end defopt
299299

300-
It should be noted that the settings above are defined in terms of
301-
each other, so that by customizing the standard indentation offset
302-
@code{gpr-ts-mode-indent-offset}, the other indentation offsets will be
303-
adjusted accordingly. In other words, those settings either use the
304-
same value or a value derived from it. Therefore, customizing the
300+
@defopt gpr-ts-mode-indent-strategy
301+
Indentation strategy to use, typically when @samp{RET} or @samp{TAB} are pressed
302+
during editing. Historically, this would drive a line-based
303+
indentation, however more complex indentation strategies can be used
304+
due to the availability of the syntax tree.
305+
@end defopt
306+
307+
It should be noted that the offsets defined above are described in
308+
terms of each other, so that by customizing the standard indentation
309+
offset @code{gpr-ts-mode-indent-offset}, the other indentation offsets will
310+
be adjusted accordingly. In other words, those settings either use
311+
the same value or a value derived from it. Therefore, customizing the
305312
base value will have an affect on the remaining values as well. If
306313
this is not the desired outcome, the other offsets should be
307314
customized as well.
@@ -311,8 +318,8 @@ indentation will align stacked items under each other. This applies
311318
to lists, function call parameters, import lists, discrete choice
312319
lists in case constructions, etc.
313320

314-
Indentation rules used in gpr-ts-mode (as in all tree-sitter based
315-
modes) are based on the syntax of the language. When what currently
321+
Indentation rules used in @code{gpr-ts-mode}, as in all tree-sitter based
322+
modes, are based on the syntax of the language. When what currently
316323
exists in the buffer is not syntactically correct, the in-memory
317324
syntax tree will contain errors, since the buffer doesn't adhere to
318325
the grammar rules of the language (i.e., it contains syntax errors).
@@ -332,6 +339,23 @@ declarations, case statements, etc.) are highly recommended. Not only
332339
can this help keep your buffer closer to a syntactically correct
333340
state, you also benefit from the productivity gains as well.
334341

342+
The indentation strategy can help recover from previously incorrect
343+
indentation that has occurred while the buffer was in a syntactically
344+
invalid state. The default @code{declaration} setting for
345+
@samp{gpr-ts-mode-indent-strategy} will re-indent at the declaration level
346+
once the declaration is in a syntactically valid state. When invalid
347+
syntax exists within a declaration, this strategy reverts back to
348+
``best effort'' line-based indentation. Once the syntax becomes valid,
349+
indentation will be applied at the declaration level.
350+
351+
If none of the existing indentation strategies are sufficient, a
352+
custom strategy can be created and used. In order to create a
353+
strategy, a new strategy symbol should be specified in
354+
@samp{gpr-ts-mode-indent-strategy}, and an implementation of
355+
@samp{gpr-ts-mode-indent} should be created, specializing on the new strategy
356+
symbol name. Refer to existing instances of this function to
357+
understand how current strategy functions are implemented.
358+
335359
@node Navigation
336360
@chapter Navigation
337361

gpr-ts-mode.el

+87-6
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@
125125
:package-version "0.5.0")
126126
;;;###autoload(put 'gpr-ts-mode-indent-exp-item-offset 'safe-local-variable #'integerp)
127127

128+
(defcustom gpr-ts-mode-indent-strategy 'declaration
129+
"Indentation strategy to utilize."
130+
:type '(choice :tag "Indentation Strategy"
131+
(const :tag "Declaration" declaration)
132+
(const :tag "Line" line))
133+
:group 'gpr-ts
134+
:link '(custom-manual :tag "Indentation" "(gpr-ts-mode)Indentation")
135+
:package-version "0.6.0")
136+
128137
(defcustom gpr-ts-mode-grammar "https://github.com/brownts/tree-sitter-gpr"
129138
"Configuration for downloading and installing the tree-sitter language grammar.
130139
@@ -314,6 +323,50 @@ Return nil if no child of that type is found."
314323
(lambda (n)
315324
(equal (treesit-node-type n) type)))))
316325

326+
(cl-defgeneric gpr-ts-mode-indent (strategy)
327+
"Indent according to STRATEGY."
328+
(error "Unknown indentation strategy: %s" strategy))
329+
330+
(cl-defmethod gpr-ts-mode-indent ((_strategy (eql line)))
331+
"Indent according to line STRATEGY."
332+
(treesit-indent))
333+
334+
(cl-defmethod gpr-ts-mode-indent ((_strategy (eql declaration)))
335+
"Indent according to declaration STRATEGY."
336+
(let ((initial-point-column (current-column))
337+
(initial-indentation-column (current-indentation))
338+
(region
339+
(save-excursion
340+
(forward-line 0)
341+
(skip-chars-forward " \t")
342+
(unless (looking-at (rx (* whitespace) eol) t)
343+
(let* ((node (treesit-node-at (point)))
344+
(root (treesit-buffer-root-node))
345+
(candidate
346+
(treesit-parent-until
347+
node
348+
(lambda (node)
349+
(or (treesit-node-eq node root)
350+
(string-equal (treesit-node-type node) "ERROR")
351+
(gpr-ts-mode--declaration-p node)))
352+
'include-node)))
353+
(when (and (gpr-ts-mode--declaration-p candidate)
354+
(not (treesit-search-subtree candidate "ERROR")))
355+
(cons (treesit-node-start candidate)
356+
(treesit-node-end candidate))))))))
357+
(if region
358+
(progn
359+
(treesit-indent-region (car region) (cdr region))
360+
;; Move point if it was in the indentation.
361+
(when (<= initial-point-column
362+
initial-indentation-column)
363+
(back-to-indentation)))
364+
(treesit-indent))))
365+
366+
(defun gpr-ts-mode--indent-line ()
367+
"Indent according to `gpr-ts-mode-indent-strategy'."
368+
(gpr-ts-mode-indent gpr-ts-mode-indent-strategy))
369+
317370
(defvar gpr-ts-mode--keywords
318371
'("abstract" "all" "at"
319372
"case"
@@ -463,7 +516,7 @@ Return nil if no child of that type is found."
463516
(gpr-ts-mode--sibling-exists-p "("))
464517
(gpr-ts-mode--anchor-first-sibling-matching "(")
465518
0)
466-
;; trival recovery for syntax error or unexpected broken line
519+
;; trivial recovery for syntax error or unexpected broken line
467520
(catch-all prev-line 0)))
468521
"Tree-sitter indent rules for `gpr-ts-mode'.")
469522

@@ -620,6 +673,16 @@ Return nil if NODE is not an attribute declaration node."
620673
(when (gpr-ts-mode--attribute-declaration-p node)
621674
(gpr-ts-mode--tree-text node '("comment") "for" "use")))
622675

676+
(defun gpr-ts-mode--case_construction-p (node)
677+
"Determine if NODE is a case construction.
678+
Return non-nil to indicate it is."
679+
(string-equal (treesit-node-type node) "case_construction"))
680+
681+
(defun gpr-ts-mode--empty-declaration-p (node)
682+
"Determine if NODE is an empty declaration.
683+
Return non-nil to indicate it is."
684+
(string-equal (treesit-node-type node) "empty_declaration"))
685+
623686
(defun gpr-ts-mode--package-declaration-p (node)
624687
"Determine if NODE is a package declaration.
625688
Return non-nil to indicate it is."
@@ -691,12 +754,27 @@ Return non-nil to indicate that it is."
691754
(and (gpr-ts-mode--variable-declaration-p node)
692755
(not (gpr-ts-mode--typed-variable-declaration-p node))))
693756

757+
(defun gpr-ts-mode--with-declaration-p (node)
758+
"Determine if NODE is a with declaration.
759+
Return non-nil to indicate that it is."
760+
(string-equal (treesit-node-type node) "with_declaration"))
761+
762+
(defun gpr-ts-mode--declaration-p (node)
763+
"Determine if NODE is a declaration.
764+
Return non-nil to indicate that it is."
765+
(or (gpr-ts-mode--attribute-declaration-p node)
766+
(gpr-ts-mode--case_construction-p node)
767+
(gpr-ts-mode--empty-declaration-p node)
768+
(gpr-ts-mode--package-declaration-p node)
769+
(gpr-ts-mode--project-declaration-p node)
770+
(gpr-ts-mode--type-declaration-p node)
771+
(gpr-ts-mode--variable-declaration-p node)
772+
(gpr-ts-mode--with-declaration-p node)))
773+
694774
(defun gpr-ts-mode--with-clause-name-p (node)
695775
"Determine if NODE is a string within a with clause."
696-
(and (string-equal (treesit-node-type (treesit-node-parent node))
697-
"with_declaration")
698-
(string-equal (treesit-node-type node)
699-
"string_literal")))
776+
(and (gpr-ts-mode--with-declaration-p (treesit-node-parent node))
777+
(string-equal (treesit-node-type node) "string_literal")))
700778

701779
(defun gpr-ts-mode--imenu-index (tree item-p branch-p item-name-fn branch-name-fn)
702780
"Return Imenu index for a specific item category given TREE.
@@ -939,7 +1017,10 @@ the name of the branch given the branch node."
9391017
(attribute function number operator package variable)
9401018
(bracket delimiter error)))
9411019

942-
(treesit-major-mode-setup))
1020+
(treesit-major-mode-setup)
1021+
1022+
;; Override `treesit-major-mode-setup' settings.
1023+
(setq-local indent-line-function #'gpr-ts-mode--indent-line))
9431024

9441025
;;;###autoload
9451026
(progn

test/gpr-ts-mode-tests.el

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
If EXPECT-ERROR is non-nil, then check for an error in the parse tree,
3434
otherwise check that there is no error in the parse tree."
3535
(gpr-ts-mode)
36+
(setq-local gpr-ts-mode-indent-strategy 'line)
3637
(if expect-error
3738
(should (treesit-search-subtree
3839
(treesit-buffer-root-node) "ERROR"))

0 commit comments

Comments
 (0)