rust_analyzer/
lib.rs

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Implementation of the LSP for rust-analyzer.
//!
//! This crate takes Rust-specific analysis results from ide and translates
//! into LSP types.
//!
//! It also is the root of all state. `world` module defines the bulk of the
//! state, and `main_loop` module defines the rules for modifying it.
//!
//! The `cli` submodule implements some batch-processing analysis, primarily as
//! a debugging aid.

pub mod cli;

mod command;
mod diagnostics;
mod discover;
mod flycheck;
mod hack_recover_crate_name;
mod line_index;
mod main_loop;
mod mem_docs;
mod op_queue;
mod reload;
mod target_spec;
mod task_pool;
mod test_runner;
mod version;

mod handlers {
    pub(crate) mod dispatch;
    pub(crate) mod notification;
    pub(crate) mod request;
}

pub mod tracing {
    pub mod config;
    pub mod json;
    pub use config::Config;
    pub mod hprof;
}

pub mod config;
mod global_state;
pub mod lsp;
use self::lsp::ext as lsp_ext;

#[cfg(test)]
mod integrated_benchmarks;

use ide::{CompletionItem, CompletionRelevance};
use serde::de::DeserializeOwned;
use tenthash::TentHasher;

pub use crate::{
    lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
    version::version,
};

pub fn from_json<T: DeserializeOwned>(
    what: &'static str,
    json: &serde_json::Value,
) -> anyhow::Result<T> {
    serde_json::from_value(json.clone())
        .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
}

fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
    fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) {
        use ide_completion::{
            CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
            CompletionRelevanceTypeMatch,
        };

        hasher.update([
            u8::from(relevance.exact_name_match),
            u8::from(relevance.is_local),
            u8::from(relevance.is_name_already_imported),
            u8::from(relevance.requires_import),
            u8::from(relevance.is_private_editable),
        ]);
        if let Some(type_match) = &relevance.type_match {
            let label = match type_match {
                CompletionRelevanceTypeMatch::CouldUnify => "could_unify",
                CompletionRelevanceTypeMatch::Exact => "exact",
            };
            hasher.update(label);
        }
        if let Some(trait_) = &relevance.trait_ {
            hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
        }
        if let Some(postfix_match) = &relevance.postfix_match {
            let label = match postfix_match {
                CompletionRelevancePostfixMatch::NonExact => "non_exact",
                CompletionRelevancePostfixMatch::Exact => "exact",
            };
            hasher.update(label);
        }
        if let Some(function) = &relevance.function {
            hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
            let label = match function.return_type {
                CompletionRelevanceReturnType::Other => "other",
                CompletionRelevanceReturnType::DirectConstructor => "direct_constructor",
                CompletionRelevanceReturnType::Constructor => "constructor",
                CompletionRelevanceReturnType::Builder => "builder",
            };
            hasher.update(label);
        }
    }

    let mut hasher = TentHasher::new();
    hasher.update([
        u8::from(is_ref_completion),
        u8::from(item.is_snippet),
        u8::from(item.deprecated),
        u8::from(item.trigger_call_info),
    ]);
    hasher.update(&item.label.primary);
    if let Some(label_detail) = &item.label.detail_left {
        hasher.update(label_detail);
    }
    if let Some(label_detail) = &item.label.detail_right {
        hasher.update(label_detail);
    }
    // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
    // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
    //
    // Documentation hashing is skipped too, as it's a large blob to process,
    // while not really making completion properties more unique as they are already.
    hasher.update(item.kind.tag());
    hasher.update(&item.lookup);
    if let Some(detail) = &item.detail {
        hasher.update(detail);
    }
    hash_completion_relevance(&mut hasher, &item.relevance);
    if let Some((mutability, text_size)) = &item.ref_match {
        hasher.update(mutability.as_keyword_for_ref());
        hasher.update(u32::from(*text_size).to_le_bytes());
    }
    for (import_path, import_name) in &item.import_to_add {
        hasher.update(import_path);
        hasher.update(import_name);
    }
    hasher.finalize()
}