If you’re using Python with Emacs (using one of several competing, incompatible, and slightly different modes) you are used to a pretty… bare-bones experience: no completion; semi-functional dynamic docstring support; and little in the way of two-way communication between Python and Emacs.
Enter Jedi, a completion library. Yes, Jedi, an editor-agnostic library that publishes auto completion, docstring support, and more. Excellent.
I’ve experimented with Pymacs — an interesting science project that adds “Python-like” support Emacs, so you can avoid interacting with Elisp, except not really — rope, and ropemacs and they were… disappointing. Slow, crash-prone, obtuse and impossible to extend. So I never really used them, and lived without completion or, well, much of anything beyond the REPL and my own handcrafted modifications.
The other alternative is the 600 lbs gorilla, CEDET, and its incomplete Python support, but that’s no good either.
Imagine my surprise, after fidgeting with the dependencies for both Jedi and Jedi.el, the Emacs library for Jedi, that it… works! And it’s good! It’s up-and-coming, I should say, but perfectly usable; it doesn’t get in my way, it’s got some crazy deferreds library it depends on for asynchronous, non-blocking querying of Jedi, but that bit works great — no input lag at all.
It seems to resolve, simplistically (which is good), as many assignments and method calls as one can reasonably expect from a non-evaluating, statically analyzing Python completion library.
The Jedi.el module also Just Works with the excellent auto-complete library, as you can see in the picture above.
Aside from completion, it also offers “find symbol definition at point” (a la TAGS, but not crap) and Jedi.el sensibly binds it to
C-. by default. It also has a “related names” functionality, tracking down same-named identifiers in other modules; it uses Anything (now Helm) to display the results, and it is bound to
C-c r. And finally, it can show the documentation for the identifier at point (be it a class or function) with
C-c d. Useful.
I haven’t used Jedi and Jedi.el long enough to really get to know it, but I’m probably going to extend Jedi.el so it uses
eldoc-mode for displaying the function parameters; it’s also a bit rough around the edges, and I may want to tweak certain things to my liking, but overall: huge success!
I highly recommend you give Jedi and Jedi.el a try!
Have you ever heard of iedit for Emacs by Victor Ren (Update:Seems the github link I posted earlier was way out of date)? Me neither, until recently, and that’s a terrible shame as it is a cornerstone of my programming workflow now that I’ve learned about it. So what does it do, then? Well, quite simply, you can edit multiple, identical string occurrences at the same time. The twist here is it uses in-buffer editing without disrupting your workflow with prompts, windows or any of that stuff: you plonk your point down on a word you want to change; you run
iedit-mode; and now all the occurrences of that word is highlighted on your screen, like
isearch, and when you alter a highlighted word, the other highlighted words change also. How cool is that? Modern IDEs have it already — usually hidden away in the “Refactoring” section — and does exactly the same thing, but iedit is a lot dumber as it cannot infer context beyond I want to iedit all occurrences of word point is on.
If you regularly replace variables or words with
C-M-% — well, you can retire that workflow now, as iedit will handle it for you. Sure, go ahead; use the older way if you have complex, partial replacements you want to do, but if you’re renaming a variable in a buffer… Why not use iedit?
Here’s a sample demonstration showing what exactly it is I’m talking about, in brilliant technicolor:
So iedit’s pretty great and all that, but I don’t replace words across a whole buffer very often; sure, I hear you say: “just
C-x n d!” Indeed, narrowing’s great, but this blog is all about half-baked, half-inventions and cobbled-together scripts, and this post is no exception!
I prefer a workflow that minimizes the use of commands to do routine tasks — a fairly common goal for most Emacs hackers. The code below aim to do just that: when invoked, it will take the word at point and only iedit occurrences in the local defun (Note: don’t forget that although defun is Lisp-speak, most modes automatically support commands like
narrow-to-defun.) If you pass an argument to the function, it will iedit all occurrences in the entire buffer.
The iedit author suggest that you bind
iedit-mode — the default command for entering iedit — to
C-; and I agree: it’s rarely used and easy to type.
Update: Le Wang pointed out that I was using an older version of iedit; the code has been updated to reflect the API changes.
(defun iedit-dwim (arg)
"Starts iedit but uses \\[narrow-to-defun] to limit its scope."
;; this function determines the scope of `iedit-start'.
;; `current-word' can of course be replaced by other
(iedit-start (current-word) (point-min) (point-max)))))))
(global-set-key (kbd "C-;") 'iedit-dwim)
If you are like me, you are probably using one or more minor modes, and most of them add their own lighter to the modeline to tell you that they are running. Common examples include “Abbrev” for Abbrev mode; “yas” for Yasnippet mode and so on. Unfortunately, modeline real estate is expensive, and if you are stuck on a small screen — or if you split your Emacs windows a lot — you will find that it is often truncated, possibly hiding useful information that you actually care about, such as Which Function Mode.
There is no feature in Emacs to selectively hide or compress modeline flotsam, so I have hacked together some code to do the job for you.
For instance, in my Emacs I can collapse the following modeline string
(Python Hi AC yas Eldoc Flymake:0/12 Abbrev) into
(Py α υ Φ:0/12). I’ve chosen to completely obscure
eldoc-mode but “compress”
I used Greek characters (How?
C-u C-\ greek RET then
C-\ to write Greek characters; see Diacritics in Emacs) because they look cool.
The code works rather well, and I have yet to experience any major issues with it, but do let me know if it breaks something! One important caveat is that a lot of minor modes that reach out and call other functions, like ERC or Flymake, often use functions in lieu of strings in
mode-line-modes, so replacing them with a string might break the minor mode, or cause it to not update properly! Please keep that in mind and experiment in a separate Emacs instance unless you know what you’re doing. Case in point: Flymake. I’ve replaced its own modeline updater function with one of my own with the “Flymake” string replaced; you could probably rejiggle it by advising the original function, then tweaking the modeline string post facto, blah, blah, blah — but I always opt for the simpler solution, which is what I’ve used below!
Here’s the code. Stick it in your emacs file, evaluate it, and you’re done. You can also force a refresh by running
M-x clean-mode-line. Flymake will have to be turned off and back on for the changes to take effect in a buffer!
9:17pm update: Fixed a small bug in
`((auto-complete-mode . " α")
(yas/minor-mode . " υ")
(paredit-mode . " π")
(eldoc-mode . "")
(abbrev-mode . "")
;; Major modes
(lisp-interaction-mode . "λ")
(hi-lock-mode . "")
(python-mode . "Py")
(emacs-lisp-mode . "EL")
(nxhtml-mode . "nx"))
"Alist for `clean-mode-line'.
When you add a new element to the alist, keep in mind that you
must pass the correct minor/major mode symbol and a string you
want to use in the modeline *in lieu of* the original.")
(defun clean-mode-line ()
(loop for cleaner in mode-line-cleaner-alist
do (let* ((mode (car cleaner))
(mode-str (cdr cleaner))
(old-mode-str (cdr (assq mode minor-mode-alist))))
(setcar old-mode-str mode-str))
;; major mode
(when (eq mode major-mode)
(setq mode-name mode-str)))))
(add-hook 'after-change-major-mode-hook 'clean-mode-line)
;;; alias the new `flymake-report-status-slim' to
(defalias 'flymake-report-status 'flymake-report-status-slim)
(defun flymake-report-status-slim (e-w &optional status)
"Show \"slim\" flymake status in mode line."
(setq flymake-mode-line-e-w e-w))
(setq flymake-mode-line-status status))
(let* ((mode-line " Φ"))
(when (> (length flymake-mode-line-e-w) 0)
(setq mode-line (concat mode-line ":" flymake-mode-line-e-w)))
(setq mode-line (concat mode-line flymake-mode-line-status))
(setq flymake-mode-line mode-line)