Grab current line in buffer as a string in elisp - emacs

How can i collect the buffer's current line as a string value in elisp? i can do this,
(let (p1 p2 myLine)
(setq p1 (line-beginning-position) )
(setq p2 (line-end-position) )
(setq myLine (buffer-substring-no-properties p1 p2))
)
but is there anyway i can do it in one line as,
(with-current-buffer get-current-line)

Use thing-at-point:
(thing-at-point 'line t)
but note that this also returns any newline at the end of the line.

Related

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))))))

Emacs24 and python-mode: indention in docstrings

I have the following code/text:
def f():
"""
Return nothing.
.. NOTE::
First note line
second note line
In Emacs23 (23.4.1) I was able to press TAB in the last line ("second note line"; nomatter how this line was indented) and it was aligned correctly like this:
def f():
"""
Return nothing.
.. NOTE::
First note line
second note line
I.e., it uses the previous line and indents the following line in the same way.
Now in Emacs24 (24.3.1) this does not work anymore and it is aligned like this:
def f():
"""
Return nothing.
.. NOTE::
First note line
second note line
I.e. it aligns the multi-line string block, but does not depend on the previous line.
It affects only docstrings; code is indented as I want. I am using python-mode. How can I change this, so that pressing TAB aligns the block correctly?
Python mode has changed quite a bit between Emacs 23 and 24. There is no configuration that would allow you to do what you want.
But, Emacs is quite flexible and you can advise the (python-indent-context) function to make it return a different result that will lead to the behavior you want. The function (python-indent-context) returns a character at which the indentation is measured and used for indenting the current line. By default, when inside a string, it returns the point where the beginning of the string resides. Thus, your line will be indented to the indentation of the start of the string. We can easily modify it to return a point in the previous non-empty line instead, for instance like this:
(defun python-fake-indent-context (orig-fun &rest args)
(let ((res (apply orig-fun args))) ; Get the original result
(pcase res
(`(:inside-string . ,start) ; When inside a string
`(:inside-string . ,(save-excursion ; Find a point in previous non-empty line
(beginning-of-line)
(backward-sexp)
(point))))
(_ res)))) ; Otherwise, return the result as is
;; Add the advice
(advice-add 'python-indent-context :around #'python-fake-indent-context)
The same effect can be achieved using the old defadvice for older Emacs:
(defadvice python-indent-context (after python-fake-indent-context)
(pcase ad-return-value
(`(:inside-string . ,start) ; When inside a string
(setq ad-return-value ; Set return value
`(:inside-string . ,(save-excursion ; Find a point in previous non-empty line
(beginning-of-line)
(backward-sexp)
(point)))))))
(ad-activate 'python-indent-context)
What about editing the section de-stringified in a separate buffer? That would allow python-mode with all its facilities.
Here a first draft - original string will be stored in kill-ring:
(defun temp-edit-docstring ()
"Edit docstring in python-mode. "
(interactive "*")
(let ((orig (point))
(pps (parse-partial-sexp (point-min) (point))))
(when (nth 3 pps)
(let* (;; relative position in string
(relpos (- orig (+ 2 (nth 8 pps))))
(beg (progn (goto-char (nth 8 pps))
(skip-chars-forward (char-to-string (char-after)))(push-mark)(point)))
(end (progn (goto-char (nth 8 pps))
(forward-sexp)
(skip-chars-backward (char-to-string (char-before)))
(point)))
(docstring (buffer-substring beg end)))
(kill-region beg end)
(set-buffer (get-buffer-create "Edit docstring"))
(erase-buffer)
(switch-to-buffer (current-buffer))
(insert docstring)
(python-mode)
(goto-char relpos)))))
When ready, copy the contents back into original buffer.
Which remains to be implemented still.

How do you write an emacs lisp function to replace a word at point?

I have tried in two different ways to write my function. I decided to write a small function to convert to camel case and back with this elisp string library. At first via searching I found this tutorial on replacing things at point and made this function:
; use string manipulation library to switch between camel and snake (s.el)
(defun my_test ()
"test"
(interactive)
;; get current selection or word
(let (bds p1 p2 inputStr resultStr)
;; get boundary
(if (use-region-p)
(setq bds (cons (region-beginning) (region-end) ))
(setq bds (bounds-of-thing-at-point 'word)) )
(setq p1 (car bds) )
(setq p2 (cdr bds) )
;; grab the string
(setq inputStr (buffer-substring-no-properties p1 p2) )
(setq resultStr (s-lower-camel-case inputStr))
(message inputStr)
(delete-region p1 p2 ) ; delete the region
(insert resultStr) ; insert new string
)
)
This does not modify resultStr as expected and just repasts inputStr in there.
What I don't understand about this is that when I eval (with M-:) (setq resultStr (s-lower-camel-case "other_string")) I get the expected result ("otherString")
I even tried a different (and better for my purposes) way of writing the function inspired by this SO question:
(defun change-word-at-point (fun)
(cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'word)
(let ((str (buffer-substring-no-properties beg end)))
(delete-region beg end)
(insert (funcall fun str)))))
(defun my_test_camel ()
(interactive)
(change-word-at-point 's-lower-camel-case))
which suffers from the same problem. This makes me think that there is something wrong with the s-lower-camel-case function (or how I am calling it) but that works when called from eval as mentioned above
EDIT: modified first function to include let syntax, see comments
EDIT #2: Both of these functions work correctly, the answer has been accepted as it provides a better alternative with the information on symbol and the correct way of writing it. My problem was testing which was due to haskell-mode. New question is here
Here's an alternate definition. The comment was correct that you need to do local bindings via let. Note that this version uses the region if it's active, or else uses bounds-of-thing-at-point to get the word at point if no region is active:
(defun word-or-region-to-lcc ()
"Convert word at point (or selected region) to lower camel case."
(interactive)
(let* ((bounds (if (use-region-p)
(cons (region-beginning) (region-end))
(bounds-of-thing-at-point 'symbol)))
(text (buffer-substring-no-properties (car bounds) (cdr bounds))))
(when bounds
(delete-region (car bounds) (cdr bounds))
(insert (s-lower-camel-case text)))))
If you didn't care about the option to use region, you could bind text locally to (thing-at-point 'symbol) instead of the call to buffer-substring-no-properties.
UPDATE. It turns out you can use (thing-at-point 'symbol) rather than (thing-at-point 'word) to get the full symbol for snake case.

Emacs insert centered comment block

I would like to create a macro for emacs that will insert a latex comment block with some centerd text like:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Comment 1 %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Comment 2 Commenttext 3 %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Is this possible in emacs-lisp?
Emacs comes with the command comment-box for this purpose. It produces centered comment boxes, although the width of the box varies depending on the content. E.g., with the region set around the following line:
This is a comment
when you call M-x comment-box the text is transformed to:
;;;;;;;;;;;;;;;;;;;;;;;
;; This is a comment ;;
;;;;;;;;;;;;;;;;;;;;;;;
I use a modifed version that places the comment box around the current line if the region isn't active, and then steps out of the comment afterwards. It also temporarily reduces the fill-column, so the comment box is not wider than your longest line:
(defun ty-box-comment (beg end &optional arg)
(interactive "*r\np")
(when (not (region-active-p))
(setq beg (point-at-bol))
(setq end (point-at-eol)))
(let ((fill-column (- fill-column 6)))
(fill-region beg end))
(comment-box beg end arg)
(ty-move-point-forward-out-of-comment))
(defun ty-point-is-in-comment-p ()
"t if point is in comment or at the beginning of a commented line, otherwise nil"
(or (nth 4 (syntax-ppss))
(looking-at "^\\s *\\s<")))
(defun ty-move-point-forward-out-of-comment ()
"Move point forward until it's no longer in a comment"
(while (ty-point-is-in-comment-p)
(forward-char)))
Here's a yasnippet that you can use:
# -*- mode: snippet -*-
# name: huge_comment
# key: hc
# --
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%${1:$(repeat-char (- 33 (/ (length yas-text) 2)) " ")}$1${1:$(repeat-char (- 74 (length yas-text) (- 33 (/ (length yas-text) 2))) " ")}%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$0
How to use it: type hc, call yas-expand and start typing the text. It will re-center itself
automatically.
This snippet will work from latex-mode or text-mode. I've noticed however a bug that
messes up the cursor position if you're using AUCTeX. In that case, you can momentarily switch
to text-mode.
The question was whether it is possible in emacs-lisp. Yes it is. There are several ways to do it.
I will show one way where you can also comment several lines of text.
Maybe, in the first line there is the title of the part of text and in the second one there is the author of this part.
A better way would be to advice LaTeX-indent-line function. This way you could edit the comment text and re-indent. When I find time I will show you also this variant.
Usage: Write your comment as clear text. Mark text as region with the mouse and then run the following command.
(defun LaTeX-centered-comment (b e)
"Convert region into centered comment."
(interactive "r")
(let* ((n (count-lines b e)))
(goto-char b)
(beginning-of-line)
(insert-char ?% fill-column)
(insert ?\n)
(setq b (point))
(center-line n)
(goto-char b)
(loop for i from 1 upto n do
(replace-region (point) (+ (point) 3) "%%%")
(end-of-line)
(insert-char ?\ (max 0 (- fill-column (- (point) (line-beginning-position)) 3)))
(insert "%%%")
(forward-line))
(insert-char ?% fill-column)
(insert ?\n)
))

macro for making a template

I am new to emacs and trying to figure out if there is an "easy" way to write a macro that will create a template for a standard function specification (see lines with # below). For example, I would like to execute a command to extract the input and output variables and place them in this standard format above the function (using the R language):
#This function does something
#Input:
# var1 - h
# var2 -
# var3 -
# var4 -
# Output:
# myoutput -
MyFunction <- function(var1,var2,var3=13,var4=NULL){
...
...
return(myoutput)
}
I don't know R so I'm just guessing how it looks from your one example. Add this to your Emacs init file (and evaluate it or restart), go to a function definition line and M-x my-r-insert-function-template:
(defun my-r-insert-function-template ()
"Insert a function template."
(interactive)
(let (name inputs output pos)
(beginning-of-line)
(save-excursion
(when (re-search-forward "\\([a-zA-Z0-9_\\.]+\\)\\s-*<-\\s-*function\\s-*(" nil t)
(setq name (match-string-no-properties 1))
(backward-char)
(forward-sexp)
(setq pos (1- (point)))
(backward-sexp)
(while (re-search-forward "[a-zA-Z0-9_\\.]+" pos 'go)
(push (match-string-no-properties 0) inputs)
(search-forward "," pos 'go))
(search-forward "{")
(setq pos (point))
(backward-char)
(forward-sexp)
(when (re-search-backward "return\\s-*(\\s-*\\([a-zA-Z0-9\\.]+\\)" pos t)
(setq output (match-string-no-properties 1)))))
(when name
(insert "# " name " : This function does something\n")
(when inputs
(insert "# Input:\n")
(setq inputs (nreverse inputs))
(dolist (input inputs)
(insert "# " input " -\n")))
(when output
(insert "# Output:\n")
(insert "# " output " -\n")))))
I don't use R but it looks like ESSr-autoyas could be used to do what you want. It makes use of YASnippet (a template package for Emacs).