mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 09:53:43 +01:00
268 lines
5.9 KiB
Plaintext
Executable File
268 lines
5.9 KiB
Plaintext
Executable File
#!/usr/bin/env -S nu --stdin
|
|
# Example of using a Nushell script as a Nushell plugin
|
|
#
|
|
# This is a port of the nu_plugin_python_example plugin to Nushell itself. There is probably not
|
|
# really any reason to write a Nushell plugin in Nushell, but this is a fun proof of concept, and
|
|
# it also allows us to test the plugin interface with something manually implemented in a scripting
|
|
# language without adding any extra dependencies to our tests.
|
|
|
|
const NUSHELL_VERSION = "0.98.1"
|
|
const PLUGIN_VERSION = "0.1.1" # bump if you change commands!
|
|
|
|
def main [--stdio] {
|
|
if ($stdio) {
|
|
start_plugin
|
|
} else {
|
|
print -e "Run me from inside nushell!"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
const SIGNATURES = [
|
|
{
|
|
sig: {
|
|
name: nu_plugin_nu_example,
|
|
description: "Signature test for Nushell plugin in Nushell",
|
|
extra_description: "",
|
|
required_positional: [
|
|
[
|
|
name,
|
|
desc,
|
|
shape
|
|
];
|
|
[
|
|
a,
|
|
"required integer value",
|
|
Int
|
|
],
|
|
[
|
|
b,
|
|
"required string value",
|
|
String
|
|
]
|
|
],
|
|
optional_positional: [
|
|
[
|
|
name,
|
|
desc,
|
|
shape
|
|
];
|
|
[
|
|
opt,
|
|
"Optional number",
|
|
Int
|
|
]
|
|
],
|
|
rest_positional: {
|
|
name: rest,
|
|
desc: "rest value string",
|
|
shape: String
|
|
},
|
|
named: [
|
|
[
|
|
long,
|
|
short,
|
|
arg,
|
|
required,
|
|
desc
|
|
];
|
|
[
|
|
help,
|
|
h,
|
|
null,
|
|
false,
|
|
"Display the help message for this command"
|
|
],
|
|
[
|
|
flag,
|
|
f,
|
|
null,
|
|
false,
|
|
"a flag for the signature"
|
|
],
|
|
[
|
|
named,
|
|
n,
|
|
String,
|
|
false,
|
|
"named string"
|
|
]
|
|
],
|
|
input_output_types: [
|
|
[Any, Any]
|
|
],
|
|
allow_variants_without_examples: true,
|
|
search_terms: [
|
|
Example
|
|
],
|
|
is_filter: false,
|
|
creates_scope: false,
|
|
allows_unknown_args: false,
|
|
category: Experimental
|
|
},
|
|
examples: []
|
|
}
|
|
]
|
|
|
|
def process_call [
|
|
id: int,
|
|
plugin_call: record<
|
|
name: string,
|
|
call: record<
|
|
head: record<start: int, end: int>,
|
|
positional: list,
|
|
named: list,
|
|
>,
|
|
input: any
|
|
>
|
|
] {
|
|
# plugin_call is a dictionary with the information from the call
|
|
# It should contain:
|
|
# - The name of the call
|
|
# - The call data which includes the positional and named values
|
|
# - The input from the pipeline
|
|
|
|
# Use this information to implement your plugin logic
|
|
|
|
# Print the call to stderr, in raw nuon and as a table
|
|
$plugin_call | to nuon --raw | print -e
|
|
$plugin_call | table -e | print -e
|
|
|
|
# Get the span from the call
|
|
let span = $plugin_call.call.head
|
|
|
|
# Create a Value of type List that will be encoded and sent to Nushell
|
|
let value = {
|
|
Value: [{
|
|
List: {
|
|
vals: (0..9 | each { |x|
|
|
{
|
|
Record: {
|
|
val: (
|
|
[one two three] |
|
|
zip (0..2 | each { |y|
|
|
{
|
|
Int: {
|
|
val: ($x * $y),
|
|
span: $span,
|
|
}
|
|
}
|
|
}) |
|
|
into record
|
|
),
|
|
span: $span
|
|
}
|
|
}
|
|
}),
|
|
span: $span
|
|
}
|
|
}, null]
|
|
}
|
|
|
|
write_response $id { PipelineData: $value }
|
|
}
|
|
|
|
def tell_nushell_encoding [] {
|
|
print -n "\u{0004}json"
|
|
}
|
|
|
|
def tell_nushell_hello [] {
|
|
# A `Hello` message is required at startup to inform nushell of the protocol capabilities and
|
|
# compatibility of the plugin. The version specified should be the version of nushell that this
|
|
# plugin was tested and developed against.
|
|
let hello = {
|
|
Hello: {
|
|
protocol: "nu-plugin", # always this value
|
|
version: $NUSHELL_VERSION,
|
|
features: []
|
|
}
|
|
}
|
|
$hello | to json --raw | print
|
|
}
|
|
|
|
def write_response [id: int, response: record] {
|
|
# Use this format to send a response to a plugin call. The ID of the plugin call is required.
|
|
let wrapped_response = {
|
|
CallResponse: [
|
|
$id,
|
|
$response,
|
|
]
|
|
}
|
|
$wrapped_response | to json --raw | print
|
|
}
|
|
|
|
def write_error [id: int, text: string, span?: record<start: int, end: int>] {
|
|
# Use this error format to send errors to nushell in response to a plugin call. The ID of the
|
|
# plugin call is required.
|
|
let error = if ($span | is-not-empty) {
|
|
{
|
|
Error: {
|
|
msg: "ERROR from plugin",
|
|
labels: [
|
|
{
|
|
text: $text,
|
|
span: $span,
|
|
}
|
|
],
|
|
}
|
|
}
|
|
} else {
|
|
{
|
|
Error: {
|
|
msg: "ERROR from plugin",
|
|
help: $text,
|
|
}
|
|
}
|
|
}
|
|
write_response $id $error
|
|
}
|
|
|
|
def handle_input []: any -> nothing {
|
|
match $in {
|
|
{ Hello: $hello } => {
|
|
if ($hello.version != $NUSHELL_VERSION) {
|
|
exit 1
|
|
}
|
|
}
|
|
"Goodbye" => {
|
|
exit 0
|
|
}
|
|
{ Call: [$id, $plugin_call] } => {
|
|
match $plugin_call {
|
|
"Metadata" => {
|
|
write_response $id {
|
|
Metadata: {
|
|
version: $PLUGIN_VERSION
|
|
}
|
|
}
|
|
}
|
|
"Signature" => {
|
|
write_response $id { Signature: $SIGNATURES }
|
|
}
|
|
{ Run: $call_info } => {
|
|
process_call $id $call_info
|
|
}
|
|
_ => {
|
|
write_error $id $"Operation not supported: ($plugin_call | to json --raw)"
|
|
}
|
|
}
|
|
}
|
|
$other => {
|
|
print -e $"Unknown message: ($other | to json --raw)"
|
|
exit 1
|
|
}
|
|
}
|
|
}
|
|
|
|
def start_plugin [] {
|
|
lines |
|
|
prepend (do {
|
|
# This is a hack so that we do this first, but we can also take input as a stream
|
|
tell_nushell_encoding
|
|
tell_nushell_hello
|
|
[]
|
|
}) |
|
|
each { from json | handle_input } |
|
|
ignore
|
|
}
|