30

A bit of background.

I’m trying to version-control my latex documents, and the effectiveness of this initiative would be greatly improved if I adopt a one-sentence-per-line approach.

For instance, the following is what my documents will look like.

Some text here.
New stencence on the same paragraph.
Some more text, still on the same paragraph.

This is another paragraph.

My question is simple.
How much of that can I automate/improve with Emacs?

The following two points are what I have in mind.

  1. My sentences are long, so I need to wrap lines without actually filling them. That is, a very long sentence needs to be displayed over several lines on my screen, but it needs to be a single line in the “.tex” file. This would preferably be done in a way that doesn’t wrap equations.

    visual-line-mode wraps at window width, that is too wide. I need something that wraps lines and limits their width to 80 or so characters. Just like fill-paragraph would normally do, but without actually breaking the lines in the file.

  2. I can manually break the line after each sentence, but it would be highly preferable if I could configure fill-paragraph (or maybe even auto-fill-mode) to put one sentence per line as well.
Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • 1
    What kind of improvement/automation are you after exactly ? Do you want to know how to edit effectively in files like this ? – Rangi Lin Sep 29 '14 at 10:16
  • @RangiLin Yes. The first point I mentioned is the most important thing for me. But anything else to make this more effective is welcome. – Malabarba Sep 29 '14 at 10:21
  • The description in first point looks like `visual-line-mode`. But in addition to that line wrapping, you also need to auto insert a newline character after each sentence too, correct? – Kaushal Modi Sep 29 '14 at 10:23
  • @kaushalmodi Yes, but the newline after each sentence is real, it should be in the file. The problem with visual line mode is that it's just too wide by default. If there's a way to reduce it, it'd be great. – Malabarba Sep 29 '14 at 10:26
  • @Malabarba: Do you know why? To me, it's the most useful wrapping mode by far... – Tikhon Jelvis Sep 30 '14 at 17:17
  • @TikhonJelvis are you referring to visual or longlines? – Malabarba Sep 30 '14 at 18:34
  • @Malabarba: Oops, I commented on the wrong post. I was wondering why `longlines-mode` is marked as obsolete. – Tikhon Jelvis Sep 30 '14 at 20:59
  • @TikhonJelvis I asked on the bug tracker. They consider that visual-lines combined with window margins supersedes anything that longlines can do out of the box. They're right of course, but longlines is implemented in elisp, which makes it more patchable for us. – Malabarba Sep 30 '14 at 21:06
  • If it receives an answer, I found your question while asking [mine](http://emacs.stackexchange.com/q/5330/2264). You might find it helpful, too. – Sean Allred Dec 12 '14 at 16:38
  • Perhaps you could use `electrit-layout-mode` for this. – Andrew Swann Dec 15 '14 at 12:47
  • If wrapping at the window width is too wide, then perhaps the problem is not the wrapping mechanism but rather the size you have chosen for your window. – Psychonaut Aug 25 '20 at 09:17

5 Answers5

14

For (1), I would use an enlarged margin, so that visual-line-mode wraps lines at the desired fill-column. This will affect both text lines and equations, though.

As for (2), one can define a custom filling command to be bound to M-q and correctly fill paragraphs. I haven't yet managed to write a command with the correct behaviour for auto-filling.

Wrapping this all in a minor mode could look like the following. It is not very beautiful code, but should work in most cases. (I have had the unfill-paragraph function in my init.el for quite some time without noticing problems).

(define-minor-mode ospl-mode
  "One Sentence Per Line"
  :init-value nil
  :lighter " ospl"
  :keymap (let ((map (make-sparse-keymap)))
            (define-key map (kbd "M-q") 'ospl/fill-paragraph)
            map)

  (if ospl-mode
      (progn
        (visual-line-mode 1)
        (setq right-margin-width (- (window-body-width) fill-column)))
    (visual-line-mode -1)
    (setq right-margin-width 0))

  ;; Account for new margin width
  (set-window-buffer (selected-window) (current-buffer)))


(defun ospl/unfill-paragraph ()
  "Unfill the paragraph at point.

This repeatedly calls `join-line' until the whole paragraph does
not contain hard line breaks any more."
  (interactive)
  (forward-paragraph 1)
  (forward-paragraph -1)
  (while (looking-at paragraph-start)
    (forward-line 1))
  (let ((beg (point)))
    (forward-paragraph 1)
    (backward-char 1)
    (while (> (point) beg)
      (join-line)
      (beginning-of-line))))


(defun ospl/fill-paragraph ()
  "Fill the current paragraph until there is one sentence per line.

This unfills the paragraph, and places hard line breaks after each sentence."
  (interactive)
  (save-excursion
    (fill-paragraph)         ; takes care of putting 2 spaces if needed
    (ospl/unfill-paragraph)  ; remove hard line breaks

    ;; insert line breaks again
    (let ((end-of-paragraph (make-marker)))
      (save-excursion
        (forward-paragraph)
        (backward-sentence)
        (forward-sentence)
        (set-marker end-of-paragraph (point)))
      (forward-sentence) 
      (while (< (point) end-of-paragraph)
        (just-one-space)
        (delete-backward-char 1)
        (newline)
        (forward-sentence))
      (set-marker end-of-paragraph nil)))) 
Scott Weldon
  • 2,695
  • 1
  • 17
  • 31
François Févotte
  • 5,917
  • 1
  • 24
  • 37
  • The paragraph filling works great, I wish I could accept two answers. Just replace `unfill-paragraph` with `ospl/unfill-paragraph`. The margins also work, but indented text looks bad and they also wrap equations, whereas I managed to hack the longlines method below fix these two problems. – Malabarba Sep 30 '14 at 10:15
  • Fixed, thanks. As I said, I've had `unfill-paragraph` (unprefixed) in my `init.el` for a long time. I just prefixed it yesterday for consistency and introduced a bug.... – François Févotte Sep 30 '14 at 11:55
  • Yeah, I figured it was something like that. I also realised we can probably unfill paragraph by just doing something like `(let ((fill-column 1000000)) (fill-pragraph))`. Haven't tested it yet though. – Malabarba Sep 30 '14 at 13:15
  • Yep, such a solution works. I just don't like having magic constants (but my code is not very beautiful either, so one could argue which is better...) – François Févotte Sep 30 '14 at 15:16
  • 1
    Love this solution. Is there a simple way to avoid breaking commented lines into multiple lines? – Ramprasad Jul 28 '15 at 12:38
  • Emacs has an `unfill-paragraph` function already defined (in `unfill.el`). It seems to work as well as `(fill-paragraph)(ospl/unfill-paragraph)`… and I like refactoring code with the delete key :) Also if you go check its code, it does what @Malabarba suggests without the magic constant. – Damien Pollet Sep 14 '17 at 14:39
  • Oh wait, @Malabarba is mentioned in `unfill.el`… coincidence? I think not :) – Damien Pollet Sep 14 '17 at 14:49
  • If there is comment `%%` before the text, the text moves to comment line, is it possible to prevent it? – alper Jul 06 '22 at 18:41
  • This solution does not add newline after `.`, is it normal? – alper Dec 14 '22 at 12:33
12

If you just want something like visual-line-mode but configurable, you can try longlines-mode which is what I use for most of my prose. longlines-mode wraps your text similarly to visual-line-mode with the width configured by fill-column.

Here's a screenshot with fill-column set to 70 (the window actually extends even more to the right).

An example of longlines-mode wrapping text.

Configuring fill-paragraph would be neat, but I'm not sure how to do it. Instead, here's a reasonable temporary hack: make your . character electric in TeX mode, inserting a newline. This just involves rebinding it in whatever the appropriate mode hook is:

(defun my-electric-dot ()
  (interactive)
  (insert ".\n"))
(defun my-tex-hook ()
  (local-set-key (kbd ".") 'my-electric-dot))
(add-hook 'TeX-mode-hook 'my-tex-hook)
Scott Weldon
  • 2,695
  • 1
  • 17
  • 31
Tikhon Jelvis
  • 6,152
  • 2
  • 27
  • 40
  • 1
    Thanks a lot for pointing out longlines-mode! I managed to get it to not wrap equations and even fix the look of indented lines. Here is the gist https://gist.github.com/c532f77144ddf86b4ea4.git – Malabarba Sep 30 '14 at 10:23
  • @Malabarba: Cool. I'll probably use some of those changes myself! Your link doesn't work properly though; here's a web-friendly version: https://gist.github.com/Bruce-Connor/c532f77144ddf86b4ea4 – Tikhon Jelvis Sep 30 '14 at 10:45
  • Thanks. Also, forgot to mention, that advice uses a function from the latex-extra package. – Malabarba Sep 30 '14 at 10:53
  • By the way, it's worth noting that longlines-mode is marked as obsolete in 24.4 (not that it has an equivalent alternative), – Malabarba Sep 30 '14 at 13:33
  • longlines-mode is kinda deprecated. It was written by Stephan before visual-line-mode is in emacs. It sometimes corrupt your lines, e.g. when pasting big text. There were several bug reports about it. It works by actually reformatting lines behind the scene. IIRC. Not sure what's the current status though. – Xah Lee Oct 01 '14 at 00:59
  • i think all you need is just call set-window-margins? as for automating the process, can't you write a simple elisp that temp set fill-column to big, then call fill so paragraphs becomes single line, then find/replace period space to 2 line breaks? then hook this to Return key if needed. – Xah Lee Oct 01 '14 at 01:01
  • 2
    The electric dot isn't going to work well because dots are not just used to end sentences but also for all kinds of other purposes. It would be better to replace a dot followed by two spaces by a dot followed by a newline. – tmalsburg Jan 07 '15 at 11:19
9

Would your version-control issues be avoided/resolved if you use git diff --color-words or latexdiff? Then you can look at changed words.

Sacha Chua
  • 716
  • 5
  • 5
  • 2
    Coloring words is hepful indeed! But I still need the *olps* approach to help avoid conflicts when merging other people's contributions and when reverting old commits. – Malabarba Sep 30 '14 at 09:16
2

One way of improving this that I have used (at times) for several years is to display sentences with line breaks as flowing after each other by folding the line breaks using the facilities of tex-fold (a part of AUCTeX).

This means that this

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Mauris pellentesque fringilla justo, quis dapibus velit tincidunt quis?
Quisque varius ligula arcu, ut imperdiet risus maximus nec.

is folded to

Lorem ipsum dolor sit amet, consectetur adipiscing elit⁎ Mauris pellentesque fringilla justo, quis dapibus velit tincidunt quis❓  Quisque varius ligula arcu, ut imperdiet risus maximus nec⁎

I've recently put this together as a minor mode in a package. Perhaps someone else will also find this useful: https://github.com/andersjohansson/tex-fold-linebreaks

1

Jan Seeger has made Twauctex. One of the features it provides is

Enable one-sentence-per-line mode.

It says:

Usage

Simply write your latex as you would normally. twauctex will take care of breaking the line whenever you enter a space after a sentence end. If the standard settings do not agree with you, use (customize-group 'twauctex), and you can customize the builtin configuration.

At the time of writing this, the package is not available on MELPA. You need to following installation instructions as provided on the GitHub repo page of Twauctex.

prash
  • 200
  • 1
  • 6