When I published my post about
text around in Emacs buffers, Mickey Petersen from
Mastering Emacs told me that he had
build something like that for Python code blocks in the past. As that
was way back and his code relies on an old version of
it’s not published anywhere. In this post I will show you how to
implement this (and more) for objed.
One nice thing about
objed is that, once you have some
functionality, it works with any defined text object. If you want to
move python blocks around like that, all you need to do is to define a
python block object.
Thanks to macros, adding an object only needs a few lines of code.
Defining it also means you get all the other navigation and editing
objed for free:
Navigate to the next/previous instance of the object
Navigate to the first/last instance of the object
Toggle between the inner part and the whole object
Jump to an object on the screen using
Search for an instance by it’s first line
Apply an operation or any Emacs region command on it
Before diving right into the code for the block object, let us start
with a simpler object first. In general there should already be some
base object for which you want to write a mode specific version. To
see which objects are defined by default, see
following defines a
(objed-define-object python defun :mode python-mode :no-skip t :get-obj (objed-bounds-from-region-cmd #'python-mark-defun) ;; the following two keywords are redundant, ;; but kept for illustration. They can be left unused, ;; to inherit from the default defun definition. :try-next (beginning-of-defun -1) :try-prev (beginning-of-defun 1))
As you can see the amount of code is quite manageable. First comes the
package name, in this case
python and the object name, which is
defun obviously. The object should be used in
as defined by the
:mode keyword. The object code will be loaded as
soon as python is provided.
:no-skip option is needed because in python a defun could also
be a class definition. As a class contains methods you don’t want to
skip the current object before the search for the next one starts. For
many other objects it makes sense to skip the current object before
searching for the next one. That’s all there is to it.
The remaining part gets the position data using
:get-obj and defines
how next or previous instances can be found with keywords
:try-prev. After evaluating the above definition the new object
definition will be used in
python-mode buffers automatically.
Now lets move on to the block object:
(objed-define-object python block :mode python-mode :commands (python-nav-backward-block python-nav-forward-block) :no-skip t :try-next (python-nav-forward-block) :try-prev (python-nav-backward-block) :beg (python-nav-beginning-of-block) (objed--skip-ws t (line-beginning-position)) (point) :ibeg (forward-line 1) (objed--skip-ws) (point) :iend (python-nav-end-of-block) (point) :end (forward-line 1) (point))
There are a few differences here:
First we have a
:commandskeyword. This tells objed that we want to activate with this object after any of these commands are executed. That means if you press M-a in
objedwill activate with the block object.
Instead of returning the object bounds in one go, we compute the positions separately using
:endkeywords. These are the beginning, inner beginning, inner end and end position of the object. The code for these keywords is allowed to move the point and runs in the same order the keywords are provided.
objed knows about blocks let us look at an example,
where I’m testing some objed features in a python buffer: