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)]
42pub 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 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 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}