Sorting lines with multiline expressions under Emacs - emacs

I'm looking for a variant of M-x sort-lines that can handle multiline expressions properly. For instance:
this is the first line
this is the second lien
this is the (third
line
spanning multiple lines because of parens)
Any ideas?

Here is a similar question solved in a similar way
(defun end-of-chunk ()
"forward line or to ends of mid-expression."
(interactive)
(goto-char (point-at-eol))
(let ((limit (point-at-bol))
temp
expr-beg)
(while (and (setq temp (nth 1 (syntax-ppss)))
(<= limit temp))
(goto-char temp)
(setq expr-beg (point)))
(when expr-beg
(goto-char expr-beg)
(forward-sexp))))
(defun sort-lines-as-exprs (reverse beg end)
"sort lines, or whole expression if line ends mid-expression."
(interactive "P\nr")
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(sort-subr reverse
'forward-line
'end-of-chunk))))

Related

Forward sentence by comma by partial sentence in org

Strike M-e in org which invokes org-forward-sentence and thus move point to end of the sentence.
I desire to move by comma. When refer to org-forward-sentence, notice the last two lines of
(let ((sentence-end (concat (sentence-end) "\\|^\\*+ .*$")))
(call-interactively #'forward-sentence)))))))
From the completed definition.
(defun org-forward-sentence (&optional _arg)
"Go to end of sentence, or end of table field.
This will call `forward-sentence' or `org-table-end-of-field',
depending on context."
(interactive)
(if (and (org-at-heading-p)
(save-restriction (skip-chars-forward " \t") (not (eolp))))
(save-restriction
(narrow-to-region (line-beginning-position) (line-end-position))
(call-interactively #'forward-sentence))
(let* ((element (org-element-at-point))
(contents-end (org-element-property :contents-end element))
(table (org-element-lineage element '(table) t)))
(if (and table
(>= (point) (org-element-property :contents-begin table))
(< (point) contents-end))
(call-interactively #'org-table-end-of-field)
(save-restriction
(when (and contents-end
(> (point-max) contents-end)
;; Skip blank lines between elements.
(< (org-element-property :end element)
(save-excursion (goto-char contents-end)
(skip-chars-forward " \r\t\n"))))
(narrow-to-region (org-element-property :contents-begin element)
contents-end))
;; End of heading is considered as the end of a sentence.
(let ((sentence-end (concat (sentence-end) "\\|^\\*+ .*$")))
(call-interactively #'forward-sentence)))))))
Then changed dot to comma
(let ((sentence-end (concat (sentence-end) "\\|^\\*+ ,*$"))) ;;changee . to ,
(call-interactively #'forward-sentence)))))))
However, it proved wrong.
Where should I change within the original function.
Define it as
(def org-forward-partial-sentence (&optional arg)
and (global-set-key "\C-m"
That . has special meaning in a regex context, see (emacs)Regexps in the manual.
A very simplistic modification could be,
(concat (sentence-end) "\\|^\\*+ .*$\\|,")
to move to , as well.
Instead of changing the entire function, you could just let bind sentence-end around org-forward-sentence, eg.
(defun my-org-forward-sentence ()
(interactive)
(let ((sentence-end (concat (sentence-end) "\\|,")))
(call-interactively #'org-forward-sentence)))

Split lines of current paragraph in Emacs

I want to add a function (para2lines) to Emacs by which I can split the current paragraph into its sentences and print them line by line in a separate buffer. Following is code in Racket/Scheme:
(define (p2l paraString)
(define lst (string-split paraString ". "))
(for ((i lst))
(displayln i)))
Testing:
(p2l "This is a test. For checking only. Only three lines.")
Output:
This is a test.
For checking only.
Only three lines.
In Emacs Lisp, I could manage following code:
(defun pl (ss)
(interactive)
(let ((lst (split-string (ss))))
(while lst
(print (pop lst)))))
But I do not know how to get the text from the paragraph with current position. How can I correct this function?
Edit: basically, I want to read it as separate lines but want to save it as paragraph.
Here's an example that might help you on your way. It will do your conversion to the current paragraph (i.e. where the cursor is positioned), rather than to a new buffer. You could modify this to pass a string to your function if that's what you require.
(defun p2l ()
"Format current paragraph into single lines."
(interactive "*")
(save-excursion
(forward-paragraph)
(let ((foo (point)))
(backward-paragraph)
(replace-regexp "\n" " " nil (1+ (point)) foo)
(backward-paragraph)
(replace-regexp "\\. ?" ".\n" nil (point) foo))))
I would just run Emacs commands or write a macro to convert a paragraph to single-sentence lines, but maybe you are really just wanting to read wrapped paragraphs as lines, thus the need to have an Emacs command.
Here's something that will grab the current paragraph, insert a new buffer *Lines*, and then convert sentences to lines.
(defun para-lines ()
"Split sentences of paragraph to lines in new buffer."
(interactive)
;; Move the paragraph to a new buffer.
(let ((b (generate-new-buffer "*Lines*")))
(with-output-to-temp-buffer b
(let ((beg (save-excursion (forward-paragraph -1) (point)))
(end (save-excursion (forward-paragraph +1) (point))))
(princ (buffer-substring-no-properties beg end))))
;; Switch to new buffer
(with-current-buffer b
;; Since the name starts with "*", shut off Help Mode
(fundamental-mode)
;; Make sure buffer is writable
(setq buffer-read-only nil)
;; From the start of the buffer
(goto-char (point-min))
;; While not at the end of the buffer
(while (< (point) (point-max))
(forward-sentence 1)
;; Delete spaces between sentences before making new new line
(delete-horizontal-space)
;; Don't add a new line, if already at the end of the line
(unless (= (line-end-position) (point))
(newline))))))
To avoid using forward-sentence, and just use a regular expression, use re-search-forward. For instance, to match semi-colons as well as periods.
(defun para-lines ()
"Split sentences of paragraph to lines in new buffer."
(interactive)
;; Move the paragraph to a new buffer.
(let ((b (generate-new-buffer "*Lines*")))
(with-output-to-temp-buffer b
(let ((beg (save-excursion (forward-paragraph -1) (point)))
(end (save-excursion (forward-paragraph +1) (point))))
(princ (buffer-substring-no-properties beg end))))
;; Switch to new buffer
(with-current-buffer b
;; Since the name starts with "*", shut off Help Mode
(fundamental-mode)
;; Make sure buffer is writable
(setq buffer-read-only nil)
;; From the start of the buffer
(goto-char (point-min))
;; While not at the end of the buffer
(while (< (point) (point-max))
(re-search-forward "[.;]\\s-+" nil t)
;; Delete spaces between sentences before making new new line
(delete-horizontal-space)
;; Don't add a new line, if already at the end of the line
(unless (= (line-end-position) (point))
(newline))))))

Delete spaces in beginning of kill-ring in Emacs

I would like to insert the contents of the kill-ring at the point using (yank), however if there is white space in the beginning of the yanked text, it should be deleted before insertion.
How can this be done?
(I have looked at save-excursion and re-search-backward but could not get it to work)..
You could try
(defun my-yank ()
(interactive)
(let ((start (point)))
(call-interactively 'yank)
(let ((end (point)))
(save-excursion
(goto-char start)
(delete-region (point)
(progn (skip-chars-forward " \t" end) (point)))))))
Here is a possible solution
(defun yank-no-spaces (&optional arg)
(interactive "*P")
(yank arg)
(save-restriction
(save-excursion
(narrow-to-region (point) (mark))
(goto-char (point-min))
(just-one-space 0))))

Define a copy-section command in Emacs

I would like to set up a command that put the content of the lines between two § characters without moving the point (not including the lines containg the §).
Here is my current attempt
(defun copy-section ()
"Copy current section, that is lines between two §."
(interactive)
(save-excursion
(when (not (search-backward-regexp "§" nil t))
(goto-char (point-min)) )
(forward-line 1)
(when (not (search-forward-regexp "§" nil t))
(goto-char (point-max)) )
(move-beginning-of-line nil)
(kill-ring-save (mark) (point)) ) )
It works well but the remarks in the documentation about moving around the mark being bad style make me think taht there is a better way to achieve the same result.
Does saving position into variable (which I do not know how to do it) allows for a cleaner function.
Part of the code above comes from ergoemacs.
No "regexp" form needed as only a char is looked for
(defun copy-section ()
"Copy current section, that is lines between two §."
(interactive)
(save-excursion
(let* ((start (and (search-backward "§" nil t)
(forward-line 1)
(point)))
(end (progn (and start (search-forward "§" nil t))
(forward-line -1)
(end-of-line)
(point))))
(and start end (kill-new (buffer-substring-no-properties start end))))))
This version saves the beginning and end of your section in temporary local variables, and doesn't use the mark at all:
(defun copy-section ()
"Copy current page as defined by form feed characters."
(interactive)
(let (start end)
(save-excursion
(when (not (search-backward-regexp "§" nil t))
(goto-char (point-min)) )
(forward-line 1)
(setq start (point))
(when (not (search-forward-regexp "§" nil t))
(goto-char (point-max)) )
(move-beginning-of-line nil)
(setq end (point))
(kill-ring-save start end))))

Emacs: how to write a defun which acts on region, but acts on point if there's no region?

I write a simple defun for a region, and I want to apply it even if there's no region – i.e. call it with no selection at all. I thought I could do something like the following:
(defun region-study (strt end)
(interactive "r")
(if (= strt end)
(progn ....) ;; then
(progn ....))) ;; else
But it doesn't work. As it turns out, when you call (interactive "r") with no region it doesn't just set boundaries to be equal. Try this:
(defun region-study (strt end)
(interactive "r")
(message "strt=%d; end=%d" strt end))
So my question is that: "how to write a defun which acts on region, but acts on point if there's no region?"
Edit:
So I wanted to put selection in brackets or just to insert brackets and (backward-char 1). Here's a solution:
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(progn
(setq pP (point))
(setq strt (region-beginning))
(setq end (region-end))
(setq meat (buffer-substring-no-properties strt end))
(setq news (concat lft meat rit))
(delete-region strt end)
(goto-char strt)
(insert news)
(if (= pP strt)
(goto-char strt) ; then
(goto-char (+ end 1)))) ; else
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
(defun bk-put-in-braces ()
(interactive)
(put-in-lft-rit "(" ")"))
(defun bk-put-in-curly-braces ()
(interactive)
(put-in-lft-rit "{" "}"))
(defun bk-put-in-quotes ()
(interactive)
(put-in-lft-rit "'" "'"))
(defun bk-put-in-double-quotes ()
(interactive)
(put-in-lft-rit "\"" "\""))
(defun bk-put-in-square-brackes ()
(interactive)
(put-in-lft-rit "[" "]"))
And then you bind in .emacs:
(global-set-key (kbd "C-<f9>") 'bk-put-in-square-brackes)
(global-set-key (kbd "<f9>") 'bk-put-in-curly-braces)
(global-set-key (kbd "S-<f7>") 'bk-put-in-quotes)
(global-set-key (kbd "S-<f8>") 'bk-put-in-double-quotes)
(global-set-key (kbd "S-<f9>") 'bk-put-in-braces)
That's it! Should be working in all modes.
Edit2:
#phils
Thanks. You are definetely right. One thing though - my code had an additional feature of leaving the point at the beginning or end of the region - depending on where it was in the selection. Here's Your code with this feature added:
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(let ((strt (region-beginning))
(end (region-end))
(pP (point)))
(save-excursion
(goto-char end)
(insert rit)
(goto-char strt)
(insert lft))
(if (= pP strt)
(goto-char strt) ; then
(goto-char (+ end 1)))) ; else
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
A few notes on your solution...
It's good practice to avoid unnecessary global-scope setqs. Use (let) instead to define a temporary scope for your variables.
You are doing a lot more work than required. Instead of copying the region, concatenating that copy and the delimiters into a 'news' variable, deleting the region, and then inserting 'news', all you need to do is insert the delimiter characters at the beginning and end of the region.
(In general, if you try to "think like an editor" when writing elisp, and focus on manipulating buffers rather than variables, you'll generally wind up with more efficient code.)
save-excursion is very useful (along with several other save- and with- forms).
 
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(let ((strt (region-beginning))
(end (region-end)))
(save-excursion
(goto-char end)
(insert rit)
(goto-char strt)
(insert lft)))
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
use-region-p should return t if your function should act on the region instead of at a point.
You may like to use the function region-or-word-at-point defined in thingatpt+.el