How to automatically align comments in different pieces of code? - emacs

I have to do a very specific task to re-do over and over again and would like to have it put permanently in my .emacs file. But I am not versed enough in emacs-lisp to manage it:
Keystroke-1, say [F8]
remember the current cursors' column position in, say, xtab
Keystroke-2, say [F9] while cursor is in some other line:
find leftmost string // in current line, if none is there, beep and stop
insert as many spaces so the // gets to previously remembered column xtab, or do nothing if cursor is already beyond xtab
search-forward for next // and place the cursor on it
I managed to assign it to a temporary keyboard macro, but have to re-record it for every changing xtab value.
The ultimate goal is that I want to align the comments in different pieces of code easily, from
int main() { // the enty function
int x = 100; // my new variable
for(int i=1; i<2012; ++i) { // loop a lot
x -= i;
}
} // end of all things
to
int main() { // the entry function
int x = 100; // my new variable
for(int i=1; i<2012; ++i) { // loop a lot
x -= i;
}
} // end of all things
Any idea how I can automate this? What do I have to put in my .emacs-file to archive this -- or similar?

As tungd said, align-regexp is good for this sort of thing.
(defun my-align-comments (beginning end)
"Align instances of // within marked region."
(interactive "*r")
(let (indent-tabs-mode align-to-tab-stop)
(align-regexp beginning end "\\(\\s-*\\)//")))
Which is like the interactive call:
M-x align-regexp RET // RET
Or for a more language-agnostic version:
(defun my-align-comments (beginning end)
"Align comments within marked region."
(interactive "*r")
(let (indent-tabs-mode align-to-tab-stop)
(align-regexp beginning end (concat "\\(\\s-*\\)"
(regexp-quote comment-start)))))

Not exactly an answer to your question, but to achieve the desired goal you can just mark the region and use align-regexp.

