Automatic f-strings #132
-
Let me know if this isn't an appropriate place to file an issue, but I'm trying to remember elisp and wanted to clone a new PyCharm feature:
So I came up with what is likely a very brittle solution and was wondering if you had any advice: ;; todo: run after character insert, maybe tree-sitter-after-change-functions?
(defun maybe-convert-string-to-f-string()
(if (= ?{ (char-after))
(make-node-f-string (tree-sitter-node-at-point))))
(defun make-node-f-string(node)
;; todo: should this query object be made outside of the function for performance?
(let ((is-string-query (ts-make-query (tree-sitter-require 'python) "(string) @string")))
;; todo: text-function seems extraneous, is there a better way to just check if the node i'm
;; on is a string, returning t or nil?
(when (ts-query-matches is-string-query node 'ts--buffer-substring-no-properties)
(save-excursion
;; todo: is there a tree-sitter way of replacing a node?
(goto-char (ts-node-start-position node))
(insert "f"))))) |
Beta Was this translation helpful? Give feedback.
Replies: 6 comments
-
Love this idea! I always forget the |
Beta Was this translation helpful? Give feedback.
-
I believe this is a use case where you relay on this package, but at least in my opinion it is out of scope of the project to include such functionality |
Beta Was this translation helpful? Give feedback.
-
Definitely. I probably wasn't clear, I'm not proposing adding this to this repository, I was asking for advice on the best way to do this using tree-sitter. |
Beta Was this translation helpful? Give feedback.
-
OH, okay maybe @ubolonton can point you into the right direction as |
Beta Was this translation helpful? Give feedback.
-
We need a function that makes a string into an f-string, if it's not one already: (defun ublt/python-f-string-ify (&rest _)
;; Does nothing if point is not on a string.
(when-let* ((str (tree-sitter-node-at-point 'string))
(text (ts-node-text str)))
;; This shouldn't be necessary when/if the Python grammar
;; gets a separate node type for "prefixed strings".
(let ((is-f-string (string-match-p "^[bru]*f+[bru]*\\(\"\\|'\\)" text)))
(unless is-f-string
(save-excursion
(goto-char (ts-node-start-position str))
(insert "f")))))) If you already use some "smart-bracket" package, this could be added as an advice on the function that handles (define-advice paredit-open-curly (:after (&rest _))
(when (and (eq 'python major-mode)
tree-sitter-mode)
(ublt/python-f-string-ify))) Otherwise, you can write a custom "smart-curly" command and bind it to (defun ublt/python-smart-insert ()
(interactive)
(call-interactively #'self-insert-command)
(ublt/python-f-string-ify))
In 0.10.0, (eq 'string (ts-node-type node))
Tree-sitter only deals with parsing. It doesn't do source code transformation/generation. You should use Emacs's normal text editing functions. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the snippet. I changed it a little bit, make it even more smarter (i guess). Code: ;;;; Smart f-strings
;; https://github.com/ubolonton/emacs-tree-sitter/issues/52
(defun fk/python-f-string-ify (&rest _)
;; Does nothing if major-mode is not python or point is not on a string.
(when-let* ((python-mode-p (eq major-mode 'python-mode))
(str (tree-sitter-node-at-point 'string))
(text (ts-node-text str)))
(let ((is-f-string (string-match-p "^[bru]*f+[bru]*\\(\"\\|'\\)" text))
(should-f-string (and (s-contains-p "{" text)
(s-contains-p "}" text))))
(if should-f-string
(unless is-f-string
(save-excursion
(goto-char (ts-node-start-position str))
(insert "f")))
(when is-f-string
(save-excursion
(goto-char (ts-node-start-position str))
(when (char-equal (char-after) ?f)
(delete-char 1))))))))
;; When not using wrap-region or anything that changes "{" keybinding
;; (define-key python-mode-map (kbd "{") (lambda ()
;; (interactive)
;; (call-interactively 'self-insert-command)
;; (fk/python-f-string-ify)))
(defadvice wrap-region-trigger (after smart-f-string activate)
(fk/python-f-string-ify))
(defadvice delete-char (after smart-f-string activate)
(fk/python-f-string-ify))
(defadvice delete-active-region (after smart-f-string activate)
(fk/python-f-string-ify)) |
Beta Was this translation helpful? Give feedback.
Thanks for the snippet. I changed it a little bit, make it even more smarter (i guess).
Code: