emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] Changes to emacs/lisp/progmodes/python.el [emacs-unicode-2


From: Miles Bader
Subject: [Emacs-diffs] Changes to emacs/lisp/progmodes/python.el [emacs-unicode-2]
Date: Mon, 28 Jun 2004 04:36:14 -0400

Index: emacs/lisp/progmodes/python.el
diff -c emacs/lisp/progmodes/python.el:1.6.2.1 
emacs/lisp/progmodes/python.el:1.6.2.2
*** emacs/lisp/progmodes/python.el:1.6.2.1      Fri Apr 16 12:50:36 2004
--- emacs/lisp/progmodes/python.el      Mon Jun 28 07:29:44 2004
***************
*** 3,8 ****
--- 3,9 ----
  ;; Copyright (C) 2003, 04  Free Software Foundation, Inc.
  
  ;; Author: Dave Love <address@hidden>
+ ;; Maintainer: FSF
  ;; Created: Nov 2003
  ;; Keywords: languages
  
***************
*** 45,63 ****
  ;; I've installed a minor mode to do the job properly in Emacs 22.
  ;; Other things seem more natural or canonical here, e.g. the
  ;; {beginning,end}-of-defun implementation dealing with nested
! ;; definitions, and the inferior mode following `cmuscheme'.  (The
! ;; inferior mode should be able to find the source of errors from
! ;; `python-send-region' & al via `compilation-minor-mode', but I can't
! ;; make that work with the current (March '04) compile.el.)
! ;; Successive TABs cycle between possible indentations for the line.
  
  ;; Even where it has similar facilities, this is incompatible with
  ;; python-mode.el in various respects.  For instance, various key
  ;; bindings are changed to obey Emacs conventions, and things like
  ;; marking blocks and `beginning-of-defun' behave differently.
  
! ;; TODO: See various Fixmes below.  It should be possible to arrange
! ;; some sort of completion using the inferior interpreter.
  
  ;;; Code:
  
--- 46,63 ----
  ;; I've installed a minor mode to do the job properly in Emacs 22.
  ;; Other things seem more natural or canonical here, e.g. the
  ;; {beginning,end}-of-defun implementation dealing with nested
! ;; definitions, and the inferior mode following `cmuscheme'.  The
! ;; inferior mode can find the source of errors from
! ;; `python-send-region' & al via `compilation-minor-mode'.  Successive
! ;; TABs cycle between possible indentations for the line.  There is
! ;; symbol completion using lookup in Python.
  
  ;; Even where it has similar facilities, this is incompatible with
  ;; python-mode.el in various respects.  For instance, various key
  ;; bindings are changed to obey Emacs conventions, and things like
  ;; marking blocks and `beginning-of-defun' behave differently.
  
! ;; TODO: See various Fixmes below.
  
  ;;; Code:
  
***************
*** 66,75 ****
  (require 'comint)
  (eval-when-compile
    (require 'compile)
-   (autoload 'Info-last "info")
-   (autoload 'Info-exit "info")
    (autoload 'info-lookup-maybe-add-help "info-look"))
! (autoload 'compilation-start "compile")       ; spurious compiler warning 
anyway
  
  (defgroup python nil
    "Silly walks in the Python language"
--- 66,73 ----
  (require 'comint)
  (eval-when-compile
    (require 'compile)
    (autoload 'info-lookup-maybe-add-help "info-look"))
! (autoload 'compilation-start "compile")
  
  (defgroup python nil
    "Silly walks in the Python language"
***************
*** 204,209 ****
--- 202,209 ----
      (define-key map "\C-c\C-z" 'python-switch-to-python)
      (define-key map "\C-c\C-m" 'python-load-file)
      (define-key map "\C-c\C-l" 'python-load-file) ; a la cmuscheme
+     (substitute-key-definition 'complete-symbol 'python-complete-symbol
+                              map global-map)
      ;; Fixme: Add :help to menu.
      (easy-menu-define python-menu map "Python Mode menu"
        '("Python"
***************
*** 262,270 ****
  ;;;; Utility stuff
  
  (defsubst python-in-string/comment ()
!   "Return non-nil if point is in a Python literal (a comment or string).
! Optional argument LIM indicates the beginning of the containing form,
! i.e. the limit on how far back to scan."
    (syntax-ppss-context (syntax-ppss)))
  
  (defconst python-space-backslash-table
--- 262,268 ----
  ;;;; Utility stuff
  
  (defsubst python-in-string/comment ()
!   "Return non-nil if point is in a Python literal (a comment or string)."
    (syntax-ppss-context (syntax-ppss)))
  
  (defconst python-space-backslash-table
***************
*** 299,314 ****
               (syntax-ppss (line-beginning-position)))))))
  
  (defun python-comment-line-p ()
!   "Return non-nil if current line has only a comment or is blank."
    (save-excursion
!     (back-to-indentation)
!     (looking-at (rx (or (syntax comment-start) line-end)))))
  
  (defun python-beginning-of-string ()
    "Go to beginning of string around point.
  Do nothing if not in string."
    (let ((state (syntax-ppss)))
!     (when (nth 3 state)
        (goto-char (nth 8 state)))))
  
  (defun python-open-block-statement-p (&optional bos)
--- 297,314 ----
               (syntax-ppss (line-beginning-position)))))))
  
  (defun python-comment-line-p ()
!   "Return non-nil iff current line has only a comment."
    (save-excursion
!     (end-of-line)
!     (when (eq 'comment (syntax-ppss-context (syntax-ppss)))
!       (back-to-indentation)
!       (looking-at (rx (or (syntax comment-start) line-end))))))
  
  (defun python-beginning-of-string ()
    "Go to beginning of string around point.
  Do nothing if not in string."
    (let ((state (syntax-ppss)))
!     (when (eq 'string (syntax-ppss-context state))
        (goto-char (nth 8 state)))))
  
  (defun python-open-block-statement-p (&optional bos)
***************
*** 323,329 ****
                                     line-end))
                            (save-excursion (python-end-of-statement))
                            t)
!        (not (python-in-string/comment)))))
  
  (defun python-close-block-statement-p (&optional bos)
    "Return non-nil if current line is a statement closing a block.
--- 323,330 ----
                                     line-end))
                            (save-excursion (python-end-of-statement))
                            t)
!        (not (progn (goto-char (match-beginning 0))
!                    (python-in-string/comment))))))
  
  (defun python-close-block-statement-p (&optional bos)
    "Return non-nil if current line is a statement closing a block.
***************
*** 384,390 ****
  (defcustom python-honour-comment-indentation nil
    "Non-nil means indent relative to preceding comment line.
  Only do this for comments where the leading comment character is followed
! by space."
    :type 'boolean
    :group 'python)
  
--- 385,392 ----
  (defcustom python-honour-comment-indentation nil
    "Non-nil means indent relative to preceding comment line.
  Only do this for comments where the leading comment character is followed
! by space.  This doesn't apply to comment lines, which are always indented
! in lines with preceding comments."
    :type 'boolean
    :group 'python)
  
***************
*** 514,519 ****
--- 516,531 ----
                                     (- python-indent)))
                          0)))))))))
  
+ (defun python-comment-indent ()
+   "`comment-indent-function' for Python."
+   ;; If previous non-blank line was a comment, use its indentation.
+   ;; FIXME: This seems unnecessary since the default code delegates to
+   ;; indent-according-to-mode.  --Stef
+   (unless (bobp)
+     (save-excursion
+       (forward-comment -1)
+       (if (eq ?# (char-after)) (current-column)))))
+ 
  ;;;; Cycling through the possible indentations with successive TABs.
  
  ;; These don't need to be buffer-local since they're only relevant
***************
*** 538,548 ****
                      (point))))
  
  (defun python-indentation-levels ()
!   "Return a list of possible indentations for this statement.
  Includes the default indentation and those which would close all
! enclosing blocks."
    (save-excursion
!     (let ((levels (list (cons (current-indentation) nil))))
        ;; Only one possibility if we immediately follow a block open or
        ;; are in a continuation line.
        (unless (or (python-continuation-line-p)
--- 550,566 ----
                      (point))))
  
  (defun python-indentation-levels ()
!   "Return a list of possible indentations for this line.
  Includes the default indentation and those which would close all
! enclosing blocks.  Assumes the line has already been indented per
! `python-indent-line'.  Elements of the list are actually pairs:
! \(INDENTATION . TEXT), where TEXT is the initial text of the
! corresponding block opening (or nil)."
    (save-excursion
!     (let ((levels (list (cons (current-indentation)
!                             (save-excursion
!                               (if (python-beginning-of-block)
!                                   (python-initial-text)))))))
        ;; Only one possibility if we immediately follow a block open or
        ;; are in a continuation line.
        (unless (or (python-continuation-line-p)
***************
*** 568,575 ****
        (if (> (- (point-max) pos) (point))
          (goto-char (- (point-max) pos))))))
  
! ;; Fixme: Is the arg necessary?
! (defun python-indent-line (&optional arg)
    "Indent current line as Python code.
  When invoked via `indent-for-tab-command', cycle through possible
  indentations for current line.  The cycle is broken by a command different
--- 586,592 ----
        (if (> (- (point-max) pos) (point))
          (goto-char (- (point-max) pos))))))
  
! (defun python-indent-line ()
    "Indent current line as Python code.
  When invoked via `indent-for-tab-command', cycle through possible
  indentations for current line.  The cycle is broken by a command different
***************
*** 586,598 ****
                 (beginning-of-line)
                 (delete-horizontal-space)
                 (indent-to (car (nth python-indent-index python-indent-list)))
!                (let ((text (cdr (nth python-indent-index
!                                      python-indent-list))))
!                  (if text (message "Closes: %s" text)))))
        (python-indent-line-1)
        (setq python-indent-list (python-indentation-levels)
            python-indent-list-length (length python-indent-list)
            python-indent-index (1- python-indent-list-length)))))
  
  ;;;; Movement.
  
--- 603,632 ----
                 (beginning-of-line)
                 (delete-horizontal-space)
                 (indent-to (car (nth python-indent-index python-indent-list)))
!                (if (python-block-end-p)
!                    (let ((text (cdr (nth python-indent-index
!                                          python-indent-list))))
!                      (if text
!                          (message "Closes: %s" text))))))
        (python-indent-line-1)
        (setq python-indent-list (python-indentation-levels)
            python-indent-list-length (length python-indent-list)
            python-indent-index (1- python-indent-list-length)))))
+ 
+ (defun python-block-end-p ()
+   "Non-nil if this is a line in a statement closing a block,
+ or a blank line indented to where it would close a block."
+   (and (not (python-comment-line-p))
+        (or (python-close-block-statement-p t)
+          (< (current-indentation)
+             (save-excursion
+               (python-previous-statement)
+               (current-indentation))))))
+ 
+ ;; Fixme: Define an indent-region-function.  It should probably leave
+ ;; lines alone if the indentation is already at one of the allowed
+ ;; levels.  Otherwise, M-C-\ typically keeps indenting more deeply
+ ;; down a function.
  
  ;;;; Movement.
  
***************
*** 629,636 ****
    "`end-of-defun-function' for Python.
  Finds end of innermost nested class or method definition."
    (let ((orig (point))
!       (pattern (rx (and line-start (0+ space)
!                         (or "def" "class") space))))
      ;; Go to start of current block and check whether it's at top
      ;; level.  If it is, and not a block start, look forward for
      ;; definition statement.
--- 663,669 ----
    "`end-of-defun-function' for Python.
  Finds end of innermost nested class or method definition."
    (let ((orig (point))
!       (pattern (rx (and line-start (0+ space) (or "def" "class") space))))
      ;; Go to start of current block and check whether it's at top
      ;; level.  If it is, and not a block start, look forward for
      ;; definition statement.
***************
*** 829,835 ****
  Makes nested Imenu menus from nested `class' and `def' statements.
  The nested menus are headed by an item referencing the outer
  definition; it has a space prepended to the name so that it sorts
! first with `imenu--sort-by-name'."
    (unless (boundp 'python-recursing)          ; dynamically bound below
      (goto-char (point-min)))          ; normal call from Imenu
    (let (index-alist                   ; accumulated value to return
--- 862,869 ----
  Makes nested Imenu menus from nested `class' and `def' statements.
  The nested menus are headed by an item referencing the outer
  definition; it has a space prepended to the name so that it sorts
! first with `imenu--sort-by-name' (though, unfortunately, sub-menus
! precede it)."
    (unless (boundp 'python-recursing)          ; dynamically bound below
      (goto-char (point-min)))          ; normal call from Imenu
    (let (index-alist                   ; accumulated value to return
***************
*** 914,926 ****
                                        (file-name-nondirectory name))))))))
    (setq python-saved-check-command command)
    (save-some-buffers (not compilation-ask-about-save) nil)
!   (compilation-start command))
  
  ;;;; Inferior mode stuff (following cmuscheme).
  
  (defcustom python-python-command "python"
    "*Shell command to run Python interpreter.
! Any arguments can't contain whitespace."
    :group 'python
    :type 'string)
  
--- 948,967 ----
                                        (file-name-nondirectory name))))))))
    (setq python-saved-check-command command)
    (save-some-buffers (not compilation-ask-about-save) nil)
!   (let ((compilation-error-regexp-alist
!        (cons '("(\\([^,]+\\), line \\([0-9]+\\))" 1 2)
!              compilation-error-regexp-alist)))
!     (compilation-start command)))
  
  ;;;; Inferior mode stuff (following cmuscheme).
  
+ ;; Fixme: Make sure we can work with IPython.
+ 
  (defcustom python-python-command "python"
    "*Shell command to run Python interpreter.
! Any arguments can't contain whitespace.
! Note that IPython may not work properly; it must at least be used with the
! `-cl' flag, i.e. use `ipython -cl'."
    :group 'python
    :type 'string)
  
***************
*** 937,976 ****
  et al.")
  
  (defvar python-buffer nil
!   "*The current python process buffer.
! To run multiple Python processes, start the first with \\[run-python].
! It will be in a buffer named *Python*.  Rename that with
! \\[rename-buffer].  Now start a new process with \\[run-python].  It
! will be in a new buffer, named *Python*.  Switch between the different
! process buffers with \\[switch-to-buffer].
! 
! Commands that send text from source buffers to Python processes have
! to choose a process to send to.  This is determined by global variable
! `python-buffer'.  Suppose you have three inferior Pythons running:
!     Buffer    Process
!     foo               python
!     bar               python<2>
!     *Python*    python<3>
! If you do a \\[python-send-region-and-go] command on some Python source
! code, what process does it go to?
! 
! - In a process buffer (foo, bar, or *Python*), send it to that process.
! - In some other buffer (e.g. a source file), send it to the process
!   attached to `python-buffer'.
! Process selection is done by function `python-proc'.
! 
! Whenever \\[run-python] starts a new process, it resets `python-buffer'
! to be the new process's buffer.  If you only run one process, this will
! do the right thing.  If you run multiple processes, you can change
! `python-buffer' to another process buffer with \\[set-variable].")
  
  (defconst python-compilation-regexp-alist
    `((,(rx (and line-start (1+ (any " \t")) "File \""
               (group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c
               "\", line " (group (1+ digit))))
!      1 python-compilation-line-number))
    "`compilation-error-regexp-alist' for inferior Python.")
  
  (define-derived-mode inferior-python-mode comint-mode "Inferior Python"
    "Major mode for interacting with an inferior Python process.
  A Python process can be started with \\[run-python].
--- 978,1043 ----
  et al.")
  
  (defvar python-buffer nil
!   "The current python process buffer."
!   ;; Fixme: a single process is currently assumed, so that this doc
!   ;; is misleading.
! 
! ;;   "*The current python process buffer.
! ;; To run multiple Python processes, start the first with \\[run-python].
! ;; It will be in a buffer named *Python*.  Rename that with
! ;; \\[rename-buffer].  Now start a new process with \\[run-python].  It
! ;; will be in a new buffer, named *Python*.  Switch between the different
! ;; process buffers with \\[switch-to-buffer].
! 
! ;; Commands that send text from source buffers to Python processes have
! ;; to choose a process to send to.  This is determined by global variable
! ;; `python-buffer'.  Suppose you have three inferior Pythons running:
! ;;     Buffer Process
! ;;     foo            python
! ;;     bar            python<2>
! ;;     *Python*    python<3>
! ;; If you do a \\[python-send-region-and-go] command on some Python source
! ;; code, what process does it go to?
! 
! ;; - In a process buffer (foo, bar, or *Python*), send it to that process.
! ;; - In some other buffer (e.g. a source file), send it to the process
! ;;   attached to `python-buffer'.
! ;; Process selection is done by function `python-proc'.
! 
! ;; Whenever \\[run-python] starts a new process, it resets `python-buffer'
! ;; to be the new process's buffer.  If you only run one process, this will
! ;; do the right thing.  If you run multiple processes, you can change
! ;; `python-buffer' to another process buffer with \\[set-variable]."
!   )
  
  (defconst python-compilation-regexp-alist
+   ;; FIXME: maybe these should move to compilation-error-regexp-alist-alist.
    `((,(rx (and line-start (1+ (any " \t")) "File \""
               (group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c
               "\", line " (group (1+ digit))))
!      1 2)
!     (,(rx (and " in file " (group (1+ not-newline)) " on line "
!              (group (1+ digit))))
!      1 2))
    "`compilation-error-regexp-alist' for inferior Python.")
  
+ (defvar inferior-python-mode-map
+   (let ((map (make-sparse-keymap)))
+     ;; This will inherit from comint-mode-map.
+     (define-key map "\C-c\C-l" 'python-load-file)
+     (define-key map "\C-c\C-v" 'python-check)
+     ;; Note that we _can_ still use these commands which send to the
+     ;; Python process even at the prompt iff we have a normal prompt,
+     ;; i.e. '>>> ' and not '... '.  See the comment before
+     ;; python-send-region.  Fixme: uncomment these if we address that.
+ 
+     ;; (define-key map [(meta ?\t)] 'python-complete-symbol)
+     ;; (define-key map "\C-c\C-f" 'python-describe-symbol)
+     map))
+ 
+ ;; Fixme: This should inherit some stuff from python-mode, but I'm not
+ ;; sure how much: at least some keybindings, like C-c C-f; syntax?;
+ ;; font-locking, e.g. for triple-quoted strings?
  (define-derived-mode inferior-python-mode comint-mode "Inferior Python"
    "Major mode for interacting with an inferior Python process.
  A Python process can be started with \\[run-python].
***************
*** 991,1004 ****
    :group 'python
    (set-syntax-table python-mode-syntax-table)
    (setq mode-line-process '(":%s"))
!   ;; Fixme: Maybe install some python-mode bindings too.
!   (define-key inferior-python-mode-map "\C-c\C-l" 'python-load-file)
!   (define-key inferior-python-mode-map "\C-c\C-z" 'python-switch-to-python)
!   (add-hook 'comint-input-filter-functions 'python-input-filter nil t)
    (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter
            nil t)
!   ;; Still required by `comint-redirect-send-command', for instance:
!   (set (make-local-variable 'comint-prompt-regexp) "^\\([>.]\\{3\\} \\)+")
    (set (make-local-variable 'compilation-error-regexp-alist)
         python-compilation-regexp-alist)
    (compilation-shell-minor-mode 1))
--- 1058,1070 ----
    :group 'python
    (set-syntax-table python-mode-syntax-table)
    (setq mode-line-process '(":%s"))
!   (set (make-local-variable 'comint-input-filter) 'python-input-filter)
    (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter
            nil t)
!   ;; Still required by `comint-redirect-send-command', for instance
!   ;; (and we need to match things like `>>> ... >>> '):
!   (set (make-local-variable 'comint-prompt-regexp)
!        (rx (and line-start (1+ (and (repeat 3 (any ">.")) ?\ )))))
    (set (make-local-variable 'compilation-error-regexp-alist)
         python-compilation-regexp-alist)
    (compilation-shell-minor-mode 1))
***************
*** 1009,1023 ****
    :type 'regexp
    :group 'python)
  
- (defvar python-orig-start nil
-   "Marker to the start of the region passed to the inferior Python.
- It can also be a filename.")
- 
  (defun python-input-filter (str)
    "`comint-input-filter' function for inferior Python.
! Don't save anything for STR matching `inferior-python-filter-regexp'.
! Also resets variables for adjusting error messages."
!   (setq python-orig-start nil)
    (not (string-match inferior-python-filter-regexp str)))
  
  ;; Fixme: Loses with quoted whitespace.
--- 1075,1083 ----
    :type 'regexp
    :group 'python)
  
  (defun python-input-filter (str)
    "`comint-input-filter' function for inferior Python.
! Don't save anything for STR matching `inferior-python-filter-regexp'."
    (not (string-match inferior-python-filter-regexp str)))
  
  ;; Fixme: Loses with quoted whitespace.
***************
*** 1030,1050 ****
          (t (let ((pos (string-match "[^ \t]" string)))
               (if pos (python-args-to-list (substring string pos))))))))
  
- (defun python-compilation-line-number (file col)
-   "Return error descriptor of error found for FILE, column COL.
- Used as line-number hook function in `python-compilation-regexp-alist'."
-   (let ((line (string-to-number (match-string 2))))
-     (cons (point-marker)
-         (if (and (markerp python-orig-start)
-                  (marker-buffer python-orig-start))
-             (with-current-buffer (marker-buffer python-orig-start)
-               (goto-char python-orig-start)
-               (forward-line (1- line)))
-           (list (if (stringp python-orig-start) python-orig-start file)
-                 line nil)))))
- 
  (defvar python-preoutput-result nil
!   "Data from output line last `_emacs_out' line seen by the preoutput 
filter.")
  
  (defvar python-preoutput-continuation nil
    "If non-nil, funcall this when `python-preoutput-filter' sees `_emacs_ok'.")
--- 1090,1097 ----
          (t (let ((pos (string-match "[^ \t]" string)))
               (if pos (python-args-to-list (substring string pos))))))))
  
  (defvar python-preoutput-result nil
!   "Data from last `_emacs_out' line seen by the preoutput filter.")
  
  (defvar python-preoutput-continuation nil
    "If non-nil, funcall this when `python-preoutput-filter' sees `_emacs_ok'.")
***************
*** 1055,1061 ****
  ;; `python-preoutput-continuation' if we get it.
  (defun python-preoutput-filter (s)
    "`comint-preoutput-filter-functions' function: ignore prompts not at bol."
!   (cond ((and (string-match "\\`[.>]\\{3\\} \\'" s)
              (/= (let ((inhibit-field-text-motion t))
                    (line-beginning-position))
                  (point)))
--- 1102,1110 ----
  ;; `python-preoutput-continuation' if we get it.
  (defun python-preoutput-filter (s)
    "`comint-preoutput-filter-functions' function: ignore prompts not at bol."
!   (cond ((and (string-match (rx (and string-start (repeat 3 (any ".>"))
!                                    " " string-end))
!                           s)
              (/= (let ((inhibit-field-text-motion t))
                    (line-beginning-position))
                  (point)))
***************
*** 1076,1085 ****
  CMD is the Python command to run.  NOSHOW non-nil means don't show the
  buffer automatically.
  If there is a process already running in `*Python*', switch to
! that buffer.  Interactively a prefix arg, allows you to edit the initial
! command line (default is the value of `python-command'); `-i' etc. args
! will be added to this as appropriate.  Runs the hooks
! `inferior-python-mode-hook' (after the `comint-mode-hook' is run).
  \(Type \\[describe-mode] in the process buffer for a list of commands.)"
    (interactive (list (if current-prefix-arg
                         (read-string "Run Python: " python-command)
--- 1125,1134 ----
  CMD is the Python command to run.  NOSHOW non-nil means don't show the
  buffer automatically.
  If there is a process already running in `*Python*', switch to
! that buffer.  Interactively, a prefix arg allows you to edit the initial
! command line (default is `python-command'); `-i' etc. args will be added
! to this as appropriate.  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 (list (if current-prefix-arg
                         (read-string "Run Python: " python-command)
***************
*** 1089,1170 ****
    ;; Fixme: Consider making `python-buffer' buffer-local as a buffer
    ;; (not a name) in Python buffers from which `run-python' &c is
    ;; invoked.  Would support multiple processes better.
!   (unless (comint-check-proc "*Python*")
!     (let ((cmdlist (append (python-args-to-list cmd) '("-i"))))
        (set-buffer (apply 'make-comint "Python" (car cmdlist) nil
!                        (cdr cmdlist))))
      (inferior-python-mode)
      ;; Load function defintions we need.
      ;; Before the preoutput function was used, this was done via -c in
      ;; cmdlist, but that loses the banner and doesn't run the startup
!     ;; file.
!     (python-send-string "\
! def _emacs_execfile (file):  # execute file and remove it
!     from os import remove
!     try: execfile (file, globals (), globals ())
!     finally: remove (file)
! 
! def _emacs_args (name):  # get arglist of name for eldoc &c
!     import inspect
!     parts = name.split ('.')
!     if len (parts) > 1:
!         try: exec 'import ' + parts[0]
!         except: return None
!     try: exec 'func='+name # lose if name is keyword or undefined
!     except: return None
!     if inspect.isbuiltin (func):
!         doc = func.__doc__
!         if doc.find (' ->') != -1:
!             print '_emacs_out', doc.split (' ->')[0]
!         elif doc.find ('\\n') != -1:
!             print '_emacs_out', doc.split ('\\n')[0]
!         return None
!     if inspect.ismethod (func): func = func.im_func
!     if not inspect.isfunction (func):
!         return None
!     (args, varargs, varkw, defaults) = inspect.getargspec (func)
!     print '_emacs_out', func.__name__+inspect.formatargspec (args, varargs, 
varkw, defaults)
! 
! print '_emacs_ok'"))
!   (unless noshow (pop-to-buffer (setq python-buffer "*Python*"))))
  
  (defun python-send-region (start end)
    "Send the region to the inferior Python process."
    ;; The region is evaluated from a temporary file.  This avoids
    ;; problems with blank lines, which have different semantics
    ;; interactively and in files.  It also saves the inferior process
!   ;; buffer filling up with interpreter prompts.  We need a function
!   ;; to remove the temporary file when it has been evaluated, which
!   ;; unfortunately means using a not-quite pristine interpreter
!   ;; initially.  Unfortunately we also get tracebacks which look like:
!   ;; 
!   ;; >>> Traceback (most recent call last):
!   ;;   File "<stdin>", line 1, in ?
!   ;;   File "<string>", line 4, in _emacs_execfile
!   ;;   File "/tmp/py7734RSB", line 11
    ;;
    ;; The compilation-minor-mode parsing takes care of relating the
!   ;; reference to the temporary file to the source.  Fixme:
!   ;; comint-filter the first two lines of the traceback?
    (interactive "r")
    (let* ((f (make-temp-file "py"))
!        (command (format "_emacs_execfile(%S)" f))
         (orig-start (copy-marker start)))
!     (if (save-excursion
!         (goto-char start)
!         (/= 0 (current-indentation))) ; need dummy block
!       (write-region "if True:\n" nil f nil 'nomsg))
      (write-region start end f t 'nomsg)
!     (when python-buffer
        (with-current-buffer python-buffer
!       (let ((end (marker-position (process-mark (python-proc)))))
!         (set (make-local-variable 'python-orig-start) orig-start)
!         (set (make-local-variable 'compilation-error-list) nil)
!         (let ((comint-input-filter-functions
!                (delete 'python-input-filter comint-input-filter-functions)))
!           (python-send-string command))
!         (set-marker compilation-parsing-end end)
!         (setq compilation-last-buffer (current-buffer)))))))
  
  (defun python-send-string (string)
    "Evaluate STRING in inferior Python process."
--- 1138,1215 ----
    ;; Fixme: Consider making `python-buffer' buffer-local as a buffer
    ;; (not a name) in Python buffers from which `run-python' &c is
    ;; invoked.  Would support multiple processes better.
!   (unless (comint-check-proc python-buffer)
!     (let* ((cmdlist (append (python-args-to-list cmd) '("-i")))
!          (path (getenv "PYTHONPATH"))
!          (process-environment         ; to import emacs.py
!           (push (concat "PYTHONPATH=" data-directory
!                         (if path (concat ":" path)))
!                 process-environment)))
        (set-buffer (apply 'make-comint "Python" (car cmdlist) nil
!                        (cdr cmdlist)))
!       (setq python-buffer "*Python*"))
      (inferior-python-mode)
      ;; Load function defintions we need.
      ;; Before the preoutput function was used, this was done via -c in
      ;; cmdlist, but that loses the banner and doesn't run the startup
!     ;; file.  The code might be inline here, but there's enough that it
!     ;; seems worth putting in a separate file, and it's probably cleaner
!     ;; to put it in a module.
!     (python-send-string "import emacs"))
!   (unless noshow (pop-to-buffer python-buffer)))
! 
! ;; Fixme: We typically lose if the inferior isn't in the normal REPL,
! ;; e.g. prompt is `help> '.  Probably raise an error if the form of
! ;; the prompt is unexpected; actually, it needs to be `>>> ', not
! ;; `... ', i.e. we're not inputting a block &c.  However, this may not
! ;; be the place to do it, e.g. we might actually want to send commands
! ;; having set up such a state.
! 
! (defun python-send-command (command)
!   "Like `python-send-string' but resets `compilation-minor-mode'."
!   (goto-char (point-max))
!   (let ((end (marker-position (process-mark (python-proc)))))
!     (compilation-forget-errors)
!     (python-send-string command)
!     (set-marker compilation-parsing-end end)
!     (setq compilation-last-buffer (current-buffer))))
  
  (defun python-send-region (start end)
    "Send the region to the inferior Python process."
    ;; The region is evaluated from a temporary file.  This avoids
    ;; problems with blank lines, which have different semantics
    ;; interactively and in files.  It also saves the inferior process
!   ;; buffer filling up with interpreter prompts.  We need a Python
!   ;; function to remove the temporary file when it has been evaluated
!   ;; (though we could probably do it in Lisp with a Comint output
!   ;; filter).  This function also catches exceptions and truncates
!   ;; tracebacks not to mention the frame of the function itself.
    ;;
    ;; The compilation-minor-mode parsing takes care of relating the
!   ;; reference to the temporary file to the source.
!   ;;
!   ;; Fixme: Write a `coding' header to the temp file if the region is
!   ;; non-ASCII.
    (interactive "r")
    (let* ((f (make-temp-file "py"))
!        (command (format "emacs.eexecfile(%S)" f))
         (orig-start (copy-marker start)))
!     (when (save-excursion
!           (goto-char start)
!           (/= 0 (current-indentation))) ; need dummy block
!       (save-excursion
!       (goto-char orig-start)
!       ;; Wrong if we had indented code at buffer start.
!       (set-marker orig-start (line-beginning-position 0)))
!       (write-region "if True:\n" nil f nil 'nomsg))
      (write-region start end f t 'nomsg)
!     (let ((proc (python-proc)))               ;Make sure we're running a 
process.
        (with-current-buffer python-buffer
!       (python-send-command command)
!       ;; Tell compile.el to redirect error locations in file `f' to
!       ;; positions past marker `orig-start'.  It has to be done *after*
!       ;; python-send-command's call to compilation-forget-errors.
!       (compilation-fake-loc orig-start f)))))
  
  (defun python-send-string (string)
    "Evaluate STRING in inferior Python process."
***************
*** 1177,1182 ****
--- 1222,1229 ----
    (interactive)
    (python-send-region (point-min) (point-max)))
  
+ ;; Fixme: Try to define the function or class within the relevant
+ ;; module, not just at top level.
  (defun python-send-defun ()
    "Send the current defun (class or method) to the inferior Python process."
    (interactive)
***************
*** 1223,1261 ****
  module-qualified names."
    (interactive (comint-get-source "Load Python file: " python-prev-dir/file
                                  python-source-modes
!                                 t)) ; because execfile needs exact name
!   (comint-check-source file-name) ; Check to see if buffer needs saved.
    (setq python-prev-dir/file (cons (file-name-directory file-name)
                                   (file-name-nondirectory file-name)))
!   (when python-buffer
      (with-current-buffer python-buffer
!       (let ((end (marker-position (process-mark (python-proc)))))
!       (set (make-local-variable 'compilation-error-list) nil)
!       ;; (set (make-local-variable 'compilation-old-error-list) nil)
!       (let ((comint-input-filter-functions
!              (delete 'python-input-filter comint-input-filter-functions)))
!         (python-send-string
!          (if (string-match "\\.py\\'" file-name)
!              ;; Fixme: make sure the directory is in the path list
!              (let ((module (file-name-sans-extension
!                             (file-name-nondirectory file-name))))
!                (set (make-local-variable 'python-orig-start) nil)
!                (format "\
! if globals().has_key(%S): reload(%s)
! else: import %s
! " module module module))
!            (set (make-local-variable 'python-orig-start) file-name)
!            (format "execfile('%s')" file-name))))
!       (set-marker compilation-parsing-end end)
!       (setq compilation-last-buffer (current-buffer))))))
  
! ;; Fixme: Should this start a process if there isn't one?  (Unlike cmuscheme.)
  (defun python-proc ()
!   "Return the current Python process.  See variable `python-buffer'."
!   (let ((proc (get-buffer-process (if (eq major-mode 'inferior-python-mode)
!                                     (current-buffer)
!                                   python-buffer))))
!     (or proc (error "No current process.  See variable `python-buffer'"))))
  
  ;;;; Context-sensitive help.
  
--- 1270,1302 ----
  module-qualified names."
    (interactive (comint-get-source "Load Python file: " python-prev-dir/file
                                  python-source-modes
!                                 t))   ; because execfile needs exact name
!   (comint-check-source file-name)     ; Check to see if buffer needs saving.
    (setq python-prev-dir/file (cons (file-name-directory file-name)
                                   (file-name-nondirectory file-name)))
!   (let ((proc (python-proc)))         ;Make sure we have a process.
      (with-current-buffer python-buffer
!       ;; Fixme: I'm not convinced by this logic from python-mode.el.
!       (python-send-command
!        (if (string-match "\\.py\\'" file-name)
!          (let ((module (file-name-sans-extension
!                         (file-name-nondirectory file-name))))
!            (format "emacs.eimport(%S,%S)"
!                    module (file-name-directory file-name)))
!        (format "execfile(%S)" file-name)))
!       (message "%s loaded" file-name))))
  
! ;; Fixme: If we need to start the process, wait until we've got the OK
! ;; from the startup.
  (defun python-proc ()
!   "Return the current Python process.
! See variable `python-buffer'.  Starts a new process if necessary."
!   (or (if python-buffer
!         (get-buffer-process (if (eq major-mode 'inferior-python-mode)
!                                 (current-buffer)
!                               python-buffer)))
!       (progn (run-python nil t)
!            (python-proc))))
  
  ;;;; Context-sensitive help.
  
***************
*** 1267,1299 ****
    "Syntax table giving `.' symbol syntax.
  Otherwise inherits from `python-mode-syntax-table'.")
  
  ;; Fixme: Should this actually be used instead of info-look, i.e. be
! ;; bound to C-h S?
  (defun python-describe-symbol (symbol)
!   "Get help on SYMBOL using `pydoc'.
! Interactively, prompt for symbol."
!   ;; Note that we do this in the inferior process, not a separate one to
    ;; ensure the environment is appropriate.
    (interactive
     (let ((symbol (with-syntax-table python-dotty-syntax-table
                   (current-word)))
!        (enable-recursive-minibuffers t)
!        val)
!      (setq val (read-string (if symbol
!                               (format "Describe symbol (default %s): "
!                                     symbol)
!                             "Describe symbol: ")
!                           nil nil symbol))
!      (list (or val symbol))))
    (if (equal symbol "") (error "No symbol"))
    (let* ((func `(lambda ()
!                 (comint-redirect-send-command (format "help(%S)\n" ,symbol)
                                                "*Help*" nil))))
      ;; Ensure we have a suitable help buffer.
!     (let (temp-buffer-show-hook)      ; avoid xref stuff
!       (with-output-to-temp-buffer "*Help*"
        (with-current-buffer standard-output
!         (set (make-local-variable 'comint-redirect-subvert-readonly) t))))
      (if (and python-buffer (get-buffer python-buffer))
        (with-current-buffer python-buffer
          (funcall func))
--- 1308,1354 ----
    "Syntax table giving `.' symbol syntax.
  Otherwise inherits from `python-mode-syntax-table'.")
  
+ (defvar view-return-to-alist)
+ (eval-when-compile (autoload 'help-buffer "help-fns"))
+ 
  ;; Fixme: Should this actually be used instead of info-look, i.e. be
! ;; bound to C-h S?  Can we use other pydoc stuff before python 2.2?
  (defun python-describe-symbol (symbol)
!   "Get help on SYMBOL using `help'.
! Interactively, prompt for symbol.
! 
! Symbol may be anything recognized by the interpreter's `help' command --
! e.g. `CALLS' -- not just variables in scope.
! This only works for Python version 2.2 or newer since earlier interpreters
! don't support `help'."
!   ;; Note that we do this in the inferior process, not a separate one, to
    ;; ensure the environment is appropriate.
    (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))))
    (if (equal symbol "") (error "No symbol"))
    (let* ((func `(lambda ()
!                 (comint-redirect-send-command (format "emacs.ehelp(%S)\n"
!                                                       ,symbol)
                                                "*Help*" nil))))
      ;; Ensure we have a suitable help buffer.
!     ;; Fixme: Maybe process `Related help topics' a la help xrefs and
!     ;; allow C-c C-f in help buffer.
!     (let ((temp-buffer-show-hook      ; avoid xref stuff
!          (lambda ()
!            (toggle-read-only 1)
!            (setq view-return-to-alist
!                  (list (cons (selected-window) help-return-method))))))
!       (help-setup-xref (list 'python-describe-symbol symbol) (interactive-p))
!       (with-output-to-temp-buffer (help-buffer)
        (with-current-buffer standard-output
!         (set (make-local-variable 'comint-redirect-subvert-readonly) t)
!         (print-help-return-message))))
      (if (and python-buffer (get-buffer python-buffer))
        (with-current-buffer python-buffer
          (funcall func))
***************
*** 1302,1307 ****
--- 1357,1371 ----
  
  (add-to-list 'debug-ignored-errors "^No symbol")
  
+ (defun python-send-receive (string)
+   "Send STRING to inferior Python (if any) and return result.
+ The result is what follows `_emacs_out' in the output (or nil)."
+   (let ((proc (python-proc)))
+     (python-send-string string)
+     (setq python-preoutput-result nil)
+     (accept-process-output proc 5)
+     python-preoutput-result))
+ 
  ;; Fixme: try to make it work with point in the arglist.  Also, is
  ;; there anything reasonable we can do with random methods?
  ;; (Currently only works with functions.)
***************
*** 1310,1323 ****
  Only works when point is in a function name, not its arglist, for instance.
  Assumes an inferior Python is running."
    (let ((symbol (with-syntax-table python-dotty-syntax-table
!                 (current-word)))
!       (proc (and python-buffer (python-proc))))
!     (when (and proc symbol)
!       (python-send-string
!        (format "_emacs_args(%S)" symbol))
!       (setq python-preoutput-result nil)
!       (accept-process-output proc 1)
!       python-preoutput-result)))
  
  ;;;; Info-look functionality.
  
--- 1374,1382 ----
  Only works when point is in a function name, not its arglist, for instance.
  Assumes an inferior Python is running."
    (let ((symbol (with-syntax-table python-dotty-syntax-table
!                 (current-word))))
!     (when symbol
!       (python-send-receive (format "emacs.eargs(%S)" symbol)))))
  
  ;;;; Info-look functionality.
  
***************
*** 1331,1341 ****
         ;; Whether info files have a Python version suffix, e.g. in Debian.
         (versioned
          (with-temp-buffer
!           (Info-mode)
            (condition-case ()
                ;; Don't use `info' because it would pop-up a *info* buffer.
!               (Info-goto-node (format "(python%s-lib)Miscellaneous Index"
!                                       version))
              (error nil)))))
      (info-lookup-maybe-add-help
       :mode 'python-mode
--- 1390,1402 ----
         ;; Whether info files have a Python version suffix, e.g. in Debian.
         (versioned
          (with-temp-buffer
!           (with-no-warnings (Info-mode))
            (condition-case ()
                ;; Don't use `info' because it would pop-up a *info* buffer.
!               (with-no-warnings
!                (Info-goto-node (format "(python%s-lib)Miscellaneous Index"
!                                        version))
!                t)
              (error nil)))))
      (info-lookup-maybe-add-help
       :mode 'python-mode
***************
*** 1401,1407 ****
                (while (re-search-forward
                        (rx (and line-start (or "import" "from") (1+ space)
                                 (group (1+ (not (any " \t\n."))))))
!                       10000        ; Probably not worth customizing.
                        t)
                  (if (member (match-string 1) python-jython-packages)
                      (throw 'done t))))
--- 1462,1468 ----
                (while (re-search-forward
                        (rx (and line-start (or "import" "from") (1+ space)
                                 (group (1+ (not (any " \t\n."))))))
!                       (+ (point-min) 10000) ; Probably not worth customizing.
                        t)
                  (if (member (match-string 1) python-jython-packages)
                      (throw 'done t))))
***************
*** 1519,1529 ****
    (python-end-of-block)
    (exchange-point-and-mark))
  
  ;;;; Modes.
  
  (defvar outline-heading-end-regexp)
  (defvar eldoc-print-current-symbol-info-function)
! (defvar python-mode-running)
  ;;;###autoload
  (define-derived-mode python-mode fundamental-mode "Python"
    "Major mode for editing Python files.
--- 1580,1676 ----
    (python-end-of-block)
    (exchange-point-and-mark))
  
+ ;;;; Completion.
+ 
+ (defun python-symbol-completions (symbol)
+   "Return a list of completions of the string SYMBOL from Python process.
+ The list is sorted."
+   (when symbol
+     (let ((completions
+          (condition-case ()
+              (car (read-from-string (python-send-receive
+                                      (format "emacs.complete(%S)" symbol))))
+            (error nil))))
+       (sort
+        ;; We can get duplicates from the above -- don't know why.
+        (delete-dups completions)
+        #'string<))))
+ 
+ (defun python-partial-symbol ()
+   "Return the partial symbol before point (for completion)."
+   (let ((end (point))
+       (start (save-excursion
+                (and (re-search-backward
+                      (rx (and (or buffer-start (regexp "[^[:alnum:]._]"))
+                               (group (1+ (regexp "[[:alnum:]._]")))
+                               point))
+                      nil t)
+                     (match-beginning 1)))))
+     (if start (buffer-substring-no-properties start end))))
+ 
+ ;; Fixme: We should have an abstraction of this sort of thing in the
+ ;; core.
+ (defun python-complete-symbol ()
+   "Perform completion on the Python symbol preceding point.
+ Repeating the command scrolls the completion window."
+   (interactive)
+   (let ((window (get-buffer-window "*Completions*")))
+     (if (and (eq last-command this-command)
+            window (window-live-p window) (window-buffer window)
+            (buffer-name (window-buffer window)))
+       (with-current-buffer (window-buffer window)
+         (if (pos-visible-in-window-p (point-max) window)
+             (set-window-start window (point-min))
+           (save-selected-window
+             (select-window window)
+             (scroll-up))))
+       ;; Do completion.
+       (let* ((end (point))
+            (symbol (python-partial-symbol))
+            (completions (python-symbol-completions symbol))
+            (completion (if completions
+                            (try-completion symbol completions))))
+       (when symbol
+         (cond ((eq completion t))
+               ((null completion)
+                (message "Can't find completion for \"%s\"" symbol)
+                (ding))
+               ((not (string= symbol completion))
+                (delete-region (- end (length symbol)) end)
+                (insert completion))
+               (t
+                (message "Making completion list...")
+                (with-output-to-temp-buffer "*Completions*"
+                  (display-completion-list completions))
+                (message "Making completion list...%s" "done"))))))))
+ 
+ (eval-when-compile (require 'hippie-exp))
+ 
+ (defun python-try-complete (old)
+   "Completion function for Python for use with `hippie-expand'."
+   (when (eq major-mode 'python-mode)  ; though we only add it locally
+     (unless old
+       (let ((symbol (python-partial-symbol)))
+       (he-init-string (- (point) (length symbol)) (point))
+       (if (not (he-string-member he-search-string he-tried-table))
+           (push he-search-string he-tried-table))
+       (setq he-expand-list
+             (and symbol (python-symbol-completions symbol)))))
+     (while (and he-expand-list
+               (he-string-member (car he-expand-list) he-tried-table))
+       (pop he-expand-list))
+     (if he-expand-list
+       (progn
+         (he-substitute-string (pop he-expand-list))
+         t)
+       (if old (he-reset-string))
+       nil)))
+ 
  ;;;; Modes.
  
  (defvar outline-heading-end-regexp)
  (defvar eldoc-print-current-symbol-info-function)
! 
  ;;;###autoload
  (define-derived-mode python-mode fundamental-mode "Python"
    "Major mode for editing Python files.
***************
*** 1565,1575 ****
                                   ))
    (set (make-local-variable 'parse-sexp-lookup-properties) t)
    (set (make-local-variable 'comment-start) "# ")
!   ;; Fixme: define a comment-indent-function?
    (set (make-local-variable 'indent-line-function) #'python-indent-line)
    (set (make-local-variable 'paragraph-start) "\\s-*$")
!   (set (make-local-variable 'fill-paragraph-function)
!        'python-fill-paragraph)
    (set (make-local-variable 'require-final-newline) t)
    (set (make-local-variable 'add-log-current-defun-function)
         #'python-current-defun)
--- 1712,1721 ----
                                   ))
    (set (make-local-variable 'parse-sexp-lookup-properties) t)
    (set (make-local-variable 'comment-start) "# ")
!   (set (make-local-variable 'comment-indent-function) #'python-comment-indent)
    (set (make-local-variable 'indent-line-function) #'python-indent-line)
    (set (make-local-variable 'paragraph-start) "\\s-*$")
!   (set (make-local-variable 'fill-paragraph-function) 'python-fill-paragraph)
    (set (make-local-variable 'require-final-newline) t)
    (set (make-local-variable 'add-log-current-defun-function)
         #'python-current-defun)
***************
*** 1587,1592 ****
--- 1733,1741 ----
         #'python-eldoc-function)
    (add-hook 'eldoc-mode-hook
            '(lambda () (run-python 0 t)) nil t) ; need it running
+   (if (featurep 'hippie-exp)
+       (set (make-local-variable 'hippie-expand-try-functions-list)
+          (cons 'python-try-complete hippie-expand-try-functions-list)))
    (unless font-lock-mode (font-lock-mode 1))
    (when python-guess-indent (python-guess-indent))
    (set (make-local-variable 'python-command) python-python-command)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]