1

I'm currently trying to improve the advanced-return function for C/C++, I found somewhere on the internet. It basically detects if a comment is started and on hitting ENTER continues it on the next line. I was able to improve the single line comment detection, but failed trying to implement behaviour I witnessed the c-electric-paren function (and others) doing. By hitting C-q prior to the respective paren key, it inserts a literal paren, skipping the balancing. This functionality I want to replicate, i.e. C-q <RET> should only insert a newline and not try to continue a potential comment.

By looking at the source of c-electric-paren, I reckoned it must have something to do with the interactive specification and managed to cook up a basic example:

(defun cq-test (arg) (interactive "*P") (if (not arg) (message-box "No argument provided.") (if (> (prefix-numeric-value arg) 0) (message-box "Called with numeric argument.") (message-box "C-q but no numeric value provided") ) ) 

I then bound this test function to <RET> for quick testing. The (not arg) part works and detects C-q not being hit prior to invocation. But as soon as I hit C-q right before, my function does not seem to be invoked at all (none of the message boxes appearing whatsoever), but a strange character (^M in my case) is being added to the buffer (a control character representing the line ending maybe?).

I feel like I'm somewhat close to a solution, but missing some crucial fact(s). Can someone explain to me why my function is not invoked at all with C-q? Could someone also please tell me if my handling of arg is able to achieve the behaviour I'm looking for?

3
  • By default, does M-j provide the advanced-return behaviour you were originally wanting? Commented Jan 4 at 3:44
  • I didn't know about that one. But it seems it does pretty much the same as the advanced-return I have. Commented Jan 6 at 22:08
  • I've added an extra answer to elaborate on that, which might be useful. Commented Jan 6 at 23:09

3 Answers 3

0

C-q is quoted-insert. It inserts the next character literally1. This cancels any special behavior that is configured for the character (by binding a command to the character, as well as a few other mechanisms such as abbrevs). For example, ( in C mode is bound to c-electric-paren, which is what triggers special behaviors such as potentially inserting a matching parenthesis and reindenting the current line. Pressing C-q ( just inserts a parenthesis because that's what C-q does: it ignores any key binding for (.

C-q C-m inserts a literal control-M character. Emacs prints C-m as RET when describing key sequences. Unfortunately, for historical reasons, there are two control characters associated with line breaks: carriage return = control-M, and line feed = control-J. This dates back to typewriters where a line break required two mechanical operations: returning the carriage to the left margin, and advancing the paper by one line. On keyboards, the Return or Enter or key sends the control-M character. But in text files, on most modern platforms except Windows, a line break is represented by a control-J character. On Windows, a line break is the two-character sequence control-M control-J, but Emacs converts it to control-J internally22. As a consequence, C-q RET doesn't insert a line break, it inserts a rarely-used control character. To insert a line break with no special effects, you need C-q C-j.

There is a separate mechanism by which you can pass a numerical argument to the next command in a generic way: prefix arguments. If you press C-u, then type some digits, it passes that number as the prefix argument to the next command. For example, C-u 4 2 ( invokes the command that is bound to ( (so c-electric-paren in C mode) with the prefix argument 42. There are a few other ways to type a prefix argument, including M-4 M-2 ( and ESC 4 2 (. (ESC alone doesn't start a prefix argument, only when it's followed by a digit.) This prefix argument is what interactive functions get when they use p or P in an interactive specification. p is the numerical value, while P gives you more control, in particular to distinguish between no prefix argument (nil) and an explicit argument with the value 1 (1).

For insertions and motion, there is a convention that the prefix argument N means repeating the command N times, and also bypasses “electric” behavior. For example, M-1 ( inserts a parenthesis with no electric behavior, because c-electric-paren detects the prefix argument and does not perform any electric behavior in that case. Similarly, M-1 RET or C-u 1 RET inserts a line break without triggering some of the usual extra behaviors. However, the default newline command still performs some extra things even with a prefix argument, such as indenting the newly created line.

You can choose what to do in your advanced-return function based on the prefix argument. Use P in the interactive specification and only invoke special behaviors if the argument is non-nil ((when arg …)).

1 Except digits, which let you insert a character via its code point, but that's not relevant here.
2 Similar to how C converts line breaks to a single '\n' when reading text files.

1
  • I see, so I misunderstood some things about prefix arguments, and what C-q actually does. Thanks for the clarification. Armed with this knowledge I'll go try and see if I can incorporate it in a useful way. Commented Jan 6 at 22:05
1

Can someone explain to me why my function is not invoked at all with C-q?

Yes; the purpose of C-q is to bypass any functions bound to keys, and instead insert the literal character associated with that key -- which is exactly what it did; hence why you see ^M which is ascii control character 13, typically known as RET in Emacs (and the same thing as C-m).

(kbd "RET") => "^M" 

As C-h C-q tells you:

C-q runs the command quoted-insert

Read next input character and insert it.
This is useful for inserting control characters.

Also why C-q ( just inserts a paren, of course (i.e. nothing at all to do with the definition of c-electric-paren, because that function isn't being called. There isn't any interactive declaration relating to C-q).

0

This additional answer is just a tangent to the advanced-return comments, as the purpose of that ("detect if a comment is started and on hitting ENTER continues it on the next line") is already available as standard via the key binding M-j (or occasionally C-M-j just to make things inconsistent).

The actual command bound to those keys may vary, but we can simply look up the key binding to discover what it is for any given buffer.

I use the following code in a function I've added to prog-mode-hook along with a few other major mode hooks:

 ;; Make RET do whatever C-M-j or M-j does. Note that there are libraries ;; which bind only C-M-j (e.g. fortran.el and octave.el), which I'm guessing ;; is the older key sequence, even though the manual mostly refers to M-j. ;; As such, I'm giving C-M-j precedence. ;; (info "(emacs)Comment Commands") ;; (info "(emacs)Options for Comments") (local-set-key (kbd "RET") (or (key-binding (kbd "C-M-j")) (key-binding (kbd "M-j")))) (local-set-key (kbd "<S-return>") 'newline) 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.