1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! # Scorched earth
//!
//! A minimal port of
//! [scorched earth by boxofrox](https://github.com/boxofrox/neovim-scorched-earth)
//! to nvim-rs. Works the same, but foregoes any error handling, removes the
//! customisation of color, and removes some abstractions that aren't helpfull
//! in an example.
//!
//! Note that this example uses `tokio`, while `scorched_earth_as` uses
//! async-std.
//!
//! ## Usage
//!
//! First, build this example via
//!
//! ```sh
//! cargo build --example scorched_earth
//! ```
//!
//! The binary will be located in `target/debug/examples/scorched_earth`.
//!
//! Follow the
//! [steps](https://github.com/boxofrox/neovim-scorched-earth#try-it-out)
//! described for the original plugin. Of course, you don't need to build it.
//! Before step 4, put
//!
//! ```viml
//! let g:scorched_earth_program = '<path to nvim-rs>/target/debug/examples/scorched_earth'
//! ```
//!
//! into `init.vim`. That's it, fire it up and enjoy.
//!
//! ## Description
//!
//! Some overview over the code:
//!
//! * The associated type for our [`Handler`](crate::rpc::handler::Handler) is
//! out stdout. But tokio's [`Stdout`](tokio::io::Stdout) does not implement
//! [`futures::io::AsyncWrite`](futures::io::AsyncWrite), so it needs to be
//! wrapped in the provided [`Compat`](crate::compat::tokio::Compat) type.
//!
//! * The handler struct `NeovimHandler` needs to contain some plugin state,
//! namely two cursor positions `start` and `end`. It needs to be `Send` and
//! `Sync`, and we need mutable access, so we wrap it in a `Arc<Mutex<_>>`.
//!
//! * Implementing the [`Handler`](crate::Handler) trait requires some magic
//! because of the async functions, we we use the
//! [`async_trait`](https://docs.rs/async-trait/0.1.21/async_trait/) macro.
//!
//! * We use `Stdout` as the type for the `Writer` because neovim acts as our
//! parent, so it reads from our stdout. Note that this is the [async
//! version](tokio::io::Stdout) from Tokio.
//!
//! * We only implement `handle_notify` since we don't want to serve requests.
//! It gets a [`Neovim`](crate::Neovim) passed that we can use to send
//! requests to neovim. All requests are async methods, so we need to `await`
//! them.
//!
//! * The main function is denoted `#[tokio::main]` to use async notation, but
//! it would be perfectly feasible to explicitely create a runtime and use that.
//!
//! * After creation of the handler, we connect to neovim via one of the
//! [`new_*`](crate::create) functions. It gives back a
//! [`Neovim`](crate::Neovim) instance which we could use for requests, and a
//! handle for the io loop.
//!
//! * The plugin quits by ending the IO task when neovim closes the channel, so
//!   we don't need to do anything special. Any cleanup-logic can happen after
//!   the IO task has finished. Note that we're loosing access to our
//!   [`Handler`](crate::Handler), so we might need to implement
//!   [`Drop`](std::ops::Drop) for it, see the
//!   [example](crate::examples::handler_drop).
//!
//! * After the IO task has finished, we're inspecting the errors to see why it
//! went. A join error simply gets printed, then we inspect potential errors
//! from the io loop itself. First, if we did not see a general reader error, we
//! try to send some last notification to the neovim user. Secondly, we quietly
//! ignore the channel being closed, because this usually means that it was
//! closed by neovim, which isn't always an error.
//!
//!   *Note*: A closed channel could still mean an error, so the plugin has the
//!   option to react to this.
//!
//! * As with the other examples, we implement [`Spawn`](futures::task::Spawn)
//! for our `NeovimHandler` most trivially.