unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH] Add smart-space command.
@ 2012-12-04 22:36 Michal Nazarewicz
  2012-12-04 22:55 ` Drew Adams
  2012-12-10 14:57 ` [PATCHv2] Add cycle-spacing command Michal Nazarewicz
  0 siblings, 2 replies; 8+ messages in thread
From: Michal Nazarewicz @ 2012-12-04 22:36 UTC (permalink / raw)
  To: emacs-devel

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: mina86@mina86.com-20121204215056-c7379szmw0d8dj1m
# 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: eggert@cs.ucla.edu-20121204204229-wbbjjgyv88jj2icz
# 
# 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  <mina86@mina86.com>
+
+	* simple.el: Add smart-space command.
+
 2012-12-04  Stefan Monnier  <monnier@iro.umontreal.ca>
 
 	* 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
+together.
+
+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))
 \f
 (defun beginning-of-buffer (&optional arg)
   "Move point to the beginning of the buffer.




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH] Add smart-space command.
  2012-12-04 22:36 [PATCH] Add smart-space command Michal Nazarewicz
@ 2012-12-04 22:55 ` Drew Adams
  2012-12-04 23:11   ` Michal Nazarewicz
  2012-12-10 14:57 ` [PATCHv2] Add cycle-spacing command Michal Nazarewicz
  1 sibling, 1 reply; 8+ messages in thread
From: Drew Adams @ 2012-12-04 22:55 UTC (permalink / raw)
  To: 'Michal Nazarewicz', emacs-devel

> 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 have nothing special to say about the command, and I haven't tried it or
looked at the code.  But from the description I would suggest perhaps renaming
it to suggest more of what it does: `cycle-spacing' or some such.

Personally, I dislike "smart" names - they often say nothing about what the
function does.  They tend to suggest only "this command is an improvement over
the command I'm replacing it by".

At best, they sometimes suggest that the command does something different
depending on the context (aka context-sensitive, ~aka DWIM).

In this case, the command does not seem to be deciding its behavior based on the
textual context.  From your desription, it is the user who controls the behavior
by explicitly repeating the command.  The command is flexible, not smart.

Wrt adding a command that does what you describe: I think it's a good idea.  The
cycling might even include using a hard (nobreak) space.  Or include
`fixup-whitespace'.  Or `delete-horizontal-space'.  The cycling choices could
even be customizable.

Just one opinion.




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH] Add smart-space command.
  2012-12-04 22:55 ` Drew Adams
@ 2012-12-04 23:11   ` Michal Nazarewicz
  0 siblings, 0 replies; 8+ messages in thread
From: Michal Nazarewicz @ 2012-12-04 23:11 UTC (permalink / raw)
  To: Drew Adams, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1580 bytes --]

>> 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

On Tue, Dec 04 2012, Drew Adams wrote:
> I have nothing special to say about the command, and I haven't tried it or
> looked at the code.  But from the description I would suggest perhaps renaming
> it to suggest more of what it does: `cycle-spacing' or some such.

Sure, that sounds fine by me.  Truth to be said, I could not came up
with a good name so I escaped into the “smart” realm. ;)

> Wrt adding a command that does what you describe: I think it's a good idea.  The
> cycling might even include using a hard (nobreak) space.  Or include
> `fixup-whitespace'.  Or `delete-horizontal-space'.  The cycling choices could
> even be customizable.

The second state is `delete-horizontal-space' (with backward-only being
nil) essentially, but yes, I thought about making something even more
configurable, like accepting a list of states or something, but I didn't
want to make the function too complex, and rather get it to do what
I think would be the most useful thing.

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +----<email/xmpp: mpn@google.com>--------------ooO--(_)--Ooo--

[-- Attachment #2.1: Type: text/plain, Size: 0 bytes --]



[-- Attachment #2.2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCHv2] Add cycle-spacing command.
  2012-12-04 22:36 [PATCH] Add smart-space command Michal Nazarewicz
  2012-12-04 22:55 ` Drew Adams
@ 2012-12-10 14:57 ` Michal Nazarewicz
  2013-01-17 11:41   ` [PATCHv3] " Michal Nazarewicz
  1 sibling, 1 reply; 8+ messages in thread
From: Michal Nazarewicz @ 2012-12-10 14:57 UTC (permalink / raw)
  To: emacs-devel, Drew Adams

From: Michal Nazarewicz <mina86@mina86.com>

This commit adds a cycle-spacing command which is, in a sense,
generalisation of the just-on-space command.  When run continuously,
it cycles between having only one space, having no spaces and original
spacing.
---
 etc/NEWS       |    5 +++
 lisp/ChangeLog |    4 ++
 lisp/simple.el |   86 ++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 80 insertions(+), 15 deletions(-)

 v2: changed name to cycle-spacing

