How to fix a strange indentation behaviour in Emacs sass-mode - emacs

This issue ( https://github.com/nex3/sass-mode/issues/5 ) already explained what I want to say, please have a look at it. Is there any way to fix it.
When I hit backspace, for instance after c-j, which always goes to one
level deeper instead of the same level, nothing happens except for the
control buffer giving the message "Mark set". When I check I see it's
bound to haml-electric-backspace. Thus the only two ways I can create
a new line and move my cursor to the same level of indentation as the
last line is ret tab tab or c-j followed by c-b c-b or tab tab. I
think c-j should default to the same level and that backspace should
have the default emacs behavior.

I found a bunch of bugs with sass-mode.
Here's some code I keep in my .emacs that might help:
(defconst sass-line-keywords
'(("#\\(\\w+\\)" 0 font-lock-keyword-face sass-highlight-directive)
("/[/*].*" 0 font-lock-comment-face)
("[=+]\\w+" 0 font-lock-variable-name-face sass-highlight-script-after-match)
("!\\w+" 0 font-lock-variable-name-face sass-highlight-script-after-match)
(":\\w+" 0 font-lock-variable-name-face)
("\\w+\s*:" 0 font-lock-variable-name-face)
("\\(\\w+\\)\s*=" 1 font-lock-variable-name-face sass-highlight-script-after-match)
("\\(:\\w+\\)\s*=" 1 font-lock-variable-name-face sass-highlight-script-after-match)
(".*" sass-highlight-selector)))
(defconst sass-selector-font-lock-keywords
'( ;; Attribute selectors (e.g. p[foo=bar])
("\\[\\([^]=]+\\)" (1 font-lock-variable-name-face)
("[~|$^*]?=\\([^]=]+\\)" nil nil (1 font-lock-string-face)))
("&" 0 font-lock-constant-face)
("\\.\\w+" 0 font-lock-type-face)
("#\\w+" 0 font-lock-keyword-face)
;; Pseudo-selectors, optionally with arguments (e.g. :first, :nth-child(12))
("\\(::?\\w+\\)" (1 font-lock-variable-name-face)
("(\\([^)]+\\))" nil nil (1 font-lock-string-face)))))
(defconst sass-non-block-openers
'("^.*,$" ;; Continued selectors
"^ *#\\(extend\\|debug\\|warn\\|include\\|import\\)" ;; Single-line mixins
"^ *[$!]" ;; Variables
".*[^\s-]+: [^\s-]" ;; a setting of some sort
))
I think the problem was particularly with sass-non-block-openers. I'm sorry that I'm just dumping some code on you. Hope this helps somehow anyway.

when finished, call RET, not C-j
backspace will step down level
everything okay AFAIS, no bug

Related

Move to the beginning of the file in Emacs

