branch: elpa/go-mode
commit 6b67088ef7bc6204403109e76862d88c41c267c2
Author: Muir Manders <[email protected]>
Commit: Peter Sanford <[email protected]>
Support trailing commas as dangling operators.
Sometimes a trailing comma behaves like a dangling
operator (i.e. indents following line). For example:
// This comma is a dangling operator since it indents the "2".
return 1,
2
// This comma isn't a dangling operator since it doesn't indent
// the "2" (the "2" is indented due to the paren block, not the
// dangling comma).
foo(
1,
2,
)
// Similarly, not a dangling operator because the indent is due
// to the curlies.
Foo{
A: 1,
B: 2,
}
We now have special logic to sometimes treat a trailing comma as a
dangling operator. Specifically, if we are inside parens, a composite
literal or a struct definition, do not treat the comma as a dangling
operator.
Fixes #42
---
go-mode.el | 122 +++++++++++++++------
.../indentation_tests/dangling_operator.go | 10 +-
.../testdata/indentation_tests/multiline_struct.go | 52 ++++++---
3 files changed, 133 insertions(+), 51 deletions(-)
diff --git a/go-mode.el b/go-mode.el
index 5340b7c..6ccddde 100644
--- a/go-mode.el
+++ b/go-mode.el
@@ -58,14 +58,18 @@ function."
(progn (forward-visible-line arg) (point))))))
(defun go-goto-opening-parenthesis (&optional _legacy-unused)
- "Move up one level of parentheses."
+ "Move up one level of parentheses.
+
+Return non-nil if there was a paren to move up to."
;; The old implementation of go-goto-opening-parenthesis had an
;; optional argument to speed up the function. It didn't change the
;; function's outcome.
;; Silently fail if there's no matching opening parenthesis.
(condition-case nil
- (backward-up-list)
+ (progn
+ (backward-up-list)
+ t)
(scan-error nil)))
@@ -544,8 +548,11 @@ The returned value is the beginning of the line with the
dangling operator."
(save-excursion
(beginning-of-line)
(go--backward-irrelevant t)
- (if (looking-back go-dangling-operators-regexp
- (- (point) go--max-dangling-operator-length))
+ (if (or
+ (looking-back go-dangling-operators-regexp
+ (- (point) go--max-dangling-operator-length))
+ ;; treat comma as dangling operator in certain cases
+ (and (eq (char-before) ?,) (go--commas-indent-p)))
(setq val (line-beginning-position))
(setq val nil))
@@ -593,19 +600,9 @@ current line will be returned."
"Return non-nil if current dangling line indents the following line."
(save-excursion
(let ((line-begin (line-beginning-position))
- (start-paren-level (go-paren-level)))
-
- (go-goto-opening-parenthesis)
-
- (let ((in-parens (and
- ;; opening paren-like character is actually a paren
- (eq (char-after) ?\()
- ;; point is before the closing paren
- (< (go-paren-level) start-paren-level)
- ;; still on starting line
- (>= (point) line-begin))))
+ (open-paren (go--open-paren-position)))
- (if (not in-parens)
+ (if (not (and open-paren (>= open-paren line-begin)))
;; if line doesn't open a paren, check if we are a dangling line
under
;; a dangling assignment with nothing on RHS of "="
;;
@@ -614,29 +611,86 @@ current line will be returned."
;; baz ||
;; qux
(progn
- (goto-char line-begin)
(let ((prev-line (go-previous-line-has-dangling-op-p)))
- (when prev-line
+ (goto-char line-begin)
+ (when (and
+ prev-line
+ (not (looking-at ".*,[[:space:]]*$"))) ;; doesn't apply
to dangling commas
(goto-char prev-line)
(and
(not (go-previous-line-has-dangling-op-p))
(looking-at ".*=[[:space:]]*$")))))
- (or
- ;; previous line is dangling and opens indent
- (let ((prev-line (go-previous-line-has-dangling-op-p)))
- (when prev-line
- (save-excursion
- (goto-char prev-line)
- (end-of-line)
- (go--dangling-line-opens-indent-p))))
-
- ;; or paren is only preceded by identifier or other parens
- (string-match-p "^[[:space:]]*[[:word:][:multibyte:]]*(*$"
(buffer-substring line-begin (point)))
-
- ;; or a preceding paren on this line opens an indent
- (and
- (> (point) line-begin)
- (progn (backward-char) (go--dangling-line-opens-indent-p)))))))))
+
+ (goto-char open-paren)
+
+ (or
+ ;; previous line is dangling and opens indent
+ (let ((prev-line (go-previous-line-has-dangling-op-p)))
+ (when prev-line
+ (save-excursion
+ (goto-char prev-line)
+ (end-of-line)
+ (go--dangling-line-opens-indent-p))))
+
+ ;; or paren is only preceded by identifier or other parens
+ (string-match-p "^[[:space:]]*[[:word:][:multibyte:]]*(*$"
(buffer-substring line-begin (point)))
+
+ ;; or a preceding paren on this line opens an indent
+ (and
+ (> (point) line-begin)
+ (progn (backward-char) (go--dangling-line-opens-indent-p))))))))
+
+(defun go--commas-indent-p ()
+ "Return non-nil if in a context where dangling commas indent next line."
+ (not (or
+ (go--open-paren-position)
+ (go--in-composite-literal-p)
+ (go--in-struct-definition-p))))
+
+(defun go--in-struct-definition-p ()
+ "Return non-nil if inside a struct definition."
+ (save-excursion
+ (and
+ ;; inside curlies
+ (go-goto-opening-parenthesis)
+ (eq (char-after) ?{)
+
+ ;; "struct" appears before opening curly
+ (backward-word)
+ (looking-at "struct[[:space:]]"))))
+
+(defun go--in-composite-literal-p ()
+ "Return non-nil if point is in a composite literal."
+ (save-excursion
+ (and
+ (go-goto-opening-parenthesis)
+
+ ;; opening paren-like character is curly
+ (eq (char-after) ?{)
+
+ (or
+ ;; preceded by non space (e.g. "Foo|{")
+ (not (looking-back "[[:space:]]" (1- (point))))
+
+ ;; or curly itself is in a composite literal (e.g. "Foo{|{")
+ (go--in-composite-literal-p)))))
+
+(defun go--open-paren-position ()
+ "Return non-nil if point is between '(' and ')'.
+
+The return value is the position of the opening paren."
+ (save-excursion
+ (let ((start-paren-level (go-paren-level)))
+ (and
+ (go-goto-opening-parenthesis)
+
+ ;; opening paren-like character is actually a paren
+ (eq (char-after) ?\()
+
+ ;; point is before the closing paren
+ (< (go-paren-level) start-paren-level)
+
+ (point)))))
(defun go-indentation-at-point ()
(save-excursion
diff --git a/test/testdata/indentation_tests/dangling_operator.go
b/test/testdata/indentation_tests/dangling_operator.go
index 281ea53..78c0bb2 100644
--- a/test/testdata/indentation_tests/dangling_operator.go
+++ b/test/testdata/indentation_tests/dangling_operator.go
@@ -40,5 +40,13 @@ func init() {
true == false ||
false == false
- return
+ a, b :=
+ 1,
+ 2
+
+ a,
+ b := 1, 2
+
+ return 123,
+ 456
}
diff --git a/test/testdata/indentation_tests/multiline_struct.go
b/test/testdata/indentation_tests/multiline_struct.go
index 500dea6..26a5f6f 100644
--- a/test/testdata/indentation_tests/multiline_struct.go
+++ b/test/testdata/indentation_tests/multiline_struct.go
@@ -1,19 +1,39 @@
-testCase{char59 + "a." + char63 + "." + char63 + "." +
- char63 + ".com",
- false}
+func _() {
+ testCase{char59 + "a." + char63 + "." + char63 + "." +
+ char63 + ".com",
+ false}
-ifi := Interface{
- Index: int(index),
- MTU: int(row.Mtu),
- Name: name,
- HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]),
- Flags: flags}
-ift = append(ift, ifi)
+ foo := Bar{
+ {
+ Index: int(index),
+ MTU: int(row.Mtu),
+ },
+ {
+ Index: int(index),
+ MTU: int(row.Mtu),
+ },
+ }
-{"230-Anonymous access granted, restrictions apply\n" +
- "Read the file README.txt,\n" +
- "230 please",
- 23,
- 230,
- "Anonymous access granted, restrictions apply\nRead the file
README.txt,\n please",
+ ifi := Interface{
+ Index: int(index),
+ MTU: int(row.Mtu),
+ Name: name,
+ HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]),
+ Flags: flags}
+ ift = append(ift, ifi)
+
+ Interface{
+ {"230-Anonymous access granted, restrictions apply\n" +
+ "Read the file README.txt,\n" +
+ "230 please",
+ 23,
+ 230,
+ "Anonymous access granted, restrictions apply\nRead the
file README.txt,\n please"},
+ }
+
+ var _ struct {
+ foo, // super
+ bar, // important
+ baz int //comments
+ }
}