diff --git a/etc/NEWS b/etc/NEWS
index 77e7e47..8112573 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -92,6 +92,11 @@ when its arg ADJACENT is non-nil (when called interactively with C-u C-u)
 it works like the utility `uniq'.  Otherwise by default it deletes
 duplicate lines everywhere in the region without regard to adjacency.
 
+** New `cycle-spacing' 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
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 7f05613..7ac779a 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,7 @@
+2012-12-10  Michal Nazarewicz  <mina86@mina86.com>
+
+	* simple.el: Add cycle-spacing command.
+
 2012-12-10  Eli Zaretskii  <eliz@gnu.org>
 
 	* subr.el (w32notify-handle-event): New function.
diff --git a/lisp/simple.el b/lisp/simple.el
index 78b7657..dcb8cf7 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -742,25 +742,81 @@ If BACKWARD-ONLY is non-nil, only delete them before point."
        (skip-chars-backward " \t")
        (constrain-to-field nil orig-pos)))))
 
+(defvar cycle-spacing--context nil
+  "Store context used in consecutive calls to `cycle-spacing' command.
+The first time this function is run, it saves the original point
+position and original spacing around the point in this
+variable.")
+
+(defun cycle-spacing (&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 `cycle-spacing' 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
+together.
+
+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 cycle-spacing--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 cycle-spacing--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 cycle-spacing--context))
+      (goto-char (car cycle-spacing--context))
+      (setq cycle-spacing--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, leaving -N spaces."
   (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)))))
+  (cycle-spacing n nil t))
 \f
 (defun beginning-of-buffer (&optional arg)
   "Move point to the beginning of the buffer.
-- 
1.7.7.3




^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCHv3] Add cycle-spacing command.
  2012-12-10 14:57 ` [PATCHv2] Add cycle-spacing command Michal Nazarewicz
@ 2013-01-17 11:41   ` Michal Nazarewicz
  2013-01-17 13:06     ` Lele Gaifax
  0 siblings, 1 reply; 8+ messages in thread
From: Michal Nazarewicz @ 2013-01-17 11:41 UTC (permalink / raw)
  To: emacs-devel, Drew Adams

From: Michal Nazarewicz <mina86@mina86.com>

This commit adds a cycle-spacing command which is, in a sense,
generalisation of the just-on-space command.  When run continuously,
it cycles between having only one space, having no spaces and original
spacing.
---
 So, any interest in this at all?

 etc/NEWS       |    5 +++
 lisp/ChangeLog |    4 ++
 lisp/simple.el |   86 ++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 80 insertions(+), 15 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 0471683..8400cd0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -142,6 +142,11 @@ when its arg ADJACENT is non-nil (when called interactively with C-u C-u)
 it works like the utility `uniq'.  Otherwise by default it deletes
 duplicate lines everywhere in the region without regard to adjacency.
 
+** New `cycle-spacing' 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
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 684beb5..096a2a3 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,7 @@
+2013-01-17  Michal Nazarewicz  <mina86@mina86.com>
+
+	* simple.el: Add cycle-spacing command.
+
 2013-01-17  Jürgen Hötzel  <juergen@archlinux.org>
 
 	* eshell/esh-util.el (eshell-path-env): Make it buffer local, in
diff --git a/lisp/simple.el b/lisp/simple.el
index 847c07a..989b504 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -742,25 +742,81 @@ If BACKWARD-ONLY is non-nil, only delete them before point."
        (skip-chars-backward " \t")
        (constrain-to-field nil orig-pos)))))
 
