Neovim Java
neovim-java - Java Client for Neovim API
Neovim Java is a Java Client for Neovim RPC API. Currently it is best suited for creating a GUI using Java and either an embedded Neovim instance or connecting to an exisitng instance either using Unix domain sockets or TCP sockets. Plugin development may be possible as well, but it was not tested.
This library consists of multiple modules and you may choose which one you want to use (depending on preferences and level of abstraction needed):
- Core RPC - core of the library providing most basic interface for communicating with Neovim - it can be used without other modules, but other modules are recommended for most uses
- Reactive Core RPC - simple wrapper around Core RPC module for providing a reactive interface
- Unix Socket Connection - simple addition of another RPCConnection for using unix domain sockets
- Neovim API - main interface for communicating with neovim. This is the recommended module to use, it provides a reactive interface and greatly simplified interface, providing complete implementations for all functions provided by Neovim.
- Neovim RX API - small wrapper around Neovim API providing RxJava2 interface instead of Java reactive interface
- Handler annotations - addition allowing creation of annotation based handlers for requests and notifications
- Neovim notifications - addition allowing usage of notifications through Java 9 Flows interface and also provides data models for all neovim notifications
- API Explorer - simple JavaFX application used for testing library and exploring neovim API
- Plugin host - library for building remote plugins - this should be the main dependency when developing Neovim plugins (either standalone or hosted by
plugins-common-host
) - enables easy usage with neovim-java-plugin-host - Plugins common host - executable which can be used as a remote plugin in Neovim, which should load all other plugins from classpath (provided by neovim-java-plugin-host)
Core RPC
Core of the library with a basic interface for RPC communication through a RPCConnection
. This implements basic Neovim API definition, without any specific types or functions. It can be used if functions to be used may not be known beforehand. All of the classes in this module may be replaced with a different implementation if special behaviour is required.
Basic usage:
// Get an instance of RPCStreamer
RPCStreamer client = RPCClient.getDefaultAsyncInstance();
// Prepare a connection to neovim
ProcessBuilder pb = new ProcessBuilder("nvim", "--embed");
Process neovim = pb.start(); // Start embedded neovim instance
RPCConnection neovimConnection = new ProcessRPCConnection(neovim, true); // true to kill neovim once connection is closed
// Connect to neovim
client.attach(neovimConnection);
// Send feed keys call
client.send(new RequestMessage.Builder("nvim_feedkeys").addArgument("jjjj").addArgument("").addArgument(false));
// Send get current line call and print out the response
client.send(new RequestMessage.Builder("nvim_get_current_line"), (id, response) -> System.out.println(response));
Customizations
By default, RPCClient
will use a sequential message id generator, meaning every new message will have its id increased by one. This makes it possible to match up response for given request. If you need a different behaviour, you may implement MessageIdGenerator
interface and provide a custom RPCStreamer
to RPCClient
which will use that generator.
MessageIdGenerator customGenerator = createCustomGenerator();
RPCStreamer customStreamer = new PackStream(sender, listener, customGenerator);
RPCStreamer client = new RPCClient.Builder()
.withRPCStreamer(customStreamer)
.build();
Similar to this, different parts of RPCClient
can be changed using the builder:
ExecutorService
used to listen for requestsObjectMapper
used for parsing requests and responsesRPCSender
used to send messagesRPCListener
used to listen for requests
Info
If you wish to include this module (and not some other module), you may add it as your dependency: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>core-rpc</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:core-rpc:${neovimjava.version}'
Reactive Core RPC
Reactive core RPC module is a simple wrapper around core RPC module. It provides a reactive interface using Java 9 Flows. Requests and notifications
are provided through a Flow
, while sending messages returns a CompletableFuture
which can return a response.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>reactive-core-rpc</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:reactive-core-rpc:${neovimjava.version}'
It handles the same RPCConnection
interface as core RPC module and creation is very similar:
Socket socket = new Socket("127.0.0.1", 1234);
RPCConnection localConnection = new TcpSocketRPCConnection(socket);
ReactiveRPCStreamer rpcStreamer = ReactiveRPCClient.getDefaultInstance(); // shared singleton
rpcStreamer.attach(localConnection);
Message request = new RequestMessage.Builder("nvim_get_current_line");
rpcStreamer.response(request).thenAccept(System.out::println); // requesting
rpcStreamer.notificationsFlow().subscribe(notificationsSubscriber); // notifications subscription
RxJava wrapper is not provided, since it is very easy to wrap this Java 9 Flow implementation into an RxJava implementation.
Unix Socket Connection
Unix socket connection module provides a very simple additional implementation of RPCConnection
based on Unix domain sockets.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>unix-socket-connection</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:unix-socket-connection:${neovimjava.version}'
Example usage:
File socket = new File("/var/nvim/random");
RPCConnection fileConnection = new UnixDomainSocketRPCConnection(socket);
// It can now be used for communication
rpcStreamer.attach(fileConnection);
rpcStreamer.sent(message); // send a message to unix domain socket located on /var/nvim/random
Neovim API
This is the main high level interface for this library. It provides all of the Neovim RPC API functions in an easy to use way. It holds all types as models and provides 4 separate APIs: Neovim, Buffer, Tabpage and Window (the way they were meant to be used in OOP languages).
This does not proivide atomic calls yet.
All calls are made using reactive-core-rpc module and all calls return CompletableFuture
. If you prefer RxJava over Java 9 Flows, you can use
neovim-rx-api module.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>neovim-api</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:neovim-api:${neovimjava.version}'
Example usage:
Socket socket = new Socket("127.0.0.1", 1234);
RPCConnection localConnection = new TcpSocketRPCConnection(socket);
NeovimApi api = NeovimApis.getApiForConnection(localConnection);
api.getCurrentLine().thenAccept(System.out::println);
NeovimApi another = new NeovimStreamApi(ReactiveRPCClient.getDefaultInstance());
Neovim RX API
Neovim RX api module provides a same API as neovim API module, but using RxJava2.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>neovim-rx-api</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:neovim-rx-api:${neovimjava.version}'
Example:
Socket socket = new Socket("127.0.0.1", 1234);
RPCConnection localConnection = new TcpSocketRPCConnection(socket);
NeovimApi api = NeovimApis.getApiForConnection(localConnection); // Create regular API
NeovimRxApi rxApi = new NeovimRxWrapper(api); // And then wrap it in RxJava2 interface
api.getCurrentLine().thenAccept(System.out::println);
rxApi.getCurrentLine().subscribe(System.out::println);
Handler annotations
Handler annotations module provides a way to register listeners for requests and notifications using annotations and different handlers. There needs to be an
object with methods annotated with NeovimNotificationHandler
or NeovimRequestHandler
. These methods need to take only one parameter, NotificationMessage
and RequestMessage
respectively.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>handler-annotations</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:handler-annotations:${neovimjava.version}'
Example usage:
class AHandlerClass {
@NeovimNotificationHandler("redraw")
public void handleRedraw(NotificationMessage message) {
System.out.println("Received a message: " + message);
}
}
NeovimHandlerManager neovimHandlerManager = new NeovimHandlerManager();
neovimHandlerManager.registerNeovimHandler(new AHandlerClass());
neovimHandlerManager.attachToStream(neovimStream);
By default, all notifications and requests will be blocking. If you need a different behaviour, you can use a different constructor for NeovimHandlerManager
.
NeovimHandlerManager customHandlerManager = new NeovimHandlerManager(new NeovimHandlerProxy(customExecutorService));
Neovim notifications
Neovim notifications module provides a way to receive notifications through Java 9 Flows. Besides that it provides models for all neovim notifications, which can only be checked using instanceof operator currently, but provide an easier way to parse data.
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>neovim-notifications</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:neovim-notifications:${neovimjava.version}'
Example usage:
NeovimNotificationHandler notificationHandler = new NeovimNotificationHandler(streamer);
notificationHandler.uiEvents().subscribe(uiEventSubscriber);
notificationHandler.bufferEvents().subscribe(bufferEventSubscriber);
API Explorer
Simple JavaFX application loading Neovim API information using nvim --api-info
. Displays all loaded information in tables for simple overview.
It can also be used as an example of usage of library.
Plugin host
This is the plugin host module, the main module when developing neovim remote plugins. It uses rest of the core modules to communicate with neovim and it provides annotations to easily define commands and autocommands. It can also be used to create hosted plugins, which can’t be run on their own, but depend on neovim-java-plugin-host.
It provides NeovimJavaPluginHost
which holds everything needed to communicate with neovim. It provides a simple start
method to connect to neovim via stdio
(useful for plugins). It immediately loads API info, to be a able to provide PluginApi
functionality, which enables easy command and autocommand creation.
This module also provides annotations for even easier command and autocommand creation (@NeovimCommand
and @NeovimAutocommand
).
Include it in your dependencies: Maven:
<dependency>
<groupId>com.ensarsarajcic.neovim.java</groupId>
<artifactId>plugin-host</artifactId>
<version>${neovimjava.version}</version>
</dependency>
Gradle:
compile 'com.ensarsarajcic.neovim.java:plugin-host:${neovimjava.version}'
Example of usage:
import com.ensarsarajcic.neovim.java.pluginhost.NeovimJavaPluginHost;
public final class Plugin {
public static void main(String[] args) {
var host = new NeovimJavaPluginHost();
host.start().thenCompose(() -> {
host.getApi().executeCommand("echo \"hello from java\"");
});
}
}
Example of hosted usage:
import com.ensarsarajcic.neovim.java.corerpc.message.RequestMessage;
import com.ensarsarajcic.neovim.java.pluginhost.NeovimJavaPluginHost;
import com.ensarsarajcic.neovim.java.pluginhost.annotations.NeovimCommand;
import com.ensarsarajcic.neovim.java.pluginhost.annotations.NeovimJavaHostedPlugin;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
@NeovimJavaHostedPlugin
public final class HostedPlugin {
private final NeovimJavaPluginHost host;
public HostedPlugin(NeovimJavaPluginHost host) {
this.host = host;
}
// Hook before initializing all the plugins
// useful to add autocommand groups to be used with annotations
public CompletableFuture<Void> prepare() {
host.getApi().createAugroup("MyHostedPluginGroup", new HashMap<>());
}
// Hook after all plugins have been initialized
// All hooks can either return CompletableFuture<Void> or void
public void onReady() {
}
// All command handlers can either take `RequestMessage` or `CommandState`
// NOTE: if sync = false, take `NotificationMessage` instead
@NeovimCommand(value = "MyHostedPluginCommand", sync = true)
public String incrementCalls(RequestMessage message)
throws NeovimRequestException {
// command handlers can throw NeovimRequestException to return error to neovim
return "Hello from java";
}
// All autocommand handlers can either take `NotificationMessage` or `AutocommandState`
// NOTE: if sync = true, take `RequestMessage` instead
@NeovimAutocommand(value = {"BufRead"}, pattern = "*", group = "MyHostedPluginGroup")
public void onBufRead(AutocommandState autocommandState) {
System.err.println("Autocommand: " + autocommandState);
}
}
NOTE: @NeovimJavaHostedPlugin
annotation can be used in standalone examples too. Any class annotated with it will automatically be registered and any @NeovimCommand
and @NeovimAutocommand
annotations will be processed.
Check out rplugin-example
and rplugin-hosted-example
too.
Plugins common host
Check out neovim-java-plugin-host for guide on how to use it.
Neovim-java logo is based on Neovim logo by Jason Long, CC BY 3.0. Modified logo is also licensed under CC BY 3.0 and available on this location.