Hello Racketeers,

I wrote a new package, an API client for the Neovim text editor:

With this it is possible to control Neovim with Racket. In particular,
it is now possible to write Neovim plugins in Racket rather than

What is Neovim?

Neovim (or Nvim for short) is a fork of Vim. Unlike the usual "I'll
rewrite Vim in my favourite language" projects which aim to imitate Vim,
but end up being incompatible with most Vim plugins, Neovim intends to
clean up the plumbing, but keep the porcelain the same. This means we
get to keep using the existing ecosystem of extensions and other
techniques, while the core of the editor gets cleaned up and new
features get added.

One such new feature are the language hosts. In Vim if you want to write
plugins in a foreign language you have to compile Vim with support for
that language. For something as common as Python you can be fairly
certain that most users have it and that support in Vim is always up to
date. However, the more you move away from that, the worse it gets. Vim
has support for Racket, but it's still called MzScheme and who knows
when it was last updated.

What is the Racket host?

Neovim externalises the problem. The editor no longer comes with support
for any foreign languages, only VimScript and Lua are the default.
Instead, it defines an API interface so that external programs can
connect to the editor and exchange messages. Among other things this
allows plugins to be written in any language if a host for that language
exists. The maintenance burden is now on the host author and hosts can
be added, removed and swapped without having to re-compile Neovim.
Messages can be sent asynchronously, so it is possible for example to
have semantic code highlighting without blocking:

This type of communication works both ways: Neovim can control a Racket
instance, but Racket can also control a Neovim instance. One could write
a GUI for Neovim in Racket, the GUI would be just a Racket program which
spawns a headless Neovim instance, connects to it, and then sends user
inputer over and receives the result for display.

What I am personally interested in are remote plugins, plugins written
for Neovim in Racket. See the readme of the repo to see what a remote
plugin looks like. I would like to make use of the DrRacket code to
implement IDE-like capabilities as a plugin.

Status of the Racket host

Right now the host is feature-complete, so if anyone wants to try it out
it is ready. There are a few issues I would like to see sorted out
before it can considered to be finished.

- Why is the documentation not building? I can compile the scribble file
  normally, but running `raco setup` does not build it, and it doesn't
  show up on https://docs.racket-lang.org/ either.

- The directory layout is weird. The reason for this is that the repo is
  both a Racket package, and a Neovim plugin and has to be installed as
  both. This works fine, but now Racket seems to think that there is
  also an "autoload", "doc", "plugin" and "test" collection. Is there a
  way to tell Racket "only the nvim directory is a collection"?

- The API bindings (nvim/api.rkt) are a big sore in my eyes. Neovim
  defines a set of "API methods", basically the specifications for
  messages you can send over RPC.
  Right now I'm generating the function bindings using macros, but it
  looks weird:

    ;; Set the current line
    (set-current-line "Hello world!")
    (define two (eval "2"))  ; This is not the Racket eval

  There is no exclamation mark and the API can collide with existing
  names. The name collision can be resolved by adding a prefix like
  `nvim-` or `nvim:`. Which one would be better?

  Would it be better to have separate getter- and setter functions, or
  is it better to use `set!`?

    ;; Setters and getters
    (define line (get-current-line))
    (set-current-line! (string-append line "!!!"))
    ;; using set!
    (define line (current-line))
    (set! (current-line) (string-append line "!!!"))

  At the moment I am generating the API automatically, but I think I'll
  have to do it manually. I can for example append a `!` if the word
  `set_` occurs in the name, but there are also other functions which
  have side effects (e.g. `nvim_del_current_line()`). And some like
  `nvim_call_function()` may or may not mutate state.

  Some API calls are limited to a part of the editor, like a tab page, a
  window or a buffer. What should their names be?

    ;; Two prefixes
    (define line-count (nvim-buffer-line-count buf))
    ;; One prefix
    (define line-count (buffer-line-count buf))
    ;; Keyword argument
    (define line-count (nvim-line-count #:buffer buf))

  Only buffers have a line count, but some concepts apply to multiple
  aspects; for example, there are global variables, buffer variables,
  tab-page variables and window variables.

    ;; Two prefixes
    (define v (nvim-get-var "detail"))
    (define v (nvim-buffer-get-var buf "detail"))
    ;; One prefix
    (define v (nvim-get-var "detail"))
    (define v (buffer-get-var b "detail"))
    ;; Keyword argument
    (define v (nvim-get-var "detail" #:buffer b))

- Finally, about testing, how can I test the code without having to rely
  on Neovim being installed on the testing machine? I want the tests on
  the Racket package repository to pass. Currently (test/connection.rkt)
  I am using byte stream ports, but they have the problem that they are
  always ready for synchronisation, so I have to do ugly hacks to get
  them to work.

  Normally I use a Unix socket to connect Racket and Neovim, but any
  types of binary input and output ports suffice (see `start-client` in
  nvim/client.rkt). Should I use a temporary file created with the
  function `make-temporary-file` instead?

You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to