+(defvar cycle-spacing--context nil
+  "Store context used in consecutive calls to `cycle-spacing' command.
+The first time this function is run, it saves the original point
+position and original spacing around the point in this
+variable.")
+
+(defun cycle-spacing (&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 `cycle-spacing' 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
+together.
+
+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 cycle-spacing--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 cycle-spacing--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 (i 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 cycle-spacing--context))
+      (goto-char (car cycle-spacing--context))
+      (setq cycle-spacing--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, leaving -N spaces."
   (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 (_ 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)))))
+  (cycle-spacing n nil t))
 \f
 (defun beginning-of-buffer (&optional arg)
   "Move point to the beginning of the buffer.



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCHv3] Add cycle-spacing command.
  2013-01-17 11:41   ` [PATCHv3] " Michal Nazarewicz
@ 2013-01-17 13:06     ` Lele Gaifax
  2013-01-26 15:26       ` [PATCHv4] " Michal Nazarewicz
  0 siblings, 1 reply; 8+ messages in thread
From: Lele Gaifax @ 2013-01-17 13:06 UTC (permalink / raw)
  To: emacs-devel

Michal Nazarewicz <mpn@google.com> writes:

> diff --git a/lisp/simple.el b/lisp/simple.el
> ...
> +For example, if buffer contains \"foo ^ bar\" with \"^\" donating the

Probably you meant “denoting” here?

bye, lele.
-- 
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
lele@metapensiero.it  |                 -- Fortunato Depero, 1929.




^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCHv4] Add cycle-spacing command.
  2013-01-17 13:06     ` Lele Gaifax
@ 2013-01-26 15:26       ` Michal Nazarewicz
  2013-01-26 17:14         ` Stephen J. Turnbull
  0 siblings, 1 reply; 8+ messages in thread
From: Michal Nazarewicz @ 2013-01-26 15:26 UTC (permalink / raw)
  To: emacs-devel, Lele Gaifax

From: Michal Nazarewicz <mina86@mina86.com>

This commit adds a cycle-spacing command which is, in a sense,
generalisation of the just-on-space command.  When run continuously,
it cycles between having only one space, having no spaces and original
spacing.
---
 > Michal Nazarewicz <mpn@google.com> writes:
 >> diff --git a/lisp/simple.el b/lisp/simple.el
 >> ...
 >> +For example, if buffer contains \"foo ^ bar\" with \"^\" donating the

 On Thu, Jan 17 2013, Lele Gaifax <lele@metapensiero.it> wrote:
 > Probably you meant “denoting” here?

 Right, thanks.

 etc/NEWS       |  5 ++++
 lisp/ChangeLog |  4 +++
 lisp/simple.el | 86 ++++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 80 insertions(+), 15 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 3e8bd3e..5959bc6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -157,6 +157,11 @@ when its arg ADJACENT is non-nil (when called interactively with C-u C-u)
 it works like the utility `uniq'.  Otherwise by default it deletes
 duplicate lines everywhere in the region without regard to adjacency.
 
+** New `cycle-spacing' 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
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 17a4a99..0ac59ff 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,7 @@
+2013-01-26  Michal Nazarewicz  <mina86@mina86.com>
+
+	* simple.el: Add cycle-spacing command.
+
 2013-01-25  Alan Mackenzie  <acm@muc.de>
 
 	AWK Mode: Fix indentation bug at top level.  Bug #12274.
diff --git a/lisp/simple.el b/lisp/simple.el
index 847c07a..6533aef 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -742,25 +742,81 @@ If BACKWARD-ONLY is non-nil, only delete them before point."
        (skip-chars-backward " \t")
        (constrain-to-field nil orig-pos)))))
 
+(defvar cycle-spacing--context nil
+  "Store context used in consecutive calls to `cycle-spacing' command.
+The first time this function is run, it saves the original point
+position and original spacing around the point in this
+variable.")
+
+(defun cycle-spacing (&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 \"^\" denoting the
+point, calling `cycle-spacing' 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
+together.
+
+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 cycle-spacing--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 cycle-spacing--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 (i 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 cycle-spacing--context))
+      (goto-char (car cycle-spacing--context))
+      (setq cycle-spacing--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, leaving -N spaces."
   (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 (_ 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)))))
+  (cycle-spacing n nil t))
 \f
 (defun beginning-of-buffer (&optional arg)
   "Move point to the beginning of the buffer.



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCHv4] Add cycle-spacing command.
  2013-01-26 15:26       ` [PATCHv4] " Michal Nazarewicz
@ 2013-01-26 17:14         ` Stephen J. Turnbull
  0 siblings, 0 replies; 8+ messages in thread
From: Stephen J. Turnbull @ 2013-01-26 17:14 UTC (permalink / raw)
  To: Michal Nazarewicz; +Cc: Lele Gaifax, emacs-devel

Michal Nazarewicz writes:

 > +** New `cycle-spacing' 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.

Typo: "use leave".  You probably meant just "leave".

 > +If SINGLE-SHOT is non-nil, will only perform the first step.  In
 > +other words, it will work just like `just-on-space' command."

Typo: I think you mean "`just-one-space' command."




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-01-26 17:14 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-04 22:36 [PATCH] Add smart-space command Michal Nazarewicz
2012-12-04 22:55 ` Drew Adams
2012-12-04 23:11   ` Michal Nazarewicz
2012-12-10 14:57 ` [PATCHv2] Add cycle-spacing command Michal Nazarewicz
2013-01-17 11:41   ` [PATCHv3] " Michal Nazarewicz
2013-01-17 13:06     ` Lele Gaifax
2013-01-26 15:26       ` [PATCHv4] " Michal Nazarewicz
2013-01-26 17:14         ` Stephen J. Turnbull

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).