How to define whole line comment syntax in Emacs? - emacs

I want the sequence // to start a comment when it is at the beginning of a line. But inside of a line it should not start any comments.
// this is a comment
This is a URL: http://example.com
Is it possible?

I'd do it this way:
(defvar my-foo-mode-syntax-table
(let ((st (make-syntax-table)))
;; Add other entries appropriate for my-foo-mode.
(modify-syntax-entry ?/ ". 12" st)
(modify-syntax-entry ?\n "> " st)
st))
(defvar my-foo-font-lock-keywords
;; Add other rules appropriate for my-foo-mode.
())
(define-derived-mode my-foo-mode nil "My-Foo"
(setq-local font-lock-keywords '(my-foo-font-lock-keywords))
;; Add other settings appropriate for my-foo-mode.
(setq-local syntax-propertize-function
(syntax-propertize-rules ("./\\(/+\\)" (1 ".")))))
Notice: No need for any special font-lock rule since font-lock automatically highlights comments for you, based on the syntax-tables.

You can do this by writing a syntax-propertize-function
I have written an example major mode that shows this below.
Emacs built in parsing will call your syntax-propertize-function so that it can manually set the syntax-table text property on lines starting with //.
(define-derived-mode my-syntax-test-mode fundamental-mode
"A major mode where // denotes a comment but only if it is at the beginning of a line."
:syntax-table (make-syntax-table)
(setq mode-name "my syntax test")
;; our mode will use `apply-my-custom-syntax-table-appropriately' to manually set
;; the syntax-table text property on lines starting with //"
(setq syntax-propertize-function 'apply-my-custom-syntax-table-appropriately)
;; change `comment-dwim` to handle this type of comments correctly
(local-set-key [remap comment-dwim] 'my-comment-dwim))
(defvar my-custom-syntax-table
;; syntax table where // starts a comment and \n ends it
(let ((table (make-syntax-table)))
(modify-syntax-entry ?/ "< 1" table)
(modify-syntax-entry ?/ "< 2" table)
(modify-syntax-entry ?\n "> " table)
table))
(defun apply-my-custom-syntax-table-appropriately (beg end)
(save-excursion
(save-restriction
(widen)
(goto-char beg)
;; for every line between points BEG and END
(while (and (not (eobp)) (< (point) end))
(beginning-of-line)
;; if it starts with a //
(when (looking-at "^//")
;; remove current syntax-table property
(remove-text-properties (1- (line-beginning-position))
(1+ (line-end-position))
'(syntax-table))
;; set syntax-table property to our custom one
;; for the whole line including the beginning and ending newlines
(add-text-properties (1- (line-beginning-position))
(1+ (line-end-position))
(list 'syntax-table my-custom-syntax-table)))
(forward-line 1)))))
(defun my-comment-dwim (arg)
(interactive "*P")
(require 'newcomment)
(save-excursion
(let ((comment-start "//") (comment-end "")
(comment-column 0)
;; don't indent comments
(comment-style 'plain))
;; create the region containing current line if there is no active region
(unless (use-region-p)
(end-of-line)
(push-mark (line-beginning-position))
(setq mark-active t))
(comment-dwim nil))))

Answer: Use regexps
Short and simple
(define-derived-mode my-foo-mode prog-mode "My-Foo"
(setq-local font-lock-keywords t)
(setq-local syntax-propertize-function
(syntax-propertize-rules
((rx line-start (* whitespace) (group "//")) (1 "<"))
((rx (group "\n")) (1 ">")))))
That is all you need, but read on if you'd like to know more.
Explanation
This is based on #stefan's excellent solution which uses syntax-propertize-function to add to a syntax-table. While simpler isn't always better, #stefan's answer does more than what the original question asked for, so I've created this answer for people who only need a small hint or who just want to modify an existing mode.
It turns out directly manipulating a syntax table is unnecessary since the function syntax-propertize-rules makes it easy to map from regular expressions to syntax classes. For example, the syntax class < means "start of comment" and > means "end of comment". (See the Emacs lisp manual.)
I set font-lock-keywords to t as that is the minimum needed to enable syntax highlighting. If you are editing an existing mode, it likely already sets that variable and will not need to be changed.
And, finally, I use Emacs' rx function because it makes regular expressions sane in Lisp. (If you like Lisp, regular expressions, and sanity, I highly recommend using rx.)
About syntax-propertize-rules
I was going to link to the documentation for syntax-propertize-rules, but the Emacs manual does not (as of Emacs 28.1) even mention it. Until that gets remedied, I'll paste here the builtin documentation from C-hf:
syntax-propertize-rules is a Lisp macro in ‘syntax.el’.
(syntax-propertize-rules &rest RULES)
Probably introduced at or before Emacs version 24.1.
Make a function that applies RULES for use in
‘syntax-propertize-function’. The function will scan the buffer,
applying the rules where they match. The buffer is scanned a single
time, like "lex" would, rather than once per rule.
Each RULE can be a symbol, in which case that symbol’s value should
be, at macro-expansion time, a precompiled set of rules, as returned
by ‘syntax-propertize-precompile-rules’.
Otherwise, RULE should have the form (REGEXP HIGHLIGHT1 ...
HIGHLIGHTn), where REGEXP is an expression (evaluated at time of
macro-expansion) that returns a regexp, and where HIGHLIGHTs have the
form (NUMBER SYNTAX) which means to apply the property SYNTAX to the
chars matched by the subgroup NUMBER of the regular expression, if
NUMBER did match. SYNTAX is an expression that returns a value to
apply as ‘syntax-table’ property. Some expressions are handled
specially:
if SYNTAX is a string, then it is converted with ‘string-to-syntax’;
if SYNTAX has the form (prog1 EXP . EXPS) then the value returned by EXP will be applied to the buffer before running EXPS and if EXP is
a string it is also converted with ‘string-to-syntax’. The SYNTAX
expression is responsible to save the ‘match-data’ if needed for
subsequent HIGHLIGHTs. Also SYNTAX is free to move point, in which
case RULES may not be applied to some parts of the text or may be
applied several times to other parts.
Note: back-references in REGEXPs do not work.

Related

Inline code in org-mode

Markdown allows for embedded code. How can this be done in org-mode?
I know about source-code blocks:
#+begin_example
blah-blah
#+end_example
But what I want is something like this (obviously, with the right syntax, which I do not know):
This is `embeded code`.
Can this be done in org-mode? Not possible to find that in the documentation ...
While monospaced is good enough for most cases, inline code blocks have the form src_LANG[headers]{your code}. For example, src_xml[:exports code]{<tag>text</tag>}.
Edit: Code highlighting of inline code is certainly possible, albeit with patching org.el itself: The answer given here https://stackoverflow.com/a/20652913/594138 works as advertised, turning
- Inline code src_sh[:exports code]{echo -e "test"}
Into
in html-export. And the winning answer in this post, https://stackoverflow.com/a/28059832/594138, achieves the same without the need to patch org.el, but you will have to adapt it if you don't like the optics during editing.
You can enclose the text within = or ~ signs to have it typeset in monospaced font and export it verbatim (which means it is not processed for org-specific syntax):
This is =verbatim text= or ~code~.
You'll find all information about org-mode markup elements in the relevant section of the manual.
I wrote a function which I hope will be useful to help manage the code inline.
You put this code in your init file
(defun org-insert-inline-code()
"This function insert inline code `src_lang{inline code}' \nYour buffer must contain '#+PROPERTY: header-args:lang :exports code' where `lang` can be python or an other programming language."
(interactive (if (use-region-p)
(progn
(setq start (region-beginning))
(setq end (region-end))
(goto-char start)
(if (re-search-backward "^#\\+PROPERTY: header-args:[^[:blank:]]*" 1 t 1)
(progn
(forward-char 24)
(setq org-inline-lang (word-at-point))
(goto-char start)
(insert (concat "src_" org-inline-lang "{"))
(goto-char (+ 11 end))
(insert "}")
)))
(progn
(setq start (point))
(if (re-search-backward "^#\\+PROPERTY: header-args:[^[:blank:]]*" 1 t 1)
(progn
(forward-char 24)
(setq org-inline-lang (word-at-point))
(goto-char start)
(insert (concat "src_" org-inline-lang "{} "))
(backward-char 2)
))))))
(define-key org-mode-map (kbd "C-M-,") 'org-insert-inline-code)
You put this kind of PROPERTY in the org-file
#+PROPERTY: header-args:python :exports code
The required [:exports code] is given that way and the programming language can be identify by the function too.
Insert the code in line with C-M-, (the function then search back to read the language in the PROPERTY line and insert the correct command).

'Semantic' movement across a line

Consider the following line of Lisp code:
(some-function 7 8 | 9) ;; some comment. note the extra indentation
The point is placed between '8' and '9'. If I perform (move-beginning-of-line), the point will be placed at the absolute beginning of the line, rather than at '('.
Same for move-end-of-line: I'd find it more desirable for it to place the point at ')' if I perform it once, and at the absolute end of the line if I perform it a second time. Some IDEs behave like that.
I tried to implement this but got stuck, my solution behaves particularly bad near the end of a buffer, and on the minibuffer as well. Is there a library that provides this functionality?
I don't know of any library, but it can be done in a few lines of Elisp.
For the beginning of line part, the bundled functions beginning-of-line-text and back-to-indentation (M-m) move to the beginning of the “interesting” part of the line. back-to-indentation ignores only whitespace whereas beginning-of-line-text skips over the fill prefix (in a programming language, this is typically the comment marker, if in a comment). See Smart home in Emacs for how to flip between the beginning of the actual and logical line.
For the end of line part, the following function implements what you're describing. The function end-of-line-code moves to the end of the line, except for trailing whitespace and an optional trailing comment. The function end-of-line-or-code does this, except that if the point was already at the target position, or if the line only contains whitespace and a comment, the point moves to the end of the actual line.
(defun end-of-line-code ()
(interactive "^")
(save-match-data
(let* ((bolpos (progn (beginning-of-line) (point)))
(eolpos (progn (end-of-line) (point))))
(if (comment-search-backward bolpos t)
(search-backward-regexp comment-start-skip bolpos 'noerror))
(skip-syntax-backward " " bolpos))))
(defun end-of-line-or-code ()
(interactive "^")
(let ((here (point)))
(end-of-line-code)
(if (or (= here (point))
(bolp))
(end-of-line))))
Some suggestions that almost do what you ask:
In lisp code, you can sort-of do what you want, with the sexp movement commands. To get to the beginning of the expression from somewhere in the middle, use backward-up-list, which is bound to M-C-u. In your example, that would bring you to the open parenthesis. To move backwards over individual elements in the list, use backward-sexp, bound to M-C-b; forward-sexp moves the other way, and is bound to M-C-f. From the beginning of an sexp, you can skip to the next with M-C-n; reverse with M-C-p.
None of these commands are actually looking at the physical line you are on, so they'll go back or forward over multiple lines.
Other options include Ace Jump mode, which is a very slick way to quickly navigate to the beginning of any word visible on the screen. That might eliminate your need to use line-specific commands. For quick movement within a line, I usually use M-f and M-b to jump over words. Holding the M key down while tapping on b or f is quick enough that I end up using that by default most of the time.
Edit:
Forgot one other nice command - back-to-indentation, bound to M-m. This will back you up to the first non-whitespace character in a line. You could advice this to behave normally on the first call, and then to back up to the beginning of the line on the second call:
(defadvice back-to-indentation (around back-to-back)
(if (eq last-command this-command)
(beginning-of-line)
ad-do-it))
(ad-activate 'back-to-indentation)
I just wrote these two functions that have the behavior you are looking for.
(defun move-beginning-indent ()
(interactive)
(if (eq last-command this-command)
(beginning-of-line)
(back-to-indentation))
)
(defun move-end-indent ()
(interactive)
(if (eq last-command this-command)
(end-of-line)
(end-of-line)
(search-backward-regexp "\\s)" nil t) ; searches backwards for a
(forward-char 1)) ; closed delimiter such as ) or ]
)
(global-set-key [f7] 'move-beginning-indent)
(global-set-key [f8] 'move-end-indent)
Just try them out, they should behave exactly the way you'd want them to.
I use this:
(defun beginning-of-line-or-text (arg)
"Move to BOL, or if already there, to the first non-whitespace character."
(interactive "p")
(if (bolp)
(beginning-of-line-text arg)
(move-beginning-of-line arg)))
(put 'beginning-of-line-or-text 'CUA 'move)
;; <home> is still bound to move-beginning-of-line
(global-set-key (kbd "C-a") 'beginning-of-line-or-text)
(defun end-of-code-or-line ()
"Move to EOL. If already there, to EOL sans comments.
That is, the end of the code, ignoring any trailing comment
or whitespace. Note this does not handle 2 character
comment starters like // or /*. Such will not be skipped."
(interactive)
(if (not (eolp))
(end-of-line)
(skip-chars-backward " \t")
(let ((pt (point))
(lbp (line-beginning-position))
(comment-start-re (concat (if comment-start
(regexp-quote
(replace-regexp-in-string
"[[:space:]]*" "" comment-start))
"[^[:space:]][[:space:]]*$")
"\\|\\s<"))
(comment-stop-re "\\s>")
(lim))
(when (re-search-backward comment-start-re lbp t)
(setq lim (point))
(if (re-search-forward comment-stop-re (1- pt) t)
(goto-char pt)
(goto-char lim) ; test here ->
(while (looking-back comment-start-re (1- (point)))
(backward-char))
(skip-chars-backward " \t"))))))
(put 'end-of-code-or-line 'CUA 'move)
;; <end> is still bound to end-of-visual-line
(global-set-key (kbd "C-e") 'end-of-code-or-line)

Elisp macros for cleaning LaTeX-code

does anyone know of some good elisp macros for cleaning up LaTeX code?
I do a lot of LaTeX editing of other peoples sources and I'd like to extend my set of clean up tools since not everyone organize their code in the manner I like it ;-)
One in particular would be interesting, to run function X on a buffer and get all LaTeX environments (\begin{...} and \end{...} pairs) to sit on lines of their own, this helps readability of the code.
I could try this myself, but would like to hear suggestions as to a best practice for programming such a function, e.g. it should of course not introduce blank lines of its own.
suggestions?
Edit: For the archives, here are my current version based on the answer given (assumes the use of auctex). It more or less suits my needs at the moment. I added the y-or-n test just to be able to detect corner cases that I had not thought of.
(defun enviro-split ()
"Find begin and end macros, and put them on their own line."
(interactive)
(save-excursion
(beginning-of-buffer)
;; loop over document looking for begin and end macros
(while (re-search-forward "\\\\\\(begin\\|end\\)" nil t)
(catch 'continue
; if the line is a pure comment, then goto next
(if (TeX-in-commented-line)
(throw 'continue nil)
)
;; when you find one, back up to the beginning of the macro
(search-backward "\\")
;; If it's not at the beginning of the line, add a newline
(when (not (looking-back "^[ \t]*"))
(if (y-or-n-p "newline?")
(insert "\n")
)
)
;; move over the arguments, one or two pairs of matching braces
(search-forward "{") ; start of the argument
(forward-char -1)
(forward-sexp) ; move over the argument
(if (looking-at "[ \t]*{") ; is there a second argument?
(forward-sexp)
) ; move over it if so
(if (looking-at "[ \t]*\\[") ; is there a second argument?
(forward-sexp)
) ; move over it if so
(when (looking-at (concat "[ \t]*" (regexp-quote TeX-esc) "label"))
(goto-char (match-end 0))
(forward-sexp)
)
(if (looking-at (concat "[ \t]*%" ))
(throw 'continue nil)
)
;; If there is anything other than whitespace following the macro,
;; insert a newline
(if (not (looking-at "\\s *$"))
;;(insert "\n")
(if (y-or-n-p "newline (a)?")
(insert "\n")
)
)
) ; end catch 'continue
)
(LaTeX-fill-buffer 'left)
)
)
You could probably work up a single regexp and do a regexp replace for this. However, I find the logic of these manipulations becomes pretty hairy, particularly when you want to account for various edge-cases. In your example, you need to deal with some environments taking one argument, while others take two. I think it is easier to combine a series of simple regexps with basic text editing commands for this:
(defun enviro-split ()
"Find begin and end macros, and put them on their own line."
(interactive)
(save-excursion
(beginning-of-buffer)
;; loop over document looking for begin and end macros
(while (re-search-forward "\\\\\\(begin\\|end\\)" nil t)
;; when you find one, back up to the beginning of the macro
(search-backward "\\")
;; If it's not at the beginning of the line, add a newline
(when (not (looking-at "^"))
(insert "\n"))
;; move over the arguments, one or two pairs of matching braces
(search-forward "{") ; start of the argument
(forward-char -1)
(forward-sexp) ; move over the argument
(if (looking-at "\\s *{") ; is there a second argument?
(forward-sexp)) ; move over it if so
;; If there is anything other than whitespace following the macro,
;; insert a newline
(if (not (looking-at "\\s *$"))
(insert "\n")))))
This approach has the advantage of using Emacs' built-in functions for moving over sexps, which is much easier than coming up with your own regexp that can handle multiple, potentially nested, expressions inside braces.

Change mark-paragraph behaviour

How to change default emacs mark-paragraph function behaviour to not select first empty line?
my emacs http://dl.dropbox.com/u/1019877/e2.PNG
I've made Bohzidars upgraded version and it works for first line too.
(global-set-key (kbd "M-h") (lambda ()
(interactive)
(mark-paragraph)
(if (> (line-number-at-pos) 1)
(next-line))
(beginning-of-line)))
thanks guys for the tips.
The currently accepted answer has two disadvantages: 1) doesn't accept arguments and 2) doesn't allow to mark more paragraphs by repeated calls (especially this is very useful). Here is my solution - it is the original mark-paragraph with a next-line command at the end. The condition assures it works also on the first of the file.
Possibly a more economic solution is to use advices, but I don't know how to use them yet :).
(defun rs-mark-paragraph (&optional arg allow-extend)
"The original default mark-paragraph, but doesn't mark the first
empty line. Put point at beginning of this paragraph, mark at
end. The paragraph marked is the one that contains point or
follows point.
With argument ARG, puts mark at end of a following paragraph, so that
the number of paragraphs marked equals ARG.
If ARG is negative, point is put at end of this paragraph, mark is put
at beginning of this or a previous paragraph.
Interactively, if this command is repeated
or (in Transient Mark mode) if the mark is active,
it marks the next ARG paragraphs after the ones already marked."
(interactive "p\np")
(unless arg (setq arg 1))
(when (zerop arg)
(error "Cannot mark zero paragraphs"))
(cond ((and allow-extend
(or (and (eq last-command this-command) (mark t))
(and transient-mark-mode mark-active)))
(set-mark
(save-excursion
(goto-char (mark))
(forward-paragraph arg)
(point))))
(t
(forward-paragraph arg)
(push-mark nil t t)
(backward-paragraph arg)
(if (/= (line-number-at-pos) 1)
(next-line)))))
You cannot change the behaviour of mark-paragraph, but you can easily bind another command to the C-M-h keystroke (to resemble the original M-h):
(global-set-key (kbd "C-M-h") (lambda ()
(interactive)
(mark-paragraph)
(next-line)
(beginning-of-line)))
Something like this should do the trick.
I'm not sure I see a convenient way to do this. mark-paragraph calls forward-paragraph and backward-paragraph to do the bulk of the work, and in the documentation for backward-paragraph, we have "if the first real line of a paragraph is preceded by a blank line, the paragraph starts at that blank line."
The most relevant variables to look at appear to be paragraph-start and paragraph-separate, two regular expressions used inside paragraphs.el to figure out this sort of thing. I'd be leary of changing them though, as they are going to have quite a lot of other effects.
Another option is to write your own function that does something like the following:
(defun dg-mark-paragraph ()
(interactive)
(mark-paragraph)
(goto-char (region-beginning))
(when (= (string-match paragraph-separate (thing-at-point 'line)) 0)
(forward-line)))

How do I get Emacs to fill sentences, but not paragraphs?

I've seen at least two recommendations on StackOverflow to insert newlines between sentences when editing LaTeX documents. The reason being that the practice facilitates source control, diffing, and collaborative editing.
I'm basically convinced, but I'm lazy, and I don't want to have to think about it.
So I'm searching for some emacs incantation to handle it for me. Could be a minor mode, could be a set of variables that need to be set.
I think what I don't want is
Soft wrapping of text (say using the longlines and (set long-lines-auto-wrap 't)). This is because I don't want to impose requirements on my collaborators' editors, and I sometimes use other unix tools to examine these files.
I think what I do want is
For fill-paragraph to fill between newlines that look like they mark the end of a sentence.
A solution that works with auto-fill-mode would be a bonus.
That is:
chat chat chat.
A new sentence
with goofed up wrapping that needs to be fixed.
Mumble mumble
Transformed to:
chat chat chat.
A new sentence with goofed up wrapping that needs to be fixed.
Mumble mumble
Your comments and suggestions are appreciated.
Edit: The suggestion by Jouni K. Seppänen pointed me at LaTeX-fill-break-at-separators, which suggests that emacs almost knows how to do this already. Anyway, I'm off to read some code, and will report back. Thanks again.
More general version of the same question: Editor showdown: Maintain newlines at the ends of sentences. Thanks, dreeves.
Here's what I use, which was mostly cribbed from Luca de Alfaro:
(defun fill-sentence ()
(interactive)
(save-excursion
(or (eq (point) (point-max)) (forward-char))
(forward-sentence -1)
(indent-relative t)
(let ((beg (point))
(ix (string-match "LaTeX" mode-name)))
(forward-sentence)
(if (and ix (equal "LaTeX" (substring mode-name ix)))
(LaTeX-fill-region-as-paragraph beg (point))
(fill-region-as-paragraph beg (point))))))
I bind this to M-j with
(global-set-key (kbd "M-j") 'fill-sentence)
The references to "LaTeX" are for AUCTeX support. If you don't use AUCTeX, the let can be simplified to
(let (beg (point))
(forward-sentence)
(fill-region-as-paragraph beg (point)))
I have been meaning to do this forever and I recently found this blog post which worked fairly well for me. So here is (a slightly modified version of) what I have been using for a few days.
(defun auto-fill-by-sentences ()
(if (looking-back (sentence-end))
;; Break at a sentence
(progn
(LaTeX-newline)
t)
;; Fall back to the default
(do-auto-fill)))
(add-hook 'LaTeX-mode-hook (lambda () (setq auto-fill-function 'auto-fill-by-sentences)))
;; Modified from http://pleasefindattached.blogspot.com/2011/12/emacsauctex-sentence-fill-greatly.html
(defadvice LaTeX-fill-region-as-paragraph (around LaTeX-sentence-filling)
"Start each sentence on a new line."
(let ((from (ad-get-arg 0))
(to-marker (set-marker (make-marker) (ad-get-arg 1)))
tmp-end)
(while (< from (marker-position to-marker))
(forward-sentence)
;; might have gone beyond to-marker---use whichever is smaller:
(ad-set-arg 1 (setq tmp-end (min (point) (marker-position to-marker))))
ad-do-it
(ad-set-arg 0 (setq from (point)))
(unless (or (looking-back "^\\s *")
(looking-at "\\s *$"))
(LaTeX-newline)))
(set-marker to-marker nil)))
(ad-activate 'LaTeX-fill-region-as-paragraph)
If you put a comment marker at the end of each sentence, Emacs knows not to move the next line inside the comment:
chat chat chat.%
A new sentence
with goofed up wrapping that needs to be fixed.%
Mumble mumble%
Then M-q fills each sentence separately, at least in AUCTeX 11.85. (If you test this in Emacs, there seems to be a bug where if this is the first paragraph in the buffer and you type M-q, you get an error message. Just put a newline before the text to work around it.)
If you don't want to type the comment characters, you could take LaTeX-fill-paragraph and modify it so that sentence-ending punctuation at end of line works similarly to comments.
(defun wrap-at-sentences ()
"Fills the current paragraph, but starts each sentence on a new line."
(interactive)
(save-excursion
;; Select the entire paragraph.
(mark-paragraph)
;; Move to the start of the paragraph.
(goto-char (region-beginning))
;; Record the location of the end of the paragraph.
(setq end-of-paragraph (region-end))
;; Wrap lines with 'hard' newlines (i.e., real line breaks).
(let ((use-hard-newlines 't))
;; Loop over each sentence in the paragraph.
(while (< (point) end-of-paragraph)
;; Determine the region spanned by the sentence.
(setq start-of-sentence (point))
(forward-sentence)
;; Wrap the sentence with hard newlines.
(fill-region start-of-sentence (point))
;; Delete the whitespace following the period, if any.
(while (char-equal (char-syntax (preceding-char)) ?\s)
(delete-char -1))
;; Insert a newline before the next sentence.
(insert "\n")))))
(global-set-key (kbd "M-q") 'wrap-at-sentences)
May not work in all circumstances, but:
(defun my-fill-sentence ()
"Fill sentence separated by punctuation or blank lines."
(interactive)
(let (start end)
(save-excursion
(re-search-backward "\\(^\\s-*$\\|[.?!]\\)" nil t)
(skip-syntax-forward "^w")
(setq start (point-at-bol)))
(save-excursion
(re-search-forward "\\(^\\s-*$\\|[.?!]\\)" nil t)
(setq end (point-at-eol)))
(save-restriction
(narrow-to-region start end)
(fill-paragraph nil))))
To make it work with auto-fill-mode, add (setq normal-auto-fill-function 'my-fill-sentence) to your LaTeX mode hook (I think).
I am assuming you know elisp.
There are a few approaches you can take:
Hook into auto-fill-mode. There are a lot of hard-coded
conditionals there, so it might not work for you. You can
potentially play with auto-fill-function and see if you have
the hook you need there.
Make a character (probably .) "electric" so that when you press
it, it inserts itself and then calls a function to determine how
to fill the line you're on.
Set an after-change-hook to call a function that determines how
to fill the sentence. This function will be called after every
change to the buffer, so do it efficiently. (This mechanism is
used by font-lock, so don't worry about it too much. It sounds
slow, but really isn't -- people type slowly.)
Once you have hooked in at the right place, you just have to implement
the filling logic. The source for sentence-at-point (from thingatpt) may be
instructive.
Anyway, I've never heard of anyone doing this... but it is definitely possible. Like most things in Emacs, it's just a Simple Matter Of Programming.
If the other answers are too automatic, here's a semiautomatic approach.
It's basically what you would do repeatedly if you were going to manually reformat, but condensed so you can hit a single key repeatedly instead.
;; - go to the end of the line,
;; - do ^d to suck the previous line onto this one,
;; - make sure there's only one space between the now-concatenated
;; lines, and then
;; - jump to the end and hit space so that (with auto-fill-mode)
;; the line nicely rewraps itself:
;; (turn on auto-fill-mode with M-x auto-fill-mode)
(defalias 'fill-sentence
(read-kbd-macro "C-e C-d SPC M-x just- one- space RET C-e SPC <backspace>"))
(define-key global-map [f4] 'fill-sentence) ; or whatever key you like
I like Chris Conway's macro a lot but it only works after you manually line-break each sentence. I'm a lazy guy so I want emacs to do it for me. This morning I finally sat down and looked into the problem. The solution I have now is to hack the built-in macro fill-region-as-paragraph.
After applying the following hack, a new option newline-after-sentence will be set to true. The standard M-q (fill-paragraph) will automatically fill and create line-breaks between sentences. Note that tests are only done with GNU Emacs 23.3.1 — use it at your own risk.
The full macro is long so I won't post it here. The idea is to add the following loops in fill-region-as-paragraph
...
;; Insert a line break after each sentence
(while (< (point) to)
(forward-sentence)
(if (< (point) to) (fill-newline)))
;; This is the actual filling loop.
(goto-char from)
(let (sentbeg sentend)
(while (< (point) to)
(setq sentbeg (point))
(end-of-line)
(setq sentend (point))
(fill-one-line sentbeg sentend justify) ;; original filling loop
(forward-line)))))
...
You can find the full macro in my git repository. Some details are also written in my blog. In case you don't want to read my poor English, you can simply use
$ curl http://fermi.mycloudnas.com/cgit.cgi/fill/plain/hack.el >> ~/.emacs
to append the hack to your ~/.emacs and give it a try. Comments and bug reports are all welcome.
An alternative approach would be to leave your .tex file as is, and use a tool like latexdiff
(described in this StackExchange post) instead of Unix diff. This produces a .tex file with Word-style track changes marks, and handles whitespace correctly so you don't have to worry about where your sentences end.
I wrote the following which loops over a region and inserts newlines. Instead of using forward-sentence which didn't work for me, I use re-search-forward "[.?!][]\"')}]*\\( \\)", which finds all sentences followed only by two spaces (the regexp is a modified sentence-end). The newline is made using newline-and-indent.
(defun fill-sentences-in-paragraph ()
"Put a newline at the end of each sentence in paragraph."
(interactive)
(save-excursion
(mark-paragraph)
(call-interactively 'fill-sentences-in-region)))
(defun fill-sentences-in-region (start end)
"Put a newline at the end of each sentence in region."
(interactive "*r")
(call-interactively 'unfill-region)
(save-excursion
(goto-char start)
(while (re-search-forward "[.?!][]\"')}]*\\( \\)" end t)
(newline-and-indent))))
To be able to fix improperly formatted text such as the example "chat chat chat...", fill-sentences-in-region first calls unfill-region which gets rid of sentence-breaking whitespace:
(defun unfill-region (beg end)
"Unfill the region, joining text paragraphs into a
single logical line. This is useful, e.g., for use
with 'visual-line-mode'."
(interactive "*r")
(let ((fill-column (point-max)))
(fill-region beg end)))
I use visual-line-mode and replace my default paragraph fill M-q to fill-sentences-in-paragraph with (global-set-key "\M-q" 'fill-sentences-in-paragraph).