(nth 8 (syntax-ppss)))
(define-obsolete-function-alias
- 'python-info-ppss-context #'python-syntax-context "24.2")
+ 'python-info-ppss-context #'python-syntax-context "24.3")
(define-obsolete-function-alias
- 'python-info-ppss-context-type #'python-syntax-context-type "24.2")
+ 'python-info-ppss-context-type #'python-syntax-context-type "24.3")
(define-obsolete-function-alias
'python-info-ppss-comment-or-string-p
- #'python-syntax-comment-or-string-p "24.2")
+ #'python-syntax-comment-or-string-p "24.3")
(defvar python-font-lock-keywords
;; Keywords
(1 font-lock-variable-name-face nil nil))))
(defconst python-syntax-propertize-function
- ;; Make outer chars of matching triple-quote sequences into generic
- ;; string delimiters. Fixme: Is there a better way?
- ;; First avoid a sequence preceded by an odd number of backslashes.
(syntax-propertize-rules
- (;; ¡Backrefs don't work in syntax-propertize-rules!
- (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix.
- "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)")
- (3 (ignore (python-quote-syntax))))))
-
-(defun python-quote-syntax ()
- "Put `syntax-table' property correctly on triple quote.
-Used for syntactic keywords. N is the match number (1, 2 or 3)."
- ;; Given a triple quote, we have to check the context to know
- ;; whether this is an opening or closing triple or whether it's
- ;; quoted anyhow, and should be ignored. (For that we need to do
- ;; the same job as `syntax-ppss' to be correct and it seems to be OK
- ;; to use it here despite initial worries.) We also have to sort
- ;; out a possible prefix -- well, we don't _have_ to, but I think it
- ;; should be treated as part of the string.
-
- ;; Test cases:
- ;; ur"""ar""" x='"' # """
- ;; x = ''' """ ' a
- ;; '''
- ;; x '"""' x """ \"""" x
- (save-excursion
- (goto-char (match-beginning 0))
- (let ((syntax (save-match-data (syntax-ppss))))
- (cond
- ((eq t (nth 3 syntax)) ; after unclosed fence
- ;; Consider property for the last char if in a fenced string.
- (goto-char (nth 8 syntax)) ; fence position
- (skip-chars-forward "uUrR") ; skip any prefix
- ;; Is it a matching sequence?
- (if (eq (char-after) (char-after (match-beginning 2)))
- (put-text-property (match-beginning 3) (match-end 3)
- 'syntax-table (string-to-syntax "|"))))
- ((match-end 1)
- ;; Consider property for initial char, accounting for prefixes.
- (put-text-property (match-beginning 1) (match-end 1)
- 'syntax-table (string-to-syntax "|")))
- (t
- ;; Consider property for initial char, accounting for prefixes.
- (put-text-property (match-beginning 2) (match-end 2)
- 'syntax-table (string-to-syntax "|"))))
- )))
+ ((rx
+ (and
+ ;; Match even number of backslashes.
+ (or (not (any ?\\ ?\' ?\")) point
+ ;; Quotes might be preceeded by a escaped quote.
+ (and (or (not (any ?\\)) point) ?\\
+ (* ?\\ ?\\) (any ?\' ?\")))
+ (* ?\\ ?\\)
+ ;; Match single or triple quotes of any kind.
+ (group (or "\"" "\"\"\"" "'" "'''"))))
+ (0 (ignore (python-syntax-stringify))))))
+
+(defsubst python-syntax-count-quotes (quote-char &optional point limit)
+ "Count number of quotes around point (max is 3).
+QUOTE-CHAR is the quote char to count. Optional argument POINT is
+the point where scan starts (defaults to current point) and LIMIT
+is used to limit the scan."
+ (let ((i 0))
+ (while (and (< i 3)
+ (or (not limit) (< (+ point i) limit))
+ (eq (char-after (+ point i)) quote-char))
+ (incf i))
+ i))
+
+(defun python-syntax-stringify ()
+ "Put `syntax-table' property correctly on single/triple quotes."
+ (let* ((num-quotes (length (match-string-no-properties 1)))
+ (ppss (prog2
+ (backward-char num-quotes)
+ (syntax-ppss)
+ (forward-char num-quotes)))
+ (string-start (and (not (nth 4 ppss)) (nth 8 ppss)))
+ (quote-starting-pos (- (point) num-quotes))
+ (quote-ending-pos (point))
+ (num-closing-quotes
+ (and string-start
+ (python-syntax-count-quotes
+ (char-before) string-start quote-starting-pos))))
+ (cond ((and string-start (= num-closing-quotes 0))
+ ;; This set of quotes doesn't match the string starting
+ ;; kind. Do nothing.
+ nil)
+ ((not string-start)
+ ;; This set of quotes delimit the start of a string.
+ (put-text-property quote-starting-pos (1+ quote-starting-pos)
+ 'syntax-table (string-to-syntax "|")))
+ ((= num-quotes num-closing-quotes)
+ ;; This set of quotes delimit the end of a string.
+ (put-text-property (1- quote-ending-pos) quote-ending-pos
+ 'syntax-table (string-to-syntax "|")))
+ ((> num-quotes num-closing-quotes)
+ ;; This may only happen whenever a triple quote is closing
+ ;; a single quoted string. Add string delimiter syntax to
+ ;; all three quotes.
+ (put-text-property quote-starting-pos quote-ending-pos
+ 'syntax-table (string-to-syntax "|"))))))
(defvar python-mode-syntax-table
(let ((table (make-syntax-table)))
:safe 'booleanp)
(define-obsolete-variable-alias
- 'python-indent 'python-indent-offset "24.2")
+ 'python-indent 'python-indent-offset "24.3")
(define-obsolete-variable-alias
- 'python-guess-indent 'python-indent-guess-indent-offset "24.2")
+ 'python-guess-indent 'python-indent-guess-indent-offset "24.3")
(defvar python-indent-current-level 0
"Current indentation level `python-indent-line-function' is using.")
`python-indent-levels'. Afterwards it sets the variable
`python-indent-current-level' correctly so offset is equal
to (`nth' `python-indent-current-level' `python-indent-levels')"
- (if (or (and (eq this-command 'indent-for-tab-command)
- (eq last-command this-command))
- force-toggle)
- (if (not (equal python-indent-levels '(0)))
- (python-indent-toggle-levels)
- (python-indent-calculate-levels))
- (python-indent-calculate-levels))
- (beginning-of-line)
- (delete-horizontal-space)
- (indent-to (nth python-indent-current-level python-indent-levels))
+ (or
+ (and (or (and (eq this-command 'indent-for-tab-command)
+ (eq last-command this-command))
+ force-toggle)
+ (not (equal python-indent-levels '(0)))
+ (or (python-indent-toggle-levels) t))
+ (python-indent-calculate-levels))
+ (let* ((starting-pos (point-marker))
+ (indent-ending-position
+ (+ (line-beginning-position) (current-indentation)))
+ (follow-indentation-p
+ (or (bolp)
+ (and (<= (line-beginning-position) starting-pos)
+ (>= indent-ending-position starting-pos))))
+ (next-indent (nth python-indent-current-level python-indent-levels)))
+ (unless (= next-indent (current-indentation))
+ (beginning-of-line)
+ (delete-horizontal-space)
+ (indent-to next-indent)
+ (goto-char starting-pos))
+ (and follow-indentation-p (back-to-indentation)))
(python-info-closing-block-message))
(defun python-indent-line-function ()
If DEDICATED is t and the variable `buffer-file-name' is non-nil
returns a string with the form
`python-shell-buffer-name'[variable `buffer-file-name'] else
-returns the value of `python-shell-buffer-name'. After
-calculating the process name adds the buffer name for the process
-in the `same-window-buffer-names' list."
+returns the value of `python-shell-buffer-name'."
(let ((process-name
(if (and dedicated
buffer-file-name)
(format "%s[%s]" python-shell-buffer-name buffer-file-name)
(format "%s" python-shell-buffer-name))))
- (add-to-list 'same-window-buffer-names (purecopy
- (format "*%s*" process-name)))
process-name))
(defun python-shell-internal-get-process-name ()
(get-buffer-process proc-buffer-name)))
(define-obsolete-function-alias
- 'python-proc 'python-shell-internal-get-or-create-process "24.2")
+ 'python-proc 'python-shell-internal-get-or-create-process "24.3")
(define-obsolete-variable-alias
- 'python-buffer 'python-shell-internal-buffer "24.2")
+ 'python-buffer 'python-shell-internal-buffer "24.3")
(define-obsolete-variable-alias
- 'python-preoutput-result 'python-shell-internal-last-output "24.2")
+ 'python-preoutput-result 'python-shell-internal-last-output "24.3")
(defun python-shell-send-string (string &optional process msg)
"Send STRING to inferior Python PROCESS.
(interactive "sPython command: ")
(let ((process (or process (python-shell-get-or-create-process)))
(lines (split-string string "\n" t)))
- (when msg
- (message (format "Sent: %s..." (nth 0 lines))))
+ (and msg (message "Sent: %s..." (nth 0 lines)))
(if (> (length lines) 1)
- (let* ((temp-file-name (make-temp-file "py"))
+ (let* ((temporary-file-directory
+ (if (file-remote-p default-directory)
+ (concat (file-remote-p default-directory) "/tmp")
+ temporary-file-directory))
+ (temp-file-name (make-temp-file "py"))
(file-name (or (buffer-file-name) temp-file-name)))
(with-temp-file temp-file-name
(insert string)
(string-match "\n[ \t].*\n?$" string))
(comint-send-string process "\n")))))
+;; Shell output catching stolen from gud-gdb
+(defvar python-shell-fetch-lines-in-progress nil)
+(defvar python-shell-fetch-lines-string nil)
+(defvar python-shell-fetched-lines nil)
+
+(defun python-shell-fetch-lines-filter (string)
+ "Filter used to read the list of lines output by a command.
+STRING is the output to filter."
+ (setq string (concat python-shell-fetch-lines-string string))
+ (while (string-match "\n" string)
+ (push (substring string 0 (match-beginning 0))
+ python-shell-fetched-lines)
+ (setq string (substring string (match-end 0))))
+ (if (equal (string-match comint-prompt-regexp string) 0)
+ (progn
+ (setq python-shell-fetch-lines-in-progress nil)
+ string)
+ (progn
+ (setq python-shell-fetch-lines-string string)
+ "")))
+
(defun python-shell-send-string-no-output (string &optional process msg)
"Send STRING to PROCESS and inhibit output.
When MSG is non-nil messages the first line of STRING. Return
the output."
- (let* ((output-buffer "")
- (process (or process (python-shell-get-or-create-process)))
- (comint-preoutput-filter-functions
- (append comint-preoutput-filter-functions
- '(ansi-color-filter-apply
- (lambda (string)
- (setq output-buffer (concat output-buffer string))
- ""))))
- (inhibit-quit t))
+ (let ((process (or process (python-shell-get-or-create-process)))
+ (comint-preoutput-filter-functions
+ '(python-shell-fetch-lines-filter))
+ (python-shell-fetch-lines-in-progress t)
+ (inhibit-quit t))
(or
(with-local-quit
(python-shell-send-string string process msg)
- (accept-process-output process)
- (replace-regexp-in-string
- (if (> (length python-shell-prompt-output-regexp) 0)
- (format "\n*%s$\\|^%s\\|\n$"
- python-shell-prompt-regexp
- (or python-shell-prompt-output-regexp ""))
- (format "\n*$\\|^%s\\|\n$"
- python-shell-prompt-regexp))
- "" output-buffer))
+ (while python-shell-fetch-lines-in-progress
+ (accept-process-output process))
+ (prog1
+ (mapconcat #'identity
+ (reverse python-shell-fetched-lines) "\n")
+ (setq python-shell-fetched-lines nil)))
(with-current-buffer (process-buffer process)
(comint-interrupt-subjob)))))
(python-shell-internal-get-or-create-process) nil)))
(define-obsolete-function-alias
- 'python-send-receive 'python-shell-internal-send-string "24.2")
+ 'python-send-receive 'python-shell-internal-send-string "24.3")
(define-obsolete-function-alias
- 'python-send-string 'python-shell-internal-send-string "24.2")
+ 'python-send-string 'python-shell-internal-send-string "24.3")
(defun python-shell-send-region (start end)
"Send the region delimited by START and END to inferior Python process."
(defun python-shell-send-buffer (&optional arg)
"Send the entire buffer to inferior Python process.
-
-With prefix ARG include lines surrounded by \"if __name__ == '__main__':\""
+With prefix ARG allow execution of code inside blocks delimited
+by \"if __name__== '__main__':\""
(interactive "P")
(save-restriction
(widen)
- (python-shell-send-region
- (point-min)
- (or (and
- (not arg)
- (save-excursion
- (re-search-forward (python-rx if-name-main) nil t))
- (match-beginning 0))
- (point-max)))))
+ (let ((str (buffer-substring (point-min) (point-max))))
+ (and
+ (not arg)
+ (setq str (replace-regexp-in-string
+ (python-rx if-name-main)
+ "if __name__ == '__main__ ':" str)))
+ (python-shell-send-string str))))
(defun python-shell-send-defun (arg)
"Send the current defun to inferior Python process.
(interactive "fFile to send: ")
(let* ((process (or process (python-shell-get-or-create-process)))
(temp-file-name (when temp-file-name
- (expand-file-name temp-file-name)))
- (file-name (or (expand-file-name file-name) temp-file-name)))
+ (expand-file-name
+ (or (file-remote-p temp-file-name 'localname)
+ temp-file-name))))
+ (file-name (or (when file-name
+ (expand-file-name
+ (or (file-remote-p file-name 'localname)
+ file-name)))
+ temp-file-name)))
(when (not file-name)
(error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil"))
(python-shell-send-string
"Send all setup code for shell.
This function takes the list of setup code to send from the
`python-shell-setup-codes' list."
- (let ((msg "Sent %s")
- (process (get-buffer-process (current-buffer))))
+ (let ((process (get-buffer-process (current-buffer))))
(dolist (code python-shell-setup-codes)
(when code
- (message (format msg code))
+ (message "Sent %s" code)
(python-shell-send-string
(symbol-value code) process)))))
:type 'string
:group 'python)
-(defun python-shell-completion--get-completions (input process completion-code)
- "Retrieve available completions for INPUT using PROCESS.
-Argument COMPLETION-CODE is the python code used to get
-completions on the current context."
- (with-current-buffer (process-buffer process)
- (let ((completions (python-shell-send-string-no-output
- (format completion-code input) process)))
- (when (> (length completions) 2)
- (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t)))))
-
-(defun python-shell-completion--do-completion-at-point (process)
- "Do completion at point for PROCESS."
- (with-syntax-table python-dotty-syntax-table
- (let* ((beg
- (save-excursion
+(defun python-shell-completion-get-completions (process line input)
+ "Do completion at point for PROCESS.
+LINE is used to detect the context on how to complete given
+INPUT."
+ (let* ((prompt
+ ;; Get the last prompt for the inferior process
+ ;; buffer. This is used for the completion code selection
+ ;; heuristic.
+ (with-current-buffer (process-buffer process)
+ (buffer-substring-no-properties
+ (overlay-start comint-last-prompt-overlay)
+ (overlay-end comint-last-prompt-overlay))))
+ (completion-context
+ ;; Check whether a prompt matches a pdb string, an import
+ ;; statement or just the standard prompt and use the
+ ;; correct python-shell-completion-*-code string
+ (cond ((and (> (length python-shell-completion-pdb-string-code) 0)
+ (string-match
+ (concat "^" python-shell-prompt-pdb-regexp) prompt))
+ 'pdb)
+ ((and (>
+ (length python-shell-completion-module-string-code) 0)
+ (string-match
+ (concat "^" python-shell-prompt-regexp) prompt)
+ (string-match "^[ \t]*\\(from\\|import\\)[ \t]" line))
+ 'import)
+ ((string-match
+ (concat "^" python-shell-prompt-regexp) prompt)
+ 'default)
+ (t nil)))
+ (completion-code
+ (case completion-context
+ (pdb python-shell-completion-pdb-string-code)
+ (import python-shell-completion-module-string-code)
+ (default python-shell-completion-string-code)
+ (t nil)))
+ (input
+ (if (eq completion-context 'import)
+ (replace-regexp-in-string "^[ \t]+" "" line)
+ input)))
+ (and completion-code
+ (> (length input) 0)
+ (with-current-buffer (process-buffer process)
+ (let ((completions (python-shell-send-string-no-output
+ (format completion-code input) process)))
+ (and (> (length completions) 2)
+ (split-string completions
+ "^'\\|^\"\\|;\\|'$\\|\"$" t)))))))
+
+(defun python-shell-completion-complete-at-point (&optional process)
+ "Perform completion at point in inferior Python.
+Optional argument PROCESS forces completions to be retrieved
+using that one instead of current buffer's process."
+ (setq process (or process (get-buffer-process (current-buffer))))
+ (let* ((start
+ (save-excursion
+ (with-syntax-table python-dotty-syntax-table
(let* ((paren-depth (car (syntax-ppss)))
(syntax-string "w_")
(syntax-list (string-to-syntax syntax-string)))
- ;; Stop scanning for the beginning of the completion subject
- ;; after the char before point matches a delimiter
- (while (member (car (syntax-after (1- (point)))) syntax-list)
+ ;; Stop scanning for the beginning of the completion
+ ;; subject after the char before point matches a
+ ;; delimiter
+ (while (member
+ (car (syntax-after (1- (point)))) syntax-list)
(skip-syntax-backward syntax-string)
(when (or (equal (char-before) ?\))
(equal (char-before) ?\"))
;; honor initial paren depth
(> (car (syntax-ppss)) paren-depth)
(python-syntax-context 'string))
- (forward-char -1))))
- (point)))
- (end (point))
- (line (buffer-substring-no-properties (point-at-bol) end))
- (input (buffer-substring-no-properties beg end))
- ;; Get the last prompt for the inferior process buffer. This is
- ;; used for the completion code selection heuristic.
- (prompt
- (with-current-buffer (process-buffer process)
- (buffer-substring-no-properties
- (overlay-start comint-last-prompt-overlay)
- (overlay-end comint-last-prompt-overlay))))
- (completion-context
- ;; Check whether a prompt matches a pdb string, an import statement
- ;; or just the standard prompt and use the correct
- ;; python-shell-completion-*-code string
- (cond ((and (> (length python-shell-completion-pdb-string-code) 0)
- (string-match
- (concat "^" python-shell-prompt-pdb-regexp) prompt))
- 'pdb)
- ((and (>
- (length python-shell-completion-module-string-code) 0)
- (string-match
- (concat "^" python-shell-prompt-regexp) prompt)
- (string-match "^[ \t]*\\(from\\|import\\)[ \t]" line))
- 'import)
- ((string-match
- (concat "^" python-shell-prompt-regexp) prompt)
- 'default)
- (t nil)))
- (completion-code
- (case completion-context
- ('pdb python-shell-completion-pdb-string-code)
- ('import python-shell-completion-module-string-code)
- ('default python-shell-completion-string-code)
- (t nil)))
- (input
- (if (eq completion-context 'import)
- (replace-regexp-in-string "^[ \t]+" "" line)
- input))
- (completions
- (and completion-code (> (length input) 0)
- (python-shell-completion--get-completions
- input process completion-code))))
- (list beg end completions))))
-
-(defun python-shell-completion-complete-at-point ()
- "Perform completion at point in inferior Python process."
- (and comint-last-prompt-overlay
- (> (point-marker) (overlay-end comint-last-prompt-overlay))
- (python-shell-completion--do-completion-at-point
- (get-buffer-process (current-buffer)))))
+ (forward-char -1)))
+ (point)))))
+ (end (point)))
+ (list start end
+ (completion-table-dynamic
+ (apply-partially
+ #'python-shell-completion-get-completions
+ process (buffer-substring-no-properties
+ (line-beginning-position) end))))))
(defun python-shell-completion-complete-or-indent ()
"Complete or indent depending on the context.
(let ((process (python-shell-get-process)))
(if (not process)
(error "Completion needs an inferior Python process running")
- (python-shell-completion--do-completion-at-point process))))
+ (python-shell-completion-complete-at-point process))))
(add-to-list 'debug-ignored-errors
"^Completion needs an inferior Python process running.")
This is the function used by `python-fill-paragraph-function' to
fill comments."
:type 'symbol
- :group 'python
- :safe 'symbolp)
+ :group 'python)
(defcustom python-fill-string-function 'python-fill-string
"Function to fill strings.
This is the function used by `python-fill-paragraph-function' to
fill strings."
:type 'symbol
- :group 'python
- :safe 'symbolp)
+ :group 'python)
(defcustom python-fill-decorator-function 'python-fill-decorator
"Function to fill decorators.
This is the function used by `python-fill-paragraph-function' to
fill decorators."
:type 'symbol
- :group 'python
- :safe 'symbolp)
+ :group 'python)
(defcustom python-fill-paren-function 'python-fill-paren
"Function to fill parens.
This is the function used by `python-fill-paragraph-function' to
fill parens."
:type 'symbol
+ :group 'python)
+
+(defcustom python-fill-string-style 'pep-257
+ "Style used to fill docstrings.
+This affects `python-fill-string' behavior with regards to
+triple quotes positioning.
+
+Possible values are DJANGO, PEP-257, PEP-257-NN, SYMMETRIC and
+NIL. A value of NIL won't care about quotes position, will do
+what `fill-paragraph' does, any other value may result in one of
+the following docstring styles:
+
+DJANGO:
+
+ \"\"\"
+ Process foo, return bar.
+ \"\"\"
+
+ \"\"\"
+ Process foo, return bar.
+
+ If processing fails throw ProcessingError.
+ \"\"\"
+
+PEP-257:
+
+ \"\"\"Process foo, return bar.\"\"\"
+
+ \"\"\"Process foo, return bar.
+
+ If processing fails throw ProcessingError.
+
+ \"\"\"
+
+PEP-257-NN:
+
+ \"\"\"Process foo, return bar.\"\"\"
+
+ \"\"\"Process foo, return bar.
+
+ If processing fails throw ProcessingError.
+ \"\"\"
+
+SYMMETRIC:
+
+ \"\"\"Process foo, return bar.\"\"\"
+
+ \"\"\"
+ Process foo, return bar.
+
+ If processing fails throw ProcessingError.
+ \"\"\""
+ :type 'symbol
:group 'python
- :safe 'symbolp)
+ :safe (lambda (val) (memq val '(django pep-257 pep-257-nn symmetric nil))))
(defun python-fill-paragraph-function (&optional justify)
"`fill-paragraph-function' handling multi-line strings and possibly comments.
Optional argument JUSTIFY defines if the paragraph should be justified."
(interactive "P")
(save-excursion
- (back-to-indentation)
(cond
;; Comments
- ((funcall python-fill-comment-function justify))
+ ((python-syntax-context 'comment)
+ (funcall python-fill-comment-function justify))
;; Strings/Docstrings
- ((save-excursion (skip-chars-forward "\"'uUrR")
- (python-syntax-context 'string))
+ ((save-excursion (or (python-syntax-context 'string)
+ (equal (string-to-syntax "|")
+ (syntax-after (point)))))
(funcall python-fill-string-function justify))
;; Decorators
((equal (char-after (save-excursion
(back-to-indentation)
- (point-marker))) ?@)
+ (point))) ?@)
(funcall python-fill-decorator-function justify))
;; Parens
((or (python-syntax-context 'paren)
(defun python-fill-string (&optional justify)
"String fill function for `python-fill-paragraph-function'.
JUSTIFY should be used (if applicable) as in `fill-paragraph'."
- (let ((marker (point-marker))
- (string-start-marker
- (progn
- (skip-chars-forward "\"'uUrR")
- (goto-char (python-syntax-context 'string))
- (skip-chars-forward "\"'uUrR")
- (point-marker)))
- (reg-start (line-beginning-position))
- (string-end-marker
- (progn
- (while (python-syntax-context 'string)
- (goto-char (1+ (point-marker))))
- (skip-chars-backward "\"'")
- (point-marker)))
- (reg-end (line-end-position))
- (fill-paragraph-function))
+ (let* ((marker (point-marker))
+ (str-start-pos
+ (let ((m (make-marker)))
+ (setf (marker-position m)
+ (or (python-syntax-context 'string)
+ (and (equal (string-to-syntax "|")
+ (syntax-after (point)))
+ (point)))) m))
+ (num-quotes (python-syntax-count-quotes
+ (char-after str-start-pos) str-start-pos))
+ (str-end-pos
+ (save-excursion
+ (goto-char (+ str-start-pos num-quotes))
+ (or (re-search-forward (rx (syntax string-delimiter)) nil t)
+ (goto-char (point-max)))
+ (point-marker)))
+ (multi-line-p
+ ;; Docstring styles may vary for oneliners and multi-liners.
+ (> (count-matches "\n" str-start-pos str-end-pos) 0))
+ (delimiters-style
+ (case python-fill-string-style
+ ;; delimiters-style is a cons cell with the form
+ ;; (START-NEWLINES . END-NEWLINES). When any of the sexps
+ ;; is NIL means to not add any newlines for start or end
+ ;; of docstring. See `python-fill-string-style' for a
+ ;; graphic idea of each style.
+ (pep-257 (and multi-line-p (cons nil 2)))
+ (pep-257-nn (and multi-line-p (cons nil 1)))
+ (django (cons 1 1))
+ (symmetric (and multi-line-p (cons 1 1)))))
+ (docstring-p (save-excursion
+ ;; Consider docstrings those strings which
+ ;; start on a line by themselves.
+ (goto-char str-start-pos)
+ (skip-chars-backward (rx whitespace))
+ (= (point) (line-beginning-position))))
+ (fill-paragraph-function))
(save-restriction
- (narrow-to-region reg-start reg-end)
- (save-excursion
- (goto-char string-start-marker)
- (delete-region (point-marker) (progn
- (skip-syntax-forward "> ")
- (point-marker)))
- (goto-char string-end-marker)
- (delete-region (point-marker) (progn
- (skip-syntax-backward "> ")
- (point-marker)))
- (save-excursion
- (goto-char marker)
- (fill-paragraph justify))
- ;; If there is a newline in the docstring lets put triple
- ;; quote in it's own line to follow pep 8
- (when (save-excursion
- (re-search-backward "\n" string-start-marker t))
- (newline)
- (newline-and-indent))
- (fill-paragraph justify)))) t)
+ (narrow-to-region str-start-pos str-end-pos)
+ (fill-paragraph justify))
+ (save-excursion
+ (when (and docstring-p python-fill-string-style)
+ ;; Add the number of newlines indicated by the selected style
+ ;; at the start of the docstring.
+ (goto-char (+ str-start-pos num-quotes))
+ (delete-region (point) (progn
+ (skip-syntax-forward "> ")
+ (point)))
+ (and (car delimiters-style)
+ (or (newline (car delimiters-style)) t)
+ ;; Indent only if a newline is added.
+ (indent-according-to-mode))
+ ;; Add the number of newlines indicated by the selected style
+ ;; at the end of the docstring.
+ (goto-char (if (not (= str-end-pos (point-max)))
+ (- str-end-pos num-quotes)
+ str-end-pos))
+ (delete-region (point) (progn
+ (skip-syntax-backward "> ")
+ (point)))
+ (and (cdr delimiters-style)
+ ;; Add newlines only if string ends.
+ (not (= str-end-pos (point-max)))
+ (or (newline (cdr delimiters-style)) t)
+ ;; Again indent only if a newline is added.
+ (indent-according-to-mode))))) t)
(defun python-fill-decorator (&optional justify)
"Decorator fill function for `python-fill-paragraph-function'.
:safe 'booleanp)
(define-obsolete-variable-alias
- 'python-use-skeletons 'python-skeleton-autoinsert "24.2")
+ 'python-use-skeletons 'python-skeleton-autoinsert "24.3")
(defvar python-skeleton-available '()
"Internal list of available skeletons.")