[Python-checkins] python/dist/src/Misc python-mode.el,4.28,4.29

montanaro@users.sourceforge.net montanaro@users.sourceforge.net
Tue, 31 Dec 2002 08:56:23 -0800


Update of /cvsroot/python/python/dist/src/Misc
In directory sc8-pr-cvs1:/tmp/cvs-serv12536

Modified Files:
	python-mode.el 
Log Message:
Bernhard Herzog's paragraph and string-filling code.  I've been using it for
a month or two with great success.  Barry may want to tweak it some, but I
think it's a worthwhile enough addition to get some more people trying it
out.



Index: python-mode.el
===================================================================
RCS file: /cvsroot/python/python/dist/src/Misc/python-mode.el,v
retrieving revision 4.28
retrieving revision 4.29
diff -C2 -d -r4.28 -r4.29
*** python-mode.el	21 Oct 2002 15:58:29 -0000	4.28
--- python-mode.el	31 Dec 2002 16:56:20 -0000	4.29
***************
*** 518,521 ****
--- 518,523 ----
    (define-key py-mode-map "\C-c<"     'py-shift-region-left)
    (define-key py-mode-map "\C-c>"     'py-shift-region-right)
+   ;; paragraph and string filling
+   (define-key py-mode-map "\eq"       'py-fill-paragraph)
    ;; subprocess commands
    (define-key py-mode-map "\C-c\C-c"  'py-execute-buffer)
***************
*** 3481,3484 ****
--- 3483,3636 ----
      (push '(py-pdbtrack-is-tracking-p py-pdbtrack-minor-mode-string)
  	  minor-mode-alist))
+ 
+ 
+ 
+ ;;; paragraph and string filling code from Bernhard Herzog
+ ;;; see http://mail.python.org/pipermail/python-list/2002-May/103189.html
+ 
+ (defun py-fill-comment (&optional justify)
+   "Fill the comment paragraph around point"
+   (let (;; Non-nil if the current line contains a comment.
+ 	has-comment
+ 
+ 	;; If has-comment, the appropriate fill-prefix for the comment.
+ 	comment-fill-prefix)
+ 
+     ;; Figure out what kind of comment we are looking at.
+     (save-excursion
+       (beginning-of-line)
+       (cond
+        ;; A line with nothing but a comment on it?
+        ((looking-at "[ \t]*#[# \t]*")
+ 	(setq has-comment t
+ 	      comment-fill-prefix (buffer-substring (match-beginning 0)
+ 						    (match-end 0))))
+ 
+        ;; A line with some code, followed by a comment? Remember that the hash
+        ;; which starts the comment shouldn't be part of a string or character.
+        ((progn
+ 	  (while (not (looking-at "#\\|$"))
+ 	    (skip-chars-forward "^#\n\"'\\")
+ 	    (cond
+ 	     ((eq (char-after (point)) ?\\) (forward-char 2))
+ 	     ((memq (char-after (point)) '(?\" ?')) (forward-sexp 1))))
+ 	  (looking-at "#+[\t ]*"))
+ 	(setq has-comment t)
+ 	(setq comment-fill-prefix
+ 	      (concat (make-string (current-column) ? )
+ 		      (buffer-substring (match-beginning 0) (match-end 0)))))))
+ 
+     (if (not has-comment)
+ 	(fill-paragraph justify)
+ 
+       ;; Narrow to include only the comment, and then fill the region.
+       (save-restriction
+ 	(narrow-to-region
+ 
+ 	 ;; Find the first line we should include in the region to fill.
+ 	 (save-excursion
+ 	   (while (and (zerop (forward-line -1))
+ 		       (looking-at "^[ \t]*#")))
+ 
+ 	   ;; We may have gone to far.  Go forward again.
+ 	   (or (looking-at "^[ \t]*#")
+ 	       (forward-line 1))
+ 	   (point))
+ 
+ 	 ;; Find the beginning of the first line past the region to fill.
+ 	 (save-excursion
+ 	   (while (progn (forward-line 1)
+ 			 (looking-at "^[ \t]*#")))
+ 	   (point)))
+ 
+ 	;; Lines with only hashes on them can be paragraph boundaries.
+ 	(let ((paragraph-start (concat paragraph-start "\\|[ \t#]*$"))
+ 	      (paragraph-separate (concat paragraph-separate "\\|[ \t#]*$"))
+ 	      (fill-prefix comment-fill-prefix))
+ 	  ;;(message "paragraph-start %S paragraph-separate %S"
+ 	  ;;paragraph-start paragraph-separate)
+ 	  (fill-paragraph justify))))
+     t))
+ 
+ 
+ (defun py-fill-string (start &optional justify)
+   "Fill the paragraph around (point) in the string starting at start"
+   ;; basic strategy: narrow to the string and call the default
+   ;; implementation
+   (let (;; the start of the string's contents
+ 	string-start
+ 	;; the end of the string's contents
+ 	string-end
+ 	;; length of the string's delimiter
+ 	delim-length
+ 	;; The string delimiter
+ 	delim
+ 	)
+ 
+     (save-excursion
+       (goto-char start)
+       (if (looking-at "\\('''\\|\"\"\"\\|'\\|\"\\)\\\\?\n?")
+ 	  (setq string-start (match-end 0)
+ 		delim-length (- (match-end 1) (match-beginning 1))
+ 		delim (buffer-substring-no-properties (match-beginning 1)
+ 						      (match-end 1)))
+ 	(error "The parameter start is not the beginning of a python string"))
+ 
+       ;; if the string is the first token on a line and doesn't start with
+       ;; a newline, fill as if the string starts at the beginning of the
+       ;; line. this helps with one line docstrings
+       (save-excursion
+ 	(beginning-of-line)
+ 	(and (/= (char-before string-start) ?\n)
+ 	     (looking-at (concat "[ \t]*" delim))
+ 	     (setq string-start (point))))
+ 
+       (forward-sexp (if (= delim-length 3) 2 1))
+ 
+       ;; with both triple quoted strings and single/double quoted strings
+       ;; we're now directly behind the first char of the end delimiter
+       ;; (this doesn't work correctly when the triple quoted string
+       ;; contains the quote mark itself). The end of the string's contents
+       ;; is one less than point
+       (setq string-end (1- (point))))
+ 
+     ;; Narrow to the string's contents and fill the current paragraph
+     (save-restriction
+       (narrow-to-region string-start string-end)
+       (let ((ends-with-newline (= (char-before (point-max)) ?\n)))
+ 	(fill-paragraph justify)
+ 	(if (and (not ends-with-newline)
+ 		 (= (char-before (point-max)) ?\n))
+ 	    ;; the default fill-paragraph implementation has inserted a
+ 	    ;; newline at the end. Remove it again.
+ 	    (save-excursion
+ 	      (goto-char (point-max))
+ 	      (delete-char -1)))))
+ 
+     ;; return t to indicate that we've done our work
+     t))
+ 
+ (defun py-fill-paragraph (&optional justify)
+   "Like \\[fill-paragraph], but handle Python comments and strings.
+ If any of the current line is a comment, fill the comment or the
+ paragraph of it that point is in, preserving the comment's indentation
+ and initial `#'s.
+ If point is inside a string, narrow to that string and fill.
+ "
+   (interactive "P")
+   (let* ((bod (py-point 'bod))
+ 	 (pps (parse-partial-sexp bod (point))))
+     (cond
+      ;; are we inside a comment or on a line with only whitespace before
+      ;; the comment start?
+      ((or (nth 4 pps)
+ 	  (save-excursion (beginning-of-line) (looking-at "[ \t]*#")))
+       (py-fill-comment justify))
+      ;; are we inside a string?
+      ((nth 3 pps)
+       (py-fill-string (nth 2 pps)))
+      ;; otherwise use the default
+      (t
+       (fill-paragraph justify)))))