3

Suppose I have a sentence:

Two three four. 

Suppose I put the point on the "T" in "Two" and enter the text oneSPC. Auto-capitalize-mode will recognize that the point is at the beginning of a line or sentence, and will automatically capitalize the "O" in "One."

Current output:

One Two three four.  

But since "Two" is no longer the beginning of the sentence, can I get auto-capitalize-mode to automatically downcase the next word in the sentence if there is one?

Desired output:

One two three four.  

In other words, how can I instruct auto-capitalize-mode to downcase the next word whenever it auto-capitalizes a word if there is another word after the point before a line break or period?

incandescentman
  • 4,111
  • 16
  • 53

2 Answers2

2

In your specific example you can hit M-l to down-case the next word.

A general solution would involve some code: you'd want a custom function to run after auto-capitalize and call downcase-word. You also want to make sure the next word is part of the same sentence first. Something like:

(defun downcase-next-word-in-sentence ()
  (interactive)
  "Downcase the next word after point, unless at the end of a sentence or line."
  (unless (looking-at (sentence-end)) 
    (downcase-word 1)))

You can probably advise auto-capitalize to call this after it executes.

Update

Using 'after' advice does not seem to work here. In fact, once I add any advice to auto-capitalize it seems to stop working altogether. Not sure why that is -- perhaps someone else can comment.

The other limitation with this approach is that it is blindly downcasing the next word. After looking at the source of auto-capitalize I see it is doing smarter things like checking for words that are always supposed to be capitalized ("I"), etc.

incandescentman
  • 4,111
  • 16
  • 53
glucas
  • 20,175
  • 1
  • 51
  • 83
  • Agreed that it's not ideal to blindly downcase the next word. At the very least it would be great to have an exception for the word "I." A more intelligent function could also make exceptions for any capitalized proper nouns listed in flyspell global dictionaries ("Paris") and user local dictionaries ("Daenerys"), but that would be way more complicated. So just an exception for "I" is probably the place to start. – incandescentman Jun 26 '15 at 17:21
  • Not sure if any code from this would help. http://emacs.stackexchange.com/questions/10632/search-for-and-correct-capitalized-words-that-are-not-at-the-beginning-of-a-sent?rq=1 Obviously I don't want to globally search and replace trying to find sentences like they are doing. As you correctly noted, I want a function that will run only as an after advice to auto-capitalize. – incandescentman Jun 26 '15 at 17:26
  • what about this? `(defadvice capitalize-word (after capitalize-word-advice activate) (unless (or (looking-at " I") (looking-at (sentence-end))) (downcase-word 1))) ` – incandescentman Jul 13 '15 at 06:17
  • Not quite: this will ignore any words starting with "I". You probably want something like `(looking-at " I\\b")` to only match I followed by a word boundary. Is this working for you with `auto-capitalize`? I don't have that installed anymore. – glucas Jul 13 '15 at 15:34
  • Here's my inept, non-programmer's attempt at an entire advice. Doesn't work as intended at the moment. `(defadvice capitalize-word (after capitalize-word-advice activate) " (unless (or (looking-at " I\\b") (looking-at (sentence-end)) (looking at (line-end)); doesn't work (looking-at (user-full-name)) ) (downcase-word 1))) ` – incandescentman Jul 14 '15 at 00:18
  • Better formatting here: http://pastebin.com/s9WsmBcH – incandescentman Jul 14 '15 at 00:21
  • Perhaps a better approach than hard coding a list of capitalized words would be to build your `looking-at` regex from the same `auto-capitalize-words` list that auto-capitalize uses. When I get a chance I'll update my answer based on your findings so far and this suggestion. Also: I'm curious about your handling of line endings -- shouldn't a line break be irrelevant in deciding what to up/downcase? – glucas Jul 14 '15 at 17:23
  • Awesome, looking forward! To your question about line breaks, yes, it's necessary. Here's an example. http://pastebin.com/4MrhAaNY Suppose the point is where the carat is and you type "four five six. " Current behavior will downcase the next org-mode heading—unwanted behavior. – incandescentman Jul 14 '15 at 19:10
  • do you know how to make the downcase-word call stop when it finds a line break? – incandescentman Jul 21 '15 at 16:04
  • I suspect it's not really about line breaks? In text mode a sentence continuing on the next line should still be treated normally. You might want to handle org separately - there are org commands to navigate by element that might be used to check if you're at the end of a headline. – glucas Jul 22 '15 at 00:00
  • that sounds more complicated than it needs to be. I only use org anyway, and a line break covers all cases, including numbered lists, bulleted lists, new headings, new TODO headings, block quotes, source code blocks, literal examples, etc without the need to parse different elements. – incandescentman Jul 22 '15 at 00:03
1

Here's what I came up with, building on @glucas's idea to use defadvice. This code works, but I'm just starting to learn Lisp (and programming) so feel free to suggest improvements to my code.

(defadvice capitalize-word (after capitalize-word-advice activate)
"After capitalizing the new first word in a sentence, downcase the next word, since that next word is no longer the first word in the sentence." 
(unless 
  (or
   (looking-at " I\\b") ; never downcase the word "I" 
   (looking-at (sentence-end))
   (looking-at "[ ]*$") ; hopefully this means "zero or more whitespaces, then end of line"
   (looking-at (user-full-name))
   )
 (downcase-word 1))) 

I also created a companion function to re-capitalize the first word of the next already existing sentence if you wind up typing an entire sentence with a period at the end:

(defun smart-period ()
"When you type a period, capitalize the first word of the next sentence."
(interactive)
(insert ".") 
(save-excursion
(unless (looking-at "[ ]*$") ; zero or more whitespaces, then line break
(capitalize-unless-org-heading))
))

And since I didn't know how to concatenate looking-at regexps, I created an exception for org-mode headings in a separate function:

(defun capitalize-unless-org-heading ()
(interactive)
(unless (looking-at org-complex-heading-regexp)
(capitalize-word 1)
)) 
incandescentman
  • 4,111
  • 16
  • 53