#![doc = include_str!("../README.md")]
mod binder;
mod cfa;
mod checker;
mod config;
mod data_set;
mod deprecation;
mod document;
mod exports;
mod features;
mod helpers;
mod idx;
mod mutability;
mod refactorings;
mod types_analyzer;
mod uri;
pub use crate::config::*;
use crate::{document::Document, features::SemanticTokenKind, uri::InternUri};
use dashmap::DashMap;
use indexmap::{IndexMap, IndexSet};
use lspt::{
CodeActionKind, CodeActionOptions, CodeLensOptions, CompletionOptions, DiagnosticOptions,
InitializeParams, InitializeResult, Registration, RegistrationParams, RenameOptions,
SemanticTokensClientCapabilities, SemanticTokensLegend, SemanticTokensOptions,
ServerCapabilities, ServerInfo, SignatureHelpOptions, TextDocumentClientCapabilities,
TextDocumentSyncKind, TextDocumentSyncOptions, Union2, Union3,
notification::DidChangeConfigurationNotification,
};
use rustc_hash::FxBuildHasher;
use salsa::Database;
use std::sync::Arc;
#[salsa::db]
#[derive(Clone, Default)]
pub struct LanguageService {
storage: salsa::Storage<Self>,
semantic_token_kinds: Arc<IndexSet<SemanticTokenKind, FxBuildHasher>>,
documents: Arc<DashMap<InternUri, Document, FxBuildHasher>>,
global_config: Arc<ServiceConfig>,
configs: Arc<DashMap<InternUri, ConfigState, FxBuildHasher>>,
support_pull_config: bool,
}
#[salsa::db]
impl Database for LanguageService {}
impl LanguageService {
pub fn initialize(&mut self, params: InitializeParams) -> InitializeResult {
let mut kinds_map = IndexMap::<_, _, FxBuildHasher>::default();
if let Some(TextDocumentClientCapabilities {
semantic_tokens: Some(SemanticTokensClientCapabilities { token_types, .. }),
..
}) = params.capabilities.text_document
{
kinds_map = token_types
.iter()
.filter_map(|token_type| {
let internal_kind = match &**token_type {
"type" => SemanticTokenKind::Type,
"parameter" => SemanticTokenKind::Param,
"variable" => SemanticTokenKind::Var,
"function" => SemanticTokenKind::Func,
"keyword" => SemanticTokenKind::Keyword,
"comment" => SemanticTokenKind::Comment,
"string" => SemanticTokenKind::String,
"number" => SemanticTokenKind::Number,
"operator" => SemanticTokenKind::Op,
_ => return None,
};
Some((internal_kind, token_type.clone()))
})
.collect();
self.semantic_token_kinds = Arc::new(kinds_map.keys().cloned().collect());
}
self.support_pull_config = matches!(
params
.capabilities
.workspace
.as_ref()
.and_then(|it| it.configuration),
Some(true)
);
if let Some(config) = params
.initialization_options
.and_then(|config| serde_json::from_value(config).ok())
{
self.global_config = Arc::new(config);
}
InitializeResult {
capabilities: ServerCapabilities {
call_hierarchy_provider: Some(Union3::A(true)),
code_action_provider: Some(Union2::B(CodeActionOptions {
code_action_kinds: Some(vec![
CodeActionKind::QuickFix,
CodeActionKind::RefactorRewrite,
CodeActionKind::RefactorInline,
]),
resolve_provider: Some(false),
..Default::default()
})),
code_lens_provider: Some(CodeLensOptions {
resolve_provider: Some(true),
..Default::default()
}),
completion_provider: Some(CompletionOptions {
trigger_characters: Some(
[
'$', '(', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '@',
]
.iter()
.map(char::to_string)
.collect(),
),
all_commit_characters: Some(vec![")".into()]),
..Default::default()
}),
definition_provider: Some(Union2::A(true)),
diagnostic_provider: Some(Union2::A(DiagnosticOptions {
identifier: Some("wat".into()),
inter_file_dependencies: false,
workspace_diagnostics: false,
..Default::default()
})),
type_definition_provider: Some(Union3::A(true)),
declaration_provider: Some(Union3::A(true)),
document_formatting_provider: Some(Union2::A(true)),
document_highlight_provider: Some(Union2::A(true)),
document_range_formatting_provider: Some(Union2::A(true)),
document_symbol_provider: Some(Union2::A(true)),
folding_range_provider: Some(Union3::A(true)),
hover_provider: Some(Union2::A(true)),
inlay_hint_provider: Some(Union3::A(true)),
references_provider: Some(Union2::A(true)),
rename_provider: Some(Union2::B(RenameOptions {
prepare_provider: Some(true),
work_done_progress: Default::default(),
})),
selection_range_provider: Some(Union3::A(true)),
semantic_tokens_provider: Some(Union2::A(SemanticTokensOptions {
legend: SemanticTokensLegend {
token_types: kinds_map.into_values().collect(),
token_modifiers: vec!["mutable".into()],
},
range: Some(Union2::A(true)),
full: Some(Union2::A(true)),
..Default::default()
})),
signature_help_provider: Some(SignatureHelpOptions {
trigger_characters: Some(['(', ')'].iter().map(char::to_string).collect()),
..Default::default()
}),
type_hierarchy_provider: Some(Union3::A(true)),
text_document_sync: Some(Union2::A(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::Incremental),
will_save: Some(false),
will_save_wait_until: Some(false),
save: Some(Union2::A(false)),
})),
..Default::default()
},
server_info: Some(ServerInfo {
name: "WebAssembly Language Tools".into(),
version: Some(env!("CARGO_PKG_VERSION").into()),
}),
}
}
#[inline]
pub fn dynamic_capabilities(&self) -> RegistrationParams {
use lspt::notification::Notification;
RegistrationParams {
registrations: vec![Registration {
id: DidChangeConfigurationNotification::METHOD.into(),
method: DidChangeConfigurationNotification::METHOD.into(),
register_options: None,
}],
}
}
}