* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements @ 2023-02-08 15:27 Evgeni Kolev 2023-02-08 16:30 ` Eli Zaretskii 2023-02-08 19:20 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 2 replies; 14+ messages in thread From: Evgeni Kolev @ 2023-02-08 15:27 UTC (permalink / raw) To: 61368; +Cc: Randy Taylor, Theodor Thornhill [-- Attachment #1: Type: text/plain, Size: 2804 bytes --] [CC Randy, author of go-ts-mode, Theo, who asked to keep the patches coming :)] This is a patch which adds support to go-ts-mode to insert context-aware return statements for the current function. The return statements are pre-filled with the Go zero values of the output parameters. For example, all these return statements are inserted by M-x go-ts-mode-insert-return: ``` func f() (x, y, z int) { return 0, 0, 0 } func exotic() (bool, int, myStruct, *int, chan bool, error) { return false, 0, myStruct{}, nil, nil, err } func closure(numbers []int) { sort.Slice(numbers, func(i, j int) bool { return false }) } ``` The command go-ts-mode-insert-return is experimentally bound to a key C-c C-r ("r" as return statement). It's a user error to run C-c C-r outside of a function body. Customization: when the output params contain "error" the pre-filled text is "err". A customization variable is added to allow the user to pick a different value, for example they might pick `fmt.Errorf("...: %w", err)` which will result in error wrapping: ``` func f() (int, error) { return 0, fmt.Errorf("...: %w", err) } ``` Personally, I'll use yasnippet instead of C-c C-r. Something like this: ``` # -*- mode: snippet -*- # name: ret # key: ret # type: command # -- (let ((go-ts-mode-error-zero-value "${1:fmt.Errorf(\"${2:format}: %w\", err)}")) (yas-expand-snippet (go-ts-mode-return))) ``` I'm open to suggestions about how to best expose this functionality to the user. I think a snippet makes the most sense, but there's no standard way for major modes to expose snippets as far as I'm aware. It's possible to tweak C-c C-r to call (yas-expand-snippet) if available, otherwise call (insert). In general, I don't feel strong about the C-c C-r key binding, but I didn't have a better idea. Known bug: this example does not work, fails with error "Unknown Go type "[int]int"": ``` func notOk() (int, map[int]int) { } ``` The root cause is an issue with the tree-sitter-go parser. The bug has been reported here https://github.com/tree-sitter/tree-sitter-go/issues/107 Known limitation: unfamiliar types are assumed to be structs. This assumption does not work for aliased types. For example: ``` type MyInt = int func alias() MyInt { return MyInt{} } ``` Above the correct return statement is "return MyInt(0)". My assumption is that type aliases of primitive types are rare in Go codebases. In these rare cases, the user is expected to replace "MyInt{}" with "MyInt(0)", or not use C-c C-r at all. Inspired by: this emacs conf talk "08:21 Intelligent templates" https://emacsconf.org/2022/talks/treesitter/ Feedback is more than welcome! In particular: - how to best expose the functionality - key binding, snippet (how exactly?), something else? Evgeni [-- Attachment #2: 0001-Extend-go-ts-mode-with-support-for-pre-filling-retur.patch --] [-- Type: application/x-patch, Size: 6378 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-08 15:27 bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements Evgeni Kolev @ 2023-02-08 16:30 ` Eli Zaretskii 2023-02-09 11:47 ` Evgeni Kolev 2023-02-08 19:20 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 1 reply; 14+ messages in thread From: Eli Zaretskii @ 2023-02-08 16:30 UTC (permalink / raw) To: Evgeni Kolev; +Cc: dev, theo, 61368 > Cc: Randy Taylor <dev@rjt.dev>, Theodor Thornhill <theo@thornhill.no> > From: Evgeni Kolev <evgenysw@gmail.com> > Date: Wed, 8 Feb 2023 17:27:58 +0200 > > This is a patch which adds support to go-ts-mode to insert > context-aware return statements for the current function. The return > statements are pre-filled with the Go zero values of the output > parameters. > > For example, all these return statements are inserted by M-x > go-ts-mode-insert-return: > > ``` > func f() (x, y, z int) { > return 0, 0, 0 > } > > func exotic() (bool, int, myStruct, *int, chan bool, error) { > return false, 0, myStruct{}, nil, nil, err > } > > func closure(numbers []int) { > sort.Slice(numbers, func(i, j int) bool { > return false > }) > } > ``` > > The command go-ts-mode-insert-return is experimentally bound to a key > C-c C-r ("r" as return statement). It's a user error to run C-c C-r > outside of a function body. Instead of a command bound to a special key sequences, would it perhaps make more sense to add some kind of "electric" behavior to "normal" keys? Like perhaps typing RET after inserting "return" would add the "context-aware return statement"? Then the user could turn on the electric behavior with some minor mode, and get this behavior, while avoiding the need to dedicate a key sequence. WDYT? ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-08 16:30 ` Eli Zaretskii @ 2023-02-09 11:47 ` Evgeni Kolev 2023-02-09 13:39 ` Eli Zaretskii 0 siblings, 1 reply; 14+ messages in thread From: Evgeni Kolev @ 2023-02-09 11:47 UTC (permalink / raw) To: Eli Zaretskii; +Cc: dev, theo, 61368 On Wed, Feb 8, 2023 at 9:20 PM Theodor Thornhill <theo@thornhill.no> wrote: > > Evgeni Kolev <evgenysw@gmail.com> writes: > > > > > I'm open to suggestions about how to best expose this functionality to > > the user. I think a snippet makes the most sense, but there's no > > standard way for major modes to expose snippets as far as I'm aware. > > It's possible to tweak C-c C-r to call (yas-expand-snippet) if > > available, otherwise call (insert). In general, I don't feel strong > > about the C-c C-r key binding, but I didn't have a better idea. > > > > How about using tempo or skeleton as fallbacks when yasnippet isn't > installed? I've never used either of these packages. Is there a consensus which one is preferred for major modes to use? > > Personally I think this should be a contrib to gopls as a code action so > that others can benefit from it. Is upstreaming it to gopls too hard? > Great suggestion! I did a quick research and this seems to already be implemented in gopls here: https://github.com/golang/tools/blob/master/gopls/internal/lsp/source/completion/statements.go#L179 To use it you have to enable "usePlaceholders", reference: https://github.com/golang/tools/blob/master/gopls/doc/settings.md Do you think it makes sense go-ts-mode to also have its own implementation? I think it does - similar to how both gopls (eglot) and tree sitter provide Imenu candidates. I personally use tree sitter's Imenu (setq eglot-stay-out-of '(imenu)) because I find it advantageous to avoid the RPC call to an external process. Also having a tree sitter implementation is more flexible. For example, I'm planning to have multiple yasnippet snippets for different scenarios (wrap the error, don't wrap, return a new error, etc.). The gopls implementation is ridgid - it only works if there's an "err" variable in scope, and the trigger is one of these three "if e", "if er" and "if err". On Wed, Feb 8, 2023 at 6:30 PM Eli Zaretskii <eliz@gnu.org> wrote: > > The command go-ts-mode-insert-return is experimentally bound to a key > > C-c C-r ("r" as return statement). It's a user error to run C-c C-r > > outside of a function body. > > Instead of a command bound to a special key sequences, would it > perhaps make more sense to add some kind of "electric" behavior to > "normal" keys? Like perhaps typing RET after inserting "return" would > add the "context-aware return statement"? Then the user could turn on > the electric behavior with some minor mode, and get this behavior, > while avoiding the need to dedicate a key sequence. > > WDYT? Makes sense. I am familiar with electric-pair-mode. I see that it adds a hook in post-self-insert-hook. Is your suggestion to do something like this: - add hook in post-self-insert-hook - in the hook check if RET is typed after an "return" - if yes, replace the "return" with the "context-aware return statement", possibly using (yas-expand-snippet) I'm wondering what customization options would make sense for users - changing the electric trigger from "return" RET to "ret" RET for example. Is there another major mode which can be used as an example for such electric behavior? ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-09 11:47 ` Evgeni Kolev @ 2023-02-09 13:39 ` Eli Zaretskii 2023-02-18 11:46 ` Evgeni Kolev 0 siblings, 1 reply; 14+ messages in thread From: Eli Zaretskii @ 2023-02-09 13:39 UTC (permalink / raw) To: Evgeni Kolev; +Cc: dev, theo, 61368 > From: Evgeni Kolev <evgenysw@gmail.com> > Date: Thu, 9 Feb 2023 13:47:01 +0200 > Cc: 61368@debbugs.gnu.org, dev@rjt.dev, theo@thornhill.no > > > Instead of a command bound to a special key sequences, would it > > perhaps make more sense to add some kind of "electric" behavior to > > "normal" keys? Like perhaps typing RET after inserting "return" would > > add the "context-aware return statement"? Then the user could turn on > > the electric behavior with some minor mode, and get this behavior, > > while avoiding the need to dedicate a key sequence. > > > > WDYT? > > Makes sense. I am familiar with electric-pair-mode. I see that it adds > a hook in post-self-insert-hook. Is your suggestion to do something > like this: > - add hook in post-self-insert-hook > - in the hook check if RET is typed after an "return" > - if yes, replace the "return" with the "context-aware return > statement", possibly using (yas-expand-snippet) Yes, something like that. See electric.el for more examples. > I'm wondering what customization options would make sense for users - > changing the electric trigger from "return" RET to "ret" RET for > example. > > Is there another major mode which can be used as an example for such > electric behavior? Electric modes are usually minor modes that let major modes customize them by setting variables. Again, I think you will find examples in electric.el. ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-09 13:39 ` Eli Zaretskii @ 2023-02-18 11:46 ` Evgeni Kolev 2023-02-18 12:14 ` João Távora 0 siblings, 1 reply; 14+ messages in thread From: Evgeni Kolev @ 2023-02-18 11:46 UTC (permalink / raw) To: Eli Zaretskii, João Távora; +Cc: dev, theo, 61368 [-- Attachment #1: Type: text/plain, Size: 1931 bytes --] + João, author of yasnippet. Hi, João, I'd appreciate any suggestions you might have. I'm aiming to extend go-ts-mode with a snippet-like feature. go-ts-mode would be able to insert a "smart / context-aware" return statement. My question is - is there a way for major modes to provide yasnippet snippets? My understanding is the snippets are either created by each user individually, or distributed as a collection of snippets (e.g. yasnippet-snippets). Are there any major modes which provide snippets (I wasn't able to find any in emacs' repo)? On Thu, Feb 9, 2023 at 3:40 PM Eli Zaretskii <eliz@gnu.org> wrote: > > Makes sense. I am familiar with electric-pair-mode. I see that it adds > > a hook in post-self-insert-hook. Is your suggestion to do something > > like this: > > - add hook in post-self-insert-hook > > - in the hook check if RET is typed after an "return" > > - if yes, replace the "return" with the "context-aware return > > statement", possibly using (yas-expand-snippet) > > Yes, something like that. See electric.el for more examples. > > Electric modes are usually minor modes that let major modes customize > them by setting variables. Again, I think you will find examples in > electric.el. The attached patch now adds a (go-ts-electric-return-mode) which, when enabled, replaces "return RET" with a context-aware return statement. I used sh-script.el's (sh-electric-here-document-mode) as an example. I'm concerned this electric mode can turn out to be too aggressive/surprising for the user - "return RET" is a common text to be entered, I'm not sure the user would always want it to be replaced. For example, when debugging I might insert return statements all over the place. Put differently, I would be more comfortable if there's a distinct mechanism to trigger the context-aware return, for example with a trigger key like when a snippet is inserted. [-- Attachment #2: 0001-Extend-go-ts-mode-with-support-for-pre-filling-retur.patch --] [-- Type: application/octet-stream, Size: 6340 bytes --] From 3efaa713ac9fdcb4507e945c2db771f7bb3a1be6 Mon Sep 17 00:00:00 2001 From: Evgeni Kolev <evgenysw@gmail.com> Date: Mon, 6 Feb 2023 09:49:52 +0200 Subject: [PATCH] Extend go-ts-mode with support for pre-filling return statements Extend go-ts-mode to insert context-aware return statement for the current function. The return statement is pre-filled with the zero values of the function's output parameters. For example, this return statement is added by Emacs: ``` func f() (bool, int, myStruct, *int, chan bool, error) { return false, 0, myStruct{}, nil, nil, err } ``` * lisp/progmodes/go-ts-mode.el (go-ts-mode-error-zero-value): New defcustom. (go-ts-mode-insert-return, go-ts-mode-return, go-ts-mode--function-return-types, go-ts-mode--function-at-point, go-ts-mode--types-from-param-declaration, go-ts-mode--zero-value) New functions. --- lisp/progmodes/go-ts-mode.el | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index 2a2783f45f6..0a163753f29 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -46,6 +46,15 @@ go-ts-mode-indent-offset :safe 'integerp :group 'go) +(defcustom go-ts-mode-error-zero-value "err" + "The zero value for type \"error\". +The default is \"err\", assuming there's a varialbe named \"err\" in +the current scope." + :version "30" + :type 'string + :options '("err", "fmt.Errorf(\"format: %w\", err)", "errors.Wrap(err, \"format\")") + :group 'go) + (defvar go-ts-mode--syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?+ "." table) @@ -328,6 +337,100 @@ go-ts-mode--comment-on-previous-line-p (<= (treesit-node-start node) point (treesit-node-end node)) (string-equal "comment" (treesit-node-type node))))) +(define-minor-mode go-ts-electric-return-mode + "Make `return RET' insert a return statement with zero values." + :lighter nil + (if go-ts-electric-return-mode + (add-hook 'post-self-insert-hook #'go-ts-mode--maybe-insert-return nil t) + (remove-hook 'post-self-insert-hook #'go-ts-mode--maybe-insert-return t))) + +(defun go-ts-mode--maybe-insert-return () + "If RET is entered after `return', add zero values." + (when (and (equal (this-command-keys) (kbd "RET")) + (looking-back (rx word-boundary "return" "\n" (* space)) (pos-bol 0))) + (delete-region (point) (pos-eol 0)) ;; delete the line added by RET + (delete-char (- (length "return"))) + (insert (go-ts-mode-return)))) + +(defun go-ts-mode-return() + "Return a return statement with zero values for function at point. +It's an error to call this function outside of a function body." + (interactive) + (let* ((func-node (go-ts-mode--function-at-point)) + (body-node (treesit-node-child-by-field-name func-node "body"))) + + (unless (and body-node (< (treesit-node-start body-node) (point) (treesit-node-end body-node))) + (user-error "Point must be inside a Go function body")) + + (let* ((types (go-ts-mode--function-return-types func-node)) + (zero-values (mapcar 'go-ts-mode--zero-value types)) + (return-statement (format "return %s" (string-join zero-values ", ")))) + (string-trim-right return-statement)))) + +(defun go-ts-mode--function-at-point () + "Return the treesit function node at point. +Function, methods and closures (anonymous functions) are recognized. +If point is not in a recognized function, nil is returned." + (interactive) + (treesit-parent-until + (treesit-node-at (point)) + (lambda (x) + (member (treesit-node-type x) + '("func_literal" "function_declaration" "method_declaration"))) t)) + +(defun go-ts-mode--function-return-types (func-node) + "Return the types of the output variables of FUNC-NODE. +The result is a list which could contain a single element (the Go +function has one return argument), multiple elements (multiple return +arguments), or no elements (no return arguments)." + (let ((result-node (treesit-node-child-by-field-name func-node "result"))) + (pcase (treesit-node-type result-node) + ("parameter_list" ;; multiple return values + (let ((types-nested (treesit-induce-sparse-tree result-node "parameter_declaration" #'go-ts-mode--types-from-param-declaration))) + (apply #'append (mapcar 'car (cdr types-nested))))) + ((pred null) ;; no return value + nil) + (_ ;; single return value + (list (treesit-node-text result-node t)))))) + +(defun go-ts-mode--types-from-param-declaration (node) + "Extract the Go type(s) from NODE. It must have type parameter_declaration. + +A parameter_declaration node can contain single value `(int)', named +single value `(sum int)', multiples values `(int, int)', or multiple +named values `(x, y int)'. + +All the above cases are recognized. Only the types are returned, for +example, with parameter_declaration `(x, y int)', the result is +\(\"int\", \"int\")." + (let ((type (treesit-node-child-by-field-name node "type")) + (count (length (treesit-query-capture node "(parameter_declaration name:(_) @name)" nil nil t)))) + (make-list (if (zerop count) 1 count) (treesit-node-text type t)))) + +(defun go-ts-mode--zero-value (go-type) + "Return the Go zero value for GO-TYPE. +For type \"error\", the zero value is determined by variable +`go-ts-mode-error-zero-value'. + +This function assumes arbitrary types are Go structs, for example for +GO-TYPE \"person\", the retun value is \"person{}\"." + (pcase go-type + ((or "int" "int8" "int16" "int32" "int64") "0") + ((or "uint" "uint8" "uint16" "uint32" "uint64") "0") + ("bool" "false") + ("string" "\"\"") + ((or "uintptr" "byte" "rune") "0") + ((or "float32" "float64") "0.0") + ((or "complex64" "complex128") "complex(0, 0)") + ((pred (string-prefix-p "map")) "nil") + ((pred (string-prefix-p "*")) "nil") + ((pred (string-prefix-p "[]")) "nil") + ((pred (string-prefix-p "chan")) "nil") + ((pred (string-prefix-p "func")) "nil") + ("error" go-ts-mode-error-zero-value) + ((rx bol (1+ (or alphanumeric "_" ".")) eol) (concat go-type "{}")) + (_ (user-error "Unknown Go type \"%s\"" go-type)))) + ;; go.mod support. (defvar go-mod-ts-mode--syntax-table -- 2.39.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-18 11:46 ` Evgeni Kolev @ 2023-02-18 12:14 ` João Távora 2023-02-20 8:54 ` Evgeni Kolev 0 siblings, 1 reply; 14+ messages in thread From: João Távora @ 2023-02-18 12:14 UTC (permalink / raw) To: Evgeni Kolev; +Cc: Randy Taylor, Eli Zaretskii, Theodor Thornhill, 61368 [-- Attachment #1: Type: text/plain, Size: 1465 bytes --] On Sat, Feb 18, 2023, 11:46 Evgeni Kolev <evgenysw@gmail.com> wrote: > + João, author of yasnippet. > > Hi, João, I'd appreciate any suggestions you might have. I'm aiming to > extend go-ts-mode with a snippet-like feature. go-ts-mode would be > able to insert a "smart / context-aware" return statement. My question > is - is there a way for major modes to provide yasnippet snippets? My > understanding is the snippets are either created by each user > individually, or distributed as a collection of snippets (e.g. > yasnippet-snippets). Snippets can be distributed as files, but that's not a very good way as I see it today. One of the reasons might be too leverage the TAB and menu expansion of the yasnippet system. But probably you don't want this, and snippets can also be defined programmatically, albeit not with a beautiful language. Are there any major modes which provide snippets > (I wasn't able to find any in emacs' repo)? > That's because yasnippet is not part of the Emacs repo proper, i.e. the "core". So adding snippets to major modes in the core would introduce into the core a dependency on an non-core ELPA package. Nevertheless, there are ways to conditionally provide functionality based on whether the non-core package is detected. A soft dependency. In my opinion it makes sense for popular non-core packages: company, markdown-mode, yasnippet, etc. Look at Eglot, for example. João João [-- Attachment #2: Type: text/html, Size: 2199 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-18 12:14 ` João Távora @ 2023-02-20 8:54 ` Evgeni Kolev 2023-02-20 12:55 ` Eli Zaretskii 0 siblings, 1 reply; 14+ messages in thread From: Evgeni Kolev @ 2023-02-20 8:54 UTC (permalink / raw) To: João Távora, Eli Zaretskii Cc: Randy Taylor, Theodor Thornhill, 61368 Thank you João for the input! I think something like this would make sense in go-ts-mode.el: ``` (with-eval-after-load 'yasnippet (yas-define-snippets 'go-ts-mode '(("return" (yas-expand-snippet (go-ts-mode-return))) ("iferr" (yas-expand-snippet (format "if err != nil {\n%s\n}" (go-ts-mode-return))))))) ``` A custom variable go-ts-mode-want-yasnippet could be added to optionally prevent adding the snippets. Eli, would you consider something like the code above acceptable as an alternative to an electric "return" mode? Having optional yasnippet integration was also brought up by Theo in this message[1] and this one [2]. [1]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60805#11 [2]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60805#17 ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-20 8:54 ` Evgeni Kolev @ 2023-02-20 12:55 ` Eli Zaretskii 2023-02-22 14:36 ` Evgeni Kolev 0 siblings, 1 reply; 14+ messages in thread From: Eli Zaretskii @ 2023-02-20 12:55 UTC (permalink / raw) To: Evgeni Kolev; +Cc: dev, theo, joaotavora, 61368 > From: Evgeni Kolev <evgenysw@gmail.com> > Date: Mon, 20 Feb 2023 10:54:06 +0200 > Cc: 61368@debbugs.gnu.org, Randy Taylor <dev@rjt.dev>, > Theodor Thornhill <theo@thornhill.no> > > Thank you João for the input! I think something like this would make > sense in go-ts-mode.el: > ``` > (with-eval-after-load 'yasnippet > (yas-define-snippets 'go-ts-mode > '(("return" (yas-expand-snippet > (go-ts-mode-return))) > ("iferr" (yas-expand-snippet > (format "if err != nil {\n%s\n}" > (go-ts-mode-return))))))) > ``` > > A custom variable go-ts-mode-want-yasnippet could be added to > optionally prevent adding the snippets. > > Eli, would you consider something like the code above acceptable as an > alternative to an electric "return" mode? > > Having optional yasnippet integration was also brought up by Theo in > this message[1] and this one [2]. > > [1]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60805#11 > [2]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60805#17 IMO, if we are going to support yasnippet, we should do it in more than just one mode. So a short addition to just the go-ts-mode will not do, and if we want to include stuff like that, it should be part of a larger patch for Emacs 30. Whether this is a good alternative for some electricity is also a good topic for discussion: electric is part of Emacs whereas yasnippet isn't, so there are advantages and disadvantages to this proposal. Thanks. ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-20 12:55 ` Eli Zaretskii @ 2023-02-22 14:36 ` Evgeni Kolev 2024-01-10 22:43 ` Stefan Kangas 0 siblings, 1 reply; 14+ messages in thread From: Evgeni Kolev @ 2023-02-22 14:36 UTC (permalink / raw) To: Eli Zaretskii; +Cc: dev, theo, joaotavora, 61368 Thank you, makes sense. I'll think about the different options and come up with a proposal. ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-22 14:36 ` Evgeni Kolev @ 2024-01-10 22:43 ` Stefan Kangas 2024-01-15 8:21 ` Evgeni Kolev 0 siblings, 1 reply; 14+ messages in thread From: Stefan Kangas @ 2024-01-10 22:43 UTC (permalink / raw) To: Evgeni Kolev; +Cc: dev, Eli Zaretskii, theo, joaotavora, 61368 Evgeni Kolev <evgenysw@gmail.com> writes: > Thank you, makes sense. I'll think about the different options and > come up with a proposal. Did you make any progress here? ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2024-01-10 22:43 ` Stefan Kangas @ 2024-01-15 8:21 ` Evgeni Kolev 2024-01-15 8:39 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 14+ messages in thread From: Evgeni Kolev @ 2024-01-15 8:21 UTC (permalink / raw) To: Stefan Kangas; +Cc: dev, Eli Zaretskii, theo, joaotavora, 61368 On Thu, Jan 11, 2024 at 12:43 AM Stefan Kangas <stefankangas@gmail.com> wrote: > > Evgeni Kolev <evgenysw@gmail.com> writes: > > > Thank you, makes sense. I'll think about the different options and > > come up with a proposal. > > Did you make any progress here? I have since come across tempo.el and reviewed its use in org-tempo.el. While tempo.el is a valid option, I still think yasnippet is a better approach. However, yasnippet is still not used throughout emacs' core (other than "light" uses in eglot.el and python.el, and some faces in the themes). Please let me know in case a generic mechanism for adding snippets to major modes has been added - I might have missed it. ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2024-01-15 8:21 ` Evgeni Kolev @ 2024-01-15 8:39 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-01-15 11:40 ` João Távora 0 siblings, 1 reply; 14+ messages in thread From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-01-15 8:39 UTC (permalink / raw) To: Evgeni Kolev, Stefan Kangas; +Cc: dev, Eli Zaretskii, joaotavora, 61368 Evgeni Kolev <evgenysw@gmail.com> writes: > On Thu, Jan 11, 2024 at 12:43 AM Stefan Kangas <stefankangas@gmail.com> wrote: >> >> Evgeni Kolev <evgenysw@gmail.com> writes: >> >> > Thank you, makes sense. I'll think about the different options and >> > come up with a proposal. >> >> Did you make any progress here? > > I have since come across tempo.el and reviewed its use in org-tempo.el. > > While tempo.el is a valid option, I still think yasnippet is a better approach. > However, yasnippet is still not used throughout emacs' core (other > than "light" uses > in eglot.el and python.el, and some faces in the themes). > > Please let me know in case a generic mechanism for adding snippets to major > modes has been added - I might have missed it. Perhaps it's time to revisit the snippet.el that Joao has mentioned? Theo ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2024-01-15 8:39 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-01-15 11:40 ` João Távora 0 siblings, 0 replies; 14+ messages in thread From: João Távora @ 2024-01-15 11:40 UTC (permalink / raw) To: Theodor Thornhill; +Cc: Evgeni Kolev, Eli Zaretskii, 61368, Stefan Kangas, dev On Mon, Jan 15, 2024 at 8:39 AM Theodor Thornhill <theo@thornhill.no> wrote: > > Evgeni Kolev <evgenysw@gmail.com> writes: > > > On Thu, Jan 11, 2024 at 12:43 AM Stefan Kangas <stefankangas@gmail.com> wrote: > >> > >> Evgeni Kolev <evgenysw@gmail.com> writes: > >> > >> > Thank you, makes sense. I'll think about the different options and > >> > come up with a proposal. > >> > >> Did you make any progress here? > > > > I have since come across tempo.el and reviewed its use in org-tempo.el. > > > > While tempo.el is a valid option, I still think yasnippet is a better approach. > > However, yasnippet is still not used throughout emacs' core (other > > than "light" uses > > in eglot.el and python.el, and some faces in the themes). > > > > Please let me know in case a generic mechanism for adding snippets to major > > modes has been added - I might have missed it. > > Perhaps it's time to revisit the snippet.el that Joao has mentioned? I've cleaned it up very minimally and put the most recent version here https://github.com/joaotavora/snippet It has unit tests which you can run with /path/to/emacs -Q --batch -l snippet-tests.el -f ert-run-tests-batch-and-exit Not many docs docs, some docstrings, many TODOs. Have a look. ^ permalink raw reply [flat|nested] 14+ messages in thread
* bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements 2023-02-08 15:27 bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements Evgeni Kolev 2023-02-08 16:30 ` Eli Zaretskii @ 2023-02-08 19:20 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 0 replies; 14+ messages in thread From: Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-02-08 19:20 UTC (permalink / raw) To: evgenysw, 61368; +Cc: Randy Taylor Evgeni Kolev <evgenysw@gmail.com> writes: > [CC Randy, author of go-ts-mode, Theo, who asked to keep the patches coming :)] > :-) > This is a patch which adds support to go-ts-mode to insert > context-aware return statements for the current function. The return > statements are pre-filled with the Go zero values of the output > parameters. > > For example, all these return statements are inserted by M-x > go-ts-mode-insert-return: > > ``` > func f() (x, y, z int) { > return 0, 0, 0 > } > > func exotic() (bool, int, myStruct, *int, chan bool, error) { > return false, 0, myStruct{}, nil, nil, err > } > > func closure(numbers []int) { > sort.Slice(numbers, func(i, j int) bool { > return false > }) > } > ``` Cool! > > The command go-ts-mode-insert-return is experimentally bound to a key > C-c C-r ("r" as return statement). It's a user error to run C-c C-r > outside of a function body. > > Customization: when the output params contain "error" the pre-filled > text is "err". A customization variable is added to allow the user to > pick a different value, for example they might pick `fmt.Errorf("...: > %w", err)` which will result in error wrapping: > ``` > func f() (int, error) { > return 0, fmt.Errorf("...: %w", err) > } > ``` > > Personally, I'll use yasnippet instead of C-c C-r. Something like this: > ``` > # -*- mode: snippet -*- > # name: ret > # key: ret > # type: command > # -- > > (let ((go-ts-mode-error-zero-value "${1:fmt.Errorf(\"${2:format}: > %w\", err)}")) > (yas-expand-snippet (go-ts-mode-return))) > ``` > > I'm open to suggestions about how to best expose this functionality to > the user. I think a snippet makes the most sense, but there's no > standard way for major modes to expose snippets as far as I'm aware. > It's possible to tweak C-c C-r to call (yas-expand-snippet) if > available, otherwise call (insert). In general, I don't feel strong > about the C-c C-r key binding, but I didn't have a better idea. > How about using tempo or skeleton as fallbacks when yasnippet isn't installed? > Known bug: this example does not work, fails with error "Unknown Go > type "[int]int"": > ``` > func notOk() (int, map[int]int) { > } > ``` > The root cause is an issue with the tree-sitter-go parser. The bug has > been reported here > https://github.com/tree-sitter/tree-sitter-go/issues/107 > > Known limitation: unfamiliar types are assumed to be structs. This > assumption does not work for aliased types. For example: > ``` > type MyInt = int > > func alias() MyInt { > return MyInt{} > } > ``` > Above the correct return statement is "return MyInt(0)". My assumption > is that type aliases of primitive types are rare in Go codebases. In > these rare cases, the user is expected to replace "MyInt{}" with > "MyInt(0)", or not use C-c C-r at all. > > Inspired by: this emacs conf talk "08:21 Intelligent templates" > https://emacsconf.org/2022/talks/treesitter/ > > Feedback is more than welcome! In particular: > - how to best expose the functionality - key binding, snippet (how > exactly?), something else? > Personally I think this should be a contrib to gopls as a code action so that others can benefit from it. Is upstreaming it to gopls too hard? Theo ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2024-01-15 11:40 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-02-08 15:27 bug#61368: [PATCH] Extend go-ts-mode with support for pre-filling return statements Evgeni Kolev 2023-02-08 16:30 ` Eli Zaretskii 2023-02-09 11:47 ` Evgeni Kolev 2023-02-09 13:39 ` Eli Zaretskii 2023-02-18 11:46 ` Evgeni Kolev 2023-02-18 12:14 ` João Távora 2023-02-20 8:54 ` Evgeni Kolev 2023-02-20 12:55 ` Eli Zaretskii 2023-02-22 14:36 ` Evgeni Kolev 2024-01-10 22:43 ` Stefan Kangas 2024-01-15 8:21 ` Evgeni Kolev 2024-01-15 8:39 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-01-15 11:40 ` João Távora 2023-02-08 19:20 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).