From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Rob Wolfe Newsgroups: gmane.emacs.help Subject: Re: writing Python in Emacs Date: Sun, 20 Jan 2008 16:42:38 +0100 Organization: "Portal Gazeta.pl -> http://www.gazeta.pl" Message-ID: <87r6gc5wr5.fsf__32486.5552633584$1200861484$gmane$org@merkury.smsnet.pl> References: <160ed936-c8c0-432e-81c8-c62b8f164136@s13g2000prd.googlegroups.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1200861459 32655 80.91.229.12 (20 Jan 2008 20:37:39 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 20 Jan 2008 20:37:39 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Sun Jan 20 21:37:58 2008 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1JGgvU-00073p-LC for geh-help-gnu-emacs@m.gmane.org; Sun, 20 Jan 2008 21:37:57 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1JGgv5-00087h-6d for geh-help-gnu-emacs@m.gmane.org; Sun, 20 Jan 2008 15:37:31 -0500 Original-Path: shelby.stanford.edu!newsfeed.stanford.edu!news.tele.dk!news.tele.dk!small.news.tele.dk!news-fra1.dfn.de!newsfeed.pionier.net.pl!newsfeed.gazeta.pl!news.gazeta.pl!not-for-mail Original-Newsgroups: comp.lang.python,gnu.emacs.help Original-Lines: 377 Original-NNTP-Posting-Host: ip-79-175-204-255.cable.smsnet.pl Original-X-Trace: inews.gazeta.pl 1200843772 19905 79.175.204.255 (20 Jan 2008 15:42:52 GMT) Original-X-Complaints-To: usenet@agora.pl Original-NNTP-Posting-Date: Sun, 20 Jan 2008 15:42:52 +0000 (UTC) Cancel-Lock: sha1:zOeyra/y/KSowNqkAHoqIpIBl6Y= X-User: rw8899 User-Agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.1 (gnu/linux) Original-Xref: shelby.stanford.edu comp.lang.python:528068 gnu.emacs.help:155430 X-Mailman-Approved-At: Sun, 20 Jan 2008 15:37:08 -0500 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:50837 Archived-At: Terry Jones writes: >>>>>> "Richard" == Richard Szopa writes: 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 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