How do I move to the beginning of the file in emacs (i.e. the effect of Ctrl+Home in windowed text editors)?
Mashing pageup does not move the cursor to the beginning (nor does Ctrl+Home, ofc).
Here the shortcut for the desired result is described as:
M-< :
Move to the top of the buffer (beginning-of-buffer). With numeric argument n, move to n/10 of the way from the top.
M-> :
Move to the end of the buffer (end-of-buffer).
However Meta + < yields "No M-x tags-search or M-x tags-query-replace in progress" message.
I am typing the shortcut as Alt + Shift + , since to get the "<" I have to type "Shift + ,"
Am I doing something wrong?
Thank you.
Edit:
Turns out this is an issue only when running emacs through screen, where the keyboard shortcuts are, for some reason, misinterpreted.
For example, C-Home gives this error message:
M-[ 1 ; 5 h (translated from M-[ 1 ; 5 H) is undefined
Any way around it?
This works on newer Emacs
Esc followed by < #beginning of file
Esc followed by > #end of file
Works great in combination with ssh and Tmux
I cannot reproduce the exact behavior as C-HOME that you experience. For me it translates to M-[ 1 ;, without the 5H (but that is actually inserted...).
But, given that, here's how I would set up the binding.
I'd go into the *scratch* buffer and type
(read-key-sequence "please type C-home ") C-j
Which will prompt you for a key sequence, so do C-HOME and Emacs should insert the following right after the read-key-sequence line:
"^[[1;"
5H
This shows me the actual string for the key sequence, as well as the mysterious 5H.
Using the string, I'd set up the binding in my .emacs like so:
(global-set-key "^[[1;" 'beginning-of-buffer)
This will define the key binding appropriately, except that (for me) it now inserts 5H at the top of the buffer. I believe the 5H is a product of screen somehow...
Updated to add:
The 5H annoys me, but as far as I can tell, Emacs thinks we are literally typing it. So, I coded up two alternatives which result in the same behavior.
The first uses keyboard-quit to interrupt the key sequence after getting to the beginning of the buffer. This prevents the 5H from being inserted. But it has the downside of doing a keyboard-quit - which will flash/ding at you ever time. Kind of annoying.
(global-set-key "^[[1;" 'my-bob)
(defun my-bob ()
"Go to beginning of buffer, then quit"
(interactive)
(beginning-of-buffer)
(keyboard-quit))
To avoid the keyboard-quit, I wrote a different version which runs a little snippet of code which deletes the 5H if it exists. It's a little more involved, but does the job.
(global-set-key "^[[1;" 'my-bob)
(defun my-bob ()
"Go to beginning of buffer, then delete any 5H inserted by the binding"
(interactive)
(beginning-of-buffer)
(run-with-idle-timer 0.1 nil 'delete-inserted-chars "5H"))
(defun delete-inserted-chars (chars)
(save-excursion
(backward-char (length chars))
(if (looking-at (regexp-quote chars))
(delete-char (length chars)))))
The delete-inserted-chars can be reused for other bindings in screen which happen to insert characters as well.
One thing you could do it go to line one:
C-u 1 M-g M-g

Search-forward typo aborts macros

Search-forward seems to not work well inside emacs macros. For example, say I want to use a macro to help replace FOO with BAR in the following string:
aoeuFOOsnutehaFOOsanotehuFOO
I might begin recording a macro, search-forward for FOO, and then hit backspace a few times and type BAR. Then I can replay this macro to replace the rest of the occurrences. Pretty simple.
Suppose I hit the wrong key and search-forward for FOOO. I hit Backspace to remove the extra O, and finish recording the macro. But when I replay it, nothing happens. FOOO is not in the document, so the macro replay is immediately aborted when the search fails.
This gets annoying in longer macros. As it is, whenever I record a macro, I have to make sure I type in the search text for my search-forwards perfectly. If I make even one mistake, I have to cancel recording my macro and start over; otherwise, the macro will just abort when I replay it.
To sum up, if you use search-forward and commit a typo while recording a macro, the macro will not replay properly because it will abort as soon as it replays your typo.
Any workarounds or solutions to this problem?
You don't have to abort the macro: The easiest thing to do is to simply finish recording the macro (typos and all) and then edit it via C-x C-k C-e (kmacro-edit-macro-repeat) to remove the typo(s).
For instance, when you call this command after defining a macro that is supposed to simply search for occurrences of foo in the current buffer but contains a typo in the search (you typed fooo instead of foo before fixing the typo), the buffer for editing it would look like this:
C-s ;; isearch-forward
f ;; self-insert-command
ooo ;; self-insert-command * 3
DEL ;; delete-backward-char
RET ;; newline
To fix the macro, delete one of the os from the third line and remove the fourth line:
C-s ;; isearch-forward
f ;; self-insert-command
oo ;; self-insert-command * 2
RET ;; newline
Note that you don't have to change self-insert-command * 3 to self-insert-command * 2, I just did that to avoid confusion.
When you're done, hit C-c C-c to recompile the macro and close the *Edit macro* buffer.
The documentation of the ding function says (emphasis mine):
(ding &optional ARG)
Beep, or flash the screen.
Also, unless an argument is given, terminate any keyboard macro currently executing.
Digging into the source we can see that all isearch-* functions that call ding do so without passing a non-nil ARG. You could redefine these functions to change the way they call ding (not sure if it would be good idea to remove the calls to ding entirely), but there is an easier way to achieve what you want:
You can make sure ding is always called with a non-nil ARG by advising it as follows:
(defadvice ding (before be-nice activate compile)
(ad-set-arg 0 t))
As documented here, the ad-set-arg macro
sets the value of the actual argument at position to value.
So what the advice does is tell Emacs to set ARG to t before running the body of the original ding function, causing ding to be nice and not terminate keyboard macros anymore.
With the advice in place, you can now transform
aoeuFOOsnutehaFOOsanotehuFOO
into
aoeuBARsnutehaBARsanotehuBAR
with a "faulty" macro that contains a corrected typo:
C-s ;; isearch-forward
F ;; self-insert-command
OOO ;; self-insert-command * 3
DEL ;; delete-backward-char
RET ;; newline
3*DEL ;; delete-backward-char
BAR ;; self-insert-command * 3
EDIT
As #phils mentions in the comments below, allowing macro execution to continue regardless of errors can lead to unwanted consequences, so use this solution with care. If you want to be able to quickly enable or disable the advice so that you can use it selectively (i.e., in situations where you are sure it won't mess things up), define it like this:
(defadvice ding (before be-nice) ; advice not activated by default
(ad-set-arg 0 t))
and add a command for turning it on and off to your .emacs:
(defun toggle-ding-advice ()
(interactive)
(if (ad-is-active 'ding)
(ad-disable-advice 'ding 'before 'be-nice)
(ad-enable-advice 'ding 'before 'be-nice))
(ad-activate 'ding))
(global-set-key (kbd "C-c a") 'toggle-ding-advice)
You can then toggle the advice by simply pressing C-c a.

word boundary in Emacs font lock keywords

The following code fails to highlight 23's in 23-23 if pasted and evaluated in the scratch buffer, but not if done in a text buffer.
;; Example 1
'(1234 23 23-23 end)
(progn
(font-lock-add-keywords nil
`(("\\b23\\b"
(0 'success))
"end"))
(font-lock-fontify-buffer))
Why does it fail when M-x isearch-forward-regexp RET \b23\b still matches 23's in 23-23?
Even if I change to the following code, only the first 23 in 23-23 gets highlighted.
;;; Example 2
'(1234 23 23-23 end)
(progn
(font-lock-add-keywords nil
`((,(rx (or word-boundary
"-")
(group "23")
(or word-boundary
"-"))
(1 'success))
"end"))
(font-lock-fontify-buffer))
Side note: "end" is there so that I can detect if the highlighter for 23 is ill formed. If it is ill formed or signals errors silently, end won't get highlighted.
;;; Example 3 (with xy instead of 23. also passing t and 'append.)
;;; if evaluated in the scratch buffer, it doesn't highlight xy in xy-xy
'(wxyz xy xy-xy end)
(progn
(font-lock-add-keywords nil
`(("\\bxy\\b"
(0 'success t))
"end")
'append)
(font-lock-fontify-buffer))
The fact that it does not in buffer *scratch* suggests that it is a problem with the current mode. There are two main possibilities:
What #wvcvw suggested: check what the syntax class of chars 2 and 3 is.
The font-lock-keywords already defined for the mode interact with your code -- e.g., they override it. Try adding 'APPEND as a third arg to font-lock-add-keywords. Try adding t as a HIGHLIGHT expression to your highlighter sexp (see the doc). That should let your highlighting override any that might already be there otherwise.
BTW, you say it does not work in a "text buffer", but what does that mean? From emacs -Q, evaluating your code in a buffer in text-mode shows that it does work. Investigate what your "text buffer" mode is and try the suggestions above (both bullets if necessary, but try the second one first).

emacs equivalent of ct

looking for an equivalent cut and paste strategy that would replicate vim's 'cut til'. I'm sure this is googleable if I actually knew what it was called in vim, but heres what i'm looking for:
if i have a block of text like so:
foo bar (baz)
and I was at the beginning of the line and i wanted to cut until the first paren, in visual mode, I'd do:
ct (
I think there is probably a way to look back and i think you can pass more specific regular expressions. But anyway, looking for some emacs equivalents to doing this kind of text replacement. Thanks.
Here are three ways:
Just type M-dM-d to delete two words. This will leave the final space, so you'll have to delete it yourself and then add it back if you paste the two words back elsewhere.
M-z is zap-to-char, which deletes text from the cursor up to and including a character you specify. In this case you'd have to do something like M-2M-zSPC to zap up to and including the second space character.
Type C-SPC to set the mark, then go into incremental search with C-s, type a space to jump to the first space, then C-s to search forward for the next space, RET to terminate the search, and finally C-w to kill the text you selected.
Personally I'd generally go with #1.
as ataylor said zap-to-char is the way to go, The following modification to the zap-to-char is what exactly you want
(defun zap-up-to-char (arg char)
"Like standard zap-to-char, but stops just before the given character."
(interactive "p\ncZap up to char: ")
(kill-region (point)
(progn
(search-forward (char-to-string char) nil nil arg)
(forward-char (if (>= arg 0) -1 1))
(point))))
(define-key global-map [(meta ?z)] 'zap-up-to-char) ; Rebind M-z to our version
BTW don't forget that it has the ability to go backward with a negative prefix
That sounds like zap-to-char in emacs, bound to M-z by default. Note that zap-to-char will cut all the characters up to and including the one you've selected.

Emacs C-mode indent problem with Doxygen style comment

I am having a problem with doxygen style multi-line comments with emacs indent feature in c-mode. According to doxygen manual (http://www.doxygen.nl/manual/docblocks.html) the form below is accepted.
/********************************************//**
* ... text
***********************************************/
I am trying to use this format in emacs but when I tab in on the line '* ... text' the * ends up below the /** at the end of the first line like so:
/********************************************//**
* ... text
***********************************************/
Any suggestions on how to fix this? Still learning all the in-and-outs of emacs.
The reason it is indenting as such is that (by default) multi-line comments are lined up with the start of the comment on the previous line. In this case, the start of the containing comment is in column 47.
Now, how to fix it. Here's how I figured out how to fix it, the solution is at the end.
First, there's the cc-mode manual, specifically the section on customizing indentation. A useful key is C-c C-s which tells you which syntax is being used for indentation. In this case it is ((c 61)) - the c is the important part for now.
To customize it interactively, you can type C-c C-o (when the point is on the line whose indentation you want to fix). You'll be prompted for which syntax entry you want to customize (defaults to c in this case b/c that's the current syntax), then you'll be prompted for what you want to change the syntax entry to (default is c-lineup-C-comments).
Now we can look at that function to see how we might customize it to meet your needs. M-x find-function c-lineup-C-comments.
That's where it gets more difficult. You can customize the way cc-mode handles comment indentation, but what it looks like you want it to do (in this case) is to recognize that the c-comment you're in is immediately preceded by another c-comment, and that comment is the one you want to align indentation to.
How do you do that? The easiest way I can think of is to advise 'c-lineup-C-comments to recognize this special case and change the value of its first argument to be what you want. My limited testing shows this works for your example:
(defadvice c-lineup-C-comments (before c-lineup-C-comments-handle-doxygen activate)
(let ((langelm (ad-get-arg 0)))
(save-excursion
(save-match-data
(goto-char (1+ (c-langelem-pos langelem)))
(if (progn
(beginning-of-line)
;; only when the langelm is of form (c . something)
;; and we're at a doxygen comment line
(and (eq 'c (car langelm))
(looking-at "^\\(\\s-*\\)/\\*+//\\*\\*$")))
;; set the goal position to what we want
(ad-set-arg 0 (cons 'c (match-end 1))))))))
The end result of this advice should be that the argument passed into c-lineup-C-comments should be transformed from (c . 61) to (c . 17) (or something like that), essentially fooling the routine into lining up with the comment at the beginning of the line, and not the comment which you're currently modifying.
Which version of emacs are you using? My emacs 22 has this problem, but on another machine with emacs 23 does not. This is probalby due to some "electric" indentation. Try M-x describe-key RET RET and also M-x describe-mode to get a nice place to start searching for clues. There is also http://doxymacs.sourceforge.net/ but I have not tesed it personally.