Here's the code:
(defvar c-current-comment-col 30)
(defun c-set-comment-col ()
(interactive)
(setq c-current-comment-col (current-column)))
(defun c-comment-to-col ()
(interactive)
(beginning-of-line)
(when (re-search-forward "//" (line-end-position) t)
(backward-char 2)
(let ((delta (- c-current-comment-col
(current-column))))
(if (plusp delta)
(insert (make-string delta ? ))
(if (looking-back
(format "\\( \\{%d\\}\\)" (- delta)))
(delete-region
(match-beginning 1)
(match-end 1))
(message
"I'm sorry Dave, I afraid can't do that.")))))
(next-line 1))
(global-set-key [C-f6] 'c-set-comment-col)
(global-set-key [f6] 'c-comment-to-col)
I've added a next-line call to the end. Now you can do
C-f6 f3 f6 M-0 f4 to align until end of buffer.

M-x align is very powerful and will automatically handle the particular example given.
However, it will also align variable declarations, which may be more than you want. In that case, you would have to customize align-region-separate or use the align-regexp answer.

Related

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.

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

Moving the cursor inside brackets and intent when pressing RETURN

Let's see if I achieve to explain it. When I type this in emacs 24:
int foo() {|}
Note: | = Cursor
And press the Return key, I get the next output:
int foo() {
|}
So, my question is: how can I achieve the next behaviour?
int foo() {
|
}
Instead of global-set-key you should probably use something like (define-key 'c++-mode-map ..., but here's the basics.
(defun newline-and-push-brace ()
"`newline-and-indent', but bracket aware."
(interactive)
(insert "\n")
(when (looking-at "}")
(insert "\n")
(indent-according-to-mode)
(forward-line -1))
(indent-according-to-mode))
(global-set-key (kbd "RET") 'newline-and-push-brace)
You could define a function that checks if you're in that situation and does what you want if you are, and otherwise just calls whatever the newline command for your major mode is, e.g.:
(defun brackets-newline (point)
(interactive "d")
(setq next-char (char-before point))
(if (and next-char
(char-equal next-char 123))
;; if we are sitting in front of a close bracket, do what you want
(progn
(newline)
(newline)
(previous-line)
;;call whatever "TAB" is in this mode
(funcall (key-binding (kbd "TAB"))))
;; otherwise just insert a newline
(newline)))
Then bind this to (kbd "RET")
There may be a better way to do this using defadvice or some such, this seemed to work pretty well for me though.

Context-sensitive font-locking in emacs

Basically, I'm trying to syntax highlight the following piece of coffeescript code the way I want it. Explanation of the syntax of coffeescript functions can be found here.
nameHere = (tstamp, moo, boo) ->
...
The names tstamp, moo and boo should be colored pink (and nothing else, not the commas and not the brackets) because they are parameters to a lambda function.
highOrderFun ((x) -> x * x) someList
Here it is the first x that is the parameter. Parameters can have default arguments:
class Foo
meth: (msg = "Hello", bar = "foo") ->
....
Default arguments can be variables themselves:
defColor = "red"
print = (msg, color = defColor) ->
...
So msg and color above should be highlighted, but not defColor. An even trickier case is functions with default arguments that themselves are functions. I think that is to hard for emacs' font-lock to highlight correctly, but I'm including it anyway:
funTakingFuns = (f1 = ((a, b) -> a*b), f2 = ((c, d) -> c/d)) ->
...
This appears to be pretty complicated to achieve in emacs because you want the highlighting to be context sensitive. I've read up on the documentation on font-lock but haven't been able to figure it out.
I'd be grateful if someone could show me what to set font-lock-defaults to make it syntax highlight the way I want it.
Update Showing more coffeescript syntax examples.
font-lock-keywords allows function values in the MATCHER field:
where MATCHER can be either the regexp to search for, or the function name to
call to make the search (called with one argument, the limit of the search;
it should return non-nil, move point, and set match-data appropriately if
it succeeds; like re-search-forward would).
So we need to write a function that would search for the next function argument in the buffer.
Something like this:
(defun coffee-match-next-argument (limit)
(let ((start (point)))
;; Look for the arrow.
(when (re-search-forward ") *->" limit t)
;; Save the position of the closing paren.
(let ((stop (point)))
(goto-char (match-beginning 0))
;; Go to the opening paren.
(goto-char (nth 1 (syntax-ppss)))
;; If we're before our initial position, go forward.
;; We don't want to find the same symbols again.
(when (> start (point))
(goto-char start))
;; Look for the next symbol until the arrow.
(or (re-search-forward "\\((\\|,\\) *\\(\\(\\sw\\|_\\)+\\)" stop 'mv)
(coffee-match-next-argument limit))))))
And the setup, to use with existing coffee-mode:
(font-lock-add-keywords
'coffee-mode
'((coffee-match-next-argument 2 font-lock-variable-name-face)))
You can also use this in font-lock-defaults, of course.
This will likely use some other color than pink, but that's easy to change.
This is more kind of a hack, it's far from optimal (as I am not familiar at all with coffeescript), but perhaps with a little tweaking yourself, you can get this done.
All the ingredients are there.
The triggering of the commands/functions are based on the assumption that you use coffee-mode.
If you do not, this is not a big trouble, you'll just have to hook these things differently.
Put the following line in your .emacs:
(eval-after-load 'coffee '(load "/PATH/custom-coffee-font-lock.el"))
You can just save the below text as a file, and it will:
(1) Font lock when you trigger coffee-mode
(2) Font lock current line when you type the ">" as part of "->"
(3) Allow to font-lock the buffer by running M-x coffee-init-font-lock
;;;; custom-coffee-font-lock
;; Firstly, create a new font for this.
(make-face 'font-lock-coffeescript-face)
(set-face-foreground 'font-lock-coffeescript-face "pink")
;; Next, one function that should be ran after a file is identified as
;; a coffeescript file. It will do the font-locking you want on
;; the whole buffer. It is also possible to run it manually.
(defun coffee-init-font-lock ()
(interactive)
(save-excursion
(goto-char 1)
(while (search-forward-regexp "=.+->" nil t)
(search-backward-regexp "(")
(forward-char 1)
(add-text-properties
(point) (- (search-forward-regexp "," nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))
(add-text-properties
(point) (- (search-forward-regexp "," nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))
(add-text-properties
(point) (- (search-forward-regexp ")" nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))
(move-end-of-line 1)))
)
;; This actually runs that function.
(coffee-init-font-lock)
;; This advice will be ran everytime you write something. It will check
;; whether "->" is before it, so when you type the final ">", it will
;; do the font locking for the current line (it also checks for your mode).
(defadvice self-insert-command (after coffee-font-lock activate)
(when (and (looking-back "->") (eq major-mode 'coffee-mode))
(save-excursion
(search-backward-regexp "(")
(forward-char 1)
(add-text-properties
(point) (- (search-forward-regexp "," nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))
(add-text-properties
(point) (- (search-forward-regexp "," nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))
(add-text-properties
(point) (- (search-forward-regexp ")" nil nil) 1)
'(font-lock-face font-lock-coffeescript-face))))
)
(provide 'custom-coffee-font-lock)
;;; custom-coffee-font-lock.el
If you have any requests, let me know. Like I said, I do not use CoffeeScript, so this might throw huge errors your way. At the very least it should help with some basic ideas.
Result:

How to switch the point to previous line after a } was input?

I am working with emacs24 with cc-mode, I want to know how to make my emacs more "clever". After I type a }, it will auto insert a new line and indent as excepted. I want to know how to switch the point to previous line.
For example, when i define a function, Now my emacs behavior is:
void f()
{
}
//point
"//point" is the position of cursor after } was input.
But i want is this:
void f()
{
//point
}
I hope the position of cursor can switch to previous line and indent automatically.
I know emacs can do this, but I don't know how to do it, who can help me?
I think you are after these.. C-M-u, C-M-d, C-M-f and C-M-b
Practice a bit... They are kind of global and they do behave contextually in almost all modes..
UPDATE:
ohh.. It seems you want to place the cursor automatically.. actually in more general Emacs will help you not to type } at all. I mean emacs can insert closing paran automatically.
There is inbuilt one
electric pair mode
third party
autopair.el
I don't trust anything electric, so I wrote this function.
(defconst insert-logical-brackets-logical-bracket-begin "{")
(defconst insert-logical-brackets-logical-bracket-end "}")
(defconst insert-logical-brackets-default-style 0)
(make-variable-buffer-local 'logical-bracket-begin)
(make-variable-buffer-local 'logical-bracket-end)
(make-variable-buffer-local 'insert-logical-brackets-default-style)
(defun insert-logical-brackets(&optional style)
"If STYLE = 0(default, according to `insert-logical-brackets-default-style' value), make a newline before opening bracket, if line is not empty. Make a newline after closing bracket, if there is something after this bracket. Make two newlines in the middle.
If STYLE = 1, don't make newlines before opening a bracket(one of c styles).
If STYLE = 2, don't make newlines before opening and after closing bracket.
If STYLE = 3, allways make all newlines.
If STYLE is not nil, don't make newlines between brackets(still makes before/after lines)."
(interactive "P")
(when (eq style nil)
(setq style insert-logical-brackets-default-style))
(funcall indent-line-function)
(unless (or (eq 1 style) (eq 2 style))
(when (or (/= (point) (save-excursion (back-to-indentation) (point))) (eq 3 style))
(newline)
(funcall indent-line-function)))
(unless (and (integerp style) (= 2 style))
(when (or (not (looking-at "\n")) (eq 3 style))
(newline)
(funcall indent-line-function)
(forward-line -1)
(goto-char (point-at-eol))))
(insert logical-bracket-begin)
(funcall indent-line-function)
(let ((return-point (point)))
(when (or (not style) (or (eq 0 style) (eq 1 style) (eq 2 style) (eq 3 style)))
(newline)
(funcall indent-line-function)
(setq return-point (point))
(newline))
(insert logical-bracket-end)
(funcall indent-line-function)
(goto-char return-point)))
Take a look at template systems like yasnippet: http://www.emacswiki.org/emacs/CategoryTemplates
auto-indent-mode maybe what you want!