-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Editing Clojure's multi-line blocks wrapped in (str)
blocks
#38
Comments
I think I'm very close to figuring it out. Here's what I got: (defun separedit--remove-clj-str-delimeters (_ &optional _)
(let ((result-string ""))
(save-excursion
(goto-char (point-min))
(while (search-forward-regexp "\"\\([^\"]*\\)\"" nil t)
(setq result-string (concat result-string (match-string-no-properties 1) "\n"))))
(delete-region (point-min) (point-max))
(insert (substring result-string 0 -1))))
(setq
separedit-block-regexp-plists
'((:header "(str\s+"
:footer ".*\"\s?)"
:body ""
:straight t
:keep-header t
:keep-footer t
:modes (clojure-mode clojurec-mode)
:delimiter-remove-fn separedit--remove-clj-str-delimeters
:edit-mode markdown-mode) Now, if I run the But, if I move the cursor outside of quotes it does pick up the structure and even However, there's a problem. If I place
That's what gets sent into the indirect separedit buffer. I'm removing all the crap leaving only the text (with
So how do I make it, so it marks only I think I just need to tweak regexes in |
Ahem, I solved it by using advice. Here's the full working solution: ;; What this does is that it removes all the quotes surrounding
;; each line that gets sent to separedit buffer
(defun separedit--remove-clj-str-delimeters (_ &optional _)
(save-excursion
(replace-regexp-in-region "^\s*\"" "" (point-min))
(replace-regexp-in-region "\"\s*$" "" (point-min))))
;; After editing the lines in the separadit buffer,
;; we need to wrap each line into quotes,
;; replace every empty line with explicit \n, maybe do some other text manipulations
(defun separedit--restore-clj-str-delimeters (&optional _)
(save-excursion
(replace-regexp-in-region "^\\s-*$" "\\\\n" (point-min))
(replace-regexp-in-region "^" "\"" (point-min))
(replace-regexp-in-region ".$" "\\& \"" (point-min))))
;; This is the 'magic' that teaches separedit to understand Clojure (str) blocks
;; basically saying that whenever it encounters something between `(str "` and `")`
;; that it should be treated differently
(add-to-list
'separedit-block-regexp-plists
'(:header "(str\s+\""
:footer ".*\"\s?)"
:body ""
;; We're keeping the footer and the header (because they contain relevant content),
;; but we need only to send the relevant text (and not the entire clojure form) into
;; the separadit buffer,
;; that's why the following trick with advice is important for this to work
:keep-header t
:keep-footer t
;; The modes where separedit looks for these blocks
;; i.e., if you try to edit `(str "foo" "bar")` in Elisp,
;; it won't get triggered
:modes (clojure-mode clojurec-mode clojurescript-mode)
;; Some pre and post-processing of the existing text required
:delimiter-remove-fn separedit--remove-clj-str-delimeters
:delimiter-restore-fn separedit--restore-clj-str-delimeters
;; Optionally, we can set the mode of separedit buffer
:edit-mode markdown-mode))
;; Since we're telling separedit to keep the header and the footer, it will try to send
;; everything within the lines of `(str "` and `")`, inclusively.
;; It will mark the entire region from the top line of (str
;; So, we need to trick it to think that it's editing only the content within quotes
;; We do that by slightly modifying the marked region
(defun fix-separadit-region-for-clj-a (block-info-fn &optional)
"Fix separadit block for Clojure (str) multi-line."
(let ((block-info (funcall block-info-fn)))
(when-let* ((_ (member 'clojure-mode
(plist-get
(plist-get block-info :regexps)
:modes)))
(beg (plist-get block-info :beginning))
(end (plist-get block-info :end)))
(goto-char beg)
(search-forward-regexp "(str\s+")
(plist-put block-info :beginning (point))
(goto-char end)
(search-backward-regexp "\")")
(forward-char)
(plist-put block-info :end (point)))
block-info))
(advice-add 'separedit--block-info :around #'fix-separadit-region-for-clj-a) |
@agzam Thank you for providing a full working solution. That's a great idea. I thought maybe should add support for other lisp-like (or even c-like) languages . I just pushed my implementation to this branch: https://github.com/twlz0ne/separedit.el/tree/feat-multi-line-string-block, hope it works as you expected. |
I'm not sure what you mean by adding support for other languages. This is specifically a Clojure thing, i.e., even though these two snippets look similar, they are not the same. (def some-var
{:description "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec hendrerit tempor tellus.
Donec pretium posuere tellus. Proin quam nisl, tincidunt et, mattis eget, convallis nec, purus. Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla posuere. Donec vitae dolor. Nullam tristique
diam non turpis. Cras placerat accumsan nulla. Nullam rutrum. Nam vestibulum accumsan nisl. "})
(def some-var
{:description
(str "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
"Donec hendrerit tempor tellus. Donec pretium posuere tellus. "
"Proin quam nisl, tincidunt et, mattis eget, convallis nec, purus. "
"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. "
"Nulla posuere. Donec vitae dolor. Nullam tristique diam non turpis. "
"Cras placerat accumsan nulla. Nullam rutrum. Nam vestibulum accumsan nisl.")}) It's some known inconvenience Clojurists sometimes have to deal with. How does your improvement makes the difference for this use-case? |
For example in c: char* my_str =
"Here is the first line.\n"
"Here is the second line."; But I haven't implement it yet.
Does the indentation below 1st line in the string will show up in the final doc? |
No, that's a bad example. Typically, the second and subsequent lines are not indented in that case; they would start at the margin. |
That means we just treat it like a normal string block? I made some changes and now supports multiple strings whether they are arranged horizontally or vertically: Maybe it shouldn't only works in It is now also support other languages, for example c: My goal is implement a generic multple line string editing, no need to add rules to But the new problem is that I think it is more convenient to edit multiple strings together in situations like following (point in the string
But sometimes I just want to edit the string The latest code: https://github.com/twlz0ne/separedit.el/tree/feat-multi-string-block |
Yah, that would be awesome. I just realized that even in elisp I sometimes do stuff like: (concat "Integer placerat tristique nisl"
"Sed bibendum")
|
Clojure doesn't have support for multilines that preserve indentation, i.e., if you want to break some text into multiple lines you typically have to wrap it in
(str)
block like this:I think it's possible to teach separedit to understand this structure so you can edit the text while being able to use fill-paragraph, etc.
Can someone please help me figuring it out?
The text was updated successfully, but these errors were encountered: