I don't see Richard's original post, so I reply to Terry.
Richard> I am a devoted Emacs user and I write a lot in Python.
Me too.
The good news is that I managed to configure completion for Python
in Emacs using pymacs, python-mode.el, pycomplete.el and pycomplete.py.
For contents of my pycomplete.el, pycomplete.py and necessary
settings in .emacs see below.
Richard> I need the following features:
Richard> 1) Tab completion, ideally Slime like. That is, when there's not
Richard> enough letters to unambiguously complete a symbol, I want it to
Richard> show a buffer (w/o taking the focus) w/ the possible
Richard> completions. In an ideal world, it would be able to complete
Richard> fo.ba<TAB> to foo.bar. I imagine this would require quite tight
Richard> Emacs-Python integration.
Works for me.
[...]
Richard> 2) Sending the toplevel definition (class or function) to the Python
Richard> buffer.
That feature is defined in python-mode.el:
"\e\C-x" 'py-execute-def-or-class
"\C-c|" 'py-execute-region
[...]
Richard> 3) Hints on function/method arguments. IDLE has this done nearly
Richard> right, but the hints are a bit too intrusive for me. I would like to
Richard> see them in the minibuffer.
Works for me, but only for pure python functions
(`inspect.getargspec` constraint).
[...]
Richard> I have tried a couple of times both python-modes (the one shipped w/
Richard> Python and the one shipped w/ Emacs), pymacs and stuff like that...
Richard> And, as I said, never got it right. But, maybe I just cannot find the
Richard> way to configure it, and some configuration hints will be enough...
I mixed solutions found around the net and finally got it working:
- hitting TAB complete function/method name
- f1 shows description of object at point
- hitting '(' and ',' shows function parameters
Copy `pycomplete.py` on your PYTHONPATH (e.g. /usr/lib/python2.5/site-packages)
and `pycomplete.el` on your Emacs load_path (e.g. /usr/share/emacs/site-lisp).
Copy my settings to your `.emacs` file and hopefully it will work.
My files:
# .emacs
(require 'pycomplete)
(setq auto-mode-alist (cons '("\\.py$" . python-mode) auto-mode-alist))
(autoload 'python-mode "python-mode" "Python editing mode." t)
(autoload 'pymacs-load "pymacs" nil t)
(autoload 'pymacs-eval "pymacs" nil t)
(autoload 'pymacs-apply "pymacs")
(autoload 'pymacs-call "pymacs")
(setq interpreter-mode-alist(cons '("python" . python-mode)
interpreter-mode-alist))
(setq python-mode-hook
'(lambda () (progn
(set-variable 'py-python-command "/usr/bin/python2.5")
(set-variable 'py-indent-offset 4)
(set-variable 'py-smart-indentation nil)
(set-variable 'indent-tabs-mode nil))))
# end of .emacs
# pycomplete.el
(require 'pymacs)
(require 'python-mode)
(pymacs-load "pycomplete")
;;check if prev character is blank-type
(defun char-before-blank ()
(save-excursion
(forward-char -1)
(looking-at "[\n\t\r]")))
(defun py-complete ()
(interactive)
(let ((pymacs-forget-mutability t))
(if (and
(and (eolp) (not (bolp))
(not (char-before-blank))))
(insert (pycomplete-pycomplete (py-symbol-near-point) (py-find-global-imports)))
(indent-for-tab-command))))
(defun py-find-global-imports ()
(save-excursion
(let ((imports nil))
(goto-char (point-min))
(while (re-search-forward
"\\(import \\|from \\([A-Za-z_][A-Za-z_0-9\\.]*\\) import \\).*"
nil t)
(setq imports
(append imports (list (buffer-substring
(match-beginning 0)
(match-end 0))))))
imports)))
(defun py-complete-python-dotexpr-begin nil
(interactive)
(re-search-backward "[^a-zA-Z_0-9\\.]")
(forward-char))
(defun py-complete-python-dotexpr-end nil
(interactive)
(re-search-forward "[a-zA-Z_0-9\\.]*"))
(put 'python-dotexpr 'beginning-op 'py-complete-python-dotexpr-begin)
(put 'python-dotexpr 'end-op 'py-complete-python-dotexpr-end)
(defun py-complete-show (string)
(display-message-or-buffer string "*PythonHelp*"))
(defun py-complete-help (string)
"get help on a python expression"
(let ((help-string
(pycomplete-pyhelp string (py-find-global-imports))))
(if (and help-string (> (length help-string) 300))
(with-output-to-temp-buffer "*Python Help*"
(print help-string))
(py-complete-show help-string))))
(defun py-complete-help-thing-at-point nil
(interactive)
(require 'thingatpt)
(let ((sym (thing-at-point 'python-dotexpr)))
(if sym
(py-complete-help sym))))
(set 'py-complete-current-signature nil)
(defun py-complete-signature (function)
"get signature of a python function or method"
(interactive)
(set 'py-complete-current-signature
(pycomplete-pysignature function)))
(defun py-complete-signature-show nil
(interactive)
(require 'thingatpt)
(let ((sym (thing-at-point 'python-dotexpr)))
(if sym
(progn
(py-complete-show (py-complete-signature sym))))))
(defun py-complete-signature-expr nil
(interactive)
(require 'thingatpt)
(let ((dotexpr (read-string "signature on: "
(thing-at-point 'python-dotexpr))))
(if dotexpr
(py-complete-show
(py-complete-signature dotexpr)))))
(defun py-complete-electric-lparen nil
"electricly insert '(', and try to get a signature for the stuff to the left"
(interactive)
(py-complete-signature-show)
(self-insert-command 1))
(defun py-complete-electric-comma nil
"electricly insert ',', and redisplay latest signature"
(interactive)
(self-insert-command 1)
(if py-complete-current-signature
(py-complete-show (format "%s" py-complete-current-signature))))
(define-key py-mode-map "\M-\C-i" 'py-complete)
(define-key py-mode-map "\t" 'py-complete)
(define-key py-mode-map [f1] 'py-complete-help-thing-at-point)
(define-key py-mode-map "(" 'py-complete-electric-lparen)
(define-key py-mode-map "," 'py-complete-electric-comma)
(define-key py-mode-map [f2] 'py-complete-signature-expr)
(provide 'pycomplete)
# end of pycomplete.el
# pycomplete.py
import sys
import inspect
from StringIO import StringIO
import os.path
try:
x = set
except NameError:
from sets import Set as set
else:
del x
from Pymacs import lisp
sys.path.append('.')
def pycomplete(s, imports=None, debug=False):
"""Display completion in Emacs window"""
completions = _get_all_completions(s, imports)
dots = s.split(".")
result = os.path.commonprefix([k[len(dots[-1]):] for k in completions])
if result == "":
if completions:
if debug:
width = 80
else:
width = lisp.window_width() - 2
column = width / 20
white = " " * 20
msg = ""
counter = 0
for completion in completions :
if len(completion) < 20 :
msg += completion + white[len(completion):]
counter += 1
else :
msg += completion + white[len(completion) - 20:]
counter += 2
if counter >= column:
counter = 0
msg += '\n'
else:
msg = "no completions!"
if debug:
print msg
else:
lisp.message(msg)
return result
def pyhelp(s, imports=None):
"""Return object description"""
_import_modules(imports, globals(), None)
return _getdoc(s)
def pysignature(s):
"""Return info about function parameters"""
f = None
try:
f = eval(s)
except Exception, ex:
return "%s" % ex
if inspect.ismethod(f):
f = f.im_func
if not inspect.isfunction(f):
return ''
(args, varargs, varkw, defaults) = inspect.getargspec(f)
return('%s: %s'
% (f.__name__, inspect.formatargspec(args,varargs,varkw,defaults)))
def _getdoc(s):
"""Return string printed by `help` function"""
obj = None
try:
obj = eval(s)
except Exception, ex:
return "%s" % ex
out = StringIO()
old = sys.stdout
sys.stdout = out
help(obj)
sys.stdout = old
return out.getvalue()
def _import_modules(imports, dglobals, dlocals):
"""If given, execute import statements"""
if imports is not None:
for stmt in imports:
try:
exec stmt in dglobals, dlocals
except TypeError:
raise TypeError, 'invalid type: %s' % stmt
except:
continue
def _get_all_completions(s, imports=None):
"""Return contextual completion of s (string of >= zero chars)"""
dlocals = {}
_import_modules(imports, globals(), dlocals)
dots = s.split(".")
if not s or len(dots) == 1:
keys = set()
keys.update(dlocals.keys())
keys.update(globals().keys())
import __builtin__
keys.update(dir(__builtin__))
keys = list(keys)
keys.sort()
if s:
return [k for k in keys if k.startswith(s)]
else:
return keys
sym = None
for i in range(1, len(dots)):
s = ".".join(dots[:i])
try:
sym = eval(s, globals(), dlocals)
except NameError:
try:
sym = __import__(s, globals(), dlocals, [])
except ImportError:
return []
if sym is not None:
s = dots[-1]
return [k for k in dir(sym) if k.startswith(s)]
def _test():
print ' ->', pycomplete('', debug=True)
print 'sys.get ->', pycomplete('sys.get', debug=True)
print 'settr ->', pycomplete('settr', debug=True)
print 'settr (plat in context) ->',
print pycomplete('settr', imports=['from sys import settrace'], debug=True)
print 'foo. ->', pycomplete('foo.', debug=True)
print 'Enc (email * imported) ->',
print pycomplete('Enc', imports=['from email import *'], debug=True)
print 'E (email * imported) ->',
print pycomplete('E', imports=['from email import *'], debug=True)
print 'Enc ->', pycomplete('Enc', debug=True)
print 'E ->', pycomplete('E', debug=True)
if __name__ == "__main__":
_test()
# end of pycomplete.py
HTH,
Rob