Skip to content

Commit 31322dd

Browse files
authored
Documentation for engine calls, nushell/nushell#12029 (#1290)
1 parent e0d7495 commit 31322dd

File tree

2 files changed

+371
-18
lines changed

2 files changed

+371
-18
lines changed

Diff for: contributor-book/plugin_protocol_reference.md

+236-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Typical plugin interaction after the initial handshake looks like this:
2525

2626
The plugin **should** respond to further plugin calls. The engine **may** send additional plugin calls before responses have been received, and it is up to the plugin to decide whether to handle each call immediately as it is received, or to process only one at a time and hold on to them for later. In any case, sending another plugin call before a response has been received **should not** cause an error.
2727

28+
The plugin **may** send [engine calls](#enginecall) during the execution of a call to request operations from the engine. Engine calls are [only valid within the context of a call](#enginecall-context) and may not be sent otherwise.
29+
2830
The engine **may** send a [`Goodbye`](#goodbye) message to the plugin indicating that it will no longer send any more plugin calls. Upon receiving this message, the plugin **may** choose not to accept any more plugin calls, and **should** exit after any in-progress plugin calls have finished.
2931

3032
## `Hello`
@@ -79,12 +81,11 @@ Example:
7981

8082
Tell the plugin to run a command. The argument is the following map:
8183

82-
| Field | Type | Usage |
83-
| ---------- | ------------------------------------------- | ----------------------------------------------------- |
84-
| **name** | string | The name of the command to run |
85-
| **call** | [`EvaluatedCall`](#evaluatedcall) | Information about the invocation, including arguments |
86-
| **input** | [`PipelineDataHeader`](#pipelinedataheader) | Pipeline input to the command |
87-
| **config** | [`Value`](#value) or null | Plugin configuration, if available |
84+
| Field | Type | Usage |
85+
| --------- | ------------------------------------------- | ----------------------------------------------------- |
86+
| **name** | string | The name of the command to run |
87+
| **call** | [`EvaluatedCall`](#evaluatedcall) | Information about the invocation, including arguments |
88+
| **input** | [`PipelineDataHeader`](#pipelinedataheader) | Pipeline input to the command |
8889

8990
<a name="evaluatedcall"></a>
9091

@@ -177,6 +178,77 @@ There is currently one supported operation: `ToBaseValue`, which **should** retu
177178
}
178179
```
179180

181+
### `EngineCallResponse`
182+
183+
A response to an [engine call](#enginecall) made by the plugin. The argument is a 2-tuple (array): (`engine_call_id`, `engine_call`)
184+
185+
The `engine_call_id` refers to the same number that the engine call being responded to originally contained. The plugin **must** send unique IDs for each engine call it makes. Like [`CallResponse`](#callresponse), there are multiple types of responses:
186+
187+
#### `Error` engine call response
188+
189+
A failure result. Contains a [`ShellError`](#shellerror).
190+
191+
Example:
192+
193+
```json
194+
{
195+
"EngineCallResponse": [
196+
0,
197+
{
198+
"Error": {
199+
"IOError": {
200+
"msg": "The connection closed."
201+
}
202+
}
203+
}
204+
]
205+
}
206+
```
207+
208+
#### `PipelineData` engine call response
209+
210+
A successful result with a Nu [`Value`](#value) or stream. The body is a [`PipelineDataHeader`](#pipelinedataheader).
211+
212+
Example:
213+
214+
```json
215+
{
216+
"EngineCallResponse": [
217+
0,
218+
{
219+
"ListStream": {
220+
"id": 23
221+
}
222+
}
223+
]
224+
}
225+
```
226+
227+
#### `Config` engine call response
228+
229+
A successful result of a [`Config` engine call](#config-engine-call). The body is a [`Config`](#config).
230+
231+
Example:
232+
233+
```json
234+
{
235+
"EngineCallResponse": [
236+
0,
237+
{
238+
"Config": {
239+
"external_completer": null,
240+
"filesize_metric": true,
241+
"table_mode": "Rounded",
242+
"table_move_header": false,
243+
...
244+
}
245+
}
246+
]
247+
}
248+
```
249+
250+
This example is abbreviated, as the [`Config`](#config) object is large and ever-changing.
251+
180252
### `Goodbye`
181253

182254
Indicate that no further plugin calls are expected, and that the plugin **should** exit as soon as it is finished processing any in-progress plugin calls.
@@ -225,7 +297,7 @@ Example:
225297
}
226298
```
227299

228-
### `Signature` plugin call response
300+
#### `Signature` plugin call response
229301

230302
A successful response to a [`Signature` plugin call](#signature-plugin-call). The body is an array of [signatures](https://docs.rs/nu-protocol/0.90.1/nu_protocol/struct.PluginSignature.html).
231303

@@ -275,9 +347,9 @@ Example:
275347
}
276348
```
277349

278-
### `PipelineData` plugin call response
350+
#### `PipelineData` plugin call response
279351

280-
A successful result with a Nu [`Value`](#value) or stream. The body is a map: see [`PipelineDataHeader`](#pipelinedataheader).
352+
A successful result with a Nu [`Value`](#value) or stream. The body is a [`PipelineDataHeader`](#pipelinedataheader).
281353

282354
Example:
283355

@@ -300,6 +372,113 @@ Example:
300372
}
301373
```
302374

375+
### `EngineCall`
376+
377+
Plugins can make engine calls during execution of a [call](#call). The body is a map with the following keys:
378+
379+
| Field | Type | Usage |
380+
| ----------- | ------------ | --------------------------------------------------------------------------------------- |
381+
| **context** | integer | The ID of the [call](#call) that this engine call relates to. |
382+
| **id** | integer | A unique ID for this engine call, in order to send the [response](#enginecallresponse). |
383+
| **call** | `EngineCall` | One of the options described below. |
384+
385+
<a name="enginecall-context"></a>
386+
387+
The context **must** be an ID of a [call](#call) that was received that is currently in one of two states:
388+
389+
1. The [response](#callresponse) has not been sent yet.
390+
2. The response contained stream data (i.e. [`ListStream`](#pipelinedataheader-liststream) or [`ExternalStream`](#pipelinedataheader-externalstream)), and at least one of the streams started by the response is still sending data (i.e. [`End`](#end) has not been sent).
391+
392+
After a response has been fully sent, and streams have ended, the `context` from that call can no longer be used.
393+
394+
The engine call ID **must** be unique for the lifetime of the plugin, and it is suggested that this be a sequentially increasing number across all engine calls made by the plugin. It is not separated by `context`; the response only contains the `id`.
395+
396+
#### `GetConfig` engine call
397+
398+
Get the Nushell engine configuration. Returns a [`Config` response](#config-engine-call-response) if
399+
successful.
400+
401+
Example:
402+
403+
```json
404+
{
405+
"EngineCall": {
406+
"context": 0,
407+
"id": 0,
408+
"call": "GetConfig"
409+
}
410+
}
411+
```
412+
413+
#### `GetPluginConfig` engine call
414+
415+
Get the configuration for the plugin, from its section in `$env.config.plugins.NAME` if present. Returns a [`PipelineData` response](#pipelinedata-engine-call-response) if successful, which will contain either a [`Value`](#pipelinedataheader-value) or be [`Empty`](#pipelinedataheader-empty) if there is no configuration for the plugin set.
416+
417+
If the plugin configuration was specified as a closure, the engine will evaluate that closure and return the result, which may cause an [error response](#error-engine-call-response).
418+
419+
Example:
420+
421+
```json
422+
{
423+
"EngineCall": {
424+
"context": 3,
425+
"id": 8,
426+
"call": "GetPluginConfig"
427+
}
428+
}
429+
```
430+
431+
#### `EvalClosure` engine call
432+
433+
Pass a [`Closure`](#closure) and arguments to the engine to be evaluated. Returns a [`PipelineData` response](#pipelinedata-engine-call-response) if successful with the output of the closure, which may be a stream.
434+
435+
| Field | Type | Usage |
436+
| ------------------- | ------------------------------------------- | ---------------------------------------------------------------------- |
437+
| **closure** | `Spanned`<[`Closure`](#closure)> | The closure to call, generally from a [`Value`](#value). |
438+
| **positional** | [`Value`](#value) array | Positional arguments for the closure. |
439+
| **input** | [`PipelineDataHeader`](#pipelinedataheader) | Input for the closure. |
440+
| **redirect_stdout** | boolean | Whether to redirect stdout if the closure ends in an external command. |
441+
| **redirect_stderr** | boolean | Whether to redirect stderr if the closure ends in an external command. |
442+
443+
Example:
444+
445+
```json
446+
{
447+
"EngineCall": {
448+
"context": 7,
449+
"id": 40,
450+
"call": {
451+
"EvalClosure": {
452+
"closure": {
453+
"item": {
454+
"block_id": 72,
455+
"captures": []
456+
},
457+
"span": {
458+
"start": 780,
459+
"end": 812
460+
}
461+
},
462+
"positional": [
463+
{
464+
"Int": {
465+
"val": 7,
466+
"span": {
467+
"start": 3080,
468+
"end": 3081
469+
}
470+
}
471+
}
472+
],
473+
"input": "Empty",
474+
"redirect_stdout": true,
475+
"redirect_stderr": false
476+
}
477+
}
478+
}
479+
}
480+
```
481+
303482
## Stream messages
304483

305484
Streams can be sent by both the plugin and the engine. The agent that is sending the stream is known as the _producer_, and the agent that receives the stream is known as the _consumer_.
@@ -638,3 +817,51 @@ Example:
638817
[Documentation](https://docs.rs/nu-protocol/latest/nu_protocol/enum.ShellError.html)
639818

640819
This enum describes errors produced by the engine. As this enum is quite large and grows frequently, it is recommended to try to send this back to the engine without interpreting it, if possible.
820+
821+
### `Config`
822+
823+
[Documentation](https://docs.rs/nu-protocol/latest/nu_protocol/struct.Config.html)
824+
825+
This struct describes the configuration of Nushell. It is quite large and frequently changing, so please refer to the Rust documentation if there is anything you need from it.
826+
827+
### `Closure`
828+
829+
[Documentation](https://docs.rs/nu-protocol/latest/nu_protocol/struct.Closure.html)
830+
831+
This is the representation of a closure within the engine, which references a block ID and a list of captures with variable IDs and their contents. It may be contained within [`Value`](#value).
832+
833+
The plugin **should not** try to inspect the contents of the closure. It is recommended that this is only used as an argument to the [`EvalClosure` engine call](#evalclosure-engine-call). The exact representation of a closure is likely to change in the future to avoid serializing all of the captures.
834+
835+
Example:
836+
837+
```json
838+
{
839+
"block_id": 782,
840+
"captures": [
841+
[
842+
490,
843+
{
844+
"Int": {
845+
"val": 72,
846+
"span": {
847+
"start": 78760,
848+
"end": 78762
849+
}
850+
}
851+
}
852+
],
853+
[
854+
491,
855+
{
856+
"String": {
857+
"val": "Hello",
858+
"span": {
859+
"start": 78770,
860+
"end": 78777
861+
}
862+
}
863+
}
864+
]
865+
]
866+
}
867+
```

0 commit comments

Comments
 (0)