diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index a4aa61905e4..2fe1f4986ee 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -285,6 +285,40 @@ ruby-method-params-indent :safe (lambda (val) (or (memq val '(t nil)) (numberp val))) :version "29.1") +(defcustom ruby-block-indent t + "Non-nil to align the body of a block to the statement's start. + +The body and the closer will be aligned to the column where the +statement containing the block starts. Example: + + foo.bar + .each do + baz + end + +If nil, it will be aligned instead to the beginning of the line +containing the block's opener: + + foo.bar + .each do + baz + end + +Only has effect when `ruby-use-smie' is t." + :type 'boolean + :safe 'booleanp) + +(defcustom ruby-indent-simplified nil + "Non-nil to use the indentation logic with less nesting. + +The result is that statements with continuations will have just 1 +level of indentation nesting. Unless paren grouping or +hash/array literals are involved. + +Only has effect when `ruby-use-smie' is t." + :type 'boolean + :safe 'booleanp) + (defcustom ruby-deep-arglist t "Deep indent lists in parenthesis when non-nil. Also ignores spaces after parenthesis when `space'. @@ -416,6 +450,7 @@ ruby-smie-grammar '((right "=") (right "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^=" "<<=" ">>=" "&&=" "||=") + (right "?") (nonassoc ".." "...") (left "&&" "||") (nonassoc "<=>") @@ -608,10 +643,10 @@ ruby-smie--backward-token "def=") (t tok))))))) -(defun ruby-smie--indent-to-stmt () +(defun ruby-smie--indent-to-stmt (&optional offset) (save-excursion (smie-backward-sexp ";") - (cons 'column (smie-indent-virtual)))) + (cons 'column (+ (smie-indent-virtual) (or offset 0))))) (defun ruby-smie--indent-to-stmt-p (keyword) (or (eq t ruby-align-to-stmt-keywords) @@ -642,7 +677,9 @@ ruby-smie-rules (forward-comment -1) (not (eq (preceding-char) ?:)))) ;; Curly block opener. - (ruby-smie--indent-to-stmt)) + (if ruby-block-indent + (ruby-smie--indent-to-stmt) + (cons 'column (current-indentation)))) ((smie-rule-hanging-p) ;; Treat purely syntactic block-constructs as being part of their parent, ;; when the opening token is hanging and the parent is not an @@ -678,12 +715,16 @@ ruby-smie-rules (cons 'column (current-column))))) ('(:before . " @ ") (if (or (eq ruby-method-params-indent t) + (eq ruby-indent-simplified nil) (not (smie-rule-parent-p "def" "def="))) (save-excursion (skip-chars-forward " \t") (cons 'column (current-column))) (smie-rule-parent (or ruby-method-params-indent 0)))) - ('(:before . "do") (ruby-smie--indent-to-stmt)) + ('(:before . "do") + (if ruby-block-indent + (ruby-smie--indent-to-stmt) + (cons 'column (current-indentation)))) ('(:before . ".") (if (smie-rule-sibling-p) (when ruby-align-chained-calls @@ -696,8 +737,10 @@ ruby-smie-rules (not (smie-rule-bolp))))) (cons 'column (current-column))) (smie-backward-sexp ".") - (cons 'column (+ (current-column) - ruby-indent-level)))) + (if ruby-indent-simplified + (ruby-smie--indent-to-stmt ruby-indent-level) + (cons 'column (+ (current-column) + ruby-indent-level))))) (`(:before . ,(or "else" "then" "elsif" "rescue" "ensure")) (smie-rule-parent)) (`(:before . ,(or "when" "in")) @@ -710,14 +753,16 @@ ruby-smie-rules "<<=" ">>=" "&&=" "||=" "and" "or")) (and (smie-rule-parent-p ";" nil) (smie-indent--hanging-p) - ruby-indent-level)) + (if ruby-indent-simplified + (ruby-smie--indent-to-stmt ruby-indent-level) + ruby-indent-level))) (`(:before . "=") (save-excursion (and (smie-rule-parent-p " @ ") (goto-char (nth 1 (smie-indent--parent))) (smie-rule-prev-p "def=") (cons 'column (+ (current-column) ruby-indent-level -3))))) - (`(:after . ,(or "?" ":")) ruby-indent-level) + (`(:after . ,(or "?" ":")) (unless ruby-indent-simplified ruby-indent-level)) (`(:before . ,(guard (memq (intern-soft token) ruby-alignable-keywords))) (when (not (ruby--at-indentation-p)) (if (ruby-smie--indent-to-stmt-p token) @@ -725,7 +770,21 @@ ruby-smie-rules (cons 'column (current-column))))) ('(:before . "iuwu-mod") (smie-rule-parent ruby-indent-level)) - )) + (`(:before . ",") + (and ruby-indent-simplified + (smie-rule-parent-p " @ ") + (ruby-smie--indent-to-stmt ruby-indent-level))) + (`(:before . ,_) + (when (and ruby-indent-simplified + (not (or (member token '(",")) + (smie-rule-prev-p ";")))) + (let* ((stmt-beg (save-excursion + (smie-backward-sexp ";") + (point))) + (nls (1- (count-lines stmt-beg (point))))) + (when (smie-indent--hanging-p) + (cl-incf nls)) + (ruby-smie--indent-to-stmt (if (> nls 0) ruby-indent-level 0))))))) (defun ruby--at-indentation-p (&optional point) (save-excursion diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb new file mode 100644 index 00000000000..03acdda6fb0 --- /dev/null +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb @@ -0,0 +1,15 @@ +foo + .asdasd + .proc do |**args| + p(**args) + end + +foo + .asdasd + .proc { |**args| + p(**args) + } + +# Local Variables: +# ruby-block-indent: nil +# End: diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-indent-simplified.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-indent-simplified.rb new file mode 100644 index 00000000000..aaecc7f7422 --- /dev/null +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-indent-simplified.rb @@ -0,0 +1,61 @@ +4 + + 5 + + 6 + + 7 + +foo = obj.bar { |m| tee(m) } + + obj.qux { |m| hum(m) } + +foo(a, + b) + +foo. + bar + .baz + +method arg1, # bug#15594 + method2 arg2, + arg3 + +zzz = method (a + b), + c, :d => :e, + f: g + +qux = foo.fee ? + bar : + tee + +foo2 = + subject. + update( + 2 + ) + +bar.foo do + bar +end + +bar.foo(tee) do + bar +end + +bar.foo(tee) { + bar +} + +# Endless methods. +class Bar + def foo(abc) = bar + + baz +end + +x.foo do + foo +end.bar do + bar +end + +# Local Variables: +# ruby-method-params-indent: t +# ruby-indent-simplified: t +# End: diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby.rb b/test/lisp/progmodes/ruby-mode-resources/ruby.rb index 6a69d9db78a..7f2a665f2f4 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby.rb @@ -226,6 +226,7 @@ def begin foo. bar + .baz # https://github.com/rails/rails/blob/17f5d8e062909f1fcae25351834d8e89967b645e/activesupport/lib/active_support/time_with_zone.rb#L206 foo # comment intended to confuse the tokenizer @@ -380,6 +381,18 @@ def bar i + 1 end +m1 = foo + .asdasd + .proc do |**args| + p(**args) +end + +m2 = foo + .asdasd + .proc { |**args| + p(**args) +} + bar.foo do bar end @@ -398,6 +411,12 @@ def bar end end +x.foo do + foo +end.bar do + bar +end + foo | bar @@ -541,4 +560,6 @@ def baz.full_name = "#{bar} 3" # Local Variables: # ruby-method-params-indent: t +# ruby-indent-simplified: nil +# ruby-block-indent: t # End: diff --git a/test/lisp/progmodes/ruby-mode-tests.el b/test/lisp/progmodes/ruby-mode-tests.el index 560f780285a..3b0ec8a0324 100644 --- a/test/lisp/progmodes/ruby-mode-tests.el +++ b/test/lisp/progmodes/ruby-mode-tests.el @@ -957,6 +957,8 @@ ruby-deftest-indent (ruby-deftest-indent "ruby.rb") (ruby-deftest-indent "ruby-method-params-indent.rb") +(ruby-deftest-indent "ruby-indent-simplified.rb") +(ruby-deftest-indent "ruby-block-indent.rb") (ert-deftest ruby--test-chained-indentation () (with-temp-buffer