unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Michal Nazarewicz <mpn@google.com>
To: emacs-devel@gnu.org
Subject: [PATCH] Add smart-space command.
Date: Tue, 04 Dec 2012 23:36:14 +0100	[thread overview]
Message-ID: <xa1tobi930o1.fsf@mina86.com> (raw)

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.




             reply	other threads:[~2012-12-04 22:36 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-12-04 22:36 Michal Nazarewicz [this message]
2012-12-04 22:55 ` [PATCH] Add smart-space command 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xa1tobi930o1.fsf@mina86.com \
    --to=mpn@google.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).