2012-06-18 14:04:21 -04:00
|
|
|
|
;;; python.el --- Python's flying circus support for Emacs -*- coding: utf-8 -*-
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-06-14 23:22:19 -03:00
|
|
|
|
;; Copyright (C) 2003-2012 Free Software Foundation, Inc.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;; Author: Fabián E. Gallina <fabian@anue.biz>
|
2012-05-17 00:03:18 -03:00
|
|
|
|
;; URL: https://github.com/fgallina/python.el
|
2012-05-17 00:03:45 -03:00
|
|
|
|
;; Version: 0.24.2
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Maintainer: FSF
|
|
|
|
|
;; Created: Jul 2010
|
|
|
|
|
;; Keywords: languages
|
|
|
|
|
|
2012-05-17 00:03:45 -03:00
|
|
|
|
;; This file is part of GNU Emacs.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:45 -03:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published
|
|
|
|
|
;; by the Free Software Foundation, either version 3 of the License,
|
|
|
|
|
;; or (at your option) any later version.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:45 -03:00
|
|
|
|
;; GNU Emacs 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
|
|
|
|
|
;; General Public License for more details.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2012-05-17 00:03:45 -03:00
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; Major mode for editing Python files with some fontification and
|
|
|
|
|
;; indentation bits extracted from original Dave Love's python.el
|
|
|
|
|
;; found in GNU/Emacs.
|
|
|
|
|
|
|
|
|
|
;; Implements Syntax highlighting, Indentation, Movement, Shell
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; interaction, Shell completion, Shell virtualenv support, Pdb
|
|
|
|
|
;; tracking, Symbol completion, Skeletons, FFAP, Code Check, Eldoc,
|
|
|
|
|
;; imenu.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;; Syntax highlighting: Fontification of code is provided and supports
|
|
|
|
|
;; python's triple quoted strings properly.
|
|
|
|
|
|
|
|
|
|
;; Indentation: Automatic indentation with indentation cycling is
|
|
|
|
|
;; provided, it allows you to navigate different available levels of
|
2012-05-17 00:03:05 -03:00
|
|
|
|
;; indentation by hitting <tab> several times. Also when inserting a
|
|
|
|
|
;; colon the `python-indent-electric-colon' command is invoked and
|
|
|
|
|
;; causes the current line to be dedented automatically if needed.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;; Movement: `beginning-of-defun' and `end-of-defun' functions are
|
2012-05-17 00:03:25 -03:00
|
|
|
|
;; properly implemented. There are also specialized
|
2012-07-16 10:13:01 -03:00
|
|
|
|
;; `forward-sentence' and `backward-sentence' replacements called
|
|
|
|
|
;; `python-nav-forward-block', `python-nav-backward-block'
|
|
|
|
|
;; respectively which navigate between beginning of blocks of code.
|
|
|
|
|
;; Extra functions `python-nav-forward-statement',
|
|
|
|
|
;; `python-nav-backward-statement', `python-nav-statement-start',
|
|
|
|
|
;; `python-nav-statement-end', `python-nav-block-start' and
|
|
|
|
|
;; `python-nav-block-end' are included but no bound to any key.
|
2012-05-17 00:03:25 -03:00
|
|
|
|
;; `python-nav-jump-to-defun' is provided and allows jumping to a
|
|
|
|
|
;; function or class definition quickly in the current buffer.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:25 -03:00
|
|
|
|
;; Shell interaction: is provided and allows you to execute easily any
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; block of code of your current buffer in an inferior Python process.
|
|
|
|
|
|
|
|
|
|
;; Shell completion: hitting tab will try to complete the current
|
2012-05-17 00:02:56 -03:00
|
|
|
|
;; word. Shell completion is implemented in a manner that if you
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; change the `python-shell-interpreter' to any other (for example
|
|
|
|
|
;; IPython) it should be easy to integrate another way to calculate
|
2012-05-17 00:03:02 -03:00
|
|
|
|
;; completions. You just need to specify your custom
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; `python-shell-completion-setup-code' and
|
2012-05-17 00:03:16 -03:00
|
|
|
|
;; `python-shell-completion-string-code'.
|
2012-05-17 00:03:06 -03:00
|
|
|
|
|
|
|
|
|
;; Here is a complete example of the settings you would use for
|
2012-05-17 00:03:25 -03:00
|
|
|
|
;; iPython 0.11:
|
2012-05-17 00:03:06 -03:00
|
|
|
|
|
|
|
|
|
;; (setq
|
|
|
|
|
;; python-shell-interpreter "ipython"
|
|
|
|
|
;; python-shell-interpreter-args ""
|
|
|
|
|
;; python-shell-prompt-regexp "In \\[[0-9]+\\]: "
|
|
|
|
|
;; python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
|
2012-05-17 00:03:29 -03:00
|
|
|
|
;; python-shell-completion-setup-code
|
|
|
|
|
;; "from IPython.core.completerlib import module_completion"
|
2012-05-17 00:03:31 -03:00
|
|
|
|
;; python-shell-completion-module-string-code
|
2012-05-17 00:03:29 -03:00
|
|
|
|
;; "';'.join(module_completion('''%s'''))\n"
|
2012-05-17 00:03:06 -03:00
|
|
|
|
;; python-shell-completion-string-code
|
2012-05-17 00:03:29 -03:00
|
|
|
|
;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")
|
2012-05-17 00:03:25 -03:00
|
|
|
|
|
|
|
|
|
;; For iPython 0.10 everything would be the same except for
|
2012-05-17 00:03:38 -03:00
|
|
|
|
;; `python-shell-completion-string-code' and
|
|
|
|
|
;; `python-shell-completion-module-string-code':
|
2012-05-17 00:03:25 -03:00
|
|
|
|
|
|
|
|
|
;; (setq python-shell-completion-string-code
|
2012-05-17 00:03:38 -03:00
|
|
|
|
;; "';'.join(__IP.complete('''%s'''))\n"
|
|
|
|
|
;; python-shell-completion-module-string-code "")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:32 -03:00
|
|
|
|
;; Unfortunately running iPython on Windows needs some more tweaking.
|
|
|
|
|
;; The way you must set `python-shell-interpreter' and
|
|
|
|
|
;; `python-shell-interpreter-args' is as follows:
|
|
|
|
|
|
|
|
|
|
;; (setq
|
|
|
|
|
;; python-shell-interpreter "C:\\Python27\\python.exe"
|
|
|
|
|
;; python-shell-interpreter-args
|
|
|
|
|
;; "-i C:\\Python27\\Scripts\\ipython-script.py")
|
|
|
|
|
|
|
|
|
|
;; That will spawn the iPython process correctly (Of course you need
|
|
|
|
|
;; to modify the paths according to your system).
|
|
|
|
|
|
2012-05-17 00:03:07 -03:00
|
|
|
|
;; Please note that the default completion system depends on the
|
|
|
|
|
;; readline module, so if you are using some Operating System that
|
|
|
|
|
;; bundles Python without it (like Windows) just install the
|
|
|
|
|
;; pyreadline from http://ipython.scipy.org/moin/PyReadline/Intro and
|
|
|
|
|
;; you should be good to go.
|
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; Shell virtualenv support: The shell also contains support for
|
|
|
|
|
;; virtualenvs and other special environment modifications thanks to
|
2012-05-17 00:03:10 -03:00
|
|
|
|
;; `python-shell-process-environment' and `python-shell-exec-path'.
|
|
|
|
|
;; These two variables allows you to modify execution paths and
|
2012-05-17 00:03:24 -03:00
|
|
|
|
;; environment variables to make easy for you to setup virtualenv rules
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; or behavior modifications when running shells. Here is an example
|
2012-05-17 00:03:10 -03:00
|
|
|
|
;; of how to make shell processes to be run using the /path/to/env/
|
|
|
|
|
;; virtualenv:
|
|
|
|
|
|
|
|
|
|
;; (setq python-shell-process-environment
|
|
|
|
|
;; (list
|
|
|
|
|
;; (format "PATH=%s" (mapconcat
|
|
|
|
|
;; 'identity
|
|
|
|
|
;; (reverse
|
|
|
|
|
;; (cons (getenv "PATH")
|
|
|
|
|
;; '("/path/to/env/bin/")))
|
|
|
|
|
;; ":"))
|
|
|
|
|
;; "VIRTUAL_ENV=/path/to/env/"))
|
|
|
|
|
;; (python-shell-exec-path . ("/path/to/env/bin/"))
|
|
|
|
|
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; Since the above is cumbersome and can be programmatically
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; calculated, the variable `python-shell-virtualenv-path' is
|
|
|
|
|
;; provided. When this variable is set with the path of the
|
|
|
|
|
;; virtualenv to use, `process-environment' and `exec-path' get proper
|
|
|
|
|
;; values in order to run shells inside the specified virtualenv. So
|
|
|
|
|
;; the following will achieve the same as the previous example:
|
|
|
|
|
|
|
|
|
|
;; (setq python-shell-virtualenv-path "/path/to/env/")
|
|
|
|
|
|
2012-05-17 00:03:27 -03:00
|
|
|
|
;; Also the `python-shell-extra-pythonpaths' variable have been
|
|
|
|
|
;; introduced as simple way of adding paths to the PYTHONPATH without
|
|
|
|
|
;; affecting existing values.
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Pdb tracking: when you execute a block of code that contains some
|
|
|
|
|
;; call to pdb (or ipdb) it will prompt the block of code and will
|
|
|
|
|
;; follow the execution of pdb marking the current line with an arrow.
|
|
|
|
|
|
2012-05-17 00:02:56 -03:00
|
|
|
|
;; Symbol completion: you can complete the symbol at point. It uses
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; the shell completion in background so you should run
|
|
|
|
|
;; `python-shell-send-buffer' from time to time to get better results.
|
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
;; Skeletons: 6 skeletons are provided for simple inserting of class,
|
|
|
|
|
;; def, for, if, try and while. These skeletons are integrated with
|
|
|
|
|
;; dabbrev. If you have `dabbrev-mode' activated and
|
|
|
|
|
;; `python-skeleton-autoinsert' is set to t, then whenever you type
|
|
|
|
|
;; the name of any of those defined and hit SPC, they will be
|
|
|
|
|
;; automatically expanded.
|
|
|
|
|
|
2012-05-17 00:03:00 -03:00
|
|
|
|
;; FFAP: You can find the filename for a given module when using ffap
|
|
|
|
|
;; out of the box. This feature needs an inferior python shell
|
|
|
|
|
;; running.
|
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
;; Code check: Check the current file for errors with `python-check'
|
|
|
|
|
;; using the program defined in `python-check-command'.
|
2012-05-17 00:03:00 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Eldoc: returns documentation for object at point by using the
|
2012-05-17 00:02:56 -03:00
|
|
|
|
;; inferior python subprocess to inspect its documentation. As you
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; might guessed you should run `python-shell-send-buffer' from time
|
|
|
|
|
;; to time to get better results too.
|
|
|
|
|
|
2012-05-17 00:03:16 -03:00
|
|
|
|
;; imenu: This mode supports imenu. It builds a plain or tree menu
|
|
|
|
|
;; depending on the value of `python-imenu-make-tree'. Also you can
|
2012-05-17 00:03:14 -03:00
|
|
|
|
;; customize if menu items should include its type using
|
|
|
|
|
;; `python-imenu-include-defun-type'.
|
|
|
|
|
|
2012-05-17 00:03:02 -03:00
|
|
|
|
;; If you used python-mode.el you probably will miss auto-indentation
|
|
|
|
|
;; when inserting newlines. To achieve the same behavior you have
|
|
|
|
|
;; two options:
|
|
|
|
|
|
|
|
|
|
;; 1) Use GNU/Emacs' standard binding for `newline-and-indent': C-j.
|
|
|
|
|
|
|
|
|
|
;; 2) Add the following hook in your .emacs:
|
|
|
|
|
|
|
|
|
|
;; (add-hook 'python-mode-hook
|
|
|
|
|
;; #'(lambda ()
|
|
|
|
|
;; (define-key python-mode-map "\C-m" 'newline-and-indent)))
|
|
|
|
|
|
|
|
|
|
;; I'd recommend the first one since you'll get the same behavior for
|
|
|
|
|
;; all modes out-of-the-box.
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;;; Installation:
|
|
|
|
|
|
|
|
|
|
;; Add this to your .emacs:
|
|
|
|
|
|
|
|
|
|
;; (add-to-list 'load-path "/folder/containing/file")
|
|
|
|
|
;; (require 'python)
|
|
|
|
|
|
|
|
|
|
;;; TODO:
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(require 'ansi-color)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(require 'comint)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(eval-when-compile
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(require 'cl)
|
|
|
|
|
;; Avoid compiler warnings
|
|
|
|
|
(defvar view-return-to-alist)
|
|
|
|
|
(defvar compilation-error-regexp-alist)
|
|
|
|
|
(defvar outline-heading-end-regexp))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(autoload 'comint-mode "comint")
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'") 'python-mode))
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode))
|
|
|
|
|
|
|
|
|
|
(defgroup python nil
|
|
|
|
|
"Python Language's flying circus support for Emacs."
|
|
|
|
|
:group 'languages
|
|
|
|
|
:version "23.2"
|
|
|
|
|
:link '(emacs-commentary-link "python"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Bindings
|
|
|
|
|
|
|
|
|
|
(defvar python-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
;; Movement
|
|
|
|
|
(substitute-key-definition 'backward-sentence
|
2012-07-16 10:13:01 -03:00
|
|
|
|
'python-nav-backward-block
|
2012-05-17 00:03:15 -03:00
|
|
|
|
map global-map)
|
|
|
|
|
(substitute-key-definition 'forward-sentence
|
2012-07-16 10:13:01 -03:00
|
|
|
|
'python-nav-forward-block
|
2012-05-17 00:03:15 -03:00
|
|
|
|
map global-map)
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(define-key map "\C-c\C-j" 'python-nav-jump-to-defun)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Indent specific
|
|
|
|
|
(define-key map "\177" 'python-indent-dedent-line-backspace)
|
|
|
|
|
(define-key map (kbd "<backtab>") 'python-indent-dedent-line)
|
|
|
|
|
(define-key map "\C-c<" 'python-indent-shift-left)
|
|
|
|
|
(define-key map "\C-c>" 'python-indent-shift-right)
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(define-key map ":" 'python-indent-electric-colon)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
;; Skeletons
|
|
|
|
|
(define-key map "\C-c\C-tc" 'python-skeleton-class)
|
|
|
|
|
(define-key map "\C-c\C-td" 'python-skeleton-def)
|
|
|
|
|
(define-key map "\C-c\C-tf" 'python-skeleton-for)
|
|
|
|
|
(define-key map "\C-c\C-ti" 'python-skeleton-if)
|
|
|
|
|
(define-key map "\C-c\C-tt" 'python-skeleton-try)
|
|
|
|
|
(define-key map "\C-c\C-tw" 'python-skeleton-while)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Shell interaction
|
|
|
|
|
(define-key map "\C-c\C-s" 'python-shell-send-string)
|
|
|
|
|
(define-key map "\C-c\C-r" 'python-shell-send-region)
|
|
|
|
|
(define-key map "\C-\M-x" 'python-shell-send-defun)
|
|
|
|
|
(define-key map "\C-c\C-c" 'python-shell-send-buffer)
|
|
|
|
|
(define-key map "\C-c\C-l" 'python-shell-send-file)
|
|
|
|
|
(define-key map "\C-c\C-z" 'python-shell-switch-to-shell)
|
2012-05-17 00:03:00 -03:00
|
|
|
|
;; Some util commands
|
|
|
|
|
(define-key map "\C-c\C-v" 'python-check)
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(define-key map "\C-c\C-f" 'python-eldoc-at-point)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Utilities
|
|
|
|
|
(substitute-key-definition 'complete-symbol 'completion-at-point
|
2012-05-17 00:03:35 -03:00
|
|
|
|
map global-map)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(easy-menu-define python-menu map "Python Mode menu"
|
|
|
|
|
`("Python"
|
2012-05-17 00:03:35 -03:00
|
|
|
|
:help "Python-specific Features"
|
|
|
|
|
["Shift region left" python-indent-shift-left :active mark-active
|
|
|
|
|
:help "Shift region left by a single indentation step"]
|
|
|
|
|
["Shift region right" python-indent-shift-right :active mark-active
|
|
|
|
|
:help "Shift region right by a single indentation step"]
|
|
|
|
|
"-"
|
|
|
|
|
["Start of def/class" beginning-of-defun
|
|
|
|
|
:help "Go to start of outermost definition around point"]
|
|
|
|
|
["End of def/class" end-of-defun
|
|
|
|
|
:help "Go to end of definition around point"]
|
|
|
|
|
["Mark def/class" mark-defun
|
|
|
|
|
:help "Mark outermost definition around point"]
|
|
|
|
|
["Jump to def/class" python-nav-jump-to-defun
|
|
|
|
|
:help "Jump to a class or function definition"]
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"--"
|
2012-05-17 00:03:35 -03:00
|
|
|
|
("Skeletons")
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"---"
|
2012-05-17 00:03:35 -03:00
|
|
|
|
["Start interpreter" run-python
|
|
|
|
|
:help "Run inferior Python process in a separate buffer"]
|
|
|
|
|
["Switch to shell" python-shell-switch-to-shell
|
|
|
|
|
:help "Switch to running inferior Python process"]
|
|
|
|
|
["Eval string" python-shell-send-string
|
|
|
|
|
:help "Eval string in inferior Python session"]
|
|
|
|
|
["Eval buffer" python-shell-send-buffer
|
|
|
|
|
:help "Eval buffer in inferior Python session"]
|
|
|
|
|
["Eval region" python-shell-send-region
|
|
|
|
|
:help "Eval region in inferior Python session"]
|
|
|
|
|
["Eval defun" python-shell-send-defun
|
|
|
|
|
:help "Eval defun in inferior Python session"]
|
|
|
|
|
["Eval file" python-shell-send-file
|
|
|
|
|
:help "Eval file in inferior Python session"]
|
|
|
|
|
["Debugger" pdb :help "Run pdb under GUD"]
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"----"
|
2012-05-17 00:03:35 -03:00
|
|
|
|
["Check file" python-check
|
|
|
|
|
:help "Check file for errors"]
|
|
|
|
|
["Help on symbol" python-eldoc-at-point
|
|
|
|
|
:help "Get help on symbol at point"]
|
|
|
|
|
["Complete symbol" completion-at-point
|
|
|
|
|
:help "Complete symbol before point"]))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
map)
|
|
|
|
|
"Keymap for `python-mode'.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Python specialized rx
|
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(eval-when-compile
|
|
|
|
|
(defconst python-rx-constituents
|
2012-06-18 16:41:23 -04:00
|
|
|
|
`((block-start . ,(rx symbol-start
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(or "def" "class" "if" "elif" "else" "try"
|
|
|
|
|
"except" "finally" "for" "while" "with")
|
|
|
|
|
symbol-end))
|
2012-06-18 16:41:23 -04:00
|
|
|
|
(decorator . ,(rx line-start (* space) ?@ (any letter ?_)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(* (any word ?_))))
|
2012-06-18 16:41:23 -04:00
|
|
|
|
(defun . ,(rx symbol-start (or "def" "class") symbol-end))
|
|
|
|
|
(if-name-main . ,(rx line-start "if" (+ space) "__name__"
|
2012-05-17 00:03:41 -03:00
|
|
|
|
(+ space) "==" (+ space)
|
|
|
|
|
(any ?' ?\") "__main__" (any ?' ?\")
|
|
|
|
|
(* space) ?:))
|
2012-06-18 16:41:23 -04:00
|
|
|
|
(symbol-name . ,(rx (any letter ?_) (* (any word ?_))))
|
|
|
|
|
(open-paren . ,(rx (or "{" "[" "(")))
|
|
|
|
|
(close-paren . ,(rx (or "}" "]" ")")))
|
|
|
|
|
(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))
|
|
|
|
|
;; FIXME: rx should support (not simple-operator).
|
|
|
|
|
(not-simple-operator . ,(rx
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(not
|
|
|
|
|
(any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))))
|
2012-06-18 16:41:23 -04:00
|
|
|
|
;; FIXME: Use regexp-opt.
|
|
|
|
|
(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">"
|
2012-05-17 00:03:05 -03:00
|
|
|
|
"=" "%" "**" "//" "<<" ">>" "<=" "!="
|
|
|
|
|
"==" ">=" "is" "not")))
|
2012-06-18 16:41:23 -04:00
|
|
|
|
;; FIXME: Use regexp-opt.
|
|
|
|
|
(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**="
|
2012-05-17 00:03:09 -03:00
|
|
|
|
">>=" "<<=" "&=" "^=" "|="))))
|
|
|
|
|
"Additional Python specific sexps for `python-rx'"))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defmacro python-rx (&rest regexps)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
"Python mode specialized rx macro.
|
|
|
|
|
This variant of `rx' supports common python named REGEXPS."
|
|
|
|
|
(let ((rx-constituents (append python-rx-constituents rx-constituents)))
|
|
|
|
|
(cond ((null regexps)
|
|
|
|
|
(error "No regexp"))
|
|
|
|
|
((cdr regexps)
|
|
|
|
|
(rx-to-string `(and ,@regexps) t))
|
|
|
|
|
(t
|
|
|
|
|
(rx-to-string (car regexps) t)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Font-lock and syntax
|
|
|
|
|
(defvar python-font-lock-keywords
|
|
|
|
|
;; Keywords
|
|
|
|
|
`(,(rx symbol-start
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(or
|
|
|
|
|
"and" "del" "from" "not" "while" "as" "elif" "global" "or" "with"
|
|
|
|
|
"assert" "else" "if" "pass" "yield" "break" "except" "import" "class"
|
|
|
|
|
"in" "raise" "continue" "finally" "is" "return" "def" "for" "lambda"
|
|
|
|
|
"try"
|
|
|
|
|
;; Python 2:
|
|
|
|
|
"print" "exec"
|
|
|
|
|
;; Python 3:
|
|
|
|
|
;; False, None, and True are listed as keywords on the Python 3
|
|
|
|
|
;; documentation, but since they also qualify as constants they are
|
|
|
|
|
;; fontified like that in order to keep font-lock consistent between
|
|
|
|
|
;; Python versions.
|
2012-05-17 00:03:35 -03:00
|
|
|
|
"nonlocal"
|
|
|
|
|
;; Extra:
|
|
|
|
|
"self")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
symbol-end)
|
|
|
|
|
;; functions
|
|
|
|
|
(,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_))))
|
|
|
|
|
(1 font-lock-function-name-face))
|
|
|
|
|
;; classes
|
|
|
|
|
(,(rx symbol-start "class" (1+ space) (group (1+ (or word ?_))))
|
|
|
|
|
(1 font-lock-type-face))
|
|
|
|
|
;; Constants
|
2012-05-17 00:03:19 -03:00
|
|
|
|
(,(rx symbol-start
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(or
|
|
|
|
|
"Ellipsis" "False" "None" "NotImplemented" "True" "__debug__"
|
|
|
|
|
;; copyright, license, credits, quit and exit are added by the site
|
|
|
|
|
;; module and they are not intended to be used in programs
|
|
|
|
|
"copyright" "credits" "exit" "license" "quit")
|
2012-05-17 00:03:19 -03:00
|
|
|
|
symbol-end) . font-lock-constant-face)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Decorators.
|
|
|
|
|
(,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_))
|
|
|
|
|
(0+ "." (1+ (or word ?_)))))
|
|
|
|
|
(1 font-lock-type-face))
|
|
|
|
|
;; Builtin Exceptions
|
|
|
|
|
(,(rx symbol-start
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(or
|
|
|
|
|
"ArithmeticError" "AssertionError" "AttributeError" "BaseException"
|
|
|
|
|
"DeprecationWarning" "EOFError" "EnvironmentError" "Exception"
|
|
|
|
|
"FloatingPointError" "FutureWarning" "GeneratorExit" "IOError"
|
|
|
|
|
"ImportError" "ImportWarning" "IndexError" "KeyError"
|
|
|
|
|
"KeyboardInterrupt" "LookupError" "MemoryError" "NameError"
|
|
|
|
|
"NotImplementedError" "OSError" "OverflowError"
|
|
|
|
|
"PendingDeprecationWarning" "ReferenceError" "RuntimeError"
|
|
|
|
|
"RuntimeWarning" "StopIteration" "SyntaxError" "SyntaxWarning"
|
|
|
|
|
"SystemError" "SystemExit" "TypeError" "UnboundLocalError"
|
|
|
|
|
"UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError"
|
|
|
|
|
"UnicodeTranslateError" "UnicodeWarning" "UserWarning" "VMSError"
|
|
|
|
|
"ValueError" "Warning" "WindowsError" "ZeroDivisionError"
|
|
|
|
|
;; Python 2:
|
|
|
|
|
"StandardError"
|
|
|
|
|
;; Python 3:
|
|
|
|
|
"BufferError" "BytesWarning" "IndentationError" "ResourceWarning"
|
|
|
|
|
"TabError")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
symbol-end) . font-lock-type-face)
|
|
|
|
|
;; Builtins
|
2012-05-17 00:03:20 -03:00
|
|
|
|
(,(rx symbol-start
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(or
|
|
|
|
|
"abs" "all" "any" "bin" "bool" "callable" "chr" "classmethod"
|
|
|
|
|
"compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate"
|
|
|
|
|
"eval" "filter" "float" "format" "frozenset" "getattr" "globals"
|
|
|
|
|
"hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance"
|
|
|
|
|
"issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview"
|
|
|
|
|
"min" "next" "object" "oct" "open" "ord" "pow" "print" "property"
|
|
|
|
|
"range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted"
|
|
|
|
|
"staticmethod" "str" "sum" "super" "tuple" "type" "vars" "zip"
|
|
|
|
|
"__import__"
|
|
|
|
|
;; Python 2:
|
|
|
|
|
"basestring" "cmp" "execfile" "file" "long" "raw_input" "reduce"
|
|
|
|
|
"reload" "unichr" "unicode" "xrange" "apply" "buffer" "coerce"
|
|
|
|
|
"intern"
|
|
|
|
|
;; Python 3:
|
|
|
|
|
"ascii" "bytearray" "bytes" "exec"
|
|
|
|
|
;; Extra:
|
|
|
|
|
"__all__" "__doc__" "__name__" "__package__")
|
2012-05-17 00:03:20 -03:00
|
|
|
|
symbol-end) . font-lock-builtin-face)
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; assignments
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; support for a = b = c = 5
|
|
|
|
|
(,(lambda (limit)
|
2012-05-17 00:03:02 -03:00
|
|
|
|
(let ((re (python-rx (group (+ (any word ?. ?_)))
|
|
|
|
|
(? ?\[ (+ (not (any ?\]))) ?\]) (* space)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
assignment-operator)))
|
|
|
|
|
(when (re-search-forward re limit t)
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(while (and (python-info-ppss-context 'paren)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(re-search-forward re limit t)))
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(if (and (not (python-info-ppss-context 'paren))
|
2012-05-17 00:03:01 -03:00
|
|
|
|
(not (equal (char-after (point-marker)) ?=)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
t
|
|
|
|
|
(set-match-data nil)))))
|
|
|
|
|
(1 font-lock-variable-name-face nil nil))
|
|
|
|
|
;; support for a, b, c = (1, 2, 3)
|
|
|
|
|
(,(lambda (limit)
|
|
|
|
|
(let ((re (python-rx (group (+ (any word ?. ?_))) (* space)
|
|
|
|
|
(* ?, (* space) (+ (any word ?. ?_)) (* space))
|
|
|
|
|
?, (* space) (+ (any word ?. ?_)) (* space)
|
|
|
|
|
assignment-operator)))
|
|
|
|
|
(when (and (re-search-forward re limit t)
|
|
|
|
|
(goto-char (nth 3 (match-data))))
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(while (and (python-info-ppss-context 'paren)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(re-search-forward re limit t))
|
|
|
|
|
(goto-char (nth 3 (match-data))))
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(if (not (python-info-ppss-context 'paren))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
t
|
|
|
|
|
(set-match-data nil)))))
|
|
|
|
|
(1 font-lock-variable-name-face nil nil))))
|
|
|
|
|
|
2012-05-17 00:03:46 -03:00
|
|
|
|
(defconst python-syntax-propertize-function
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; Make outer chars of matching triple-quote sequences into generic
|
|
|
|
|
;; string delimiters. Fixme: Is there a better way?
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; First avoid a sequence preceded by an odd number of backslashes.
|
2012-05-17 00:03:46 -03:00
|
|
|
|
(syntax-propertize-rules
|
|
|
|
|
(;; ¡Backrefs don't work in syntax-propertize-rules!
|
|
|
|
|
(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix.
|
|
|
|
|
"\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)")
|
|
|
|
|
(3 (ignore (python-quote-syntax))))))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
|
|
|
|
(defun python-quote-syntax ()
|
2012-05-17 00:02:52 -03:00
|
|
|
|
"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))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(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.
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(goto-char (nth 8 syntax)) ; fence position
|
|
|
|
|
(skip-chars-forward "uUrR") ; skip any prefix
|
2012-05-17 00:03:21 -03:00
|
|
|
|
;; 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 "|"))))
|
|
|
|
|
)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defvar python-mode-syntax-table
|
|
|
|
|
(let ((table (make-syntax-table)))
|
|
|
|
|
;; Give punctuation syntax to ASCII that normally has symbol
|
|
|
|
|
;; syntax or has word syntax and isn't a letter.
|
|
|
|
|
(let ((symbol (string-to-syntax "_"))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(sst (standard-syntax-table)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(dotimes (i 128)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(unless (= i ?_)
|
|
|
|
|
(if (equal symbol (aref sst i))
|
|
|
|
|
(modify-syntax-entry i "." table)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(modify-syntax-entry ?$ "." table)
|
|
|
|
|
(modify-syntax-entry ?% "." table)
|
|
|
|
|
;; exceptions
|
|
|
|
|
(modify-syntax-entry ?# "<" table)
|
|
|
|
|
(modify-syntax-entry ?\n ">" table)
|
|
|
|
|
(modify-syntax-entry ?' "\"" table)
|
|
|
|
|
(modify-syntax-entry ?` "$" table)
|
|
|
|
|
table)
|
|
|
|
|
"Syntax table for Python files.")
|
|
|
|
|
|
|
|
|
|
(defvar python-dotty-syntax-table
|
|
|
|
|
(let ((table (make-syntax-table python-mode-syntax-table)))
|
|
|
|
|
(modify-syntax-entry ?. "w" table)
|
|
|
|
|
(modify-syntax-entry ?_ "w" table)
|
|
|
|
|
table)
|
|
|
|
|
"Dotty syntax table for Python files.
|
|
|
|
|
It makes underscores and dots word constituent chars.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Indentation
|
|
|
|
|
|
|
|
|
|
(defcustom python-indent-offset 4
|
|
|
|
|
"Default indentation offset for Python."
|
|
|
|
|
:group 'python
|
|
|
|
|
:type 'integer
|
|
|
|
|
:safe 'integerp)
|
|
|
|
|
|
|
|
|
|
(defcustom python-indent-guess-indent-offset t
|
|
|
|
|
"Non-nil tells Python mode to guess `python-indent-offset' value."
|
|
|
|
|
:type 'boolean
|
2012-05-17 00:03:23 -03:00
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-06-14 22:33:57 -03:00
|
|
|
|
(define-obsolete-variable-alias
|
|
|
|
|
'python-indent 'python-indent-offset "24.2")
|
|
|
|
|
|
|
|
|
|
(define-obsolete-variable-alias
|
|
|
|
|
'python-guess-indent 'python-indent-guess-indent-offset "24.2")
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defvar python-indent-current-level 0
|
|
|
|
|
"Current indentation level `python-indent-line-function' is using.")
|
|
|
|
|
|
|
|
|
|
(defvar python-indent-levels '(0)
|
|
|
|
|
"Levels of indentation available for `python-indent-line-function'.")
|
|
|
|
|
|
|
|
|
|
(defvar python-indent-dedenters '("else" "elif" "except" "finally")
|
|
|
|
|
"List of words that should be dedented.
|
|
|
|
|
These make `python-indent-calculate-indentation' subtract the value of
|
|
|
|
|
`python-indent-offset'.")
|
|
|
|
|
|
|
|
|
|
(defun python-indent-guess-indent-offset ()
|
2012-05-17 00:02:53 -03:00
|
|
|
|
"Guess and set `python-indent-offset' for the current buffer."
|
2012-06-14 22:33:57 -03:00
|
|
|
|
(interactive)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(let ((block-end))
|
|
|
|
|
(while (and (not block-end)
|
|
|
|
|
(re-search-forward
|
|
|
|
|
(python-rx line-start block-start) nil t))
|
|
|
|
|
(when (and
|
|
|
|
|
(not (python-info-ppss-context-type))
|
|
|
|
|
(progn
|
|
|
|
|
(goto-char (line-end-position))
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(if (equal (char-before) ?:)
|
|
|
|
|
t
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
(when (python-info-block-continuation-line-p)
|
|
|
|
|
(while (and (python-info-continuation-line-p)
|
|
|
|
|
(not (eobp)))
|
|
|
|
|
(forward-line 1))
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(when (equal (char-before) ?:)
|
|
|
|
|
t)))))
|
|
|
|
|
(setq block-end (point-marker))))
|
|
|
|
|
(let ((indentation
|
|
|
|
|
(when block-end
|
|
|
|
|
(goto-char block-end)
|
|
|
|
|
(python-util-forward-comment)
|
|
|
|
|
(current-indentation))))
|
|
|
|
|
(if indentation
|
|
|
|
|
(setq python-indent-offset indentation)
|
|
|
|
|
(message "Can't guess python-indent-offset, using defaults: %s"
|
|
|
|
|
python-indent-offset)))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(defun python-indent-context ()
|
|
|
|
|
"Get information on indentation context.
|
|
|
|
|
Context information is returned with a cons with the form:
|
|
|
|
|
\(STATUS . START)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
Where status can be any of the following symbols:
|
|
|
|
|
* inside-paren: If point in between (), {} or []
|
|
|
|
|
* inside-string: If point is inside a string
|
|
|
|
|
* after-backslash: Previous line ends in a backslash
|
|
|
|
|
* after-beginning-of-block: Point is after beginning of block
|
|
|
|
|
* after-line: Point is after normal line
|
|
|
|
|
* no-indent: Point is at beginning of buffer or other special case
|
|
|
|
|
START is the buffer position where the sexp starts."
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(let ((ppss (save-excursion (beginning-of-line) (syntax-ppss)))
|
|
|
|
|
(start))
|
|
|
|
|
(cons
|
|
|
|
|
(cond
|
2012-05-17 00:02:55 -03:00
|
|
|
|
;; Beginning of buffer
|
2012-05-17 00:02:56 -03:00
|
|
|
|
((save-excursion
|
|
|
|
|
(goto-char (line-beginning-position))
|
|
|
|
|
(bobp))
|
2012-05-17 00:02:55 -03:00
|
|
|
|
'no-indent)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Inside a paren
|
2012-05-17 00:03:07 -03:00
|
|
|
|
((setq start (python-info-ppss-context 'paren ppss))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
'inside-paren)
|
|
|
|
|
;; Inside string
|
2012-05-17 00:03:07 -03:00
|
|
|
|
((setq start (python-info-ppss-context 'string ppss))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
'inside-string)
|
|
|
|
|
;; After backslash
|
2012-05-17 00:03:07 -03:00
|
|
|
|
((setq start (when (not (or (python-info-ppss-context 'string ppss)
|
|
|
|
|
(python-info-ppss-context 'comment ppss)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(let ((line-beg-pos (line-beginning-position)))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(when (python-info-line-ends-backslash-p
|
|
|
|
|
(1- line-beg-pos))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(- line-beg-pos 2)))))
|
|
|
|
|
'after-backslash)
|
|
|
|
|
;; After beginning of block
|
|
|
|
|
((setq start (save-excursion
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(when (progn
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(equal (char-before) ?:))
|
|
|
|
|
;; Move to the first block start that's not in within
|
|
|
|
|
;; a string, comment or paren and that's not a
|
|
|
|
|
;; continuation line.
|
|
|
|
|
(while (and (re-search-backward
|
|
|
|
|
(python-rx block-start) nil t)
|
|
|
|
|
(or
|
|
|
|
|
(python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment)
|
|
|
|
|
(python-info-ppss-context 'paren)
|
|
|
|
|
(python-info-continuation-line-p))))
|
|
|
|
|
(when (looking-at (python-rx block-start))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(point-marker)))))
|
|
|
|
|
'after-beginning-of-block)
|
|
|
|
|
;; After normal line
|
|
|
|
|
((setq start (save-excursion
|
2012-05-17 00:03:22 -03:00
|
|
|
|
(back-to-indentation)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(python-util-forward-comment -1)
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-statement-start)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(point-marker)))
|
|
|
|
|
'after-line)
|
|
|
|
|
;; Do not indent
|
|
|
|
|
(t 'no-indent))
|
|
|
|
|
start))))
|
|
|
|
|
|
|
|
|
|
(defun python-indent-calculate-indentation ()
|
|
|
|
|
"Calculate correct indentation offset for the current line."
|
|
|
|
|
(let* ((indentation-context (python-indent-context))
|
|
|
|
|
(context-status (car indentation-context))
|
|
|
|
|
(context-start (cdr indentation-context)))
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(case context-status
|
|
|
|
|
('no-indent 0)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
;; When point is after beginning of block just add one level
|
|
|
|
|
;; of indentation relative to the context-start
|
2012-05-17 00:02:52 -03:00
|
|
|
|
('after-beginning-of-block
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(+ (current-indentation) python-indent-offset))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
;; When after a simple line just use previous line
|
|
|
|
|
;; indentation, in the case current line starts with a
|
|
|
|
|
;; `python-indent-dedenters' de-indent one level.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
('after-line
|
|
|
|
|
(-
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(current-indentation))
|
|
|
|
|
(if (progn
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(looking-at (regexp-opt python-indent-dedenters)))
|
|
|
|
|
python-indent-offset
|
|
|
|
|
0)))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
;; When inside of a string, do nothing. just use the current
|
|
|
|
|
;; indentation. XXX: perhaps it would be a good idea to
|
|
|
|
|
;; invoke standard text indentation here
|
2012-05-17 00:02:52 -03:00
|
|
|
|
('inside-string
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(current-indentation))
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; After backslash we have several possibilities.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
('after-backslash
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(cond
|
|
|
|
|
;; Check if current line is a dot continuation. For this
|
|
|
|
|
;; the current line must start with a dot and previous
|
|
|
|
|
;; line must contain a dot too.
|
|
|
|
|
((save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(when (looking-at "\\.")
|
2012-05-17 00:03:36 -03:00
|
|
|
|
;; If after moving one line back point is inside a paren it
|
|
|
|
|
;; needs to move back until it's not anymore
|
|
|
|
|
(while (prog2
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(and (not (bobp))
|
|
|
|
|
(python-info-ppss-context 'paren))))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(goto-char (line-end-position))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(while (and (re-search-backward
|
|
|
|
|
"\\." (line-beginning-position) t)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(or (python-info-ppss-context 'comment)
|
|
|
|
|
(python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'paren))))
|
|
|
|
|
(if (and (looking-at "\\.")
|
|
|
|
|
(not (or (python-info-ppss-context 'comment)
|
|
|
|
|
(python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'paren))))
|
|
|
|
|
;; The indentation is the same column of the
|
|
|
|
|
;; first matching dot that's not inside a
|
|
|
|
|
;; comment, a string or a paren
|
|
|
|
|
(current-column)
|
|
|
|
|
;; No dot found on previous line, just add another
|
|
|
|
|
;; indentation level.
|
|
|
|
|
(+ (current-indentation) python-indent-offset)))))
|
|
|
|
|
;; Check if prev line is a block continuation
|
|
|
|
|
((let ((block-continuation-start
|
|
|
|
|
(python-info-block-continuation-line-p)))
|
|
|
|
|
(when block-continuation-start
|
|
|
|
|
;; If block-continuation-start is set jump to that
|
|
|
|
|
;; marker and use first column after the block start
|
|
|
|
|
;; as indentation value.
|
|
|
|
|
(goto-char block-continuation-start)
|
|
|
|
|
(re-search-forward
|
|
|
|
|
(python-rx block-start (* space))
|
|
|
|
|
(line-end-position) t)
|
|
|
|
|
(current-column))))
|
|
|
|
|
;; Check if current line is an assignment continuation
|
|
|
|
|
((let ((assignment-continuation-start
|
|
|
|
|
(python-info-assignment-continuation-line-p)))
|
|
|
|
|
(when assignment-continuation-start
|
|
|
|
|
;; If assignment-continuation is set jump to that
|
|
|
|
|
;; marker and use first column after the assignment
|
|
|
|
|
;; operator as indentation value.
|
|
|
|
|
(goto-char assignment-continuation-start)
|
|
|
|
|
(current-column))))
|
|
|
|
|
(t
|
|
|
|
|
(forward-line -1)
|
2012-06-17 01:53:31 -07:00
|
|
|
|
(goto-char (python-info-beginning-of-backslash))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(if (save-excursion
|
|
|
|
|
(and
|
|
|
|
|
(forward-line -1)
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(goto-char
|
2012-06-17 01:53:31 -07:00
|
|
|
|
(or (python-info-beginning-of-backslash) (point)))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(python-info-line-ends-backslash-p)))
|
|
|
|
|
;; The two previous lines ended in a backslash so we must
|
|
|
|
|
;; respect previous line indentation.
|
|
|
|
|
(current-indentation)
|
|
|
|
|
;; What happens here is that we are dealing with the second
|
|
|
|
|
;; line of a backslash continuation, in that case we just going
|
|
|
|
|
;; to add one indentation level.
|
|
|
|
|
(+ (current-indentation) python-indent-offset)))))
|
|
|
|
|
;; When inside a paren there's a need to handle nesting
|
|
|
|
|
;; correctly
|
2012-05-17 00:02:52 -03:00
|
|
|
|
('inside-paren
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(cond
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; If current line closes the outermost open paren use the
|
2012-05-17 00:03:34 -03:00
|
|
|
|
;; current indentation of the context-start line.
|
|
|
|
|
((save-excursion
|
|
|
|
|
(skip-syntax-forward "\s" (line-end-position))
|
|
|
|
|
(when (and (looking-at (regexp-opt '(")" "]" "}")))
|
|
|
|
|
(progn
|
|
|
|
|
(forward-char 1)
|
|
|
|
|
(not (python-info-ppss-context 'paren))))
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(current-indentation))))
|
|
|
|
|
;; If open paren is contained on a line by itself add another
|
|
|
|
|
;; indentation level, else look for the first word after the
|
|
|
|
|
;; opening paren and use it's column position as indentation
|
|
|
|
|
;; level.
|
|
|
|
|
((let* ((content-starts-in-newline)
|
|
|
|
|
(indent
|
|
|
|
|
(save-excursion
|
|
|
|
|
(if (setq content-starts-in-newline
|
|
|
|
|
(progn
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(forward-char)
|
|
|
|
|
(save-restriction
|
|
|
|
|
(narrow-to-region
|
|
|
|
|
(line-beginning-position)
|
|
|
|
|
(line-end-position))
|
|
|
|
|
(python-util-forward-comment))
|
|
|
|
|
(looking-at "$")))
|
|
|
|
|
(+ (current-indentation) python-indent-offset)
|
|
|
|
|
(current-column)))))
|
|
|
|
|
;; Adjustments
|
|
|
|
|
(cond
|
|
|
|
|
;; If current line closes a nested open paren de-indent one
|
|
|
|
|
;; level.
|
|
|
|
|
((progn
|
2012-05-17 00:03:03 -03:00
|
|
|
|
(back-to-indentation)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(looking-at (regexp-opt '(")" "]" "}"))))
|
|
|
|
|
(- indent python-indent-offset))
|
|
|
|
|
;; If the line of the opening paren that wraps the current
|
|
|
|
|
;; line starts a block add another level of indentation to
|
|
|
|
|
;; follow new pep8 recommendation. See: http://ur1.ca/5rojx
|
|
|
|
|
((save-excursion
|
|
|
|
|
(when (and content-starts-in-newline
|
|
|
|
|
(progn
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(looking-at (python-rx block-start))))
|
|
|
|
|
(+ indent python-indent-offset))))
|
|
|
|
|
(t indent)))))))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-indent-calculate-levels ()
|
|
|
|
|
"Calculate `python-indent-levels' and reset `python-indent-current-level'."
|
|
|
|
|
(let* ((indentation (python-indent-calculate-indentation))
|
|
|
|
|
(remainder (% indentation python-indent-offset))
|
|
|
|
|
(steps (/ (- indentation remainder) python-indent-offset)))
|
2012-05-17 00:03:17 -03:00
|
|
|
|
(setq python-indent-levels (list 0))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(dotimes (step steps)
|
2012-05-17 00:03:17 -03:00
|
|
|
|
(push (* python-indent-offset (1+ step)) python-indent-levels))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(when (not (eq 0 remainder))
|
2012-05-17 00:03:17 -03:00
|
|
|
|
(push (+ (* python-indent-offset steps) remainder) python-indent-levels))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(setq python-indent-levels (nreverse python-indent-levels))
|
|
|
|
|
(setq python-indent-current-level (1- (length python-indent-levels)))))
|
|
|
|
|
|
|
|
|
|
(defun python-indent-toggle-levels ()
|
|
|
|
|
"Toggle `python-indent-current-level' over `python-indent-levels'."
|
|
|
|
|
(setq python-indent-current-level (1- python-indent-current-level))
|
|
|
|
|
(when (< python-indent-current-level 0)
|
|
|
|
|
(setq python-indent-current-level (1- (length python-indent-levels)))))
|
|
|
|
|
|
|
|
|
|
(defun python-indent-line (&optional force-toggle)
|
|
|
|
|
"Internal implementation of `python-indent-line-function'.
|
|
|
|
|
Uses the offset calculated in
|
|
|
|
|
`python-indent-calculate-indentation' and available levels
|
2012-05-17 00:03:09 -03:00
|
|
|
|
indicated by the variable `python-indent-levels' to set the
|
|
|
|
|
current indentation.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
When the variable `last-command' is equal to
|
2012-05-17 00:03:09 -03:00
|
|
|
|
`indent-for-tab-command' or FORCE-TOGGLE is non-nil it cycles
|
|
|
|
|
levels indicated in the variable `python-indent-levels' by
|
|
|
|
|
setting the current level in the variable
|
|
|
|
|
`python-indent-current-level'.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
When the variable `last-command' is not equal to
|
2012-05-17 00:03:09 -03:00
|
|
|
|
`indent-for-tab-command' and FORCE-TOGGLE is nil it calculates
|
|
|
|
|
possible indentation levels and saves it in the variable
|
|
|
|
|
`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')"
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(if (or (and (eq this-command 'indent-for-tab-command)
|
|
|
|
|
(eq last-command this-command))
|
|
|
|
|
force-toggle)
|
2012-05-17 00:03:10 -03:00
|
|
|
|
(if (not (equal python-indent-levels '(0)))
|
|
|
|
|
(python-indent-toggle-levels)
|
|
|
|
|
(python-indent-calculate-levels))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(python-indent-calculate-levels))
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(delete-horizontal-space)
|
|
|
|
|
(indent-to (nth python-indent-current-level python-indent-levels))
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(python-info-closing-block-message))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-indent-line-function ()
|
|
|
|
|
"`indent-line-function' for Python mode.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
See `python-indent-line' for details."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(python-indent-line))
|
|
|
|
|
|
|
|
|
|
(defun python-indent-dedent-line ()
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"De-indent current line."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "*")
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(when (and (not (or (python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(<= (point-marker) (save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(> (current-column) 0))
|
|
|
|
|
(python-indent-line t)
|
|
|
|
|
t))
|
|
|
|
|
|
|
|
|
|
(defun python-indent-dedent-line-backspace (arg)
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"De-indent current line.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
Argument ARG is passed to `backward-delete-char-untabify' when
|
2012-05-17 00:03:09 -03:00
|
|
|
|
point is not in between the indentation."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "*p")
|
|
|
|
|
(when (not (python-indent-dedent-line))
|
|
|
|
|
(backward-delete-char-untabify arg)))
|
2012-05-17 00:02:53 -03:00
|
|
|
|
(put 'python-indent-dedent-line-backspace 'delete-selection 'supersede)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-indent-region (start end)
|
|
|
|
|
"Indent a python region automagically.
|
|
|
|
|
|
|
|
|
|
Called from a program, START and END specify the region to indent."
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(let ((deactivate-mark nil))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char end)
|
|
|
|
|
(setq end (point-marker))
|
|
|
|
|
(goto-char start)
|
|
|
|
|
(or (bolp) (forward-line 1))
|
|
|
|
|
(while (< (point) end)
|
|
|
|
|
(or (and (bolp) (eolp))
|
|
|
|
|
(let (word)
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(setq word (current-word))
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
(when word
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(delete-horizontal-space)
|
|
|
|
|
(indent-to (python-indent-calculate-indentation)))))
|
|
|
|
|
(forward-line 1))
|
|
|
|
|
(move-marker end nil))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-indent-shift-left (start end &optional count)
|
|
|
|
|
"Shift lines contained in region START END by COUNT columns to the left.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
COUNT defaults to `python-indent-offset'. If region isn't
|
|
|
|
|
active, the current line is shifted. The shifted region includes
|
|
|
|
|
the lines in which START and END lie. An error is signaled if
|
|
|
|
|
any lines in the region are indented less than COUNT columns."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive
|
|
|
|
|
(if mark-active
|
|
|
|
|
(list (region-beginning) (region-end) current-prefix-arg)
|
|
|
|
|
(list (line-beginning-position) (line-end-position) current-prefix-arg)))
|
|
|
|
|
(if count
|
|
|
|
|
(setq count (prefix-numeric-value count))
|
|
|
|
|
(setq count python-indent-offset))
|
|
|
|
|
(when (> count 0)
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(let ((deactivate-mark nil))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char start)
|
|
|
|
|
(while (< (point) end)
|
|
|
|
|
(if (and (< (current-indentation) count)
|
|
|
|
|
(not (looking-at "[ \t]*$")))
|
|
|
|
|
(error "Can't shift all lines enough"))
|
|
|
|
|
(forward-line))
|
|
|
|
|
(indent-rigidly start end (- count))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(add-to-list 'debug-ignored-errors "^Can't shift all lines enough")
|
|
|
|
|
|
|
|
|
|
(defun python-indent-shift-right (start end &optional count)
|
|
|
|
|
"Shift lines contained in region START END by COUNT columns to the left.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
COUNT defaults to `python-indent-offset'. If region isn't
|
|
|
|
|
active, the current line is shifted. The shifted region includes
|
|
|
|
|
the lines in which START and END lie."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive
|
|
|
|
|
(if mark-active
|
|
|
|
|
(list (region-beginning) (region-end) current-prefix-arg)
|
|
|
|
|
(list (line-beginning-position) (line-end-position) current-prefix-arg)))
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(let ((deactivate-mark nil))
|
|
|
|
|
(if count
|
|
|
|
|
(setq count (prefix-numeric-value count))
|
|
|
|
|
(setq count python-indent-offset))
|
|
|
|
|
(indent-rigidly start end count)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(defun python-indent-electric-colon (arg)
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Insert a colon and maybe de-indent the current line.
|
|
|
|
|
With numeric ARG, just insert that many colons. With
|
|
|
|
|
\\[universal-argument], just insert a single colon."
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(interactive "*P")
|
|
|
|
|
(self-insert-command (if (not (integerp arg)) 1 arg))
|
2012-05-17 00:03:08 -03:00
|
|
|
|
(when (and (not arg)
|
|
|
|
|
(eolp)
|
|
|
|
|
(not (equal ?: (char-after (- (point-marker) 2))))
|
|
|
|
|
(not (or (python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment))))
|
|
|
|
|
(let ((indentation (current-indentation))
|
|
|
|
|
(calculated-indentation (python-indent-calculate-indentation)))
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(python-info-closing-block-message)
|
2012-05-17 00:03:08 -03:00
|
|
|
|
(when (> indentation calculated-indentation)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(indent-line-to calculated-indentation)
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(when (not (python-info-closing-block-message))
|
2012-05-17 00:03:08 -03:00
|
|
|
|
(indent-line-to indentation)))))))
|
2012-05-17 00:03:04 -03:00
|
|
|
|
(put 'python-indent-electric-colon 'delete-selection t)
|
|
|
|
|
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(defun python-indent-post-self-insert-function ()
|
|
|
|
|
"Adjust closing paren line indentation after a char is added.
|
|
|
|
|
This function is intended to be added to the
|
|
|
|
|
`post-self-insert-hook.' If a line renders a paren alone, after
|
|
|
|
|
adding a char before it, the line will be re-indented
|
|
|
|
|
automatically if needed."
|
|
|
|
|
(when (and (eq (char-before) last-command-event)
|
|
|
|
|
(not (bolp))
|
|
|
|
|
(memq (char-after) '(?\) ?\] ?\})))
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (line-beginning-position))
|
|
|
|
|
;; If after going to the beginning of line the point
|
|
|
|
|
;; is still inside a paren it's ok to do the trick
|
|
|
|
|
(when (python-info-ppss-context 'paren)
|
|
|
|
|
(let ((indentation (python-indent-calculate-indentation)))
|
|
|
|
|
(when (< (current-indentation) indentation)
|
|
|
|
|
(indent-line-to indentation)))))))
|
2012-05-17 00:03:42 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Navigation
|
|
|
|
|
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(defvar python-nav-beginning-of-defun-regexp
|
2012-05-17 00:03:13 -03:00
|
|
|
|
(python-rx line-start (* space) defun (+ space) (group symbol-name))
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"Regexp matching class or function definition.
|
2012-05-17 00:03:14 -03:00
|
|
|
|
The name of the defun should be grouped so it can be retrieved
|
|
|
|
|
via `match-string'.")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(defun python-nav-beginning-of-defun (&optional arg)
|
2012-05-17 00:03:13 -03:00
|
|
|
|
"Move point to `beginning-of-defun'.
|
2012-05-17 00:03:44 -03:00
|
|
|
|
With positive ARG move search backwards. With negative do the
|
|
|
|
|
same but forward. When ARG is nil or 0 defaults to 1. This is
|
|
|
|
|
the main part of `python-beginning-of-defun-function'. Return
|
|
|
|
|
non-nil if point is moved to `beginning-of-defun'."
|
|
|
|
|
(when (or (null arg) (= arg 0)) (setq arg 1))
|
|
|
|
|
(let* ((re-search-fn (if (> arg 0)
|
|
|
|
|
#'re-search-backward
|
|
|
|
|
#'re-search-forward))
|
|
|
|
|
(line-beg-pos (line-beginning-position))
|
|
|
|
|
(line-content-start (+ line-beg-pos (current-indentation)))
|
|
|
|
|
(pos (point-marker))
|
|
|
|
|
(found
|
|
|
|
|
(progn
|
|
|
|
|
(when (and (< arg 0)
|
|
|
|
|
(python-info-looking-at-beginning-of-defun))
|
|
|
|
|
(end-of-line 1))
|
|
|
|
|
(while (and (funcall re-search-fn
|
|
|
|
|
python-nav-beginning-of-defun-regexp nil t)
|
|
|
|
|
(python-info-ppss-context-type)))
|
|
|
|
|
(and (python-info-looking-at-beginning-of-defun)
|
|
|
|
|
(or (not (= (line-number-at-pos pos)
|
|
|
|
|
(line-number-at-pos)))
|
|
|
|
|
(and (>= (point) line-beg-pos)
|
|
|
|
|
(<= (point) line-content-start)
|
|
|
|
|
(> pos line-content-start)))))))
|
|
|
|
|
(if found
|
|
|
|
|
(or (beginning-of-line 1) t)
|
|
|
|
|
(and (goto-char pos) nil))))
|
|
|
|
|
|
|
|
|
|
(defun python-beginning-of-defun-function (&optional arg)
|
2012-05-17 00:03:11 -03:00
|
|
|
|
"Move point to the beginning of def or class.
|
2012-05-17 00:03:44 -03:00
|
|
|
|
With positive ARG move that number of functions backwards. With
|
|
|
|
|
negative do the same but forward. When ARG is nil or 0 defaults
|
|
|
|
|
to 1. Return non-nil if point is moved to `beginning-of-defun'."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(when (or (null arg) (= arg 0)) (setq arg 1))
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(let ((found))
|
|
|
|
|
(cond ((and (eq this-command 'mark-defun)
|
|
|
|
|
(python-info-looking-at-beginning-of-defun)))
|
|
|
|
|
(t
|
|
|
|
|
(dotimes (i (if (> arg 0) arg (- arg)))
|
|
|
|
|
(when (and (python-nav-beginning-of-defun arg)
|
|
|
|
|
(not found))
|
|
|
|
|
(setq found t)))))
|
|
|
|
|
found))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-end-of-defun-function ()
|
|
|
|
|
"Move point to the end of def or class.
|
|
|
|
|
Returns nil if point is not in a def or class."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(interactive)
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(let ((beg-defun-indent))
|
|
|
|
|
(when (or (python-info-looking-at-beginning-of-defun)
|
|
|
|
|
(python-beginning-of-defun-function 1)
|
|
|
|
|
(python-beginning-of-defun-function -1))
|
|
|
|
|
(setq beg-defun-indent (current-indentation))
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
;; Go as forward as possible
|
|
|
|
|
(while (and (or
|
|
|
|
|
(python-nav-beginning-of-defun -1)
|
|
|
|
|
(and (goto-char (point-max)) nil))
|
|
|
|
|
(> (current-indentation) beg-defun-indent)))
|
|
|
|
|
(beginning-of-line 1)
|
|
|
|
|
;; Go as backwards as possible
|
|
|
|
|
(while (and (forward-line -1)
|
|
|
|
|
(not (bobp))
|
|
|
|
|
(or (not (current-word))
|
|
|
|
|
(equal (char-after (+ (point) (current-indentation))) ?#)
|
|
|
|
|
(<= (current-indentation) beg-defun-indent)
|
|
|
|
|
(looking-at (python-rx decorator))
|
|
|
|
|
(python-info-ppss-context-type))))
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
;; If point falls inside a paren or string context the point is
|
|
|
|
|
;; forwarded at the end of it (or end of buffer if its not closed)
|
|
|
|
|
(let ((context-type (python-info-ppss-context-type)))
|
|
|
|
|
(when (memq context-type '(paren string))
|
|
|
|
|
;; Slow but safe.
|
|
|
|
|
(while (and (not (eobp))
|
|
|
|
|
(python-info-ppss-context-type))
|
|
|
|
|
(forward-line 1)))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(defun python-nav-statement-start ()
|
|
|
|
|
"Move to start of current statement."
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(interactive "^")
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(while (and (or (back-to-indentation) t)
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(not (bobp))
|
|
|
|
|
(when (or
|
|
|
|
|
(save-excursion
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(python-info-line-ends-backslash-p))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(python-info-ppss-context 'string)
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(python-info-ppss-context 'paren))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(forward-line -1)))))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(defun python-nav-statement-end ()
|
|
|
|
|
"Move to end of current statement."
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(interactive "^")
|
|
|
|
|
(while (and (goto-char (line-end-position))
|
|
|
|
|
(not (eobp))
|
|
|
|
|
(when (or
|
|
|
|
|
(python-info-line-ends-backslash-p)
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(python-info-ppss-context 'string)
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(python-info-ppss-context 'paren))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(forward-line 1)))))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(defun python-nav-backward-statement (&optional arg)
|
|
|
|
|
"Move backward to previous statement.
|
|
|
|
|
With ARG, repeat. See `python-nav-forward-statement'."
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(interactive "^p")
|
|
|
|
|
(or arg (setq arg 1))
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-forward-statement (- arg)))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(defun python-nav-forward-statement (&optional arg)
|
|
|
|
|
"Move forward to next statement.
|
|
|
|
|
With ARG, repeat. With negative argument, move ARG times
|
|
|
|
|
backward to previous statement."
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(interactive "^p")
|
|
|
|
|
(or arg (setq arg 1))
|
|
|
|
|
(while (> arg 0)
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-statement-end)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(python-util-forward-comment)
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-statement-start)
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(setq arg (1- arg)))
|
|
|
|
|
(while (< arg 0)
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-statement-start)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(python-util-forward-comment -1)
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(python-nav-statement-start)
|
|
|
|
|
(setq arg (1+ arg))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-block-start ()
|
|
|
|
|
"Move to start of current block."
|
|
|
|
|
(interactive "^")
|
|
|
|
|
(let ((starting-pos (point))
|
|
|
|
|
(block-regexp (python-rx
|
|
|
|
|
line-start (* whitespace) block-start)))
|
|
|
|
|
(if (progn
|
|
|
|
|
(python-nav-statement-start)
|
|
|
|
|
(looking-at (python-rx block-start)))
|
|
|
|
|
(point-marker)
|
|
|
|
|
;; Go to first line beginning a statement
|
|
|
|
|
(while (and (not (bobp))
|
|
|
|
|
(or (and (python-nav-statement-start) nil)
|
|
|
|
|
(python-info-current-line-comment-p)
|
|
|
|
|
(python-info-current-line-empty-p)))
|
|
|
|
|
(forward-line -1))
|
|
|
|
|
(let ((block-matching-indent
|
|
|
|
|
(- (current-indentation) python-indent-offset)))
|
|
|
|
|
(while
|
|
|
|
|
(and (python-nav-backward-block)
|
|
|
|
|
(> (current-indentation) block-matching-indent)))
|
|
|
|
|
(if (and (looking-at (python-rx block-start))
|
|
|
|
|
(= (current-indentation) block-matching-indent))
|
|
|
|
|
(point-marker)
|
|
|
|
|
(and (goto-char starting-pos) nil))))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-block-end ()
|
|
|
|
|
"Move to end of current block."
|
|
|
|
|
(interactive "^")
|
|
|
|
|
(when (python-nav-block-start)
|
|
|
|
|
(let ((block-indentation (current-indentation)))
|
|
|
|
|
(python-nav-statement-end)
|
|
|
|
|
(while (and (forward-line 1)
|
|
|
|
|
(not (eobp))
|
|
|
|
|
(or (and (> (current-indentation) block-indentation)
|
|
|
|
|
(or (python-nav-statement-end) t))
|
|
|
|
|
(python-info-current-line-comment-p)
|
|
|
|
|
(python-info-current-line-empty-p))))
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(point-marker))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-backward-block (&optional arg)
|
|
|
|
|
"Move backward to previous block of code.
|
|
|
|
|
With ARG, repeat. See `python-nav-forward-block'."
|
|
|
|
|
(interactive "^p")
|
|
|
|
|
(or arg (setq arg 1))
|
|
|
|
|
(python-nav-forward-block (- arg)))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-forward-block (&optional arg)
|
|
|
|
|
"Move forward to next block of code.
|
|
|
|
|
With ARG, repeat. With negative argument, move ARG times
|
|
|
|
|
backward to previous block."
|
|
|
|
|
(interactive "^p")
|
|
|
|
|
(or arg (setq arg 1))
|
|
|
|
|
(let ((block-start-regexp
|
|
|
|
|
(python-rx line-start (* whitespace) block-start))
|
|
|
|
|
(starting-pos (point)))
|
|
|
|
|
(while (> arg 0)
|
|
|
|
|
(python-nav-statement-end)
|
|
|
|
|
(while (and
|
|
|
|
|
(re-search-forward block-start-regexp nil t)
|
|
|
|
|
(or (python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment)
|
|
|
|
|
(python-info-ppss-context 'paren))))
|
|
|
|
|
(setq arg (1- arg)))
|
|
|
|
|
(while (< arg 0)
|
|
|
|
|
(python-nav-statement-start)
|
|
|
|
|
(while (and
|
|
|
|
|
(re-search-backward block-start-regexp nil t)
|
|
|
|
|
(or (python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment)
|
|
|
|
|
(python-info-ppss-context 'paren))))
|
|
|
|
|
(setq arg (1+ arg)))
|
|
|
|
|
(python-nav-statement-start)
|
|
|
|
|
(if (not (looking-at (python-rx block-start)))
|
|
|
|
|
(and (goto-char starting-pos) nil)
|
|
|
|
|
(and (not (= (point) starting-pos)) (point-marker)))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-forward-sexp-function (&optional arg)
|
|
|
|
|
"Move forward across one block of code.
|
|
|
|
|
With ARG, do it that many times. Negative arg -N means
|
|
|
|
|
move backward N times."
|
|
|
|
|
(interactive "^p")
|
|
|
|
|
(or arg (setq arg 1))
|
|
|
|
|
(while (> arg 0)
|
|
|
|
|
(let ((block-starting-pos
|
|
|
|
|
(save-excursion (python-nav-block-start)))
|
|
|
|
|
(block-ending-pos
|
|
|
|
|
(save-excursion (python-nav-block-end)))
|
|
|
|
|
(next-block-starting-pos
|
|
|
|
|
(save-excursion (python-nav-forward-block))))
|
|
|
|
|
(cond ((not block-starting-pos)
|
|
|
|
|
(python-nav-forward-block))
|
|
|
|
|
((= (point) block-starting-pos)
|
|
|
|
|
(if (or (not next-block-starting-pos)
|
|
|
|
|
(< block-ending-pos next-block-starting-pos))
|
|
|
|
|
(python-nav-block-end)
|
|
|
|
|
(python-nav-forward-block)))
|
|
|
|
|
((= block-ending-pos (point))
|
|
|
|
|
(let ((parent-block-end-pos
|
|
|
|
|
(save-excursion
|
|
|
|
|
(python-util-forward-comment)
|
|
|
|
|
(python-nav-block-start)
|
|
|
|
|
(python-nav-block-end))))
|
|
|
|
|
(if (and parent-block-end-pos
|
|
|
|
|
(or (not next-block-starting-pos)
|
|
|
|
|
(> next-block-starting-pos parent-block-end-pos)))
|
|
|
|
|
(goto-char parent-block-end-pos)
|
|
|
|
|
(python-nav-forward-block))))
|
|
|
|
|
(t (python-nav-block-end))))
|
|
|
|
|
(setq arg (1- arg)))
|
|
|
|
|
(while (< arg 0)
|
|
|
|
|
(let* ((block-starting-pos
|
|
|
|
|
(save-excursion (python-nav-block-start)))
|
|
|
|
|
(block-ending-pos
|
|
|
|
|
(save-excursion (python-nav-block-end)))
|
|
|
|
|
(prev-block-ending-pos
|
|
|
|
|
(save-excursion (when (python-nav-backward-block)
|
|
|
|
|
(python-nav-block-end))))
|
|
|
|
|
(prev-block-parent-ending-pos
|
|
|
|
|
(save-excursion
|
|
|
|
|
(when prev-block-ending-pos
|
|
|
|
|
(goto-char prev-block-ending-pos)
|
|
|
|
|
(python-util-forward-comment)
|
|
|
|
|
(python-nav-block-start)
|
|
|
|
|
(python-nav-block-end)))))
|
|
|
|
|
(cond ((not block-ending-pos)
|
|
|
|
|
(and (python-nav-backward-block)
|
|
|
|
|
(python-nav-block-end)))
|
|
|
|
|
((= (point) block-ending-pos)
|
|
|
|
|
(let ((candidates))
|
|
|
|
|
(dolist (name
|
|
|
|
|
'(prev-block-parent-ending-pos
|
|
|
|
|
prev-block-ending-pos
|
|
|
|
|
block-ending-pos
|
|
|
|
|
block-starting-pos))
|
|
|
|
|
(when (and (symbol-value name)
|
|
|
|
|
(< (symbol-value name) (point)))
|
|
|
|
|
(add-to-list 'candidates (symbol-value name))))
|
|
|
|
|
(goto-char (apply 'max candidates))))
|
|
|
|
|
((> (point) block-ending-pos)
|
|
|
|
|
(python-nav-block-end))
|
|
|
|
|
((= (point) block-starting-pos)
|
|
|
|
|
(if (not (> (point) (or prev-block-ending-pos (point))))
|
|
|
|
|
(python-nav-backward-block)
|
|
|
|
|
(goto-char prev-block-ending-pos)
|
|
|
|
|
(let ((parent-block-ending-pos
|
|
|
|
|
(save-excursion
|
|
|
|
|
(python-nav-forward-sexp-function)
|
|
|
|
|
(and (not (looking-at (python-rx block-start)))
|
|
|
|
|
(point)))))
|
|
|
|
|
(when (and parent-block-ending-pos
|
|
|
|
|
(> parent-block-ending-pos prev-block-ending-pos))
|
|
|
|
|
(goto-char parent-block-ending-pos)))))
|
|
|
|
|
(t (python-nav-block-start))))
|
2012-05-17 00:03:15 -03:00
|
|
|
|
(setq arg (1+ arg))))
|
|
|
|
|
|
2012-05-17 00:03:43 -03:00
|
|
|
|
(defvar python-nav-list-defun-positions-cache nil)
|
|
|
|
|
(make-variable-buffer-local 'python-nav-list-defun-positions-cache)
|
|
|
|
|
|
|
|
|
|
(defun python-nav-list-defun-positions (&optional include-type rescan)
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"Make an Alist of defun names and point markers for current buffer.
|
|
|
|
|
When optional argument INCLUDE-TYPE is non-nil the type is
|
2012-05-17 00:03:43 -03:00
|
|
|
|
included the defun name. With optional argument RESCAN the
|
|
|
|
|
`python-nav-list-defun-positions-cache' is invalidated and the
|
|
|
|
|
list of defun is regenerated again."
|
|
|
|
|
(if (and python-nav-list-defun-positions-cache (not rescan))
|
|
|
|
|
python-nav-list-defun-positions-cache
|
|
|
|
|
(let ((defs))
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
(while (re-search-backward python-nav-beginning-of-defun-regexp nil t)
|
|
|
|
|
(when (and (not (python-info-ppss-context 'string))
|
|
|
|
|
(not (python-info-ppss-context 'comment))
|
|
|
|
|
(not (python-info-ppss-context 'parent)))
|
|
|
|
|
(add-to-list
|
|
|
|
|
'defs (cons
|
|
|
|
|
(python-info-current-defun include-type)
|
|
|
|
|
(point-marker)))))
|
|
|
|
|
(setq python-nav-list-defun-positions-cache defs))))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-read-defun (&optional rescan)
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"Read a defun name of current buffer and return its point marker.
|
|
|
|
|
A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned
|
2012-05-17 00:03:44 -03:00
|
|
|
|
when defun is completed, else nil. With optional argument RESCAN
|
2012-05-17 00:03:43 -03:00
|
|
|
|
forces `python-nav-list-defun-positions' to invalidate its
|
|
|
|
|
cache."
|
|
|
|
|
(let ((defs (python-nav-list-defun-positions nil rescan)))
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(minibuffer-with-setup-hook
|
|
|
|
|
(lambda ()
|
|
|
|
|
(setq minibuffer-completion-table (mapcar 'car defs)))
|
|
|
|
|
(let ((stringdef
|
|
|
|
|
(read-from-minibuffer
|
|
|
|
|
"Jump to definition: " nil
|
|
|
|
|
minibuffer-local-must-match-map)))
|
|
|
|
|
(when (not (string= stringdef ""))
|
|
|
|
|
(assoc-string stringdef defs))))))
|
|
|
|
|
|
|
|
|
|
(defun python-nav-jump-to-defun (def)
|
2012-05-17 00:03:43 -03:00
|
|
|
|
"Jump to the definition of DEF in current file.
|
2012-05-17 00:03:44 -03:00
|
|
|
|
Locations are cached; use a `C-u' prefix argument to force a
|
2012-05-17 00:03:43 -03:00
|
|
|
|
rescan."
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(interactive
|
2012-05-17 00:03:43 -03:00
|
|
|
|
(list (python-nav-read-defun current-prefix-arg)))
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(when (not (called-interactively-p 'interactive))
|
|
|
|
|
(setq def (assoc-string def (python-nav-list-defun-positions))))
|
|
|
|
|
(let ((def-marker (cdr def)))
|
|
|
|
|
(when (markerp def-marker)
|
|
|
|
|
(goto-char (marker-position def-marker))
|
|
|
|
|
(back-to-indentation))))
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Shell integration
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-shell-buffer-name "Python"
|
|
|
|
|
"Default buffer name for Python interpreter."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defcustom python-shell-interpreter "python"
|
|
|
|
|
"Default Python interpreter for shell."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-shell-internal-buffer-name "Python Internal"
|
|
|
|
|
"Default buffer name for the Internal Python interpreter."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defcustom python-shell-interpreter-args "-i"
|
|
|
|
|
"Default arguments for the Python interpreter."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defcustom python-shell-prompt-regexp ">>> "
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Regular Expression matching top\-level input prompt of python shell.
|
|
|
|
|
It should not contain a caret (^) at the beginning."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
|
|
|
|
(defcustom python-shell-prompt-block-regexp "[.][.][.] "
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Regular Expression matching block input prompt of python shell.
|
|
|
|
|
It should not contain a caret (^) at the beginning."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-shell-prompt-output-regexp ""
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Regular Expression matching output prompt of python shell.
|
|
|
|
|
It should not contain a caret (^) at the beginning."
|
2012-05-17 00:03:06 -03:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ "
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Regular Expression matching pdb input prompt of python shell.
|
|
|
|
|
It should not contain a caret (^) at the beginning."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(defcustom python-shell-enable-font-lock t
|
2012-05-17 00:03:40 -03:00
|
|
|
|
"Should syntax highlighting be enabled in the python shell buffer?
|
|
|
|
|
Restart the python shell after changing this variable for it to take effect."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
|
|
|
|
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(defcustom python-shell-send-setup-max-wait 5
|
|
|
|
|
"Seconds to wait for process output before code setup.
|
2012-06-17 01:53:31 -07:00
|
|
|
|
If output is received before the specified time then control is
|
2012-05-17 00:03:09 -03:00
|
|
|
|
returned in that moment and not after waiting."
|
2012-05-17 00:03:23 -03:00
|
|
|
|
:type 'integer
|
2012-05-17 00:03:09 -03:00
|
|
|
|
:group 'python
|
2012-05-17 00:03:23 -03:00
|
|
|
|
:safe 'integerp)
|
2012-05-17 00:03:09 -03:00
|
|
|
|
|
2012-05-17 00:03:10 -03:00
|
|
|
|
(defcustom python-shell-process-environment nil
|
2012-05-17 00:03:24 -03:00
|
|
|
|
"List of environment variables for Python shell.
|
|
|
|
|
This variable follows the same rules as `process-environment'
|
2012-05-17 00:03:10 -03:00
|
|
|
|
since it merges with it before the process creation routines are
|
|
|
|
|
called. When this variable is nil, the Python shell is run with
|
2012-05-17 00:03:24 -03:00
|
|
|
|
the default `process-environment'."
|
2012-05-17 00:03:10 -03:00
|
|
|
|
:type '(repeat string)
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'listp)
|
|
|
|
|
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(defcustom python-shell-extra-pythonpaths nil
|
|
|
|
|
"List of extra pythonpaths for Python shell.
|
|
|
|
|
The values of this variable are added to the existing value of
|
|
|
|
|
PYTHONPATH in the `process-environment' variable."
|
|
|
|
|
:type '(repeat string)
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'listp)
|
|
|
|
|
|
2012-05-17 00:03:10 -03:00
|
|
|
|
(defcustom python-shell-exec-path nil
|
|
|
|
|
"List of path to search for binaries.
|
|
|
|
|
This variable follows the same rules as `exec-path' since it
|
|
|
|
|
merges with it before the process creation routines are called.
|
|
|
|
|
When this variable is nil, the Python shell is run with the
|
|
|
|
|
default `exec-path'."
|
|
|
|
|
:type '(repeat string)
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'listp)
|
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(defcustom python-shell-virtualenv-path nil
|
|
|
|
|
"Path to virtualenv root.
|
|
|
|
|
This variable, when set to a string, makes the values stored in
|
|
|
|
|
`python-shell-process-environment' and `python-shell-exec-path'
|
|
|
|
|
to be modified properly so shells are started with the specified
|
|
|
|
|
virtualenv."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(defcustom python-shell-setup-codes '(python-shell-completion-setup-code
|
|
|
|
|
python-ffap-setup-code
|
|
|
|
|
python-eldoc-setup-code)
|
2012-05-17 00:03:29 -03:00
|
|
|
|
"List of code run by `python-shell-send-setup-codes'."
|
2012-05-17 00:03:09 -03:00
|
|
|
|
:type '(repeat symbol)
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'listp)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defcustom python-shell-compilation-regexp-alist
|
|
|
|
|
`((,(rx line-start (1+ (any " \t")) "File \""
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c
|
|
|
|
|
"\", line " (group (1+ digit)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
1 2)
|
|
|
|
|
(,(rx " in file " (group (1+ not-newline)) " on line "
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(group (1+ digit)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
1 2)
|
|
|
|
|
(,(rx line-start "> " (group (1+ (not (any "(\"<"))))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
"(" (group (1+ digit)) ")" (1+ (not (any "("))) "()")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
1 2))
|
|
|
|
|
"`compilation-error-regexp-alist' for inferior Python."
|
|
|
|
|
:type '(alist string)
|
|
|
|
|
:group 'python)
|
|
|
|
|
|
|
|
|
|
(defun python-shell-get-process-name (dedicated)
|
2012-05-17 00:03:33 -03:00
|
|
|
|
"Calculate the appropriate process name for inferior Python process.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
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
|
2012-05-17 00:03:09 -03:00
|
|
|
|
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."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(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))
|
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(defun python-shell-internal-get-process-name ()
|
2012-05-17 00:03:33 -03:00
|
|
|
|
"Calculate the appropriate process name for Internal Python process.
|
2012-05-17 00:03:21 -03:00
|
|
|
|
The name is calculated from `python-shell-global-buffer-name' and
|
|
|
|
|
a hash of all relevant global shell settings in order to ensure
|
|
|
|
|
uniqueness for different types of configurations."
|
|
|
|
|
(format "%s [%s]"
|
|
|
|
|
python-shell-internal-buffer-name
|
|
|
|
|
(md5
|
|
|
|
|
(concat
|
|
|
|
|
(python-shell-parse-command)
|
2012-05-17 00:03:29 -03:00
|
|
|
|
python-shell-prompt-regexp
|
|
|
|
|
python-shell-prompt-block-regexp
|
|
|
|
|
python-shell-prompt-output-regexp
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(mapconcat #'symbol-value python-shell-setup-codes "")
|
2012-05-17 00:03:29 -03:00
|
|
|
|
(mapconcat #'identity python-shell-process-environment "")
|
|
|
|
|
(mapconcat #'identity python-shell-extra-pythonpaths "")
|
|
|
|
|
(mapconcat #'identity python-shell-exec-path "")
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(or python-shell-virtualenv-path "")
|
2012-05-17 00:03:29 -03:00
|
|
|
|
(mapconcat #'identity python-shell-exec-path "")))))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-shell-parse-command ()
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Calculate the string used to execute the inferior Python process."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(format "%s %s" python-shell-interpreter python-shell-interpreter-args))
|
|
|
|
|
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(defun python-shell-calculate-process-environment ()
|
|
|
|
|
"Calculate process environment given `python-shell-virtualenv-path'."
|
2012-05-17 00:03:26 -03:00
|
|
|
|
(let ((process-environment (append
|
|
|
|
|
python-shell-process-environment
|
|
|
|
|
process-environment nil))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(virtualenv (if python-shell-virtualenv-path
|
|
|
|
|
(directory-file-name python-shell-virtualenv-path)
|
|
|
|
|
nil)))
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(when python-shell-extra-pythonpaths
|
|
|
|
|
(setenv "PYTHONPATH"
|
|
|
|
|
(format "%s%s%s"
|
|
|
|
|
(mapconcat 'identity
|
|
|
|
|
python-shell-extra-pythonpaths
|
|
|
|
|
path-separator)
|
|
|
|
|
path-separator
|
|
|
|
|
(or (getenv "PYTHONPATH") ""))))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(if (not virtualenv)
|
2012-05-17 00:03:26 -03:00
|
|
|
|
process-environment
|
|
|
|
|
(setenv "PYTHONHOME" nil)
|
|
|
|
|
(setenv "PATH" (format "%s/bin%s%s"
|
2012-05-17 00:03:27 -03:00
|
|
|
|
virtualenv path-separator
|
|
|
|
|
(or (getenv "PATH") "")))
|
2012-05-17 00:03:26 -03:00
|
|
|
|
(setenv "VIRTUAL_ENV" virtualenv))
|
|
|
|
|
process-environment))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
|
|
|
|
(defun python-shell-calculate-exec-path ()
|
|
|
|
|
"Calculate exec path given `python-shell-virtualenv-path'."
|
2012-05-17 00:03:26 -03:00
|
|
|
|
(let ((path (append python-shell-exec-path
|
|
|
|
|
exec-path nil)))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(if (not python-shell-virtualenv-path)
|
|
|
|
|
path
|
|
|
|
|
(cons (format "%s/bin"
|
|
|
|
|
(directory-file-name python-shell-virtualenv-path))
|
|
|
|
|
path))))
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-comint-output-filter-function (output)
|
|
|
|
|
"Hook run after content is put into comint buffer.
|
|
|
|
|
OUTPUT is a string with the contents of the buffer."
|
|
|
|
|
(ansi-color-filter-apply output))
|
|
|
|
|
|
|
|
|
|
(define-derived-mode inferior-python-mode comint-mode "Inferior Python"
|
2012-05-17 00:03:06 -03:00
|
|
|
|
"Major mode for Python inferior process.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
Runs a Python interpreter as a subprocess of Emacs, with Python
|
|
|
|
|
I/O through an Emacs buffer. Variables
|
|
|
|
|
`python-shell-interpreter' and `python-shell-interpreter-args'
|
|
|
|
|
controls which Python interpreter is run. Variables
|
|
|
|
|
`python-shell-prompt-regexp',
|
|
|
|
|
`python-shell-prompt-output-regexp',
|
|
|
|
|
`python-shell-prompt-block-regexp',
|
2012-05-17 00:03:42 -03:00
|
|
|
|
`python-shell-enable-font-lock',
|
2012-05-17 00:03:09 -03:00
|
|
|
|
`python-shell-completion-setup-code',
|
2012-05-17 00:03:29 -03:00
|
|
|
|
`python-shell-completion-string-code',
|
2012-05-17 00:03:31 -03:00
|
|
|
|
`python-shell-completion-module-string-code',
|
2012-05-17 00:03:29 -03:00
|
|
|
|
`python-eldoc-setup-code', `python-eldoc-string-code',
|
|
|
|
|
`python-ffap-setup-code' and `python-ffap-string-code' can
|
|
|
|
|
customize this mode for different Python interpreters.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
|
|
|
|
|
You can also add additional setup code to be run at
|
|
|
|
|
initialization of the interpreter via `python-shell-setup-codes'
|
|
|
|
|
variable.
|
|
|
|
|
|
|
|
|
|
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set-syntax-table python-mode-syntax-table)
|
|
|
|
|
(setq mode-line-process '(":%s"))
|
|
|
|
|
(setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)"
|
|
|
|
|
python-shell-prompt-regexp
|
|
|
|
|
python-shell-prompt-block-regexp
|
|
|
|
|
python-shell-prompt-pdb-regexp))
|
|
|
|
|
(make-local-variable 'comint-output-filter-functions)
|
|
|
|
|
(add-hook 'comint-output-filter-functions
|
|
|
|
|
'python-comint-output-filter-function)
|
|
|
|
|
(add-hook 'comint-output-filter-functions
|
|
|
|
|
'python-pdbtrack-comint-output-filter-function)
|
|
|
|
|
(set (make-local-variable 'compilation-error-regexp-alist)
|
|
|
|
|
python-shell-compilation-regexp-alist)
|
2012-05-17 00:02:58 -03:00
|
|
|
|
(define-key inferior-python-mode-map [remap complete-symbol]
|
|
|
|
|
'completion-at-point)
|
|
|
|
|
(add-hook 'completion-at-point-functions
|
|
|
|
|
'python-shell-completion-complete-at-point nil 'local)
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(add-to-list (make-local-variable 'comint-dynamic-complete-functions)
|
|
|
|
|
'python-shell-completion-complete-at-point)
|
|
|
|
|
(define-key inferior-python-mode-map (kbd "<tab>")
|
|
|
|
|
'python-shell-completion-complete-or-indent)
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(when python-shell-enable-font-lock
|
2012-05-17 00:03:46 -03:00
|
|
|
|
(set (make-local-variable 'font-lock-defaults)
|
|
|
|
|
'(python-font-lock-keywords nil nil nil nil))
|
|
|
|
|
(set (make-local-variable 'syntax-propertize-function)
|
|
|
|
|
python-syntax-propertize-function))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(compilation-shell-minor-mode 1))
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defun python-shell-make-comint (cmd proc-name &optional pop)
|
2012-05-17 00:03:22 -03:00
|
|
|
|
"Create a python shell comint buffer.
|
2012-05-17 00:03:23 -03:00
|
|
|
|
CMD is the python command to be executed and PROC-NAME is the
|
2012-05-17 00:03:22 -03:00
|
|
|
|
process name the comint buffer will get. After the comint buffer
|
2012-05-17 00:03:23 -03:00
|
|
|
|
is created the `inferior-python-mode' is activated. If POP is
|
|
|
|
|
non-nil the buffer is shown."
|
2012-05-17 00:03:22 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(let* ((proc-buffer-name (format "*%s*" proc-name))
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(process-environment (python-shell-calculate-process-environment))
|
2012-05-17 00:03:22 -03:00
|
|
|
|
(exec-path (python-shell-calculate-exec-path)))
|
|
|
|
|
(when (not (comint-check-proc proc-buffer-name))
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(let* ((cmdlist (split-string-and-unquote cmd))
|
|
|
|
|
(buffer (apply 'make-comint proc-name (car cmdlist) nil
|
|
|
|
|
(cdr cmdlist)))
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(current-buffer (current-buffer)))
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(inferior-python-mode)
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(python-util-clone-local-variables current-buffer))))
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(when pop
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(pop-to-buffer proc-buffer-name))
|
|
|
|
|
proc-buffer-name)))
|
2012-05-17 00:03:22 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun run-python (dedicated cmd)
|
|
|
|
|
"Run an inferior Python process.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
Input and output via buffer named after
|
|
|
|
|
`python-shell-buffer-name'. If there is a process already
|
|
|
|
|
running in that buffer, just switch to it.
|
|
|
|
|
With argument, allows you to define DEDICATED, so a dedicated
|
|
|
|
|
process for the current buffer is open, and define CMD so you can
|
|
|
|
|
edit the command used to call the interpreter (default is value
|
|
|
|
|
of `python-shell-interpreter' and arguments defined in
|
|
|
|
|
`python-shell-interpreter-args'). Runs the hook
|
|
|
|
|
`inferior-python-mode-hook' (after the `comint-mode-hook' is
|
|
|
|
|
run).
|
|
|
|
|
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive
|
|
|
|
|
(if current-prefix-arg
|
|
|
|
|
(list
|
|
|
|
|
(y-or-n-p "Make dedicated process? ")
|
|
|
|
|
(read-string "Run Python: " (python-shell-parse-command)))
|
|
|
|
|
(list nil (python-shell-parse-command))))
|
2012-05-17 00:03:41 -03:00
|
|
|
|
(python-shell-make-comint cmd (python-shell-get-process-name dedicated))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
dedicated)
|
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(defun run-python-internal ()
|
|
|
|
|
"Run an inferior Internal Python process.
|
|
|
|
|
Input and output via buffer named after
|
|
|
|
|
`python-shell-internal-buffer-name' and what
|
|
|
|
|
`python-shell-internal-get-process-name' returns. This new kind
|
|
|
|
|
of shell is intended to be used for generic communication related
|
|
|
|
|
to defined configurations. The main difference with global or
|
|
|
|
|
dedicated shells is that these ones are attached to a
|
|
|
|
|
configuration, not a buffer. This means that can be used for
|
|
|
|
|
example to retrieve the sys.path and other stuff, without messing
|
|
|
|
|
with user shells. Runs the hook
|
|
|
|
|
`inferior-python-mode-hook' (after the `comint-mode-hook' is
|
|
|
|
|
run). \(Type \\[describe-mode] in the process buffer for a list
|
|
|
|
|
of commands.)"
|
|
|
|
|
(interactive)
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(set-process-query-on-exit-flag
|
|
|
|
|
(get-buffer-process
|
|
|
|
|
(python-shell-make-comint
|
|
|
|
|
(python-shell-parse-command)
|
|
|
|
|
(python-shell-internal-get-process-name))) nil))
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-shell-get-process ()
|
|
|
|
|
"Get inferior Python process for current buffer and return it."
|
|
|
|
|
(let* ((dedicated-proc-name (python-shell-get-process-name t))
|
|
|
|
|
(dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name))
|
|
|
|
|
(global-proc-name (python-shell-get-process-name nil))
|
|
|
|
|
(global-proc-buffer-name (format "*%s*" global-proc-name))
|
|
|
|
|
(dedicated-running (comint-check-proc dedicated-proc-buffer-name))
|
|
|
|
|
(global-running (comint-check-proc global-proc-buffer-name)))
|
|
|
|
|
;; Always prefer dedicated
|
|
|
|
|
(get-buffer-process (or (and dedicated-running dedicated-proc-buffer-name)
|
|
|
|
|
(and global-running global-proc-buffer-name)))))
|
|
|
|
|
|
|
|
|
|
(defun python-shell-get-or-create-process ()
|
|
|
|
|
"Get or create an inferior Python process for current buffer and return it."
|
2012-05-17 00:03:41 -03:00
|
|
|
|
(let* ((dedicated-proc-name (python-shell-get-process-name t))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name))
|
|
|
|
|
(global-proc-name (python-shell-get-process-name nil))
|
|
|
|
|
(global-proc-buffer-name (format "*%s*" global-proc-name))
|
|
|
|
|
(dedicated-running (comint-check-proc dedicated-proc-buffer-name))
|
|
|
|
|
(global-running (comint-check-proc global-proc-buffer-name))
|
|
|
|
|
(current-prefix-arg 4))
|
|
|
|
|
(when (and (not dedicated-running) (not global-running))
|
|
|
|
|
(if (call-interactively 'run-python)
|
|
|
|
|
(setq dedicated-running t)
|
|
|
|
|
(setq global-running t)))
|
|
|
|
|
;; Always prefer dedicated
|
|
|
|
|
(get-buffer-process (if dedicated-running
|
|
|
|
|
dedicated-proc-buffer-name
|
|
|
|
|
global-proc-buffer-name))))
|
|
|
|
|
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(defvar python-shell-internal-buffer nil
|
|
|
|
|
"Current internal shell buffer for the current buffer.
|
|
|
|
|
This is really not necessary at all for the code to work but it's
|
|
|
|
|
there for compatibility with CEDET.")
|
|
|
|
|
(make-variable-buffer-local 'python-shell-internal-buffer)
|
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(defun python-shell-internal-get-or-create-process ()
|
|
|
|
|
"Get or create an inferior Internal Python process."
|
|
|
|
|
(let* ((proc-name (python-shell-internal-get-process-name))
|
|
|
|
|
(proc-buffer-name (format "*%s*" proc-name)))
|
|
|
|
|
(run-python-internal)
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(setq python-shell-internal-buffer proc-buffer-name)
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(get-buffer-process proc-buffer-name)))
|
|
|
|
|
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(define-obsolete-function-alias
|
2012-06-18 14:23:59 -04:00
|
|
|
|
'python-proc 'python-shell-internal-get-or-create-process "24.2")
|
2012-05-17 00:03:32 -03:00
|
|
|
|
|
|
|
|
|
(define-obsolete-variable-alias
|
2012-06-18 14:23:59 -04:00
|
|
|
|
'python-buffer 'python-shell-internal-buffer "24.2")
|
2012-05-17 00:03:32 -03:00
|
|
|
|
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(defun python-shell-send-string (string &optional process msg)
|
|
|
|
|
"Send STRING to inferior Python PROCESS.
|
|
|
|
|
When MSG is non-nil messages the first line of STRING."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "sPython command: ")
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(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))))
|
|
|
|
|
(if (> (length lines) 1)
|
|
|
|
|
(let* ((temp-file-name (make-temp-file "py"))
|
|
|
|
|
(file-name (or (buffer-file-name) temp-file-name)))
|
|
|
|
|
(with-temp-file temp-file-name
|
|
|
|
|
(insert string)
|
|
|
|
|
(delete-trailing-whitespace))
|
|
|
|
|
(python-shell-send-file file-name process temp-file-name))
|
|
|
|
|
(comint-send-string process string)
|
|
|
|
|
(when (or (not (string-match "\n$" string))
|
|
|
|
|
(string-match "\n[ \t].*\n?$" string))
|
|
|
|
|
(comint-send-string process "\n")))))
|
|
|
|
|
|
|
|
|
|
(defun python-shell-send-string-no-output (string &optional process msg)
|
|
|
|
|
"Send STRING to PROCESS and inhibit output.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
When MSG is non-nil messages the first line of STRING. Return
|
|
|
|
|
the output."
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(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))
|
|
|
|
|
"")))))
|
|
|
|
|
(python-shell-send-string string process msg)
|
|
|
|
|
(accept-process-output process)
|
2012-05-17 00:03:30 -03:00
|
|
|
|
(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)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:21 -03:00
|
|
|
|
(defun python-shell-internal-send-string (string)
|
|
|
|
|
"Send STRING to the Internal Python interpreter.
|
|
|
|
|
Returns the output. See `python-shell-send-string-no-output'."
|
|
|
|
|
(python-shell-send-string-no-output
|
|
|
|
|
;; Makes this function compatible with the old
|
|
|
|
|
;; python-send-receive. (At least for CEDET).
|
|
|
|
|
(replace-regexp-in-string "_emacs_out +" "" string)
|
|
|
|
|
(python-shell-internal-get-or-create-process) nil))
|
|
|
|
|
|
|
|
|
|
(define-obsolete-function-alias
|
2012-06-18 14:23:59 -04:00
|
|
|
|
'python-send-receive 'python-shell-internal-send-string "24.2")
|
2012-05-17 00:03:32 -03:00
|
|
|
|
|
|
|
|
|
(define-obsolete-function-alias
|
2012-06-18 14:23:59 -04:00
|
|
|
|
'python-send-string 'python-shell-internal-send-string "24.2")
|
2012-05-17 00:03:21 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-shell-send-region (start end)
|
|
|
|
|
"Send the region delimited by START and END to inferior Python process."
|
|
|
|
|
(interactive "r")
|
2012-05-17 00:03:39 -03:00
|
|
|
|
(python-shell-send-string (buffer-substring start end) nil t))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:39 -03:00
|
|
|
|
(defun python-shell-send-buffer (&optional arg)
|
|
|
|
|
"Send the entire buffer to inferior Python process.
|
|
|
|
|
|
2012-05-17 00:03:44 -03:00
|
|
|
|
With prefix ARG include lines surrounded by \"if __name__ == '__main__':\""
|
2012-05-17 00:03:39 -03:00
|
|
|
|
(interactive "P")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
2012-05-17 00:03:39 -03:00
|
|
|
|
(python-shell-send-region
|
|
|
|
|
(point-min)
|
|
|
|
|
(or (and
|
2012-05-17 00:03:41 -03:00
|
|
|
|
(not arg)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(re-search-forward (python-rx if-name-main) nil t))
|
|
|
|
|
(match-beginning 0))
|
|
|
|
|
(point-max)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-shell-send-defun (arg)
|
2012-05-17 00:03:12 -03:00
|
|
|
|
"Send the current defun to inferior Python process.
|
2012-05-17 00:03:44 -03:00
|
|
|
|
When argument ARG is non-nil do not include decorators."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "P")
|
|
|
|
|
(save-excursion
|
2012-05-17 00:03:12 -03:00
|
|
|
|
(python-shell-send-region
|
|
|
|
|
(progn
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(end-of-line 1)
|
|
|
|
|
(while (and (or (python-beginning-of-defun-function)
|
|
|
|
|
(beginning-of-line 1))
|
|
|
|
|
(> (current-indentation) 0)))
|
|
|
|
|
(when (not arg)
|
|
|
|
|
(while (and (forward-line -1)
|
|
|
|
|
(looking-at (python-rx decorator))))
|
|
|
|
|
(forward-line 1))
|
2012-05-17 00:03:40 -03:00
|
|
|
|
(point-marker))
|
2012-05-17 00:03:12 -03:00
|
|
|
|
(progn
|
|
|
|
|
(or (python-end-of-defun-function)
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(end-of-line 1))
|
2012-05-17 00:03:40 -03:00
|
|
|
|
(point-marker)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:03 -03:00
|
|
|
|
(defun python-shell-send-file (file-name &optional process temp-file-name)
|
|
|
|
|
"Send FILE-NAME to inferior Python PROCESS.
|
|
|
|
|
If TEMP-FILE-NAME is passed then that file is used for processing
|
|
|
|
|
instead, while internally the shell will continue to use
|
|
|
|
|
FILE-NAME."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "fFile to send: ")
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(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)))
|
|
|
|
|
(when (not file-name)
|
|
|
|
|
(error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil"))
|
2012-05-17 00:02:56 -03:00
|
|
|
|
(python-shell-send-string
|
2012-05-17 00:02:54 -03:00
|
|
|
|
(format
|
2012-05-17 00:03:03 -03:00
|
|
|
|
(concat "__pyfile = open('''%s''');"
|
|
|
|
|
"exec(compile(__pyfile.read(), '''%s''', 'exec'));"
|
|
|
|
|
"__pyfile.close()")
|
|
|
|
|
(or temp-file-name file-name) file-name)
|
2012-05-17 00:02:56 -03:00
|
|
|
|
process)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-shell-switch-to-shell ()
|
|
|
|
|
"Switch to inferior Python process buffer."
|
|
|
|
|
(interactive)
|
|
|
|
|
(pop-to-buffer (process-buffer (python-shell-get-or-create-process)) t))
|
|
|
|
|
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(defun python-shell-send-setup-code ()
|
|
|
|
|
"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))))
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(accept-process-output process python-shell-send-setup-max-wait)
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(dolist (code python-shell-setup-codes)
|
|
|
|
|
(when code
|
|
|
|
|
(message (format msg code))
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(python-shell-send-string
|
2012-05-17 00:03:09 -03:00
|
|
|
|
(symbol-value code) process)))))
|
|
|
|
|
|
|
|
|
|
(add-hook 'inferior-python-mode-hook
|
|
|
|
|
#'python-shell-send-setup-code)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Shell completion
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-shell-completion-setup-code
|
2012-05-17 00:02:52 -03:00
|
|
|
|
"try:
|
|
|
|
|
import readline
|
|
|
|
|
except ImportError:
|
|
|
|
|
def __COMPLETER_all_completions(text): []
|
|
|
|
|
else:
|
|
|
|
|
import rlcompleter
|
|
|
|
|
readline.set_completer(rlcompleter.Completer().complete)
|
|
|
|
|
def __COMPLETER_all_completions(text):
|
|
|
|
|
import sys
|
|
|
|
|
completions = []
|
|
|
|
|
try:
|
|
|
|
|
i = 0
|
|
|
|
|
while True:
|
|
|
|
|
res = readline.get_completer()(text, i)
|
|
|
|
|
if not res: break
|
|
|
|
|
i += 1
|
|
|
|
|
completions.append(res)
|
|
|
|
|
except NameError:
|
|
|
|
|
pass
|
|
|
|
|
return completions"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Code used to setup completion in inferior Python processes."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-shell-completion-string-code
|
2012-05-17 00:02:52 -03:00
|
|
|
|
"';'.join(__COMPLETER_all_completions('''%s'''))\n"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Python code used to get a string of completions separated by semicolons."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(defcustom python-shell-completion-module-string-code ""
|
2012-05-17 00:03:28 -03:00
|
|
|
|
"Python code used to get completions separated by semicolons for imports.
|
2012-05-17 00:03:27 -03:00
|
|
|
|
|
|
|
|
|
For IPython v0.11, add the following line to
|
|
|
|
|
`python-shell-completion-setup-code':
|
|
|
|
|
|
|
|
|
|
from IPython.core.completerlib import module_completion
|
|
|
|
|
|
|
|
|
|
and use the following as the value of this variable:
|
|
|
|
|
|
|
|
|
|
';'.join(module_completion('''%s'''))\n"
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:03:27 -03:00
|
|
|
|
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(defcustom python-shell-completion-pdb-string-code
|
|
|
|
|
"';'.join(globals().keys() + locals().keys())"
|
|
|
|
|
"Python code used to get completions separated by semicolons for [i]pdb."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:03:31 -03:00
|
|
|
|
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(defun python-shell-completion--get-completions (input process completion-code)
|
2012-05-17 00:03:28 -03:00
|
|
|
|
"Retrieve available completions for INPUT using PROCESS.
|
|
|
|
|
Argument COMPLETION-CODE is the python code used to get
|
|
|
|
|
completions on the current context."
|
2012-05-17 00:02:57 -03:00
|
|
|
|
(with-current-buffer (process-buffer process)
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(let ((completions (python-shell-send-string-no-output
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(format completion-code input) process)))
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(when (> (length completions) 2)
|
|
|
|
|
(split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t)))))
|
2012-05-17 00:02:57 -03:00
|
|
|
|
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(defun python-shell-completion--do-completion-at-point (process)
|
2012-05-17 00:03:28 -03:00
|
|
|
|
"Do completion at point for PROCESS."
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(with-syntax-table python-dotty-syntax-table
|
2012-05-17 00:03:37 -03:00
|
|
|
|
(let* ((beg
|
|
|
|
|
(save-excursion
|
|
|
|
|
(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)
|
|
|
|
|
(skip-syntax-backward syntax-string)
|
|
|
|
|
(when (or (equal (char-before) ?\))
|
|
|
|
|
(equal (char-before) ?\"))
|
|
|
|
|
(forward-char -1))
|
|
|
|
|
(while (or
|
|
|
|
|
;; honor initial paren depth
|
|
|
|
|
(> (car (syntax-ppss)) paren-depth)
|
|
|
|
|
(python-info-ppss-context 'string))
|
|
|
|
|
(forward-char -1))))
|
|
|
|
|
(point)))
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(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.
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(prompt
|
|
|
|
|
(with-current-buffer (process-buffer process)
|
|
|
|
|
(buffer-substring-no-properties
|
|
|
|
|
(overlay-start comint-last-prompt-overlay)
|
|
|
|
|
(overlay-end comint-last-prompt-overlay))))
|
2012-05-17 00:03:38 -03:00
|
|
|
|
(completion-context
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; Check whether a prompt matches a pdb string, an import statement
|
2012-05-17 00:03:36 -03:00
|
|
|
|
;; or just the standard prompt and use the correct
|
|
|
|
|
;; python-shell-completion-*-code string
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(cond ((and (> (length python-shell-completion-pdb-string-code) 0)
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(string-match
|
|
|
|
|
(concat "^" python-shell-prompt-pdb-regexp) prompt))
|
2012-05-17 00:03:38 -03:00
|
|
|
|
'pdb)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
((and (>
|
|
|
|
|
(length python-shell-completion-module-string-code) 0)
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(string-match
|
|
|
|
|
(concat "^" python-shell-prompt-regexp) prompt)
|
2012-05-17 00:03:39 -03:00
|
|
|
|
(string-match "^[ \t]*\\(from\\|import\\)[ \t]" line))
|
2012-05-17 00:03:38 -03:00
|
|
|
|
'import)
|
2012-05-17 00:03:32 -03:00
|
|
|
|
((string-match
|
|
|
|
|
(concat "^" python-shell-prompt-regexp) prompt)
|
2012-05-17 00:03:38 -03:00
|
|
|
|
'default)
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(t nil)))
|
2012-05-17 00:03:41 -03:00
|
|
|
|
(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))
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(completions
|
2012-05-17 00:03:32 -03:00
|
|
|
|
(and completion-code (> (length input) 0)
|
|
|
|
|
(python-shell-completion--get-completions
|
2012-05-17 00:03:37 -03:00
|
|
|
|
input process completion-code))))
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(list beg end completions))))
|
2012-05-17 00:02:57 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-shell-completion-complete-at-point ()
|
|
|
|
|
"Perform completion at point in inferior Python process."
|
|
|
|
|
(interactive)
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(and comint-last-prompt-overlay
|
|
|
|
|
(> (point-marker) (overlay-end comint-last-prompt-overlay))
|
|
|
|
|
(python-shell-completion--do-completion-at-point
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(get-buffer-process (current-buffer)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-shell-completion-complete-or-indent ()
|
|
|
|
|
"Complete or indent depending on the context.
|
2012-05-17 00:03:09 -03:00
|
|
|
|
If content before pointer is all whitespace indent. If not try
|
|
|
|
|
to complete."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive)
|
|
|
|
|
(if (string-match "^[[:space:]]*$"
|
|
|
|
|
(buffer-substring (comint-line-beginning-position)
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(indent-for-tab-command)
|
2012-05-17 00:03:38 -03:00
|
|
|
|
(completion-at-point)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; PDB Track integration
|
|
|
|
|
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(defcustom python-pdbtrack-activate t
|
|
|
|
|
"Non-nil makes python shell enable pdbtracking."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-pdbtrack-stacktrace-info-regexp
|
2012-05-17 00:03:28 -03:00
|
|
|
|
"^> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Regular Expression matching stacktrace information.
|
2012-05-17 00:03:28 -03:00
|
|
|
|
Used to extract the current line and module being inspected."
|
2012-05-17 00:03:23 -03:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(defvar python-pdbtrack-tracked-buffer nil
|
|
|
|
|
"Variable containing the value of the current tracked buffer.
|
|
|
|
|
Never set this variable directly, use
|
|
|
|
|
`python-pdbtrack-set-tracked-buffer' instead.")
|
|
|
|
|
(make-variable-buffer-local 'python-pdbtrack-tracked-buffer)
|
|
|
|
|
|
|
|
|
|
(defvar python-pdbtrack-buffers-to-kill nil
|
|
|
|
|
"List of buffers to be deleted after tracking finishes.")
|
|
|
|
|
(make-variable-buffer-local 'python-pdbtrack-buffers-to-kill)
|
|
|
|
|
|
|
|
|
|
(defun python-pdbtrack-set-tracked-buffer (file-name)
|
|
|
|
|
"Set the buffer for FILE-NAME as the tracked buffer.
|
|
|
|
|
Internally it uses the `python-pdbtrack-tracked-buffer' variable.
|
|
|
|
|
Returns the tracked buffer."
|
|
|
|
|
(let ((file-buffer (get-file-buffer file-name)))
|
|
|
|
|
(if file-buffer
|
|
|
|
|
(setq python-pdbtrack-tracked-buffer file-buffer)
|
|
|
|
|
(setq file-buffer (find-file-noselect file-name))
|
|
|
|
|
(when (not (member file-buffer python-pdbtrack-buffers-to-kill))
|
|
|
|
|
(add-to-list 'python-pdbtrack-buffers-to-kill file-buffer)))
|
|
|
|
|
file-buffer))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-pdbtrack-comint-output-filter-function (output)
|
|
|
|
|
"Move overlay arrow to current pdb line in tracked buffer.
|
|
|
|
|
Argument OUTPUT is a string with the output from the comint process."
|
2012-05-17 00:03:31 -03:00
|
|
|
|
(when (and python-pdbtrack-activate (not (string= output "")))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(let* ((full-output (ansi-color-filter-apply
|
|
|
|
|
(buffer-substring comint-last-input-end (point-max))))
|
|
|
|
|
(line-number)
|
|
|
|
|
(file-name
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert full-output)
|
|
|
|
|
(goto-char (point-min))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
;; OK, this sucked but now it became a cool hack. The
|
|
|
|
|
;; stacktrace information normally is on the first line
|
|
|
|
|
;; but in some cases (like when doing a step-in) it is
|
|
|
|
|
;; on the second.
|
|
|
|
|
(when (or (looking-at python-pdbtrack-stacktrace-info-regexp)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(and
|
|
|
|
|
(forward-line)
|
|
|
|
|
(looking-at python-pdbtrack-stacktrace-info-regexp)))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(setq line-number (string-to-number
|
|
|
|
|
(match-string-no-properties 2)))
|
|
|
|
|
(match-string-no-properties 1)))))
|
|
|
|
|
(if (and file-name line-number)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(let* ((tracked-buffer
|
|
|
|
|
(python-pdbtrack-set-tracked-buffer file-name))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(shell-buffer (current-buffer))
|
|
|
|
|
(tracked-buffer-window (get-buffer-window tracked-buffer))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(tracked-buffer-line-pos))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(with-current-buffer tracked-buffer
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(set (make-local-variable 'overlay-arrow-string) "=>")
|
|
|
|
|
(set (make-local-variable 'overlay-arrow-position) (make-marker))
|
|
|
|
|
(setq tracked-buffer-line-pos (progn
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- line-number))
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(when tracked-buffer-window
|
|
|
|
|
(set-window-point
|
|
|
|
|
tracked-buffer-window tracked-buffer-line-pos))
|
|
|
|
|
(set-marker overlay-arrow-position tracked-buffer-line-pos))
|
2012-05-17 00:03:28 -03:00
|
|
|
|
(pop-to-buffer tracked-buffer)
|
|
|
|
|
(switch-to-buffer-other-window shell-buffer))
|
|
|
|
|
(when python-pdbtrack-tracked-buffer
|
|
|
|
|
(with-current-buffer python-pdbtrack-tracked-buffer
|
|
|
|
|
(set-marker overlay-arrow-position nil))
|
|
|
|
|
(mapc #'(lambda (buffer)
|
|
|
|
|
(ignore-errors (kill-buffer buffer)))
|
|
|
|
|
python-pdbtrack-buffers-to-kill)
|
|
|
|
|
(setq python-pdbtrack-tracked-buffer nil
|
|
|
|
|
python-pdbtrack-buffers-to-kill nil)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Symbol completion
|
|
|
|
|
|
|
|
|
|
(defun python-completion-complete-at-point ()
|
|
|
|
|
"Complete current symbol at point.
|
|
|
|
|
For this to work the best as possible you should call
|
|
|
|
|
`python-shell-send-buffer' from time to time so context in
|
|
|
|
|
inferior python process is updated properly."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((process (python-shell-get-process)))
|
|
|
|
|
(if (not process)
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(error "Completion needs an inferior Python process running")
|
2012-05-17 00:03:27 -03:00
|
|
|
|
(python-shell-completion--do-completion-at-point process))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(add-to-list 'debug-ignored-errors
|
|
|
|
|
"^Completion needs an inferior Python process running.")
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Fill paragraph
|
|
|
|
|
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(defcustom python-fill-comment-function 'python-fill-comment
|
|
|
|
|
"Function to fill comments.
|
|
|
|
|
This is the function used by `python-fill-paragraph-function' to
|
|
|
|
|
fill comments."
|
|
|
|
|
:type 'symbol
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'symbolp)
|
|
|
|
|
|
|
|
|
|
(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)
|
|
|
|
|
|
|
|
|
|
(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)
|
|
|
|
|
|
|
|
|
|
(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
|
|
|
|
|
:safe 'symbolp)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(defun python-fill-paragraph-function (&optional justify)
|
|
|
|
|
"`fill-paragraph-function' handling multi-line strings and possibly comments.
|
|
|
|
|
If any of the current line is in or at the end of a multi-line string,
|
|
|
|
|
fill the string or the paragraph of it that point is in, preserving
|
2012-05-17 00:02:56 -03:00
|
|
|
|
the string's indentation.
|
|
|
|
|
Optional argument JUSTIFY defines if the paragraph should be justified."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(interactive "P")
|
|
|
|
|
(save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(cond
|
|
|
|
|
;; Comments
|
2012-05-17 00:03:11 -03:00
|
|
|
|
((funcall python-fill-comment-function justify))
|
|
|
|
|
;; Strings/Docstrings
|
2012-05-17 00:02:52 -03:00
|
|
|
|
((save-excursion (skip-chars-forward "\"'uUrR")
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(python-info-ppss-context 'string))
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(funcall python-fill-string-function justify))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Decorators
|
|
|
|
|
((equal (char-after (save-excursion
|
|
|
|
|
(back-to-indentation)
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(point-marker))) ?@)
|
|
|
|
|
(funcall python-fill-decorator-function justify))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
;; Parens
|
2012-05-17 00:03:07 -03:00
|
|
|
|
((or (python-info-ppss-context 'paren)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(looking-at (python-rx open-paren))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(skip-syntax-forward "^(" (line-end-position))
|
|
|
|
|
(looking-at (python-rx open-paren))))
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(funcall python-fill-paren-function justify))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(t t))))
|
|
|
|
|
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(defun python-fill-comment (&optional justify)
|
2012-05-17 00:03:13 -03:00
|
|
|
|
"Comment fill function for `python-fill-paragraph-function'.
|
|
|
|
|
JUSTIFY should be used (if applicable) as in `fill-paragraph'."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(fill-comment-paragraph justify))
|
|
|
|
|
|
|
|
|
|
(defun python-fill-string (&optional justify)
|
2012-05-17 00:03:13 -03:00
|
|
|
|
"String fill function for `python-fill-paragraph-function'.
|
|
|
|
|
JUSTIFY should be used (if applicable) as in `fill-paragraph'."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(let ((marker (point-marker))
|
|
|
|
|
(string-start-marker
|
|
|
|
|
(progn
|
|
|
|
|
(skip-chars-forward "\"'uUrR")
|
|
|
|
|
(goto-char (python-info-ppss-context 'string))
|
|
|
|
|
(skip-chars-forward "\"'uUrR")
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(reg-start (line-beginning-position))
|
|
|
|
|
(string-end-marker
|
|
|
|
|
(progn
|
|
|
|
|
(while (python-info-ppss-context 'string)
|
|
|
|
|
(goto-char (1+ (point-marker))))
|
|
|
|
|
(skip-chars-backward "\"'")
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(reg-end (line-end-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)
|
|
|
|
|
|
|
|
|
|
(defun python-fill-decorator (&optional justify)
|
2012-05-17 00:03:13 -03:00
|
|
|
|
"Decorator fill function for `python-fill-paragraph-function'.
|
|
|
|
|
JUSTIFY should be used (if applicable) as in `fill-paragraph'."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
t)
|
|
|
|
|
|
|
|
|
|
(defun python-fill-paren (&optional justify)
|
2012-05-17 00:03:13 -03:00
|
|
|
|
"Paren fill function for `python-fill-paragraph-function'.
|
|
|
|
|
JUSTIFY should be used (if applicable) as in `fill-paragraph'."
|
2012-05-17 00:03:11 -03:00
|
|
|
|
(save-restriction
|
|
|
|
|
(narrow-to-region (progn
|
|
|
|
|
(while (python-info-ppss-context 'paren)
|
|
|
|
|
(goto-char (1- (point-marker))))
|
|
|
|
|
(point-marker)
|
|
|
|
|
(line-beginning-position))
|
|
|
|
|
(progn
|
|
|
|
|
(when (not (python-info-ppss-context 'paren))
|
|
|
|
|
(end-of-line)
|
|
|
|
|
(when (not (python-info-ppss-context 'paren))
|
|
|
|
|
(skip-syntax-backward "^)")))
|
|
|
|
|
(while (python-info-ppss-context 'paren)
|
|
|
|
|
(goto-char (1+ (point-marker))))
|
|
|
|
|
(point-marker)))
|
|
|
|
|
(let ((paragraph-start "\f\\|[ \t]*$")
|
|
|
|
|
(paragraph-separate ",")
|
|
|
|
|
(fill-paragraph-function))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(fill-paragraph justify))
|
|
|
|
|
(while (not (eobp))
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
(python-indent-line)
|
|
|
|
|
(goto-char (line-end-position)))) t)
|
|
|
|
|
|
2012-05-17 00:02:59 -03:00
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
;;; Skeletons
|
|
|
|
|
|
|
|
|
|
(defcustom python-skeleton-autoinsert nil
|
|
|
|
|
"Non-nil means template skeletons will be automagically inserted.
|
|
|
|
|
This happens when pressing \"if<SPACE>\", for example, to prompt for
|
|
|
|
|
the if condition."
|
|
|
|
|
:type 'boolean
|
2012-05-17 00:03:23 -03:00
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
|
2012-06-14 22:33:57 -03:00
|
|
|
|
(define-obsolete-variable-alias
|
|
|
|
|
'python-use-skeletons 'python-skeleton-autoinsert "24.2")
|
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(defvar python-skeleton-available '()
|
|
|
|
|
"Internal list of available skeletons.")
|
|
|
|
|
|
|
|
|
|
(define-abbrev-table 'python-mode-abbrev-table ()
|
|
|
|
|
"Abbrev table for Python mode."
|
|
|
|
|
:case-fixed t
|
|
|
|
|
;; Allow / inside abbrevs.
|
|
|
|
|
:regexp "\\(?:^\\|[^/]\\)\\<\\([[:word:]/]+\\)\\W*"
|
|
|
|
|
;; Only expand in code.
|
|
|
|
|
:enable-function (lambda ()
|
|
|
|
|
(and
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(not (or (python-info-ppss-context 'string)
|
|
|
|
|
(python-info-ppss-context 'comment)))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
python-skeleton-autoinsert)))
|
|
|
|
|
|
|
|
|
|
(defmacro python-skeleton-define (name doc &rest skel)
|
|
|
|
|
"Define a `python-mode' skeleton using NAME DOC and SKEL.
|
|
|
|
|
The skeleton will be bound to python-skeleton-NAME and will
|
|
|
|
|
be added to `python-mode-abbrev-table'."
|
2012-06-18 16:41:23 -04:00
|
|
|
|
(declare (indent 2))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(let* ((name (symbol-name name))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(function-name (intern (concat "python-skeleton-" name))))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
`(progn
|
2012-06-23 17:28:10 +08:00
|
|
|
|
(define-abbrev python-mode-abbrev-table ,name "" ',function-name
|
|
|
|
|
:system t)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(setq python-skeleton-available
|
|
|
|
|
(cons ',function-name python-skeleton-available))
|
|
|
|
|
(define-skeleton ,function-name
|
|
|
|
|
,(or doc
|
|
|
|
|
(format "Insert %s statement." name))
|
|
|
|
|
,@skel))))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
|
|
|
|
|
(defmacro python-define-auxiliary-skeleton (name doc &optional &rest skel)
|
|
|
|
|
"Define a `python-mode' auxiliary skeleton using NAME DOC and SKEL.
|
|
|
|
|
The skeleton will be bound to python-skeleton-NAME."
|
2012-06-18 16:41:23 -04:00
|
|
|
|
(declare (indent 2))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(let* ((name (symbol-name name))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(function-name (intern (concat "python-skeleton--" name)))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(msg (format
|
|
|
|
|
"Add '%s' clause? " name)))
|
|
|
|
|
(when (not skel)
|
|
|
|
|
(setq skel
|
|
|
|
|
`(< ,(format "%s:" name) \n \n
|
|
|
|
|
> _ \n)))
|
|
|
|
|
`(define-skeleton ,function-name
|
|
|
|
|
,(or doc
|
|
|
|
|
(format "Auxiliary skeleton for %s statement." name))
|
|
|
|
|
nil
|
|
|
|
|
(unless (y-or-n-p ,msg)
|
|
|
|
|
(signal 'quit t))
|
|
|
|
|
,@skel)))
|
|
|
|
|
|
|
|
|
|
(python-define-auxiliary-skeleton else nil)
|
|
|
|
|
|
|
|
|
|
(python-define-auxiliary-skeleton except nil)
|
|
|
|
|
|
|
|
|
|
(python-define-auxiliary-skeleton finally nil)
|
|
|
|
|
|
|
|
|
|
(python-skeleton-define if nil
|
|
|
|
|
"Condition: "
|
|
|
|
|
"if " str ":" \n
|
|
|
|
|
_ \n
|
|
|
|
|
("other condition, %s: "
|
|
|
|
|
<
|
|
|
|
|
"elif " str ":" \n
|
|
|
|
|
> _ \n nil)
|
|
|
|
|
'(python-skeleton--else) | ^)
|
|
|
|
|
|
|
|
|
|
(python-skeleton-define while nil
|
|
|
|
|
"Condition: "
|
|
|
|
|
"while " str ":" \n
|
|
|
|
|
> _ \n
|
|
|
|
|
'(python-skeleton--else) | ^)
|
|
|
|
|
|
|
|
|
|
(python-skeleton-define for nil
|
|
|
|
|
"Iteration spec: "
|
|
|
|
|
"for " str ":" \n
|
|
|
|
|
> _ \n
|
|
|
|
|
'(python-skeleton--else) | ^)
|
|
|
|
|
|
|
|
|
|
(python-skeleton-define try nil
|
|
|
|
|
nil
|
|
|
|
|
"try:" \n
|
|
|
|
|
> _ \n
|
|
|
|
|
("Exception, %s: "
|
|
|
|
|
<
|
|
|
|
|
"except " str ":" \n
|
|
|
|
|
> _ \n nil)
|
|
|
|
|
resume:
|
|
|
|
|
'(python-skeleton--except)
|
|
|
|
|
'(python-skeleton--else)
|
|
|
|
|
'(python-skeleton--finally) | ^)
|
|
|
|
|
|
|
|
|
|
(python-skeleton-define def nil
|
|
|
|
|
"Function name: "
|
|
|
|
|
"def " str " (" ("Parameter, %s: "
|
|
|
|
|
(unless (equal ?\( (char-before)) ", ")
|
2012-05-17 00:03:35 -03:00
|
|
|
|
str) "):" \n
|
|
|
|
|
"\"\"\"" - "\"\"\"" \n
|
|
|
|
|
> _ \n)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
|
|
|
|
|
(python-skeleton-define class nil
|
|
|
|
|
"Class name: "
|
|
|
|
|
"class " str " (" ("Inheritance, %s: "
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(unless (equal ?\( (char-before)) ", ")
|
|
|
|
|
str)
|
2012-05-17 00:03:05 -03:00
|
|
|
|
& ")" | -2
|
|
|
|
|
":" \n
|
|
|
|
|
"\"\"\"" - "\"\"\"" \n
|
|
|
|
|
> _ \n)
|
|
|
|
|
|
|
|
|
|
(defun python-skeleton-add-menu-items ()
|
|
|
|
|
"Add menu items to Python->Skeletons menu."
|
|
|
|
|
(let ((skeletons (sort python-skeleton-available 'string<))
|
|
|
|
|
(items))
|
|
|
|
|
(dolist (skeleton skeletons)
|
|
|
|
|
(easy-menu-add-item
|
|
|
|
|
nil '("Python" "Skeletons")
|
|
|
|
|
`[,(format
|
|
|
|
|
"Insert %s" (caddr (split-string (symbol-name skeleton) "-")))
|
|
|
|
|
,skeleton t]))))
|
|
|
|
|
|
2012-05-17 00:02:59 -03:00
|
|
|
|
;;; FFAP
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-ffap-setup-code
|
2012-05-17 00:02:59 -03:00
|
|
|
|
"def __FFAP_get_module_path(module):
|
|
|
|
|
try:
|
|
|
|
|
import os
|
|
|
|
|
path = __import__(module).__file__
|
|
|
|
|
if path[-4:] == '.pyc' and os.path.exists(path[0:-1]):
|
|
|
|
|
path = path[:-1]
|
|
|
|
|
return path
|
|
|
|
|
except:
|
|
|
|
|
return ''"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Python code to get a module path."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:59 -03:00
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-ffap-string-code
|
2012-05-17 00:02:59 -03:00
|
|
|
|
"__FFAP_get_module_path('''%s''')\n"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Python code used to get a string with the path of a module."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:59 -03:00
|
|
|
|
|
|
|
|
|
(defun python-ffap-module-path (module)
|
|
|
|
|
"Function for `ffap-alist' to return path for MODULE."
|
|
|
|
|
(let ((process (or
|
|
|
|
|
(and (eq major-mode 'inferior-python-mode)
|
|
|
|
|
(get-buffer-process (current-buffer)))
|
|
|
|
|
(python-shell-get-process))))
|
|
|
|
|
(if (not process)
|
|
|
|
|
nil
|
|
|
|
|
(let ((module-file
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(python-shell-send-string-no-output
|
2012-05-17 00:02:59 -03:00
|
|
|
|
(format python-ffap-string-code module) process)))
|
|
|
|
|
(when module-file
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(substring-no-properties module-file 1 -1))))))
|
2012-05-17 00:02:59 -03:00
|
|
|
|
|
|
|
|
|
(eval-after-load "ffap"
|
|
|
|
|
'(progn
|
|
|
|
|
(push '(python-mode . python-ffap-module-path) ffap-alist)
|
|
|
|
|
(push '(inferior-python-mode . python-ffap-module-path) ffap-alist)))
|
|
|
|
|
|
2012-05-17 00:03:00 -03:00
|
|
|
|
|
|
|
|
|
;;; Code check
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-check-command
|
2012-05-17 00:03:34 -03:00
|
|
|
|
"pyflakes"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Command used to check a Python file."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:03:00 -03:00
|
|
|
|
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(defcustom python-check-buffer-name
|
|
|
|
|
"*Python check: %s*"
|
|
|
|
|
"Buffer name used for check commands."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python)
|
|
|
|
|
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(defvar python-check-custom-command nil
|
|
|
|
|
"Internal use.")
|
|
|
|
|
|
|
|
|
|
(defun python-check (command)
|
|
|
|
|
"Check a Python file (default current buffer's file).
|
|
|
|
|
Runs COMMAND, a shell command, as if by `compile'. See
|
|
|
|
|
`python-check-command' for the default."
|
|
|
|
|
(interactive
|
|
|
|
|
(list (read-string "Check command: "
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(or python-check-custom-command
|
|
|
|
|
(concat python-check-command " "
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(shell-quote-argument
|
|
|
|
|
(or
|
|
|
|
|
(let ((name (buffer-file-name)))
|
|
|
|
|
(and name
|
|
|
|
|
(file-name-nondirectory name)))
|
|
|
|
|
"")))))))
|
|
|
|
|
(setq python-check-custom-command command)
|
|
|
|
|
(save-some-buffers (not compilation-ask-about-save) nil)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(let ((process-environment (python-shell-calculate-process-environment))
|
|
|
|
|
(exec-path (python-shell-calculate-exec-path)))
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(compilation-start command nil
|
|
|
|
|
(lambda (mode-name)
|
|
|
|
|
(format python-check-buffer-name command)))))
|
2012-05-17 00:03:00 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Eldoc
|
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-eldoc-setup-code
|
2012-05-17 00:02:52 -03:00
|
|
|
|
"def __PYDOC_get_help(obj):
|
|
|
|
|
try:
|
2012-05-17 00:03:18 -03:00
|
|
|
|
import inspect
|
2012-05-17 00:02:58 -03:00
|
|
|
|
if hasattr(obj, 'startswith'):
|
|
|
|
|
obj = eval(obj, globals())
|
2012-05-17 00:03:18 -03:00
|
|
|
|
doc = inspect.getdoc(obj)
|
|
|
|
|
if not doc and callable(obj):
|
|
|
|
|
target = None
|
|
|
|
|
if inspect.isclass(obj) and hasattr(obj, '__init__'):
|
|
|
|
|
target = obj.__init__
|
|
|
|
|
objtype = 'class'
|
|
|
|
|
else:
|
|
|
|
|
target = obj
|
|
|
|
|
objtype = 'def'
|
|
|
|
|
if target:
|
|
|
|
|
args = inspect.formatargspec(
|
|
|
|
|
*inspect.getargspec(target)
|
|
|
|
|
)
|
|
|
|
|
name = obj.__name__
|
|
|
|
|
doc = '{objtype} {name}{args}'.format(
|
|
|
|
|
objtype=objtype, name=name, args=args
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
doc = doc.splitlines()[0]
|
2012-05-17 00:02:52 -03:00
|
|
|
|
except:
|
2012-05-17 00:02:58 -03:00
|
|
|
|
doc = ''
|
|
|
|
|
try:
|
|
|
|
|
exec('print doc')
|
|
|
|
|
except SyntaxError:
|
|
|
|
|
print(doc)"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Python code to setup documentation retrieval."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:23 -03:00
|
|
|
|
(defcustom python-eldoc-string-code
|
2012-05-17 00:02:58 -03:00
|
|
|
|
"__PYDOC_get_help('''%s''')\n"
|
2012-05-17 00:03:23 -03:00
|
|
|
|
"Python code used to get a string with the documentation of an object."
|
|
|
|
|
:type 'string
|
2012-05-17 00:03:33 -03:00
|
|
|
|
:group 'python)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(defun python-eldoc--get-doc-at-point (&optional force-input force-process)
|
2012-05-17 00:03:03 -03:00
|
|
|
|
"Internal implementation to get documentation at point.
|
|
|
|
|
If not FORCE-INPUT is passed then what `current-word' returns
|
|
|
|
|
will be used. If not FORCE-PROCESS is passed what
|
|
|
|
|
`python-shell-get-process' returns is used."
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(let ((process (or force-process (python-shell-get-process))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(if (not process)
|
|
|
|
|
"Eldoc needs an inferior Python process running."
|
|
|
|
|
(let* ((current-defun (python-info-current-defun))
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(input (or force-input
|
|
|
|
|
(with-syntax-table python-dotty-syntax-table
|
|
|
|
|
(if (not current-defun)
|
|
|
|
|
(current-word)
|
|
|
|
|
(concat current-defun "." (current-word))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(ppss (syntax-ppss))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(help (when (and
|
|
|
|
|
input
|
|
|
|
|
(not (string= input (concat current-defun ".")))
|
|
|
|
|
(not (or (python-info-ppss-context 'string ppss)
|
|
|
|
|
(python-info-ppss-context 'comment ppss))))
|
|
|
|
|
(when (string-match
|
|
|
|
|
(concat
|
|
|
|
|
(regexp-quote (concat current-defun "."))
|
|
|
|
|
"self\\.") input)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert input)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-word)
|
|
|
|
|
(forward-char)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(delete-region
|
|
|
|
|
(point-marker) (search-forward "self."))
|
|
|
|
|
(setq input (buffer-substring
|
|
|
|
|
(point-min) (point-max)))))
|
2012-05-17 00:03:06 -03:00
|
|
|
|
(python-shell-send-string-no-output
|
2012-05-17 00:02:59 -03:00
|
|
|
|
(format python-eldoc-string-code input) process))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(with-current-buffer (process-buffer process)
|
|
|
|
|
(when comint-last-prompt-overlay
|
|
|
|
|
(delete-region comint-last-input-end
|
|
|
|
|
(overlay-start comint-last-prompt-overlay))))
|
|
|
|
|
(when (and help
|
|
|
|
|
(not (string= help "\n")))
|
|
|
|
|
help)))))
|
|
|
|
|
|
2012-05-17 00:03:00 -03:00
|
|
|
|
(defun python-eldoc-function ()
|
|
|
|
|
"`eldoc-documentation-function' for Python.
|
|
|
|
|
For this to work the best as possible you should call
|
|
|
|
|
`python-shell-send-buffer' from time to time so context in
|
|
|
|
|
inferior python process is updated properly."
|
|
|
|
|
(python-eldoc--get-doc-at-point))
|
|
|
|
|
|
|
|
|
|
(defun python-eldoc-at-point (symbol)
|
|
|
|
|
"Get help on SYMBOL using `help'.
|
|
|
|
|
Interactively, prompt for symbol."
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(interactive
|
|
|
|
|
(let ((symbol (with-syntax-table python-dotty-syntax-table
|
|
|
|
|
(current-word)))
|
|
|
|
|
(enable-recursive-minibuffers t))
|
|
|
|
|
(list (read-string (if symbol
|
|
|
|
|
(format "Describe symbol (default %s): " symbol)
|
|
|
|
|
"Describe symbol: ")
|
|
|
|
|
nil nil symbol))))
|
|
|
|
|
(let ((process (python-shell-get-process)))
|
|
|
|
|
(if (not process)
|
|
|
|
|
(message "Eldoc needs an inferior Python process running.")
|
|
|
|
|
(message (python-eldoc--get-doc-at-point symbol process)))))
|
2012-05-17 00:03:00 -03:00
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
|
|
|
|
|
;;; Imenu
|
|
|
|
|
|
|
|
|
|
(defcustom python-imenu-include-defun-type t
|
|
|
|
|
"Non-nil make imenu items to include its type."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(defcustom python-imenu-make-tree t
|
2012-05-17 00:03:14 -03:00
|
|
|
|
"Non-nil make imenu to build a tree menu.
|
|
|
|
|
Set to nil for speed."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'booleanp)
|
|
|
|
|
|
|
|
|
|
(defcustom python-imenu-subtree-root-label "<Jump to %s>"
|
|
|
|
|
"Label displayed to navigate to root from a subtree.
|
|
|
|
|
It can contain a \"%s\" which will be replaced with the root name."
|
|
|
|
|
:type 'string
|
|
|
|
|
:group 'python
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
|
|
|
|
(defvar python-imenu-index-alist nil
|
|
|
|
|
"Calculated index tree for imenu.")
|
|
|
|
|
|
|
|
|
|
(defun python-imenu-tree-assoc (keylist tree)
|
|
|
|
|
"Using KEYLIST traverse TREE."
|
|
|
|
|
(if keylist
|
|
|
|
|
(python-imenu-tree-assoc (cdr keylist)
|
|
|
|
|
(ignore-errors (assoc (car keylist) tree)))
|
|
|
|
|
tree))
|
|
|
|
|
|
|
|
|
|
(defun python-imenu-make-element-tree (element-list full-element plain-index)
|
|
|
|
|
"Make a tree from plain alist of module names.
|
2012-06-17 01:53:31 -07:00
|
|
|
|
ELEMENT-LIST is the defun name split by \".\" and FULL-ELEMENT
|
2012-05-17 00:03:14 -03:00
|
|
|
|
is the same thing, the difference is that FULL-ELEMENT remains
|
|
|
|
|
untouched in all recursive calls.
|
|
|
|
|
Argument PLAIN-INDEX is the calculated plain index used to build the tree."
|
|
|
|
|
(when (not (python-imenu-tree-assoc full-element python-imenu-index-alist))
|
|
|
|
|
(when element-list
|
|
|
|
|
(let* ((subelement-point (cdr (assoc
|
|
|
|
|
(mapconcat #'identity full-element ".")
|
|
|
|
|
plain-index)))
|
|
|
|
|
(subelement-name (car element-list))
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(subelement-position (python-util-position
|
|
|
|
|
subelement-name full-element))
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(subelement-path (when subelement-position
|
|
|
|
|
(butlast
|
|
|
|
|
full-element
|
|
|
|
|
(- (length full-element)
|
|
|
|
|
subelement-position)))))
|
|
|
|
|
(let ((path-ref (python-imenu-tree-assoc subelement-path
|
|
|
|
|
python-imenu-index-alist)))
|
|
|
|
|
(if (not path-ref)
|
|
|
|
|
(push (cons subelement-name subelement-point)
|
|
|
|
|
python-imenu-index-alist)
|
|
|
|
|
(when (not (listp (cdr path-ref)))
|
2012-06-17 01:53:31 -07:00
|
|
|
|
;; Modify root cdr to be a list.
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(setcdr path-ref
|
|
|
|
|
(list (cons (format python-imenu-subtree-root-label
|
|
|
|
|
(car path-ref))
|
|
|
|
|
(cdr (assoc
|
|
|
|
|
(mapconcat #'identity
|
|
|
|
|
subelement-path ".")
|
|
|
|
|
plain-index))))))
|
|
|
|
|
(when (not (assoc subelement-name path-ref))
|
|
|
|
|
(push (cons subelement-name subelement-point) (cdr path-ref))))))
|
|
|
|
|
(python-imenu-make-element-tree (cdr element-list)
|
|
|
|
|
full-element plain-index))))
|
|
|
|
|
|
|
|
|
|
(defun python-imenu-make-tree (index)
|
2012-05-17 00:03:25 -03:00
|
|
|
|
"Build the imenu alist tree from plain INDEX.
|
2012-05-17 00:03:14 -03:00
|
|
|
|
|
|
|
|
|
The idea of this function is that given the alist:
|
|
|
|
|
|
|
|
|
|
'((\"Test\" . 100)
|
|
|
|
|
(\"Test.__init__\" . 200)
|
|
|
|
|
(\"Test.some_method\" . 300)
|
|
|
|
|
(\"Test.some_method.another\" . 400)
|
|
|
|
|
(\"Test.something_else\" . 500)
|
|
|
|
|
(\"test\" . 600)
|
|
|
|
|
(\"test.reprint\" . 700)
|
|
|
|
|
(\"test.reprint\" . 800))
|
|
|
|
|
|
|
|
|
|
This tree gets built:
|
|
|
|
|
|
|
|
|
|
'((\"Test\" . ((\"jump to...\" . 100)
|
|
|
|
|
(\"__init__\" . 200)
|
|
|
|
|
(\"some_method\" . ((\"jump to...\" . 300)
|
|
|
|
|
(\"another\" . 400)))
|
|
|
|
|
(\"something_else\" . 500)))
|
|
|
|
|
(\"test\" . ((\"jump to...\" . 600)
|
|
|
|
|
(\"reprint\" . 700)
|
|
|
|
|
(\"reprint\" . 800))))
|
|
|
|
|
|
|
|
|
|
Internally it uses `python-imenu-make-element-tree' to create all
|
|
|
|
|
branches for each element."
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(setq python-imenu-index-alist nil)
|
|
|
|
|
(mapc (lambda (element)
|
|
|
|
|
(python-imenu-make-element-tree element element index))
|
|
|
|
|
(mapcar (lambda (element)
|
|
|
|
|
(split-string (car element) "\\." t)) index))
|
|
|
|
|
python-imenu-index-alist)
|
2012-05-17 00:03:14 -03:00
|
|
|
|
|
|
|
|
|
(defun python-imenu-create-index ()
|
|
|
|
|
"`imenu-create-index-function' for Python."
|
2012-05-17 00:03:25 -03:00
|
|
|
|
(let ((index
|
|
|
|
|
(python-nav-list-defun-positions python-imenu-include-defun-type)))
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(if python-imenu-make-tree
|
|
|
|
|
(python-imenu-make-tree index)
|
|
|
|
|
index)))
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;; Misc helpers
|
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(defun python-info-current-defun (&optional include-type)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
"Return name of surrounding function with Python compatible dotty syntax.
|
2012-05-17 00:03:14 -03:00
|
|
|
|
Optional argument INCLUDE-TYPE indicates to include the type of the defun.
|
2012-05-17 00:02:52 -03:00
|
|
|
|
This function is compatible to be used as
|
|
|
|
|
`add-log-current-defun-function' since it returns nil if point is
|
|
|
|
|
not inside a defun."
|
2012-05-17 00:03:12 -03:00
|
|
|
|
(let ((names '())
|
2012-05-17 00:03:19 -03:00
|
|
|
|
(min-indent)
|
|
|
|
|
(first-run t))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(save-excursion
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(end-of-line 1)
|
2012-05-17 00:03:18 -03:00
|
|
|
|
(setq min-indent (current-indentation))
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(while (python-beginning-of-defun-function 1)
|
2012-05-17 00:03:19 -03:00
|
|
|
|
(when (or (< (current-indentation) min-indent)
|
|
|
|
|
first-run)
|
|
|
|
|
(setq first-run nil)
|
2012-05-17 00:03:12 -03:00
|
|
|
|
(setq min-indent (current-indentation))
|
2012-05-17 00:03:13 -03:00
|
|
|
|
(looking-at python-nav-beginning-of-defun-regexp)
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(setq names (cons
|
|
|
|
|
(if (not include-type)
|
|
|
|
|
(match-string-no-properties 1)
|
|
|
|
|
(mapconcat 'identity
|
|
|
|
|
(split-string
|
|
|
|
|
(match-string-no-properties 0)) " "))
|
|
|
|
|
names))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(when names
|
|
|
|
|
(mapconcat (lambda (string) string) names "."))))
|
|
|
|
|
|
|
|
|
|
(defun python-info-closing-block ()
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Return the point of the block the current line closes."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(let ((closing-word (save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(current-word)))
|
|
|
|
|
(indentation (current-indentation)))
|
|
|
|
|
(when (member closing-word python-indent-dedenters)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(while (and (> (current-indentation) indentation)
|
|
|
|
|
(not (bobp))
|
|
|
|
|
(not (back-to-indentation))
|
|
|
|
|
(forward-line -1)))
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(cond
|
|
|
|
|
((not (equal indentation (current-indentation))) nil)
|
|
|
|
|
((string= closing-word "elif")
|
|
|
|
|
(when (member (current-word) '("if" "elif"))
|
|
|
|
|
(point-marker)))
|
|
|
|
|
((string= closing-word "else")
|
|
|
|
|
(when (member (current-word) '("if" "elif" "except" "for" "while"))
|
|
|
|
|
(point-marker)))
|
|
|
|
|
((string= closing-word "except")
|
|
|
|
|
(when (member (current-word) '("try"))
|
|
|
|
|
(point-marker)))
|
|
|
|
|
((string= closing-word "finally")
|
|
|
|
|
(when (member (current-word) '("except" "else"))
|
|
|
|
|
(point-marker))))))))
|
|
|
|
|
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(defun python-info-closing-block-message (&optional closing-block-point)
|
|
|
|
|
"Message the contents of the block the current line closes.
|
|
|
|
|
With optional argument CLOSING-BLOCK-POINT use that instead of
|
|
|
|
|
recalculating it calling `python-info-closing-block'."
|
|
|
|
|
(let ((point (or closing-block-point (python-info-closing-block))))
|
|
|
|
|
(when point
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(message "Closes %s" (save-excursion
|
|
|
|
|
(goto-char point)
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(buffer-substring
|
|
|
|
|
(point) (line-end-position))))))))
|
|
|
|
|
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(defun python-info-line-ends-backslash-p (&optional line-number)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
"Return non-nil if current line ends with backslash.
|
2012-05-17 00:03:34 -03:00
|
|
|
|
With optional argument LINE-NUMBER, check that line instead."
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(save-restriction
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(widen)
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(when line-number
|
|
|
|
|
(goto-char line-number))
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(while (and (not (eobp))
|
|
|
|
|
(goto-char (line-end-position))
|
|
|
|
|
(python-info-ppss-context 'paren)
|
|
|
|
|
(not (equal (char-before (point)) ?\\)))
|
|
|
|
|
(forward-line 1))
|
|
|
|
|
(when (equal (char-before) ?\\)
|
|
|
|
|
(point-marker)))))
|
|
|
|
|
|
2012-06-17 01:53:31 -07:00
|
|
|
|
(defun python-info-beginning-of-backslash (&optional line-number)
|
|
|
|
|
"Return the point where the backslashed line start.
|
2012-05-17 00:03:44 -03:00
|
|
|
|
Optional argument LINE-NUMBER forces the line number to check against."
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(save-restriction
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(widen)
|
2012-05-17 00:03:36 -03:00
|
|
|
|
(when line-number
|
|
|
|
|
(goto-char line-number))
|
|
|
|
|
(when (python-info-line-ends-backslash-p)
|
|
|
|
|
(while (save-excursion
|
|
|
|
|
(goto-char (line-beginning-position))
|
|
|
|
|
(python-info-ppss-context 'paren))
|
|
|
|
|
(forward-line -1))
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(point-marker)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-info-continuation-line-p ()
|
2012-05-17 00:03:34 -03:00
|
|
|
|
"Check if current line is continuation of another.
|
|
|
|
|
When current line is continuation of another return the point
|
|
|
|
|
where the continued line ends."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(let* ((context-type (progn
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(python-info-ppss-context-type)))
|
|
|
|
|
(line-start (line-number-at-pos))
|
|
|
|
|
(context-start (when context-type
|
|
|
|
|
(python-info-ppss-context context-type))))
|
|
|
|
|
(cond ((equal context-type 'paren)
|
|
|
|
|
;; Lines inside a paren are always a continuation line
|
|
|
|
|
;; (except the first one).
|
|
|
|
|
(when (equal (python-info-ppss-context-type) 'paren)
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(point-marker)))
|
|
|
|
|
((or (equal context-type 'comment)
|
|
|
|
|
(equal context-type 'string))
|
|
|
|
|
;; move forward an roll again
|
|
|
|
|
(goto-char context-start)
|
|
|
|
|
(python-util-forward-comment)
|
|
|
|
|
(python-info-continuation-line-p))
|
|
|
|
|
(t
|
|
|
|
|
;; Not within a paren, string or comment, the only way we are
|
|
|
|
|
;; dealing with a continuation line is that previous line
|
|
|
|
|
;; contains a backslash, and this can only be the previous line
|
|
|
|
|
;; from current
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(python-util-forward-comment -1)
|
|
|
|
|
(when (and (equal (1- line-start) (line-number-at-pos))
|
|
|
|
|
(python-info-line-ends-backslash-p))
|
|
|
|
|
(point-marker))))))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-info-block-continuation-line-p ()
|
|
|
|
|
"Return non-nil if current line is a continuation of a block."
|
|
|
|
|
(save-excursion
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(when (python-info-continuation-line-p)
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(when (looking-at (python-rx block-start))
|
|
|
|
|
(point-marker)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(defun python-info-assignment-continuation-line-p ()
|
2012-05-17 00:03:34 -03:00
|
|
|
|
"Check if current line is a continuation of an assignment.
|
|
|
|
|
When current line is continuation of another with an assignment
|
|
|
|
|
return the point of the first non-blank character after the
|
|
|
|
|
operator."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(save-excursion
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(when (python-info-continuation-line-p)
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(when (and (not (looking-at (python-rx block-start)))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(and (re-search-forward (python-rx not-simple-operator
|
|
|
|
|
assignment-operator
|
|
|
|
|
not-simple-operator)
|
|
|
|
|
(line-end-position) t)
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(not (or (python-info-ppss-context 'string)
|
2012-05-17 00:03:08 -03:00
|
|
|
|
(python-info-ppss-context 'paren)
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(python-info-ppss-context 'comment)))))
|
|
|
|
|
(skip-syntax-forward "\s")
|
|
|
|
|
(point-marker)))))
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:07 -03:00
|
|
|
|
(defun python-info-ppss-context (type &optional syntax-ppss)
|
|
|
|
|
"Return non-nil if point is on TYPE using SYNTAX-PPSS.
|
2012-05-17 00:03:22 -03:00
|
|
|
|
TYPE can be 'comment, 'string or 'paren. It returns the start
|
2012-05-17 00:03:07 -03:00
|
|
|
|
character address of the specified TYPE."
|
|
|
|
|
(let ((ppss (or syntax-ppss (syntax-ppss))))
|
|
|
|
|
(case type
|
|
|
|
|
('comment
|
|
|
|
|
(and (nth 4 ppss)
|
|
|
|
|
(nth 8 ppss)))
|
|
|
|
|
('string
|
|
|
|
|
(nth 8 ppss))
|
|
|
|
|
('paren
|
|
|
|
|
(nth 1 ppss))
|
|
|
|
|
(t nil))))
|
|
|
|
|
|
2012-05-17 00:03:22 -03:00
|
|
|
|
(defun python-info-ppss-context-type (&optional syntax-ppss)
|
|
|
|
|
"Return the context type using SYNTAX-PPSS.
|
|
|
|
|
The type returned can be 'comment, 'string or 'paren."
|
|
|
|
|
(let ((ppss (or syntax-ppss (syntax-ppss))))
|
|
|
|
|
(cond
|
|
|
|
|
((and (nth 4 ppss)
|
|
|
|
|
(nth 8 ppss))
|
|
|
|
|
'comment)
|
|
|
|
|
((nth 8 ppss)
|
|
|
|
|
'string)
|
|
|
|
|
((nth 1 ppss)
|
|
|
|
|
'paren)
|
|
|
|
|
(t nil))))
|
|
|
|
|
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss)
|
2012-05-17 00:03:44 -03:00
|
|
|
|
"Check if point is at `beginning-of-defun' using SYNTAX-PPSS."
|
|
|
|
|
(and (not (python-info-ppss-context-type (or syntax-ppss (syntax-ppss))))
|
2012-05-17 00:03:44 -03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(beginning-of-line 1)
|
|
|
|
|
(looking-at python-nav-beginning-of-defun-regexp))))
|
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(defun python-info-current-line-comment-p ()
|
|
|
|
|
"Check if current line is a comment line."
|
|
|
|
|
(char-equal (or (char-after (+ (point) (current-indentation))) ?_) ?#))
|
|
|
|
|
|
|
|
|
|
(defun python-info-current-line-empty-p ()
|
|
|
|
|
"Check if current line is empty, ignoring whitespace."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(beginning-of-line 1)
|
|
|
|
|
(looking-at
|
|
|
|
|
(python-rx line-start (* whitespace)
|
|
|
|
|
(group (* not-newline))
|
|
|
|
|
(* whitespace) line-end))
|
|
|
|
|
(string-equal "" (match-string-no-properties 1))))
|
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
|
|
|
|
|
;;; Utility functions
|
|
|
|
|
|
|
|
|
|
(defun python-util-position (item seq)
|
|
|
|
|
"Find the first occurrence of ITEM in SEQ.
|
|
|
|
|
Return the index of the matching item, or nil if not found."
|
|
|
|
|
(let ((member-result (member item seq)))
|
|
|
|
|
(when member-result
|
|
|
|
|
(- (length seq) (length member-result)))))
|
|
|
|
|
|
2012-05-17 00:03:24 -03:00
|
|
|
|
;; Stolen from org-mode
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(defun python-util-clone-local-variables (from-buffer &optional regexp)
|
2012-05-17 00:03:24 -03:00
|
|
|
|
"Clone local variables from FROM-BUFFER.
|
|
|
|
|
Optional argument REGEXP selects variables to clone and defaults
|
|
|
|
|
to \"^python-\"."
|
|
|
|
|
(mapc
|
|
|
|
|
(lambda (pair)
|
|
|
|
|
(and (symbolp (car pair))
|
|
|
|
|
(string-match (or regexp "^python-")
|
|
|
|
|
(symbol-name (car pair)))
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(set (make-local-variable (car pair))
|
|
|
|
|
(cdr pair))))
|
2012-05-17 00:03:24 -03:00
|
|
|
|
(buffer-local-variables from-buffer)))
|
|
|
|
|
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(defun python-util-forward-comment (&optional direction)
|
2012-05-17 00:03:44 -03:00
|
|
|
|
"Python mode specific version of `forward-comment'.
|
|
|
|
|
Optional argument DIRECTION defines the direction to move to."
|
2012-05-17 00:03:34 -03:00
|
|
|
|
(let ((comment-start (python-info-ppss-context 'comment))
|
|
|
|
|
(factor (if (< (or direction 0) 0)
|
|
|
|
|
-99999
|
|
|
|
|
99999)))
|
|
|
|
|
(when comment-start
|
|
|
|
|
(goto-char comment-start))
|
|
|
|
|
(forward-comment factor)))
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
;;;###autoload
|
2012-06-19 09:04:47 -07:00
|
|
|
|
(define-derived-mode python-mode prog-mode "Python"
|
2012-05-17 00:03:09 -03:00
|
|
|
|
"Major mode for editing Python files.
|
|
|
|
|
|
|
|
|
|
\\{python-mode-map}
|
|
|
|
|
Entry to this mode calls the value of `python-mode-hook'
|
|
|
|
|
if that value is non-nil."
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'tab-width) 8)
|
|
|
|
|
(set (make-local-variable 'indent-tabs-mode) nil)
|
|
|
|
|
|
|
|
|
|
(set (make-local-variable 'comment-start) "# ")
|
|
|
|
|
(set (make-local-variable 'comment-start-skip) "#+\\s-*")
|
|
|
|
|
|
|
|
|
|
(set (make-local-variable 'parse-sexp-lookup-properties) t)
|
|
|
|
|
(set (make-local-variable 'parse-sexp-ignore-comments) t)
|
|
|
|
|
|
2012-07-16 10:13:01 -03:00
|
|
|
|
(set (make-local-variable 'forward-sexp-function)
|
|
|
|
|
'python-nav-forward-sexp-function)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'font-lock-defaults)
|
2012-05-17 00:03:46 -03:00
|
|
|
|
'(python-font-lock-keywords nil nil nil nil))
|
|
|
|
|
|
|
|
|
|
(set (make-local-variable 'syntax-propertize-function)
|
|
|
|
|
python-syntax-propertize-function)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(set (make-local-variable 'indent-line-function)
|
|
|
|
|
#'python-indent-line-function)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'indent-region-function) #'python-indent-region)
|
|
|
|
|
|
|
|
|
|
(set (make-local-variable 'paragraph-start) "\\s-*$")
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(set (make-local-variable 'fill-paragraph-function)
|
|
|
|
|
'python-fill-paragraph-function)
|
2012-05-17 00:02:52 -03:00
|
|
|
|
|
|
|
|
|
(set (make-local-variable 'beginning-of-defun-function)
|
|
|
|
|
#'python-beginning-of-defun-function)
|
|
|
|
|
(set (make-local-variable 'end-of-defun-function)
|
|
|
|
|
#'python-end-of-defun-function)
|
|
|
|
|
|
|
|
|
|
(add-hook 'completion-at-point-functions
|
|
|
|
|
'python-completion-complete-at-point nil 'local)
|
|
|
|
|
|
2012-05-17 00:03:42 -03:00
|
|
|
|
(add-hook 'post-self-insert-hook
|
|
|
|
|
'python-indent-post-self-insert-function nil 'local)
|
|
|
|
|
|
2012-05-17 00:03:14 -03:00
|
|
|
|
(setq imenu-create-index-function #'python-imenu-create-index)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'add-log-current-defun-function)
|
|
|
|
|
#'python-info-current-defun)
|
|
|
|
|
|
2012-05-17 00:03:45 -03:00
|
|
|
|
(add-hook 'which-func-functions #'python-info-current-defun nil t)
|
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(set (make-local-variable 'skeleton-further-elements)
|
|
|
|
|
'((abbrev-mode nil)
|
|
|
|
|
(< '(backward-delete-char-untabify (min python-indent-offset
|
2012-05-17 00:03:35 -03:00
|
|
|
|
(current-column))))
|
|
|
|
|
(^ '(- (1+ (current-indentation))))))
|
2012-05-17 00:03:05 -03:00
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'eldoc-documentation-function)
|
|
|
|
|
#'python-eldoc-function)
|
|
|
|
|
|
|
|
|
|
(add-to-list 'hs-special-modes-alist
|
2012-05-17 00:03:35 -03:00
|
|
|
|
`(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#"
|
2012-05-17 00:02:52 -03:00
|
|
|
|
,(lambda (arg)
|
|
|
|
|
(python-end-of-defun-function)) nil))
|
|
|
|
|
|
2012-05-17 00:03:19 -03:00
|
|
|
|
(set (make-local-variable 'mode-require-final-newline) t)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(set (make-local-variable 'outline-regexp)
|
|
|
|
|
(python-rx (* space) block-start))
|
|
|
|
|
(set (make-local-variable 'outline-heading-end-regexp) ":\\s-*\n")
|
|
|
|
|
(set (make-local-variable 'outline-level)
|
|
|
|
|
#'(lambda ()
|
|
|
|
|
"`outline-level' function for Python mode."
|
|
|
|
|
(1+ (/ (current-indentation) python-indent-offset))))
|
|
|
|
|
|
2012-05-17 00:03:05 -03:00
|
|
|
|
(python-skeleton-add-menu-items)
|
|
|
|
|
|
2012-05-17 00:02:52 -03:00
|
|
|
|
(when python-indent-guess-indent-offset
|
|
|
|
|
(python-indent-guess-indent-offset)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'python)
|
|
|
|
|
;;; python.el ends here
|