Skip to content

Minor mode for Clojure[Script] completion powered by clj-kondo.

License

Notifications You must be signed in to change notification settings

didibus/anakondo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 

Repository files navigation

anakondo

This package makes use of clj-kondo’s analysis data to provide code editing facilities related to Clojure, ClojureScript and cljc source. This means you get advanced editing features like auto-complete without the need for a connected REPL, because clj-kondo performs its magic using static source code analysis.

Current Features:

  • It gives you contextual auto-completion using completion-at-point, which works without a REPL connected, since it relies on clj-kondo’s static source analysis. It will list out all available Vars, Ns and Aliases in a given buffer.
  • It can also perform locals completion, using an Emacs implemented heuristic completion based on dabbrev that works really well in practice and can handle unbalanced code.
  • It can also complete Java classes and their static methods and fields. It does so by relying on jar and javap JDK command line binaries.
  • It supports the notion of projects, using projectile or clojure-mode and Clojure’s tools.deps. This means it’ll pick up the buffer’s current project root using projectile or clojure-mode, and it will use Clojure’s tools.deps to get the project’s classpath. If it can’t find either, it’ll only auto-complete within the buffer, it won’t be able to show candidates from the required dependencies.

Screenshots

Clojure completion of namespaces and global Vars and fns without needing to be connected to a REPL:

./screenshots/anakondo-auto-completion-no-repl-demo.gif

Clojure completion of local bindings without needing to be connected to a REPL, which works even with unbalanced expressions:

./screenshots/anakondo-locals-auto-completion-no-repl-demo.gif

Java completion of qualified classes, default Java class imports (like Math, Thread, Integer, etc.), and of public static methods and fields, all without needing to be connected to a REPL:

./screenshots/anakondo-java-auto-completion-no-repl-demo.gif

Contents

Installation

Prerequisites

For Clojure[Script] completion, Anakondo needs the Clojure CLI tools.deps and clj-kondo to be installed and accessible from Emacs in order to run properly. To do so:

  1. Install clj-kondo by following its install instructions.
  2. Make sure clj-kondo is in your shell’s PATH.
  3. Install the Clojure CLI tools.deps by following its install instruction.
  4. Make sure the clojure CLI is in your shell’s PATH.

For Java completion, you also need to be sure that you have a JDK installed and that its /bin folder is on the environment PATH used by Emacs.

MELPA

  1. Run: M-x package-install anakondo
  2. Now put this in your init file:
    • If you want to pre-load it:
