summaryrefslogtreecommitdiff
path: root/emacs/multistring.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/multistring.el')
-rw-r--r--emacs/multistring.el220
1 files changed, 220 insertions, 0 deletions
diff --git a/emacs/multistring.el b/emacs/multistring.el
new file mode 100644
index 000000000..df8419542
--- /dev/null
+++ b/emacs/multistring.el
@@ -0,0 +1,220 @@
+;;; multistring.el --- editing multiline strings.
+
+;; Copyright (C) 2000, 2006 Free Software Foundation, Inc.
+
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free
+;;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+;;;; 02111-1307 USA
+
+;;; Author: Mikael Djurfeldt <djurfeldt@nada.kth.se>
+
+;;; Commentary:
+
+;; Commands for editing multiline ANSI C compatible string literals.
+
+;;; Code:
+
+(defun ms-ansify-string (arg)
+ "Convert a string literal spanning multiple lines into multiple literals.
+With no argument, convert the single string at point to multiple strings.
+With an argument, convert multiple strings to a single one.
+
+ANSI C doesn't allow a string literal to span multiple lines. This
+function makes editing of multiline strings easier.
+
+The programmer can edit a string spanning multiple lines and then use
+this function to convert it into multiple literals representing the
+original string through the ANSI C string concatenation feature:
+
+ \"A string consisting of
+ multiple
+ lines.\"
+
+is converted into
+
+ \"A string consisting of\n\"
+ \"multiple\n\"
+ \"lines\""
+ (interactive "*P")
+ (save-excursion
+ (let (beg end)
+ (save-restriction
+ (ms-narrow-canonicalize-ansi-c-string)
+ (if (not arg)
+ (ms-break-string))
+ (setq beg (point-min))
+ (setq end (point-max)))
+ (if (not arg)
+ (c-indent-region beg end)))))
+
+(defun ms-pack-region (from to &optional unpack-flag)
+ "Pack paragraphs into single lines and remove one newline after paragraphs.
+With no argument, do the conversion.
+With an argument, do the reverse.
+
+When doing the reverse conversion, \\[fill-region] is used to break up
+the text into multiple lines."
+ (interactive "*r\nP")
+ (save-excursion
+ (save-restriction
+ (narrow-to-region from to)
+ (if (not unpack-flag)
+ (ms-pack-buffer)
+ (ms-unpack-buffer)))))
+
+(defun ms-pack-ansify-string (arg)
+ "Pack text in a string literal and convert into multiple literals.
+With no argument, do the conversion.
+With an argument, do the reverse.
+
+This command has the combined effect of \\[ms-pack-region] and
+\\[ms-ansify-string]. It is typically used if you want to store
+entire paragraphs without newlines in an ANSI C literal, but want to
+split it into multiple literals in order for the program text to look
+sensible. Using the reverse command, you can \"unpack\" the text,
+edit it and repack the text using the forward conversion."
+ (interactive "*P")
+ (save-excursion
+ (let (beg end)
+ (save-restriction
+ (ms-narrow-canonicalize-ansi-c-string)
+ (if (not arg)
+ (ms-break-string " " "" "\\n")
+ (ms-unpack-buffer))
+ (setq beg (point-min))
+ (setq end (point-max)))
+ (if (not arg)
+ (c-indent-region beg end)))))
+
+(defun ms-pack-buffer ()
+ "Pack paragraphs into single lines and remove one newline after paragraphs."
+ (interactive "*")
+ (goto-char (point-min))
+ (skip-chars-forward "^\n")
+ (while (not (eobp))
+ (delete-char 1)
+ (skip-chars-forward "\n")
+ (skip-chars-forward "^\n")))
+
+(defun ms-unpack-buffer ()
+ "Break single lines into paragraphs and add an extra newline between each."
+ (interactive "*")
+ (goto-char (point-min))
+ (skip-chars-forward "^\n")
+ (while (not (eobp))
+ (insert ?\n)
+ (skip-chars-forward "\n")
+ (skip-chars-forward "^\n"))
+ (fill-region (point-min) (point-max) nil t))
+
+(defconst ms-whitespace " \t\n")
+(defconst ms-string-beginning "\"")
+(defconst ms-string-end "\\(\\\\*\\)\"")
+(defconst ms-quoted-newline "\\(\\\\*\\)\\(\\\\n\\)")
+
+(defun ms-in-string-p ()
+ (eq (c-in-literal) 'string))
+
+(defun ms-narrow-canonicalize-ansi-c-string ()
+ ;; Find and check reference point
+ (cond ((ms-in-string-p))
+ ((eq (char-after) ?\") (forward-char))
+ (t (error "Not in string.")))
+ (set-mark (point))
+ ;; Find beginning
+ (ms-beginning-of-string)
+ (let ((beg (point)))
+ ;; Extend string backwards
+ (while (ms-extend-backwards)
+ (setq beg (point)))
+ (goto-char (mark))
+ ;; Find end
+ (ms-end-of-string)
+ (let ((end (point)))
+ ;; Extend string forwards
+ (while (ms-extend-forwards)
+ (setq end (point)))
+ ;; Narrow
+ (narrow-to-region beg end)
+ ;; Convert \n into explicit newlines
+ (ms-convert-quoted-newlines))))
+
+(defun ms-beginning-of-string ()
+ (let ((pos (search-backward ms-string-beginning nil t)))
+ (while (and pos
+ (char-before)
+ (eq (char-before) ?\\))
+ (setq pos (search-backward ms-string-beginning nil t)))
+ (if pos
+ (progn
+ (forward-char)
+ (1+ pos)))))
+
+(defun ms-extend-backwards ()
+ (let ((end (point)))
+ (backward-char)
+ (skip-chars-backward ms-whitespace)
+ (if (eq (char-before) ?\")
+ (progn
+ (backward-char)
+ (delete-region (point) end)
+ (ms-beginning-of-string)))))
+
+(defun ms-end-of-string ()
+ (let ((pos (search-forward-regexp ms-string-end nil t)))
+ (while (and pos (= (logand (- (match-end 1) (match-beginning 1)) 1) 1))
+ (setq pos (search-forward-regexp ms-string-end nil t)))
+ (if pos
+ (progn
+ (backward-char)
+ (match-end 1)))))
+
+(defun ms-extend-forwards ()
+ (let ((start (point)))
+ (forward-char)
+ (skip-chars-forward ms-whitespace)
+ (if (eq (char-after) ?\")
+ (progn
+ (forward-char)
+ (delete-region start (point))
+ (ms-end-of-string)))))
+
+(defun ms-convert-quoted-newlines ()
+ (goto-char (point-min))
+ (while (search-forward-regexp ms-quoted-newline nil t)
+ (if (= (logand (- (match-end 1) (match-beginning 1)) 1) 0)
+ (replace-match "\n" nil t nil 2))))
+
+(defun ms-break-string (&optional single-term multi-term-1 multi-term-n)
+ (let ((single-term (or single-term "\\n"))
+ (multi-term-1 (or multi-term-1 "\\n"))
+ (multi-term-n (or multi-term-n "\\n")))
+ (goto-char (point-min))
+ (skip-chars-forward "^\n")
+ (while (not (eobp))
+ (delete-char 1)
+ (if (not (eq (char-after) ?\n))
+ (insert single-term)
+ (insert multi-term-1)
+ (while (eq (char-after) ?\n)
+ (delete-char 1)
+ (insert multi-term-n)))
+ (insert "\"\n\"")
+ (skip-chars-forward "^\n"))))
+
+(eval-after-load "cc-mode"
+ (progn
+ (define-key c-mode-base-map "\C-ca" 'ms-ansify-string)
+ (define-key c-mode-base-map "\C-cd" 'ms-pack-ansify-string)
+ ))