[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Add smart-space command.

From: Michal Nazarewicz
Subject: [PATCH] Add smart-space command.
Date: Tue, 04 Dec 2012 23:36:14 +0100
User-agent: Notmuch/ (http://notmuchmail.org) Emacs/ (x86_64-unknown-linux-gnu)

Hello everyone,

The patch below adds a smart-space command which is sort of
a generalised just-one-space.  The main difference is that it behaves
differently depending on how many times it has been called, and cycles
between three states:

1. just one space
2. no space at all
3. original spacing

I've been using various versions of this command for years now, and
every now and then, someone asks about something like that, so I thought
I'd contribute.

----------------- >8 ---------------------------------------------------

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: address@hidden
# target_branch: bzr://bzr.savannah.gnu.org/emacs/trunk
# testament_sha1: 70c3c769cbc683fc01816ddbe5cec6e7d5e9fcf8
# timestamp: 2012-12-04 23:09:38 +0100
# source_branch: .
# base_revision_id: address@hidden
# Begin patch
=== modified file 'etc/NEWS'
--- etc/NEWS    2012-12-04 17:07:09 +0000
+++ etc/NEWS    2012-12-04 21:50:56 +0000
@@ -74,6 +74,11 @@
 it works like the utility `uniq'.  Otherwise by default it deletes
 duplicate lines everywhere in the region without regard to adjacency.
+** New `smart-space' command allows cycling between having just one space,
+no spaces, or reverting to the original spacing.  Like `just-one-space'
+command it can handle or ignore newlines and use leave different number
+of spaces.
 ** Tramp
 *** New connection method "adb", which allows to access Android

=== modified file 'lisp/ChangeLog'
--- lisp/ChangeLog      2012-12-04 17:04:01 +0000
+++ lisp/ChangeLog      2012-12-04 21:50:56 +0000
@@ -1,3 +1,7 @@
+2012-12-04  Michal Nazarewicz  <address@hidden>
+       * simple.el: Add smart-space command.
 2012-12-04  Stefan Monnier  <address@hidden>
        * obsolete/terminal.el, obsolete/longlines.el: Add obsolecence info.

=== modified file 'lisp/simple.el'
--- lisp/simple.el      2012-12-03 01:08:31 +0000
+++ lisp/simple.el      2012-12-04 21:50:56 +0000
@@ -742,25 +742,80 @@
        (skip-chars-backward " \t")
        (constrain-to-field nil orig-pos)))))
+(defvar smart-space--context nil
+  "Store context used in consecutive calls to `smart-space' command.
+The first time this function is run, it saves the original point
+position and original spacing around the point in this variable.")
+(defun smart-space (&optional n preserve-nl-back single-shot)
+  "Manipulate spaces around the point in a smart way.
+When run as an interactive command, the first time it's called
+in a sequence, deletes all spaces and tabs around point leaving
+one (or N spaces).  If this does not change content of the
+buffer, skips to the second step:
+When run for the second time in a sequence, deletes all the
+spaces it has previously inserted.
+When run for the third time, returns the whitespace and point in
+a state encountered when it had been run for the first time.
+For example, if buffer contains \"foo ^ bar\" with \"^\" donating the
+point, calling `smart-space' command will replace two spaces with
+a single space, calling it again immediately after, will remove all
+spaces, and calling it for the third time will bring two spaces back
+If N is negative, delete newlines as well.  However, if
+PRESERVE-NL-BACK is t new line characters prior to the point
+won't be removed.
+If SINGLE-SHOT is non-nil, will only perform the first step.  In
+other words, it will work just like `just-on-space' command."
+  (interactive "*p")
+  (let ((orig-pos       (point))
+       (skip-characters (if (and n (< n 0)) " \t\n\r" " \t"))
+       (n               (abs (or n 1))))
+    (skip-chars-backward (if preserve-nl-back " \t" skip-characters))
+    (constrain-to-field nil orig-pos)
+    (cond
+     ;; Command run for the first time or single-shot is non-nil
+     ((or single-shot
+         (not (equal last-command this-command))
+         (not smart-space--context))
+      (let* ((start (point))
+            (n     (- n (skip-chars-forward " " (+ n (point)))))
+            (mid   (point))
+            (end   (progn
+                     (skip-chars-forward skip-characters)
+                     (constrain-to-field nil orig-pos t))))
+       (setq smart-space--context  ;; Save for later
+             ;; Special handling for case where there was no space at all
+             (unless (= start end)
+               (cons orig-pos (buffer-substring start (point)))))
+       ;; If this run causes no change in buffer content, delete all spaces,
+       ;; otherwise delete all excees spaces.
+       (delete-region (if (and (not single-shot) (zerop n) (= mid end))
+                          start mid) end)
+       (dotimes (_ n)
+         (insert ?\s))))
+     ;; Command run for the second time
+     ((not (equal orig-pos (point)))
+      (delete-region (point) orig-pos))
+     ;; Command run for the third time
+     (t
+      (insert (cdr smart-space--context))
+      (goto-char (car smart-space--context))
+      (setq smart-space--context nil)))))
 (defun just-one-space (&optional n)
   "Delete all spaces and tabs around point, leaving one space (or N spaces).
 If N is negative, delete newlines as well."
   (interactive "*p")
-  (unless n (setq n 1))
-  (let ((orig-pos (point))
-        (skip-characters (if (< n 0) " \t\n\r" " \t"))
-        (n (abs n)))
-    (skip-chars-backward skip-characters)
-    (constrain-to-field nil orig-pos)
-    (dotimes (i n)
-      (if (= (following-char) ?\s)
-         (forward-char 1)
-       (insert ?\s)))
-    (delete-region
-     (point)
-     (progn
-       (skip-chars-forward skip-characters)
-       (constrain-to-field nil orig-pos t)))))
+  (smart-space n nil t))
 (defun beginning-of-buffer (&optional arg)
   "Move point to the beginning of the buffer.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]