;; Load anakondo to make available its minor mode in clojure buffers
(require 'anakondo)
  • If you want to lazy load it:
;; Delays loading of anakondo until a clojure buffer is used
(autoload 'anakondo-minor-mode "anakondo")

use-package

Put this in your in your init file:

(use-package anakondo
  :ensure t
  :commands anakondo-minor-mode)

Manual

  1. Clone this repo somewhere on your computer.
  2. Make sure that somewhere is in your Emacs load-path.
  3. Now put this in your init file:
    • If you want to pre-load it:
;; Load anakondo to make available its minor mode in clojure buffers
(require 'anakondo)
  • If you want to lazy load it:
;; Delays loading of anakondo until a clojure buffer is used
(autoload 'anakondo-minor-mode "anakondo")

Usage

Turn on anakondo minor mode in one or more Clojure, ClojureScript or cljc buffer:

  • anakondo-minor-mode : Turn on/off anakondo minor mode in current buffer.

Run one of these commands:

  • completion-at-point : Will auto-complete symbol at point using clj-kondo’s static analysis, no repl needed
  • complete-symbol : Will auto-complete symbol at point using clj-kondo’s static analysis, no repl needed
  • anakondo-refresh-project-cache : Refresh the anakondo project analysis cache

To have it on by default for all your Clojure, ClojureScript and cljc buffers, add to your Emacs init file, after the anakondo require/autoload:

;; Enable anakondo-minor-mode in all Clojure buffers
(add-hook 'clojure-mode-hook #'anakondo-minor-mode)
;; Enable anakondo-minor-mode in all ClojureScript buffers
(add-hook 'clojurescript-mode-hook #'anakondo-minor-mode)
;; Enable anakondo-minor-mode in all cljc buffers
(add-hook 'clojurec-mode-hook #'anakondo-minor-mode)

If you are also using Cider, the order in which you add the hooks matters in the resulting completion behavior:

  1. If you add anakondo to the Clojure mode hooks first, then completion will first try to use anakondo’s, and only if it can’t complete the form will it then try to use Cider’s completion. This means if you try to complete a keyword for example, it’ll fallback to Cider’s, but for completing symbols it won’t, even if it finds no candidates.
  2. Otherwise, if you add Cider to the Clojure mode hooks first, then completion will first try to use Cider’s, and only if Cider completion is not available, because there is no connected REPL, will it then fallback to try anakondo’s completion. If Cider completion is available (because you have a REPL connected), it will never fallback to trying anakondo’s completion, even if Cider doesn’t find any completion candidates.

Currently, I recommend adding anakondo after Cider. This will make it so when no REPL is connected, you have anakondo’s clj-kondo based static analysis completion. While when a REPL is running, you have Cider’s completion. If you do it the other way around, when a REPL is running, if anakondo find completions you will only see the ones it found. I’ll be exploring options to have the completions merged in the future, so we get the best of both worlds.

This also goes if you turn on/off the modes manually, except in that case, order and effect are reversed. The last mode you turn on will be the one who is in charge of completion first. While with the hooks, it is the first mode you add to the hook that will be in charge of completion first.

Tips

  • When you first turn on the minor mode, it will do an initial analysis of the full project associated with your buffer with the classpath as defined by tools.deps. This can take a few seconds. This will happen for every project you turn on the mode in a buffer for, but won’t happen again when turning on the mode in another buffer in the same project.
  • If you change your deps.edn, or feel the completion is looking out of sync, you can force refresh the cache of the project analysis by running: M-x anakondo-refresh-project-cache command.
  • If dependency completion isn’t working, remember that anakondo only supports tools.deps for now, if you don’t have a deps.edn for your project, it will pick up your global deps.edn instead, it won’t use your lein project.clj or boot build.boot dependencies.
  • If you’ve disabled cider-mode, and somehow anakondo completion stopped working, this is because of a known bug in cider-mode, which removes all configured completion for the buffer, not just its own, I am trying to get this fixed in cider as well.
  • If you’ve killed the REPL, and somehow anakondo completion doesn’t seem to be starting back up, this is also due to a bug in cider, where it deletes all configured completion for the buffer on repl quit. I am trying to get this fixed in cider as well.
  • For Java completions of the default imports, they are going to be the ones defined by Clojure 1.10. So if you use an older or newer version of Clojure, you’ll still see the completion of the imports from Clojure 1.10. So don’t take it as a source of truth.
  • The list of Java classes you get completed comes from the boot classpath specified by the java command which is on your environment PATH, and the Java dependencies defined in your project’s deps.edn. So if you use Java 8, you will see the Java 8 standard classes, if you use Java 11, you will see Java 11’s standard classes, etc.
  • Only Java classes coming from a Jar on your boot classpath and classpath will be completed. If you have .class files in your boot classpath or classpath they will not be completed for now. So if you depend on Java dependencies, make sure it is through a Jar, and not a folder containing .class files if you want completion for them.
  • To auto-complete the static methods and fields of a Java class, type / after the class and call your completion function, such as completion-at-point or company-indent-or-complete-common, etc.
  • Java static methods and fields completion doesn’t work with custom imports for now, only fully qualified and default imports will get completion.

Changelog

0.2.1

Changes

  • Logic to identify the Clojure project enhanced to try projectile first, clojure-mode second, and finally resort to the buffer’s default-directory otherwise.

Fixes

  • Added missing requires which caused byte compilation errors like not finding concatenate
  • Fixed projectile constantly asking for project when buffer not in a projectile project

0.2

Additions

  • Add support for locals auto-completion
  • Add support for Java classes auto-completion (fully qualified + default imports only for now) (can only complete from jars on the classpath, no support for class files on the classpath for now)
  • Add support for Java static methods and fields auto-completion on press of /

Changes

  • Much faster auto-completion when using “as you type” completion like company-mode

Fixes

  • Fix bug where sym/ says No matching candidates instead of listing the available candidates that begin with sym/

Internal

  • Added a buffer local cache of completion candidates which keeps track of the completion list for the last start positions of completions, makes “as you type” completions much faster
  • Infers Java boot classpath by using the java command itself
  • Uses dabbrev with some custom heuristic to identify symbols and syntax-ppsp for locals completion
  • Uses javap to find available static methods and fields for a given class
  • Uses jar to find all classes in the classpath jars
  • Uses hard-coded list of default imports in Clojure 1.10 to auto-complete default Java imports. Will need to keep it updated as new Clojure versions release. Also means if user is using older version missing a default import, we might auto-complete it even though it doesn’t work with their target Clojure version.

0.1.2

Internal

  • Fixed all checkdoc reported issues
  • Fixed all package-lint-current-buffer reported issues
  • Added compile require on subr-x for use of hash-table-values
  • Fixed package requires for MELPA

0.1.1

Fixes

  • Fixed byte compilation issue due to macros being defined too late

0.1

Additions

  • Added anakondo-minor-mode command which lets you turn on/off clj-kondo driven auto-completion in your buffer
  • Clj-kondo auto-completion added to Emacs completion-at-point
  • Contextual auto-completion, it only lists Vars which are required in current buffer
  • Auto-completes namespaces as well
  • If a required ns has an alias, only completes with alias to avoid accidentally forgetting to use alias
  • Properly handles Clojure, ClojureScript and cljc buffers with no associated file
  • Defaults to user namespace if there are no ns form in buffer
  • Added anakondo-refresh-project-cache command which lets you refresh the cache when you’ve modified your project classpath
  • Supports company-mode through company-capf backend
  • Anakondo minor mode lighter is user customizable, defaults to ” k”

Internal

  • Clj-kondo analysis is now cached for better performance
  • Project level analysis done synchronously once on mode enter and cached for later use
  • Uses projectile to find the project root
  • Uses tools.deps to find the classpath for project root
  • On completion, re-analyses the current buffer and updates cache
  • Handles finding the symbol to complete even in complex forms like ~sym, ~@sym, @sym, =’sym=, `sym, etc.

Roadmap

Planned Features

  • Have it so completion results are merged with those of Cider’s if it is present
  • Make project analysis async, so it runs in the background and doesn’t block Emacs
  • Have project analysis refresh automatically every X seconds in the background
  • Have project analysis refresh automatically on file watch of deps.edn
  • Add support for lein projects
  • Add support for boot projects
  • Add auto-installer for clj-kondo on mode enter, if it is missing from path
  • Add command to update clj-kondo, possibly have it run on mode-enter as well (if it was auto-installed by mode)
  • Add support for jump to definition
  • Add support for jump/list vars in buffer
  • Add support for find var usages
  • Add support for showing fn/macro available signature (of various arities)
  • Add support for showing doc-string (maybe with eldoc)
  • Add support for rename refactoring
  • Add support for clj-kondo driven font-lock
  • Add support for completing keywords : pending clj-kondo-855
  • Add support for completing refered vars : pending clj-kondo-856
  • Add support for completing Java non-static methods and fields
  • Add support for completing Java inner classes, with their possible static and non-static methods and fields
  • Add support for completing Java imported classes, using class name only

Credits

This package would not have been possible without the following packages:

  • clj-kondo : which does all the heavy lifting
  • projectile : which gives Emacs a common notion of projects
  • clojure-mode : which gives Emacs a notion of Clojure projects
  • tools.deps : which lets me find the classpath effortlessly
  • json : which lets me parse clj-kondo’s analysis file inside Emacs

Thanks to all of them and their author/contributors.

License

MIT License, see accompanying LICENSE file.