mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 02:24:58 +02:00
First working plugin
This commit is contained in:
34
src/cli.rs
34
src/cli.rs
@ -64,6 +64,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
command("get", get::get),
|
||||
command("enter", enter::enter),
|
||||
command("exit", exit::exit),
|
||||
command("inc", plugin_inc::plugin_inc),
|
||||
command("lines", lines::lines),
|
||||
command("pick", pick::pick),
|
||||
command("split-column", split_column::split_column),
|
||||
@ -170,39 +171,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
&language_reporting::DefaultConfig,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// match err {
|
||||
// ShellError::Diagnostic(diag) => {
|
||||
// let host = context.host.lock().unwrap();
|
||||
// let writer = host.err_termcolor();
|
||||
// line.push_str(" ");
|
||||
// let files = crate::parser::Files::new(line);
|
||||
|
||||
// language_reporting::emit(
|
||||
// &mut writer.lock(),
|
||||
// &files,
|
||||
// &diag.diagnostic,
|
||||
// &language_reporting::DefaultConfig,
|
||||
// )
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// ShellError::TypeError(desc) => context
|
||||
// .host
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .stdout(&format!("TypeError: {}", desc)),
|
||||
|
||||
// ShellError::MissingProperty { subpath, .. } => context
|
||||
// .host
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .stdout(&format!("Missing property {}", subpath)),
|
||||
|
||||
// ShellError::String(_) => {
|
||||
// context.host.lock().unwrap().stdout(&format!("{}", err))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
LineResult::Break => {
|
||||
|
@ -18,6 +18,7 @@ crate mod lines;
|
||||
crate mod ls;
|
||||
crate mod open;
|
||||
crate mod pick;
|
||||
crate mod plugin_inc;
|
||||
crate mod ps;
|
||||
crate mod reject;
|
||||
crate mod save;
|
||||
@ -42,5 +43,5 @@ crate mod where_;
|
||||
crate use command::command;
|
||||
crate use config::Config;
|
||||
crate use open::Open;
|
||||
crate use where_::Where;
|
||||
crate use skip_while::SkipWhile;
|
||||
crate use where_::Where;
|
||||
|
@ -6,6 +6,7 @@ use crate::parser::{
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Getters)]
|
||||
@ -51,14 +52,14 @@ pub struct SinkCommandArgs {
|
||||
pub input: Vec<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum CommandAction {
|
||||
ChangePath(PathBuf),
|
||||
Enter(Value),
|
||||
Exit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ReturnValue {
|
||||
Value(Value),
|
||||
Action(CommandAction),
|
||||
|
97
src/commands/plugin_inc.rs
Normal file
97
src/commands/plugin_inc.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::registry::{CommandConfig, PositionalType};
|
||||
use crate::parser::Spanned;
|
||||
use crate::prelude::*;
|
||||
use serde::{self, Deserialize, Serialize};
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::io::{Read, Write};
|
||||
use subprocess::Exec;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct JsonRpc<T> {
|
||||
jsonrpc: String,
|
||||
pub method: String,
|
||||
pub params: T,
|
||||
}
|
||||
impl<T> JsonRpc<T> {
|
||||
pub fn new<U: Into<String>>(method: U, params: T) -> Self {
|
||||
JsonRpc {
|
||||
jsonrpc: "2.0".into(),
|
||||
method: method.into(),
|
||||
params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "method")]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum NuResult {
|
||||
response { params: VecDeque<ReturnValue> },
|
||||
}
|
||||
|
||||
pub fn plugin_inc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let args = if let Some(ref positional) = args.args.positional {
|
||||
positional.clone()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let stream = input
|
||||
.map(move |v| {
|
||||
let mut process = Exec::cmd("./target/debug/nu_plugin_inc");
|
||||
process = process.stdout(subprocess::Redirection::Pipe);
|
||||
process = process.stdin(subprocess::Redirection::Pipe);
|
||||
|
||||
let mut popen = process.popen().unwrap();
|
||||
|
||||
let mut stdout = popen.stdout.take().unwrap();
|
||||
let mut stdin = popen.stdin.take().unwrap();
|
||||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
let request = JsonRpc::new("init", args.clone());
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
stdin.write(format!("{}\n", request_raw).as_bytes());
|
||||
|
||||
// println!("Wrote out init");
|
||||
// println!("{:?}", v);
|
||||
let request = JsonRpc::new("filter", v);
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
stdin.write(format!("{}\n", request_raw).as_bytes());
|
||||
|
||||
let mut input = String::new();
|
||||
match reader.read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let response = serde_json::from_str::<NuResult>(&input);
|
||||
match response {
|
||||
Ok(NuResult::response { params }) => {
|
||||
let request: JsonRpc<std::vec::Vec<Value>> =
|
||||
JsonRpc::new("quit", vec![]);
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
stdin.write(format!("{}\n", request_raw).as_bytes());
|
||||
params
|
||||
}
|
||||
Err(_) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Error while processing input"),
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(x) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Error while processing input"),
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Ok(stream.boxed())
|
||||
}
|
@ -151,7 +151,7 @@ impl ShellError {
|
||||
}
|
||||
}
|
||||
|
||||
crate fn string(title: impl Into<String>) -> ShellError {
|
||||
pub fn string(title: impl Into<String>) -> ShellError {
|
||||
ShellError::String(StringError::new(title.into(), Value::nothing()))
|
||||
}
|
||||
|
||||
|
26
src/lib.rs
Normal file
26
src/lib.rs
Normal file
@ -0,0 +1,26 @@
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(async_await)]
|
||||
#![feature(try_trait)]
|
||||
#![feature(bind_by_move_pattern_guards)]
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod context;
|
||||
mod env;
|
||||
mod errors;
|
||||
mod evaluate;
|
||||
mod format;
|
||||
mod git;
|
||||
mod object;
|
||||
mod parser;
|
||||
mod prelude;
|
||||
mod shell;
|
||||
mod stream;
|
||||
|
||||
pub use crate::commands::command::ReturnValue;
|
||||
pub use crate::parser::Spanned;
|
||||
pub use cli::cli;
|
||||
pub use errors::ShellError;
|
||||
pub use object::base::{Primitive, Value};
|
||||
pub use parser::parse::text::Text;
|
18
src/main.rs
18
src/main.rs
@ -4,26 +4,10 @@
|
||||
#![feature(try_trait)]
|
||||
#![feature(bind_by_move_pattern_guards)]
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod context;
|
||||
mod env;
|
||||
mod errors;
|
||||
mod evaluate;
|
||||
mod format;
|
||||
mod git;
|
||||
mod object;
|
||||
mod parser;
|
||||
mod prelude;
|
||||
mod shell;
|
||||
mod stream;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use log::LevelFilter;
|
||||
use std::error::Error;
|
||||
|
||||
crate use parser::parse::text::Text;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let matches = App::new("nu shell")
|
||||
.version("0.5")
|
||||
@ -72,6 +56,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
builder.try_init()?;
|
||||
|
||||
futures::executor::block_on(crate::cli::cli())?;
|
||||
futures::executor::block_on(nu::cli())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -398,36 +398,36 @@ impl Value {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn block(e: hir::Expression, source: Text) -> Value {
|
||||
pub fn block(e: hir::Expression, source: Text) -> Value {
|
||||
Value::Block(Block::new(e, source))
|
||||
}
|
||||
|
||||
crate fn string(s: impl Into<String>) -> Value {
|
||||
pub fn string(s: impl Into<String>) -> Value {
|
||||
Value::Primitive(Primitive::String(s.into()))
|
||||
}
|
||||
|
||||
crate fn bytes(s: impl Into<u128>) -> Value {
|
||||
pub fn bytes(s: impl Into<u128>) -> Value {
|
||||
Value::Primitive(Primitive::Bytes(s.into()))
|
||||
}
|
||||
|
||||
crate fn int(s: impl Into<i64>) -> Value {
|
||||
pub fn int(s: impl Into<i64>) -> Value {
|
||||
Value::Primitive(Primitive::Int(s.into()))
|
||||
}
|
||||
|
||||
crate fn float(s: impl Into<OF64>) -> Value {
|
||||
pub fn float(s: impl Into<OF64>) -> Value {
|
||||
Value::Primitive(Primitive::Float(s.into()))
|
||||
}
|
||||
|
||||
crate fn boolean(s: impl Into<bool>) -> Value {
|
||||
pub fn boolean(s: impl Into<bool>) -> Value {
|
||||
Value::Primitive(Primitive::Boolean(s.into()))
|
||||
}
|
||||
|
||||
crate fn system_date(s: SystemTime) -> Value {
|
||||
pub fn system_date(s: SystemTime) -> Value {
|
||||
Value::Primitive(Primitive::Date(s.into()))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn date_from_str(s: &str) -> Result<Value, ShellError> {
|
||||
pub fn date_from_str(s: &str) -> Result<Value, ShellError> {
|
||||
let date = DateTime::parse_from_rfc3339(s)
|
||||
.map_err(|err| ShellError::string(&format!("Date parse error: {}", err)))?;
|
||||
|
||||
@ -437,19 +437,19 @@ impl Value {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn system_date_result(s: Result<SystemTime, std::io::Error>) -> Value {
|
||||
pub fn system_date_result(s: Result<SystemTime, std::io::Error>) -> Value {
|
||||
match s {
|
||||
Ok(time) => Value::Primitive(Primitive::Date(time.into())),
|
||||
Err(err) => Value::Error(Box::new(ShellError::string(format!("{}", err)))),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn nothing() -> Value {
|
||||
pub fn nothing() -> Value {
|
||||
Value::Primitive(Primitive::Nothing)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn list(values: impl Into<Vec<Value>>) -> Value {
|
||||
pub fn list(values: impl Into<Vec<Value>>) -> Value {
|
||||
Value::List(values.into())
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ crate use parse::flag::Flag;
|
||||
crate use parse::operator::Operator;
|
||||
crate use parse::parser::{nom_input, pipeline};
|
||||
crate use parse::pipeline::{Pipeline, PipelineElement};
|
||||
crate use parse::span::{Span, Spanned};
|
||||
pub use parse::span::{Span, Spanned};
|
||||
crate use parse::text::Text;
|
||||
crate use parse::token_tree::TokenNode;
|
||||
crate use parse::tokens::{RawToken, Token};
|
||||
|
@ -8,8 +8,8 @@ use serde_derive::{Deserialize, Serialize};
|
||||
)]
|
||||
#[get = "crate"]
|
||||
pub struct Spanned<T> {
|
||||
crate span: Span,
|
||||
crate item: T,
|
||||
pub span: Span,
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for Spanned<T> {
|
||||
|
100
src/plugins/inc.rs
Normal file
100
src/plugins/inc.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use nu::{Primitive, ReturnValue, ShellError, Spanned, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
|
||||
/// A wrapper for proactive notifications to the IDE (eg. diagnostics). These must
|
||||
/// follow the JSON 2.0 RPC spec
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct JsonRpc<T> {
|
||||
jsonrpc: String,
|
||||
pub method: String,
|
||||
pub params: T,
|
||||
}
|
||||
impl<T> JsonRpc<T> {
|
||||
pub fn new<U: Into<String>>(method: U, params: T) -> Self {
|
||||
JsonRpc {
|
||||
jsonrpc: "2.0".into(),
|
||||
method: method.into(),
|
||||
params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_response<T: Serialize>(result: T) {
|
||||
let response = JsonRpc::new("response", result);
|
||||
let response_raw = serde_json::to_string(&response).unwrap();
|
||||
println!("{}", response_raw);
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "method")]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum NuCommand {
|
||||
init { params: Vec<Spanned<Value>> },
|
||||
filter { params: Value },
|
||||
quit,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut inc_by = 1;
|
||||
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
match io::stdin().read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let command = serde_json::from_str::<NuCommand>(&input);
|
||||
|
||||
match command {
|
||||
Ok(NuCommand::init { params }) => {
|
||||
for param in params {
|
||||
match param {
|
||||
Spanned {
|
||||
item: Value::Primitive(Primitive::Int(i)),
|
||||
..
|
||||
} => {
|
||||
inc_by = i;
|
||||
}
|
||||
_ => {
|
||||
send_response(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Unrecognized type in params"),
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(NuCommand::filter { params }) => match params {
|
||||
Value::Primitive(Primitive::Int(i)) => {
|
||||
send_response(vec![ReturnValue::Value(Value::int(i + inc_by))]);
|
||||
}
|
||||
Value::Primitive(Primitive::Bytes(b)) => {
|
||||
send_response(vec![ReturnValue::Value(Value::bytes(
|
||||
b + inc_by as u128,
|
||||
))]);
|
||||
}
|
||||
_ => {
|
||||
send_response(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Unrecognized type in stream"),
|
||||
))));
|
||||
}
|
||||
},
|
||||
Ok(NuCommand::quit) => {
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
send_response(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Unrecognized type in stream"),
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
send_response(ReturnValue::Value(Value::Error(Box::new(
|
||||
ShellError::string("Unrecognized type in stream"),
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user