An emacs minor-mode to automatically add spacing around operators.
For example typing
a=10*5+2
results in
a = 10 * 5 + 2
I'm aiming to have electric-operator-mode correctly handle almost every operator for major modes built in to Emacs (and a few that aren't). If you find a case where it doesn't space something as expected then consider it a bug, and please report it :).
The simplest way to install electric-operator-mode is by using package.el
to get it from MELPA unstable.
Alternatively you can install the dependencies listed in
electric-operator.el
and add electric-operator.el
to your load path.
Either way you also need to make sure electric-operator is loaded with
(require 'electric-operator)
(or you could use
use-package
to load packages
and keep your customisations organised).
To temporarily enable electric-operator-mode call electric-operator-mode
.
To permanently enable it for a major mode add it to the relevant mode hook.
For example for python-mode add the following to your config:
(add-hook 'python-mode-hook #'electric-operator-mode)
Note that electric-operator-mode
is not a global minor mode. It must be
enabled separately for each major mode that you wish to use it with.
The spacing around operators is controlled by "rules". The simplest rule is
a pair containing the (unspaced) operator and the correctly spaced operator
as strings. For example (cons "=" " = ")
is the default rule for =
.
Alternatively the second element of the pair can be a function which
returns the correctly spaced operator.
Each major mode has its own list of rules for the spacing of operators. The
rule list for a major mode is looked up in the hash table
electric-operator-mode-rules-table
. Rule lists can be modified using the
function electric-operator-add-rules-for-mode
, which will automatically
replace any existing rules for the same operator. As an example: to
automatically add spacing around ->
and =>
in python mode you would use
(electric-operator-add-rules-for-mode 'python-mode
(cons "->" " -> ")
(cons "=>" " => "))
To print a list of the rules in use for a mode use M-x electric-operator-pretty-print-rules-for-mode
(note: this displays in the
*Messages*
buffer).
Rules can be disabled in a similar way by setting the second element of the
rule to nil. For example if you find that the *
operator in C is not
working reliably enough for pointer types you would use
(electric-operator-add-rules-for-mode 'c-mode
(cons "*" nil))
Rules for new modes can be added in exactly the same way. To add all the default prog-mode rules to a new mode use:
(apply #'electric-operator-add-rules-for-mode 'my-new-mode
(electric-operator-get-rules-for-mode 'prog-mode))
Or similarly to add all existing rules for another mode. The default rules for
text modes can be added in the same way using
(electric-operator-get-rules-for-mode text-mode)
.
Other customisation options are available to tweak behaviour for some
modes. Use M-x customize-apropos <RET> electric-operator
to see the full
list and set them as normal using setq
, customize etc. as you prefer.
Note that inferior
modes
are implemented by Emacs as separate modes to their respective programming
modes. So customisations to electric-operator for e.g. python-mode
(or
ess-r-mode
) will not automatically apply to inferior-python-mode
(respectively inferior-ess-r-mode
).
A number of basic operator rules are defined for any major mode, so if your language is vaguely C-like then a good amount of functionality should just work.
If you use electric-operator
for a major mode not listed below here
please open an issue to let me know what works.
Good support is implemented for:
- Python
- R
- Javascript (and coffeescript, typescript)
- SQL
- C
- C++
- Java
- Rust
- PHP
- Julia
- CSS
- Fortran
- Perl (except
x
,%
,<
,>
, and/
operators)
In C-like languages there are some difficulties with distinguishing between *
for pointer types and for multiplication. Similiarly for &
(reference types vs
bitwise and), and <
, >
(comparision operators vs angle brackets).
The following languages are supported but not extensively tested, please open an issue to let me know how it goes if you use them.
- Ruby
- LaTeX
Curretly I don't really support adding new lines after operators because emacs
core has support for this in electric-layout-mode
. But since it's a closely
related concept here's my not-quite-trivial configuration for electric newlines
in python which works well with electric-operator-mode
:
(defun ds/python-electric-newline ()
(let ((paren (ds/enclosing-paren)))
(if (not (or (eq paren ?\{)
(eq paren ?\[)
(eq paren ?\()
(looking-back "\\blambda\\b.*")))
'after
nil)))
(defun ds/setup-python-electric-layout ()
(make-local-variable 'electric-layout-rules)
(add-to-list 'electric-layout-rules (cons ?: #'ds/python-electric-newline)))
(electric-layout-mode 1)
(add-hook 'python-mode-hook #'ds/setup-python-electric-layout)
I'm using Cask to manage dependencies, so to
run the tests you'll need to install cask then run cask install
to install
dependencies. Then the tests can be run with make test
(or manually with
ecukes
and ert-runner
, but make sure you exclude ecukes tests tagged with
@known-failure
).
Bug reports are also welcome!
Electric-operator is based on a heavily refactored version of electric-spacing by William Xu with contributions from Nikolaj Schumacher. Electric-spacing is itself based on smart-operator, also by William Xu.
Electric-operator uses simpler and more flexible rules to define how
operators should be treated, and also adds a full suite of tests. However
it has some additional dependencies (in particular the excellent
dash
) which were decided
to be too heavy to add to electric-spacing.
- Fix treating comint-based modes (typically called inferior-...) as plain text modes by default.
- Apply default python rules to inferior-python-mode, and similiarly for R.
- Add rules pretty-printing function
electric-operator-pretty-print-rules-for-mode
. - Fix some C/C++ edge cases in declaring r-value references and pointer-to-pointers.
- Add support for postgresql json operators
- Dropped Haskell support because it was essentially unusable and made everything more complex.
- Add support for LaTeX math.
- Add Fortran support
- Fix spacing of pseudo classes in CSS/SCSS
- Add perl support
BREAKING:
electric-operator-prog-mode-rules
andelectric-operator-prose-mode-rules
have been removed, please use(get-rules-for-mode 'prog-mode)
to get the rules and(add-rules-for-mode 'prog-mode ...rules... )
to set rules for prog-mode. For prose rules use'text-mode
.
Other changes:
- Major performance improvements!
- Electric operator now uses a trie data structure instead of a list of rules
internally. This shouldn't have any effect on configs that only use
non-private functions (i.e. those without
--
in their name).
- Major performance improvements
- Expansions of operators at the start of a line now won't modify indentation
- Typescript support
- Julia support
- Better comment handling in C-like languages
- Type annotations in Python
- C++ lambda captures
- Some additional python operators
- Fix comma operator in R for current unstable version of ess-mode (July 2018)
- Improve SQL support
- Add basic Haskell support (no partially-evaluated operators)
- Add js2-mode support
- Fix running hook for
,
in ess-mode even when electric-operator is disabled
- Feature: now fixes existing whitespace after operators as well as before them
- Improve python lambdas
- Add option for R to control default argument spacing
- Improve Javascript comment handling
- Improve Javascript regex handling
- Support Javascript ES6 arrow functions
- Support some additional PHP operators
- Add support for CSS
- Disabled
<
and>
in C++ and Java modes by default. - Added support for emacs25.
- Added support for Javascript Coffeescript, Rust, PHP.
- Improved support for R, C++, C.
This section lists places where electric-operator may fail, but I don't think it's a big deal and it would be difficult to add support. Open an issue if you really want one of these to work.
In python lambda functions can have default arguments with almost arbitrary
initialisation code. If this code contains a :
, i.e. a dict initialisation or
a list slice, then electric-operator may get confused. This shouldn't be an
issue in practice because:
-
The mutable default argument problem means you probably shouldn't use dicts or lists as default arguments.
-
If you need default arguments then the function is probably complex enough to deserve a name.
It's difficult to solve because we would need to step over matching {}
and
[]
while searching for a lambda
to match the current :
. I think this
impossible with regexs so I would have to find something more powerful.
Perl has some extremely flexible operator behaviours which seem impossible to support:
- Use of
x
as both an operator and a part of a variable name. - Defining your own operators
- Defining your own regex quote charaters, e.g.
q#foo#b
.