mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 14:40:06 +02:00
Add GetSpanContents
engine call (#12439)
# Description This allows plugins to view the source code of spans. Requested by @ayax79 for implementing `polars ls`. Note that this won't really help you find the location of the span. I'm planning to add another engine call that will return information more similar to what shows up in the miette diagnostics, with filename / line number / some context, but I'll want to refactor some of the existing logic to make that happen, so it was easier to just do this first. I hope this is enough to at least have something somewhat useful show up for `polars ls`. # User-Facing Changes - Example plugin: added `example view span` command # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting - [ ] Add to plugin protocol reference
This commit is contained in:
@ -3,7 +3,7 @@ use nu_engine::{get_eval_block_with_early_return, get_full_help};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, EngineState, Redirection, Stack},
|
||||
Config, IntoSpanned, IoStream, PipelineData, PluginIdentity, ShellError, Spanned, Value,
|
||||
Config, IntoSpanned, IoStream, PipelineData, PluginIdentity, ShellError, Span, Spanned, Value,
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@ -32,6 +32,8 @@ pub trait PluginExecutionContext: Send + Sync {
|
||||
fn add_env_var(&mut self, name: String, value: Value) -> Result<(), ShellError>;
|
||||
/// Get help for the current command
|
||||
fn get_help(&self) -> Result<Spanned<String>, ShellError>;
|
||||
/// Get the contents of a [`Span`]
|
||||
fn get_span_contents(&self, span: Span) -> Result<Spanned<Vec<u8>>, ShellError>;
|
||||
/// Evaluate a closure passed to the plugin
|
||||
fn eval_closure(
|
||||
&self,
|
||||
@ -150,6 +152,14 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
|
||||
.into_spanned(self.call.head))
|
||||
}
|
||||
|
||||
fn get_span_contents(&self, span: Span) -> Result<Spanned<Vec<u8>>, ShellError> {
|
||||
Ok(self
|
||||
.engine_state
|
||||
.get_span_contents(span)
|
||||
.to_vec()
|
||||
.into_spanned(self.call.head))
|
||||
}
|
||||
|
||||
fn eval_closure(
|
||||
&self,
|
||||
closure: Spanned<Closure>,
|
||||
@ -271,6 +281,12 @@ impl PluginExecutionContext for PluginExecutionBogusContext {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_span_contents(&self, _span: Span) -> Result<Spanned<Vec<u8>>, ShellError> {
|
||||
Err(ShellError::NushellFailed {
|
||||
msg: "get_span_contents not implemented on bogus".into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_closure(
|
||||
&self,
|
||||
_closure: Spanned<Closure>,
|
||||
|
@ -11,7 +11,7 @@ use crate::protocol::{
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::Closure, Config, IntoInterruptiblePipelineData, LabeledError, ListStream, PipelineData,
|
||||
PluginSignature, ShellError, Spanned, Value,
|
||||
PluginSignature, ShellError, Span, Spanned, Value,
|
||||
};
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap, HashMap},
|
||||
@ -646,6 +646,22 @@ impl EngineInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the contents of a [`Span`] from the engine.
|
||||
///
|
||||
/// This method returns `Vec<u8>` as it's possible for the matched span to not be a valid UTF-8
|
||||
/// string, perhaps because it sliced through the middle of a UTF-8 byte sequence, as the
|
||||
/// offsets are byte-indexed. Use [`String::from_utf8_lossy()`] for display if necessary.
|
||||
pub fn get_span_contents(&self, span: Span) -> Result<Vec<u8>, ShellError> {
|
||||
match self.engine_call(EngineCall::GetSpanContents(span))? {
|
||||
EngineCallResponse::PipelineData(PipelineData::Value(Value::Binary { val, .. }, _)) => {
|
||||
Ok(val)
|
||||
}
|
||||
_ => Err(ShellError::PluginFailedToDecode {
|
||||
msg: "Received unexpected response type for EngineCall::GetSpanContents".into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the engine to evaluate a closure. Input to the closure is passed as a stream, and the
|
||||
/// output is available as a stream.
|
||||
///
|
||||
|
@ -978,6 +978,24 @@ fn interface_get_help() -> Result<(), ShellError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_get_span_contents() -> Result<(), ShellError> {
|
||||
let test = TestCase::new();
|
||||
let manager = test.engine();
|
||||
let interface = manager.interface_for_context(0);
|
||||
|
||||
start_fake_plugin_call_responder(manager, 1, move |_| {
|
||||
EngineCallResponse::value(Value::test_binary(b"test string"))
|
||||
});
|
||||
|
||||
let contents = interface.get_span_contents(Span::test_data())?;
|
||||
|
||||
assert_eq!(b"test string", &contents[..]);
|
||||
|
||||
assert!(test.has_unconsumed_write());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_eval_closure_with_stream() -> Result<(), ShellError> {
|
||||
let test = TestCase::new();
|
||||
|
@ -1180,6 +1180,13 @@ pub(crate) fn handle_engine_call(
|
||||
help.item, help.span,
|
||||
)))
|
||||
}
|
||||
EngineCall::GetSpanContents(span) => {
|
||||
let contents = context.get_span_contents(span)?;
|
||||
Ok(EngineCallResponse::value(Value::binary(
|
||||
contents.item,
|
||||
contents.span,
|
||||
)))
|
||||
}
|
||||
EngineCall::EvalClosure {
|
||||
closure,
|
||||
positional,
|
||||
|
@ -463,6 +463,8 @@ pub enum EngineCall<D> {
|
||||
AddEnvVar(String, Value),
|
||||
/// Get help for the current command
|
||||
GetHelp,
|
||||
/// Get the contents of a span. Response is a binary which may not parse to UTF-8
|
||||
GetSpanContents(Span),
|
||||
/// Evaluate a closure with stream input/output
|
||||
EvalClosure {
|
||||
/// The closure to call.
|
||||
@ -491,6 +493,7 @@ impl<D> EngineCall<D> {
|
||||
EngineCall::GetCurrentDir => "GetCurrentDir",
|
||||
EngineCall::AddEnvVar(..) => "AddEnvVar",
|
||||
EngineCall::GetHelp => "GetHelp",
|
||||
EngineCall::GetSpanContents(_) => "GetSpanContents",
|
||||
EngineCall::EvalClosure { .. } => "EvalClosure",
|
||||
}
|
||||
}
|
||||
@ -509,6 +512,7 @@ impl<D> EngineCall<D> {
|
||||
EngineCall::GetCurrentDir => EngineCall::GetCurrentDir,
|
||||
EngineCall::AddEnvVar(name, value) => EngineCall::AddEnvVar(name, value),
|
||||
EngineCall::GetHelp => EngineCall::GetHelp,
|
||||
EngineCall::GetSpanContents(span) => EngineCall::GetSpanContents(span),
|
||||
EngineCall::EvalClosure {
|
||||
closure,
|
||||
positional,
|
||||
|
Reference in New Issue
Block a user