Merge pull request #49 from nushell/git_branch_completion

Very early proof-of-concept git branch completion
This commit is contained in:
JT 2021-09-14 17:09:21 +12:00 committed by GitHub
commit 22e30d5ea7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 293 additions and 4 deletions

View File

@ -1,7 +1,11 @@
use std::{cell::RefCell, rc::Rc};
use nu_engine::eval_block;
use nu_parser::{flatten_block, parse};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::{
engine::{EngineState, EvaluationContext, Stack, StateWorkingSet},
Value,
};
use reedline::Completer;
pub struct NuCompleter {
@ -26,7 +30,39 @@ impl Completer for NuCompleter {
for flat in flattened {
if pos >= flat.0.start && pos <= flat.0.end {
match flat.1 {
match &flat.1 {
nu_parser::FlatShape::Custom(custom_completion) => {
let prefix = working_set.get_span_contents(flat.0).to_vec();
let (block, ..) =
parse(&mut working_set, None, custom_completion.as_bytes(), false);
let context = EvaluationContext {
engine_state: self.engine_state.clone(),
stack: Stack::default(),
};
let result = eval_block(&context, &block, Value::nothing());
let v: Vec<_> = match result {
Ok(Value::List { vals, .. }) => vals
.into_iter()
.map(move |x| {
let s = x.as_string().expect("FIXME");
(
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
s,
)
})
.filter(|x| x.1.as_bytes().starts_with(&prefix))
.collect(),
_ => vec![],
};
return v;
}
nu_parser::FlatShape::External | nu_parser::FlatShape::InternalCall => {
let prefix = working_set.get_span_contents(flat.0);
let results = working_set.find_commands_by_prefix(prefix);

View File

@ -39,6 +39,7 @@ impl Highlighter for NuHighlighter {
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string();
match shape.1 {
FlatShape::Custom(..) => output.push((Style::new().bold(), next_token)),
FlatShape::External => output.push((Style::new().bold(), next_token)),
FlatShape::ExternalArg => output.push((Style::new().bold(), next_token)),
FlatShape::Garbage => output.push((

View File

@ -6,8 +6,8 @@ use nu_protocol::{
};
use crate::{
where_::Where, Alias, Benchmark, BuildString, Def, Do, Each, For, If, Length, Let, LetEnv, Ls,
Table,
where_::Where, Alias, Benchmark, BuildString, Def, Do, Each, For, Git, GitCheckout, If, Length,
Let, LetEnv, ListGitBranches, Ls, Table,
};
pub fn create_default_context() -> Rc<RefCell<EngineState>> {
@ -48,6 +48,11 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
working_set.add_decl(Box::new(Table));
// This is a WIP proof of concept
working_set.add_decl(Box::new(ListGitBranches));
working_set.add_decl(Box::new(Git));
working_set.add_decl(Box::new(GitCheckout));
let sig = Signature::build("exit");
working_set.add_decl(sig.predeclare());
let sig = Signature::build("vars");

View File

@ -0,0 +1,51 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, Value};
pub struct Git;
impl Command for Git {
fn name(&self) -> &str {
"git"
}
fn usage(&self) -> &str {
"Run a block"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("git")
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
use std::process::Command as ProcessCommand;
use std::process::Stdio;
let proc = ProcessCommand::new("git").stdout(Stdio::piped()).spawn();
match proc {
Ok(child) => {
match child.wait_with_output() {
Ok(val) => {
let result = val.stdout;
Ok(Value::string(&String::from_utf8_lossy(&result), call.head))
}
Err(_err) => {
// FIXME
Ok(Value::nothing())
}
}
}
Err(_err) => {
// FIXME
Ok(Value::nothing())
}
}
}
}

View File

@ -0,0 +1,66 @@
use nu_engine::eval_expression;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct GitCheckout;
impl Command for GitCheckout {
fn name(&self) -> &str {
"git checkout"
}
fn usage(&self) -> &str {
"Checkout a git revision"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("git checkout").required(
"branch",
SyntaxShape::Custom(Box::new(SyntaxShape::String), "list-git-branches".into()),
"the branch to checkout",
)
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
use std::process::Command as ProcessCommand;
use std::process::Stdio;
let block = &call.positional[0];
let out = eval_expression(context, block)?;
let out = out.as_string()?;
let proc = ProcessCommand::new("git")
.arg("checkout")
.arg(out)
.stdout(Stdio::piped())
.spawn();
match proc {
Ok(child) => {
match child.wait_with_output() {
Ok(val) => {
let result = val.stdout;
Ok(Value::string(&String::from_utf8_lossy(&result), call.head))
}
Err(_err) => {
// FIXME
Ok(Value::nothing())
}
}
}
Err(_err) => {
// FIXME
Ok(Value::nothing())
}
}
}
}

View File

@ -6,10 +6,13 @@ mod default_context;
mod do_;
mod each;
mod for_;
mod git;
mod git_checkout;
mod if_;
mod length;
mod let_;
mod let_env;
mod list_git_branches;
mod ls;
mod table;
mod where_;
@ -22,9 +25,12 @@ pub use default_context::create_default_context;
pub use do_::Do;
pub use each::Each;
pub use for_::For;
pub use git::Git;
pub use git_checkout::GitCheckout;
pub use if_::If;
pub use length::Length;
pub use let_::Let;
pub use let_env::LetEnv;
pub use list_git_branches::ListGitBranches;
pub use ls::Ls;
pub use table::Table;

View File

@ -0,0 +1,69 @@
// Note: this is a temporary command that later will be converted into a pipeline
use std::process::Command as ProcessCommand;
use std::process::Stdio;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, Value};
pub struct ListGitBranches;
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
impl Command for ListGitBranches {
fn name(&self) -> &str {
"list-git-branches"
}
fn usage(&self) -> &str {
"List the git branches of the current directory."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("list-git-branches")
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let list_branches = ProcessCommand::new("git")
.arg("branch")
.stdout(Stdio::piped())
.spawn();
if let Ok(child) = list_branches {
if let Ok(output) = child.wait_with_output() {
let val = output.stdout;
let s = String::from_utf8_lossy(&val).to_string();
let lines: Vec<_> = s
.lines()
.filter_map(|x| {
if x.starts_with("* ") {
None
} else {
Some(x.trim())
}
})
.map(|x| Value::String {
val: x.into(),
span: call.head,
})
.collect();
Ok(Value::List {
vals: lines,
span: call.head,
})
} else {
Ok(Value::Nothing { span: call.head })
}
} else {
Ok(Value::Nothing { span: call.head })
}
}
}

View File

@ -16,6 +16,7 @@ pub enum FlatShape {
Signature,
String,
Variable,
Custom(String),
}
pub fn flatten_block(working_set: &StateWorkingSet, block: &Block) -> Vec<(Span, FlatShape)> {
@ -40,6 +41,10 @@ pub fn flatten_expression(
working_set: &StateWorkingSet,
expr: &Expression,
) -> Vec<(Span, FlatShape)> {
if let Some(custom_completion) = &expr.custom_completion {
return vec![(expr.span, FlatShape::Custom(custom_completion.clone()))];
}
match &expr.expr {
Expr::BinaryOp(lhs, op, rhs) => {
let mut output = vec![];

View File

@ -113,6 +113,7 @@ pub fn parse_external_call(
expr: Expr::ExternalCall(name, args),
span: span(spans),
ty: Type::Unknown,
custom_completion: None,
},
None,
)
@ -360,6 +361,7 @@ fn parse_multispan_value(
),
span: arg_span,
ty: Type::Unknown,
custom_completion: None,
},
error,
);
@ -374,6 +376,7 @@ fn parse_multispan_value(
expr: Expr::Keyword(keyword.clone(), keyword_span, Box::new(expr)),
span: arg_span,
ty,
custom_completion: None,
},
error,
)
@ -553,12 +556,14 @@ pub fn parse_call(
expr: Expr::Call(mut call),
span,
ty,
custom_completion: None,
} => {
call.head = orig_span;
Expression {
expr: Expr::Call(call),
span,
ty,
custom_completion: None,
}
}
x => x,
@ -596,12 +601,14 @@ pub fn parse_call(
expr: Expr::Call(mut call),
span,
ty,
custom_completion: None,
} => {
call.head = orig_span;
Expression {
expr: Expr::Call(call),
span,
ty,
custom_completion: None,
}
}
x => x,
@ -644,6 +651,7 @@ pub fn parse_call(
expr: Expr::Call(call),
span: span(spans),
ty: Type::Unknown, // FIXME
custom_completion: None,
},
err,
)
@ -668,6 +676,7 @@ pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) {
expr: Expr::Int(v),
span,
ty: Type::Int,
custom_completion: None,
},
None,
)
@ -688,6 +697,7 @@ pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) {
expr: Expr::Int(v),
span,
ty: Type::Int,
custom_completion: None,
},
None,
)
@ -708,6 +718,7 @@ pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) {
expr: Expr::Int(v),
span,
ty: Type::Int,
custom_completion: None,
},
None,
)
@ -727,6 +738,7 @@ pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) {
expr: Expr::Int(x),
span,
ty: Type::Int,
custom_completion: None,
},
None,
)
@ -745,6 +757,7 @@ pub fn parse_float(token: &[u8], span: Span) -> (Expression, Option<ParseError>)
expr: Expr::Float(x),
span,
ty: Type::Float,
custom_completion: None,
},
None,
)
@ -901,6 +914,7 @@ pub fn parse_range(
expr: Expr::Range(from, next, to, range_op),
span,
ty: Type::Range,
custom_completion: None,
},
None,
)
@ -971,6 +985,7 @@ pub fn parse_string_interpolation(
expr: Expr::String(String::from_utf8_lossy(str_contents).to_string()),
span,
ty: Type::String,
custom_completion: None,
});
}
token_start = b;
@ -1013,6 +1028,7 @@ pub fn parse_string_interpolation(
expr: Expr::String(String::from_utf8_lossy(str_contents).to_string()),
span,
ty: Type::String,
custom_completion: None,
});
}
}
@ -1044,6 +1060,7 @@ pub fn parse_string_interpolation(
})),
span,
ty: Type::String,
custom_completion: None,
},
error,
)
@ -1067,6 +1084,7 @@ pub fn parse_variable_expr(
expr: Expr::Bool(true),
span,
ty: Type::Bool,
custom_completion: None,
},
None,
);
@ -1076,6 +1094,7 @@ pub fn parse_variable_expr(
expr: Expr::Bool(false),
span,
ty: Type::Bool,
custom_completion: None,
},
None,
);
@ -1090,6 +1109,7 @@ pub fn parse_variable_expr(
expr: Expr::Var(id),
span,
ty: working_set.get_variable(id).clone(),
custom_completion: None,
},
None,
)
@ -1159,6 +1179,7 @@ pub fn parse_full_column_path(
expr: Expr::Subexpression(block_id),
span,
ty: Type::Unknown, // FIXME
custom_completion: None,
},
true,
)
@ -1175,6 +1196,7 @@ pub fn parse_full_column_path(
expr: Expr::Var(var_id),
span: Span::unknown(),
ty: Type::Unknown,
custom_completion: None,
},
false,
)
@ -1241,6 +1263,7 @@ pub fn parse_full_column_path(
expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
ty: Type::Unknown,
span: full_column_span,
custom_completion: None,
},
error,
)
@ -1268,6 +1291,7 @@ pub fn parse_string(
expr: Expr::String(token),
span,
ty: Type::String,
custom_completion: None,
},
None,
)
@ -1337,6 +1361,7 @@ pub fn parse_var_with_opt_type(
expr: Expr::Var(id),
span: span(&spans[*spans_idx - 1..*spans_idx + 1]),
ty,
custom_completion: None,
},
None,
)
@ -1347,6 +1372,7 @@ pub fn parse_var_with_opt_type(
expr: Expr::Var(id),
span: spans[*spans_idx],
ty: Type::Unknown,
custom_completion: None,
},
Some(ParseError::MissingType(spans[*spans_idx])),
)
@ -1359,6 +1385,7 @@ pub fn parse_var_with_opt_type(
expr: Expr::Var(id),
span: span(&spans[*spans_idx..*spans_idx + 1]),
ty: Type::Unknown,
custom_completion: None,
},
None,
)
@ -1395,6 +1422,7 @@ pub fn parse_row_condition(
ty: Type::Bool,
span,
expr: Expr::RowCondition(var_id, Box::new(expression)),
custom_completion: None,
},
err,
)
@ -1435,6 +1463,7 @@ pub fn parse_signature(
expr: Expr::Signature(sig),
span,
ty: Type::Unknown,
custom_completion: None,
},
error,
)
@ -1834,6 +1863,7 @@ pub fn parse_list_expression(
} else {
Type::Unknown
})),
custom_completion: None,
},
error,
)
@ -1882,6 +1912,7 @@ pub fn parse_table_expression(
expr: Expr::List(vec![]),
span,
ty: Type::List(Box::new(Type::Unknown)),
custom_completion: None,
},
None,
),
@ -1947,6 +1978,7 @@ pub fn parse_table_expression(
expr: Expr::Table(table_headers, rows),
span,
ty: Type::Table,
custom_completion: None,
},
error,
)
@ -2089,6 +2121,7 @@ pub fn parse_block_expression(
expr: Expr::Block(block_id),
span,
ty: Type::Block,
custom_completion: None,
},
error,
)
@ -2142,6 +2175,11 @@ pub fn parse_value(
}
match shape {
SyntaxShape::Custom(shape, custom_completion) => {
let (mut expression, err) = parse_value(working_set, span, shape);
expression.custom_completion = Some(custom_completion.clone());
(expression, err)
}
SyntaxShape::Number => parse_number(bytes, span),
SyntaxShape::Int => parse_int(bytes, span),
SyntaxShape::Range => parse_range(working_set, span),
@ -2254,6 +2292,7 @@ pub fn parse_operator(
expr: Expr::Operator(operator),
span,
ty: Type::Unknown,
custom_completion: None,
},
None,
)
@ -2333,6 +2372,7 @@ pub fn parse_math_expression(
expr: Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
span: op_span,
ty: result_ty,
custom_completion: None,
});
}
}
@ -2367,6 +2407,7 @@ pub fn parse_math_expression(
expr: Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
span: binary_op_span,
ty: result_ty,
custom_completion: None,
});
}
@ -2496,6 +2537,7 @@ pub fn parse_def(
expr: Expr::Call(call),
span: span(spans),
ty: Type::Unknown,
custom_completion: None,
}])),
error,
)
@ -2559,6 +2601,7 @@ pub fn parse_alias(
expr: Expr::Call(call),
span: call_span,
ty: Type::Unknown,
custom_completion: None,
}])),
None,
);
@ -2607,6 +2650,7 @@ pub fn parse_let(
expr: Expr::Call(call),
span: call_span,
ty: Type::Unknown,
custom_completion: None,
}])),
err,
);

View File

@ -6,6 +6,7 @@ pub struct Expression {
pub expr: Expr,
pub span: Span,
pub ty: Type,
pub custom_completion: Option<String>,
}
impl Expression {
@ -14,6 +15,7 @@ impl Expression {
expr: Expr::Garbage,
span,
ty: Type::Unknown,
custom_completion: None,
}
}

View File

@ -69,6 +69,9 @@ pub enum SyntaxShape {
/// A general expression, eg `1 + 2` or `foo --bar`
Expression,
/// A custom shape with custom completion logic
Custom(Box<SyntaxShape>, String),
}
impl SyntaxShape {
@ -77,6 +80,7 @@ impl SyntaxShape {
SyntaxShape::Any => Type::Unknown,
SyntaxShape::Block(_) => Type::Block,
SyntaxShape::CellPath => Type::Unknown,
SyntaxShape::Custom(custom, _) => custom.to_type(),
SyntaxShape::Duration => Type::Duration,
SyntaxShape::Expression => Type::Unknown,
SyntaxShape::FilePath => Type::FilePath,