Skip to main content

wat_service/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod binder;
4mod cfa;
5mod checker;
6mod config;
7mod data_set;
8mod deprecation;
9mod document;
10mod features;
11mod helpers;
12mod idx;
13mod imex;
14mod mutability;
15mod refactorings;
16mod types_analyzer;
17mod uri;
18
19pub use crate::config::*;
20use crate::{
21    document::Document,
22    features::{SemanticTokenType, SemanticTokenTypes},
23    uri::InternUri,
24};
25use indexmap::IndexMap;
26use lspt::{
27    CodeActionKind, CodeActionOptions, CodeLensOptions, CompletionOptions, DiagnosticOptions, InitializeParams,
28    InitializeResult, RenameOptions, SemanticTokensClientCapabilities, SemanticTokensLegend, SemanticTokensOptions,
29    ServerCapabilities, ServerInfo, SignatureHelpOptions, TextDocumentClientCapabilities, TextDocumentSyncKind,
30    TextDocumentSyncOptions, Union2, Union3,
31};
32use parking_lot::RwLock;
33use rustc_hash::{FxBuildHasher, FxHashMap};
34use salsa::Database;
35use std::{
36    panic::{AssertUnwindSafe, UnwindSafe},
37    sync::Arc,
38};
39
40#[salsa::db]
41#[derive(Clone, Default)]
42/// The language service comes with handlers for LSP requests.
43///
44/// The language service only does computation.
45/// It doesn't require IO to read source file,
46/// instead, you need to call [`commit`](LanguageService::commit) to add or update file.
47/// Also, it doesn't process language server protocol.
48/// You should call the corresponding method for each request.
49///
50/// To create a language service instance, you should call `LanguageService::default()`,
51/// not the `initialize` method.
52pub struct LanguageService {
53    storage: salsa::Storage<Self>,
54    semantic_token_types: Arc<SemanticTokenTypes>,
55    documents: Arc<RwLock<FxHashMap<InternUri, Document>>>,
56    global_config: Arc<ServiceConfig>,
57    configs: Arc<RwLock<FxHashMap<InternUri, ConfigState>>>,
58    support_pull_config: bool,
59}
60#[salsa::db]
61impl Database for LanguageService {}
62
63impl LanguageService {
64    /// Handler for `initialize` request.
65    ///
66    /// This method isn't used to create language service instance.
67    /// Instead, you can call `LanguageService::default()` to create instance,
68    /// then call this method when the language server is initializing.
69    pub fn initialize(&mut self, params: InitializeParams) -> InitializeResult {
70        let mut types_map = IndexMap::<_, _, FxBuildHasher>::default();
71        if let Some(TextDocumentClientCapabilities {
72            semantic_tokens: Some(SemanticTokensClientCapabilities { token_types, .. }),
73            ..
74        }) = params.capabilities.text_document
75        {
76            types_map = token_types
77                .iter()
78                .filter_map(|token_type| {
79                    let internal_type = match &**token_type {
80                        "type" => SemanticTokenType::Type,
81                        "parameter" => SemanticTokenType::Param,
82                        "variable" => SemanticTokenType::Var,
83                        "function" => SemanticTokenType::Func,
84                        "keyword" => SemanticTokenType::Keyword,
85                        "comment" => SemanticTokenType::Comment,
86                        "string" => SemanticTokenType::String,
87                        "number" => SemanticTokenType::Number,
88                        "operator" => SemanticTokenType::Op,
89                        _ => return None,
90                    };
91                    Some((internal_type, token_type.clone()))
92                })
93                .collect();
94            self.semantic_token_types = Arc::new(types_map.keys().cloned().collect());
95        }
96
97        self.support_pull_config = matches!(
98            params.capabilities.workspace.as_ref().and_then(|it| it.configuration),
99            Some(true)
100        );
101        if let Some(config) = params
102            .initialization_options
103            .and_then(|config| serde_json::from_value(config).ok())
104        {
105            self.global_config = Arc::new(config);
106        }
107
108        InitializeResult {
109            capabilities: ServerCapabilities {
110                call_hierarchy_provider: Some(Union3::A(true)),
111                code_action_provider: Some(Union2::B(CodeActionOptions {
112                    code_action_kinds: Some(vec![
113                        CodeActionKind::QuickFix,
114                        CodeActionKind::RefactorRewrite,
115                        CodeActionKind::RefactorInline,
116                    ]),
117                    resolve_provider: Some(false),
118                    ..Default::default()
119                })),
120                code_lens_provider: Some(CodeLensOptions {
121                    resolve_provider: Some(true),
122                    ..Default::default()
123                }),
124                completion_provider: Some(CompletionOptions {
125                    trigger_characters: Some(
126                        [
127                            '$', '(', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
128                            'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
129                            '8', '9', '.', '@',
130                        ]
131                        .iter()
132                        .map(char::to_string)
133                        .collect(),
134                    ),
135                    all_commit_characters: Some(vec![")".into()]),
136                    ..Default::default()
137                }),
138                definition_provider: Some(Union2::A(true)),
139                diagnostic_provider: Some(Union2::A(DiagnosticOptions {
140                    identifier: Some("wat".into()),
141                    inter_file_dependencies: false,
142                    workspace_diagnostics: false,
143                    ..Default::default()
144                })),
145                type_definition_provider: Some(Union3::A(true)),
146                declaration_provider: Some(Union3::A(true)),
147                document_formatting_provider: Some(Union2::A(true)),
148                document_highlight_provider: Some(Union2::A(true)),
149                document_range_formatting_provider: Some(Union2::A(true)),
150                document_symbol_provider: Some(Union2::A(true)),
151                folding_range_provider: Some(Union3::A(true)),
152                hover_provider: Some(Union2::A(true)),
153                inlay_hint_provider: Some(Union3::A(true)),
154                references_provider: Some(Union2::A(true)),
155                rename_provider: Some(Union2::B(RenameOptions {
156                    prepare_provider: Some(true),
157                    work_done_progress: Default::default(),
158                })),
159                selection_range_provider: Some(Union3::A(true)),
160                semantic_tokens_provider: Some(Union2::A(SemanticTokensOptions {
161                    legend: SemanticTokensLegend {
162                        token_types: types_map.into_values().collect(),
163                        token_modifiers: vec!["mutable".into()],
164                    },
165                    range: Some(Union2::A(true)),
166                    full: Some(Union2::A(true)),
167                    ..Default::default()
168                })),
169                signature_help_provider: Some(SignatureHelpOptions {
170                    trigger_characters: Some(['(', ')'].iter().map(char::to_string).collect()),
171                    ..Default::default()
172                }),
173                type_hierarchy_provider: Some(Union3::A(true)),
174                text_document_sync: Some(Union2::A(TextDocumentSyncOptions {
175                    open_close: Some(true),
176                    change: Some(TextDocumentSyncKind::Incremental),
177                    will_save: Some(false),
178                    will_save_wait_until: Some(false),
179                    save: Some(Union2::A(false)),
180                })),
181                ..Default::default()
182            },
183            server_info: Some(ServerInfo {
184                name: "WebAssembly Language Tools".into(),
185                version: Some(env!("CARGO_PKG_VERSION").into()),
186            }),
187        }
188    }
189
190    /// Run computation that may be cancelled on pending write.
191    /// It should only run read-only computation for LSP requests.
192    fn with_db<F, R>(&self, f: F) -> Option<R>
193    where
194        F: FnOnce(&dyn salsa::Database) -> R + UnwindSafe,
195    {
196        let service = AssertUnwindSafe(self);
197        salsa::Cancelled::catch(|| f(*service)).ok()
198    }
199}