From 3db64a400c78bbd2708e67ddc07df1001fff3f29 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 17 Feb 2021 17:53:31 +0300 Subject: rename completion -> ide_completion We don't have completion-related PRs in flight, so lets do it --- .../src/completions/postfix/format_like.rs | 287 --------------------- 1 file changed, 287 deletions(-) delete mode 100644 crates/completion/src/completions/postfix/format_like.rs (limited to 'crates/completion/src/completions/postfix') diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs deleted file mode 100644 index 3afc63021..000000000 --- a/crates/completion/src/completions/postfix/format_like.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Feature: Format String Completion. -// -// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. -// -// The following postfix snippets are available: -// -// - `format` -> `format!(...)` -// - `panic` -> `panic!(...)` -// - `println` -> `println!(...)` -// - `log`: -// + `logd` -> `log::debug!(...)` -// + `logt` -> `log::trace!(...)` -// + `logi` -> `log::info!(...)` -// + `logw` -> `log::warn!(...)` -// + `loge` -> `log::error!(...)` - -use ide_db::helpers::SnippetCap; -use syntax::ast::{self, AstToken}; - -use crate::{completions::postfix::postfix_snippet, context::CompletionContext, Completions}; - -/// Mapping ("postfix completion item" => "macro to use") -static KINDS: &[(&str, &str)] = &[ - ("format", "format!"), - ("panic", "panic!"), - ("println", "println!"), - ("eprintln", "eprintln!"), - ("logd", "log::debug!"), - ("logt", "log::trace!"), - ("logi", "log::info!"), - ("logw", "log::warn!"), - ("loge", "log::error!"), -]; - -pub(crate) fn add_format_like_completions( - acc: &mut Completions, - ctx: &CompletionContext, - dot_receiver: &ast::Expr, - cap: SnippetCap, - receiver_text: &ast::String, -) { - let input = match string_literal_contents(receiver_text) { - // It's not a string literal, do not parse input. - Some(input) => input, - None => return, - }; - - let mut parser = FormatStrParser::new(input); - - if parser.parse().is_ok() { - for (label, macro_name) in KINDS { - let snippet = parser.into_suggestion(macro_name); - - postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); - } - } -} - -/// Checks whether provided item is a string literal. -fn string_literal_contents(item: &ast::String) -> Option { - let item = item.text(); - if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { - return Some(item[1..item.len() - 1].to_owned()); - } - - None -} - -/// Parser for a format-like string. It is more allowing in terms of string contents, -/// as we expect variable placeholders to be filled with expressions. -#[derive(Debug)] -pub(crate) struct FormatStrParser { - input: String, - output: String, - extracted_expressions: Vec, - state: State, - parsed: bool, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum State { - NotExpr, - MaybeExpr, - Expr, - MaybeIncorrect, - FormatOpts, -} - -impl FormatStrParser { - pub(crate) fn new(input: String) -> Self { - Self { - input: input.into(), - output: String::new(), - extracted_expressions: Vec::new(), - state: State::NotExpr, - parsed: false, - } - } - - pub(crate) fn parse(&mut self) -> Result<(), ()> { - let mut current_expr = String::new(); - - let mut placeholder_id = 1; - - // Count of open braces inside of an expression. - // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g. - // "{MyStruct { val_a: 0, val_b: 1 }}". - let mut inexpr_open_count = 0; - - let mut chars = self.input.chars().peekable(); - while let Some(chr) = chars.next() { - match (self.state, chr) { - (State::NotExpr, '{') => { - self.output.push(chr); - self.state = State::MaybeExpr; - } - (State::NotExpr, '}') => { - self.output.push(chr); - self.state = State::MaybeIncorrect; - } - (State::NotExpr, _) => { - self.output.push(chr); - } - (State::MaybeIncorrect, '}') => { - // It's okay, we met "}}". - self.output.push(chr); - self.state = State::NotExpr; - } - (State::MaybeIncorrect, _) => { - // Error in the string. - return Err(()); - } - (State::MaybeExpr, '{') => { - self.output.push(chr); - self.state = State::NotExpr; - } - (State::MaybeExpr, '}') => { - // This is an empty sequence '{}'. Replace it with placeholder. - self.output.push(chr); - self.extracted_expressions.push(format!("${}", placeholder_id)); - placeholder_id += 1; - self.state = State::NotExpr; - } - (State::MaybeExpr, _) => { - current_expr.push(chr); - self.state = State::Expr; - } - (State::Expr, '}') => { - if inexpr_open_count == 0 { - self.output.push(chr); - self.extracted_expressions.push(current_expr.trim().into()); - current_expr = String::new(); - self.state = State::NotExpr; - } else { - // We're closing one brace met before inside of the expression. - current_expr.push(chr); - inexpr_open_count -= 1; - } - } - (State::Expr, ':') if chars.peek().copied() == Some(':') => { - // path seperator - current_expr.push_str("::"); - chars.next(); - } - (State::Expr, ':') => { - if inexpr_open_count == 0 { - // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" - self.output.push(chr); - self.extracted_expressions.push(current_expr.trim().into()); - current_expr = String::new(); - self.state = State::FormatOpts; - } else { - // We're inside of braced expression, assume that it's a struct field name/value delimeter. - current_expr.push(chr); - } - } - (State::Expr, '{') => { - current_expr.push(chr); - inexpr_open_count += 1; - } - (State::Expr, _) => { - current_expr.push(chr); - } - (State::FormatOpts, '}') => { - self.output.push(chr); - self.state = State::NotExpr; - } - (State::FormatOpts, _) => { - self.output.push(chr); - } - } - } - - if self.state != State::NotExpr { - return Err(()); - } - - self.parsed = true; - Ok(()) - } - - pub(crate) fn into_suggestion(&self, macro_name: &str) -> String { - assert!(self.parsed, "Attempt to get a suggestion from not parsed expression"); - - let expressions_as_string = self.extracted_expressions.join(", "); - format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use expect_test::{expect, Expect}; - - fn check(input: &str, expect: &Expect) { - let mut parser = FormatStrParser::new((*input).to_owned()); - let outcome_repr = if parser.parse().is_ok() { - // Parsing should be OK, expected repr is "string; expr_1, expr_2". - if parser.extracted_expressions.is_empty() { - parser.output - } else { - format!("{}; {}", parser.output, parser.extracted_expressions.join(", ")) - } - } else { - // Parsing should fail, expected repr is "-". - "-".to_owned() - }; - - expect.assert_eq(&outcome_repr); - } - - #[test] - fn format_str_parser() { - let test_vector = &[ - ("no expressions", expect![["no expressions"]]), - ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]), - ("{expr:?}", expect![["{:?}; expr"]]), - ("{malformed", expect![["-"]]), - ("malformed}", expect![["-"]]), - ("{{correct", expect![["{{correct"]]), - ("correct}}", expect![["correct}}"]]), - ("{correct}}}", expect![["{}}}; correct"]]), - ("{correct}}}}}", expect![["{}}}}}; correct"]]), - ("{incorrect}}", expect![["-"]]), - ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]), - ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]), - ( - "{SomeStruct { val_a: 0, val_b: 1 }}", - expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]], - ), - ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]), - ( - "{SomeStruct { val_a: 0, val_b: 1 }:?}", - expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], - ), - ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), - ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]), - ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]), - ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]), - ]; - - for (input, output) in test_vector { - check(input, output) - } - } - - #[test] - fn test_into_suggestion() { - let test_vector = &[ - ("println!", "{}", r#"println!("{}", $1)"#), - ("eprintln!", "{}", r#"eprintln!("{}", $1)"#), - ( - "log::info!", - "{} {expr} {} {2 + 2}", - r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#, - ), - ("format!", "{expr:?}", r#"format!("{:?}", expr)"#), - ]; - - for (kind, input, output) in test_vector { - let mut parser = FormatStrParser::new((*input).to_owned()); - parser.parse().expect("Parsing must succeed"); - - assert_eq!(&parser.into_suggestion(*kind), output); - } - } -} -- cgit v1.2.3