Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 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!(...)`
//
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]

use ide_db::{
    syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg},
    SnippetCap,
};
use syntax::{ast, AstToken};

use crate::{
    completions::postfix::{build_postfix_snippet_builder, escape_snippet_bits},
    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 postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
        Some(it) => it,
        None => return,
    };

    if let Ok((mut out, mut exprs)) = parse_format_exprs(receiver_text.text()) {
        // Escape any snippet bits in the out text and any of the exprs.
        escape_snippet_bits(&mut out);
        for arg in &mut exprs {
            if let Arg::Ident(text) | Arg::Expr(text) = arg {
                escape_snippet_bits(text)
            }
        }

        let exprs = with_placeholders(exprs);
        for (label, macro_name) in KINDS {
            let snippet = if exprs.is_empty() {
                format!(r#"{macro_name}({out})"#)
            } else {
                format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
            };

            postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_into_suggestion() {
        let test_vector = &[
            ("println!", "{}", r#"println!("{}", $1)"#),
            ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
            (
                "log::info!",
                "{} {ident} {} {2 + 2}",
                r#"log::info!("{} {ident} {} {}", $1, $2, 2 + 2)"#,
            ),
        ];

        for (kind, input, output) in test_vector {
            let (parsed_string, exprs) = parse_format_exprs(input).unwrap();
            let exprs = with_placeholders(exprs);
            let snippet = format!(r#"{kind}("{parsed_string}", {})"#, exprs.join(", "));
            assert_eq!(&snippet, output);
        }
    }

    #[test]
    fn test_into_suggestion_no_epxrs() {
        let test_vector = &[
            ("println!", "{ident}", r#"println!("{ident}")"#),
            ("format!", "{ident:?}", r#"format!("{ident:?}")"#),
        ];

        for (kind, input, output) in test_vector {
            let (parsed_string, _exprs) = parse_format_exprs(input).unwrap();
            let snippet = format!(r#"{kind}("{parsed_string}")"#);
            assert_eq!(&snippet, output);
        }
    }
}
Cell<bool>, /// If true, invocations of `sum` will wait for cancellation before /// they exit. pub(crate) sum_wait_for_cancellation: Cell<CancellationFlag>, /// Invocations of `sum` will wait for this stage prior to exiting. pub(crate) sum_wait_for_on_exit: Cell<usize>, /// Invocations of `sum` will signal this stage prior to exiting. pub(crate) sum_signal_on_exit: Cell<usize>, /// Invocations of `sum3_drop_sum` will panic unconditionally pub(crate) sum3_drop_sum_should_panic: Cell<bool>, } fn sum(db: &dyn ParDatabase, key: &'static str) -> usize { let mut sum = 0; db.signal(db.knobs().sum_signal_on_entry.get()); db.wait_for(db.knobs().sum_wait_for_on_entry.get()); if db.knobs().sum_should_panic.get() { panic!("query set to panic before exit") } for ch in key.chars() { sum += db.input(ch); } match db.knobs().sum_wait_for_cancellation.get() { CancellationFlag::Down => (), CancellationFlag::Panic => { tracing::debug!("waiting for cancellation"); loop { db.unwind_if_cancelled(); std::thread::yield_now(); } } } db.wait_for(db.knobs().sum_wait_for_on_exit.get()); db.signal(db.knobs().sum_signal_on_exit.get()); sum } fn sum2(db: &dyn ParDatabase, key: &'static str) -> usize { db.sum(key) } fn sum2_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { let _ = db.sum(key); 22 } fn sum3(db: &dyn ParDatabase, key: &'static str) -> usize { db.sum2(key) } fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { if db.knobs().sum3_drop_sum_should_panic.get() { panic!("sum3_drop_sum executed") } db.sum2_drop_sum(key) } #[ra_salsa::database( Par, crate::parallel_cycle_all_recover::ParallelCycleAllRecover, crate::parallel_cycle_none_recover::ParallelCycleNoneRecover, crate::parallel_cycle_mid_recover::ParallelCycleMidRecovers, crate::parallel_cycle_one_recovers::ParallelCycleOneRecovers )] #[derive(Default)] pub(crate) struct ParDatabaseImpl { storage: ra_salsa::Storage<Self>, knobs: KnobsStruct, } impl Database for ParDatabaseImpl { fn salsa_event(&self, event: ra_salsa::Event) { if let ra_salsa::EventKind::WillBlockOn { .. } = event.kind { self.signal(self.knobs().signal_on_will_block.get()); } } } impl ParallelDatabase for ParDatabaseImpl { fn snapshot(&self) -> Snapshot<Self> { Snapshot::new(ParDatabaseImpl { storage: self.storage.snapshot(), knobs: self.knobs.clone(), }) } } impl Knobs for ParDatabaseImpl { fn knobs(&self) -> &KnobsStruct { &self.knobs } fn signal(&self, stage: usize) { self.knobs.signal.signal(stage); } fn wait_for(&self, stage: usize) { self.knobs.signal.wait_for(stage); } }