use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::io;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime};
pub type DocumentUri = String;
pub type LspInt = i64;
pub type LspUint = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
pub line: LspUint,
pub character: LspUint,
}
impl Position {
pub fn new(line: LspUint, character: LspUint) -> Self {
Self { line, character }
}
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.character)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Range {
pub start: Position,
pub end: Position,
}
impl Range {
pub fn new(start: Position, end: Position) -> Self {
Self { start, end }
}
pub fn from_coords(start_line: LspUint, start_char: LspUint, end_line: LspUint, end_char: LspUint) -> Self {
Self {
start: Position::new(start_line, start_char),
end: Position::new(end_line, end_char),
}
}
pub fn covers(&self, pos: Position) -> bool {
if pos.line < self.start.line || pos.line > self.end.line {
return false;
}
if pos.line == self.start.line && pos.character < self.start.character {
return false;
}
if pos.line == self.end.line && pos.character > self.end.character {
return false;
}
true
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Location {
pub uri: DocumentUri,
pub range: Range,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocationLink {
pub origin_selection_range: Option<Range>,
pub target_uri: DocumentUri,
pub target_range: Range,
pub target_selection_range: Range,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextDocumentIdentifier {
pub uri: DocumentUri,
}
#[derive(Debug, Clone)]
pub struct VersionedTextDocumentIdentifier {
pub uri: DocumentUri,
pub version: LspInt,
}
#[derive(Debug, Clone)]
pub struct TextDocumentPositionParams {
pub text_document: TextDocumentIdentifier,
pub position: Position,
}
#[derive(Debug, Clone)]
pub struct InitializeParams {
pub process_id: Option<LspInt>,
pub client_info: Option<ClientInfo>,
pub locale: Option<String>,
pub root_path: Option<String>,
pub root_uri: Option<DocumentUri>,
pub initialization_options: Option<serde_json::Value>,
pub capabilities: ClientCapabilities,
pub trace: Option<TraceValue>,
pub workspace_folders: Option<Vec<WorkspaceFolder>>,
}
#[derive(Debug, Clone)]
pub struct ClientInfo {
pub name: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TraceValue {
Off,
Messages,
Verbose,
}
impl fmt::Display for TraceValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Off => write!(f, "off"),
Self::Messages => write!(f, "messages"),
Self::Verbose => write!(f, "verbose"),
}
}
}
#[derive(Debug, Clone)]
pub struct WorkspaceFolder {
pub uri: DocumentUri,
pub name: String,
}
#[derive(Debug, Clone, Default)]
pub struct ClientCapabilities {
pub workspace: Option<WorkspaceClientCapabilities>,
pub text_document: Option<TextDocumentClientCapabilities>,
pub window: Option<WindowClientCapabilities>,
pub general: Option<GeneralClientCapabilities>,
pub experimental: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceClientCapabilities {
pub apply_edit: Option<bool>,
pub workspace_edit: Option<WorkspaceEditCapabilities>,
pub did_change_configuration: Option<DidChangeConfigurationCapabilities>,
pub did_change_watched_files: Option<DidChangeWatchedFilesCapabilities>,
pub symbol: Option<WorkspaceSymbolCapabilities>,
pub execute_command: Option<ExecuteCommandCapabilities>,
pub workspace_folders: Option<bool>,
pub configuration: Option<bool>,
pub semantic_tokens: Option<SemanticTokensWorkspaceCapabilities>,
pub code_lens: Option<CodeLensWorkspaceCapabilities>,
pub file_operations: Option<FileOperationCapabilities>,
pub inline_value: Option<InlineValueWorkspaceCapabilities>,
pub inlay_hint: Option<InlayHintWorkspaceCapabilities>,
pub diagnostics: Option<DiagnosticWorkspaceCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceEditCapabilities {
pub document_changes: Option<bool>,
pub resource_operations: Option<Vec<String>>,
pub failure_handling: Option<String>,
pub normalizes_line_endings: Option<bool>,
pub change_annotation_support: Option<ChangeAnnotationSupport>,
}
#[derive(Debug, Clone, Default)]
pub struct ChangeAnnotationSupport {
pub groups_on_label: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DidChangeConfigurationCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DidChangeWatchedFilesCapabilities {
pub dynamic_registration: Option<bool>,
pub relative_pattern_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceSymbolCapabilities {
pub dynamic_registration: Option<bool>,
pub symbol_kind: Option<Vec<SymbolKind>>,
pub tag_support: Option<SymbolTagSupport>,
pub resolve_support: Option<ResolveSupport>,
}
#[derive(Debug, Clone, Default)]
pub struct SymbolTagSupport {
pub value_set: Vec<SymbolTag>,
}
#[derive(Debug, Clone, Default)]
pub struct ResolveSupport {
pub properties: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ExecuteCommandCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct SemanticTokensWorkspaceCapabilities {
pub refresh_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeLensWorkspaceCapabilities {
pub refresh_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationCapabilities {
pub dynamic_registration: Option<bool>,
pub did_create: Option<bool>,
pub will_create: Option<bool>,
pub did_rename: Option<bool>,
pub will_rename: Option<bool>,
pub did_delete: Option<bool>,
pub will_delete: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct InlineValueWorkspaceCapabilities {
pub refresh_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct InlayHintWorkspaceCapabilities {
pub refresh_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DiagnosticWorkspaceCapabilities {
pub refresh_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct TextDocumentClientCapabilities {
pub synchronization: Option<TextDocumentSyncCapabilities>,
pub completion: Option<CompletionCapabilities>,
pub hover: Option<HoverCapabilities>,
pub signature_help: Option<SignatureHelpCapabilities>,
pub declaration: Option<DeclarationCapabilities>,
pub definition: Option<DefinitionCapabilities>,
pub type_definition: Option<TypeDefinitionCapabilities>,
pub implementation: Option<ImplementationCapabilities>,
pub references: Option<ReferenceCapabilities>,
pub document_highlight: Option<DocumentHighlightCapabilities>,
pub document_symbol: Option<DocumentSymbolCapabilities>,
pub code_action: Option<CodeActionCapabilities>,
pub code_lens: Option<CodeLensCapabilities>,
pub document_link: Option<DocumentLinkCapabilities>,
pub color_provider: Option<ColorProviderCapabilities>,
pub formatting: Option<FormattingCapabilities>,
pub range_formatting: Option<RangeFormattingCapabilities>,
pub on_type_formatting: Option<OnTypeFormattingCapabilities>,
pub rename: Option<RenameCapabilities>,
pub folding_range: Option<FoldingRangeCapabilities>,
pub selection_range: Option<SelectionRangeCapabilities>,
pub publish_diagnostics: Option<PublishDiagnosticsCapabilities>,
pub call_hierarchy: Option<CallHierarchyCapabilities>,
pub semantic_tokens: Option<SemanticTokensCapabilities>,
pub linked_editing_range: Option<LinkedEditingRangeCapabilities>,
pub moniker: Option<MonikerCapabilities>,
pub type_hierarchy: Option<TypeHierarchyCapabilities>,
pub inline_value: Option<InlineValueCapabilities>,
pub inlay_hint: Option<InlayHintCapabilities>,
pub diagnostic: Option<DiagnosticCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct TextDocumentSyncCapabilities {
pub dynamic_registration: Option<bool>,
pub will_save: Option<bool>,
pub will_save_wait_until: Option<bool>,
pub did_save: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionCapabilities {
pub dynamic_registration: Option<bool>,
pub completion_item: Option<CompletionItemCapabilities>,
pub completion_item_kind: Option<CompletionItemKindCapabilities>,
pub insert_text_mode: Option<InsertTextMode>,
pub context_support: Option<bool>,
pub completion_list: Option<CompletionListCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionItemCapabilities {
pub snippet_support: Option<bool>,
pub commit_characters_support: Option<bool>,
pub documentation_format: Option<Vec<MarkupKind>>,
pub deprecated_support: Option<bool>,
pub preselect_support: Option<bool>,
pub tag_support: Option<TagSupport<CompletionItemTag>>,
pub insert_replace_support: Option<bool>,
pub resolve_support: Option<ResolveSupport>,
pub insert_text_mode_support: Option<InsertTextModeSupport>,
pub label_details_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionItemKindCapabilities {
pub value_set: Option<Vec<CompletionItemKind>>,
}
#[derive(Debug, Clone, Default)]
pub struct TagSupport<T> {
pub value_set: Vec<T>,
}
#[derive(Debug, Clone, Default)]
pub struct InsertTextModeSupport {
pub value_set: Vec<InsertTextMode>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionListCapabilities {
pub item_defaults: Option<Vec<String>>,
}
#[derive(Debug, Clone, Default)]
pub struct HoverCapabilities {
pub dynamic_registration: Option<bool>,
pub content_format: Option<Vec<MarkupKind>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MarkupKind {
PlainText,
Markdown,
}
impl fmt::Display for MarkupKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PlainText => write!(f, "plaintext"),
Self::Markdown => write!(f, "markdown"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SignatureHelpCapabilities {
pub dynamic_registration: Option<bool>,
pub signature_information: Option<SignatureInformationCapabilities>,
pub context_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct SignatureInformationCapabilities {
pub documentation_format: Option<Vec<MarkupKind>>,
pub parameter_information: Option<ParameterInformationCapabilities>,
pub active_parameter_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct ParameterInformationCapabilities {
pub label_offset_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DeclarationCapabilities {
pub dynamic_registration: Option<bool>,
pub link_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DefinitionCapabilities {
pub dynamic_registration: Option<bool>,
pub link_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct TypeDefinitionCapabilities {
pub dynamic_registration: Option<bool>,
pub link_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct ImplementationCapabilities {
pub dynamic_registration: Option<bool>,
pub link_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct ReferenceCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentHighlightCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentSymbolCapabilities {
pub dynamic_registration: Option<bool>,
pub symbol_kind: Option<Vec<SymbolKind>>,
pub hierarchical_document_symbol_support: Option<bool>,
pub tag_support: Option<TagSupport<SymbolTag>>,
pub label_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeActionCapabilities {
pub dynamic_registration: Option<bool>,
pub code_action_literal_support: Option<CodeActionLiteralSupport>,
pub is_preferred_support: Option<bool>,
pub disabled_support: Option<bool>,
pub data_support: Option<bool>,
pub resolve_support: Option<ResolveSupport>,
pub honors_change_annotations: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeActionLiteralSupport {
pub code_action_kind: CodeActionKindCapability,
}
#[derive(Debug, Clone, Default)]
pub struct CodeActionKindCapability {
pub value_set: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeLensCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentLinkCapabilities {
pub dynamic_registration: Option<bool>,
pub tooltip_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct ColorProviderCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct FormattingCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct RangeFormattingCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct OnTypeFormattingCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct RenameCapabilities {
pub dynamic_registration: Option<bool>,
pub prepare_support: Option<bool>,
pub prepare_support_default_behavior: Option<String>,
pub honors_change_annotations: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct FoldingRangeCapabilities {
pub dynamic_registration: Option<bool>,
pub range_limit: Option<LspUint>,
pub line_folding_only: Option<bool>,
pub folding_range_kind: Option<FoldingRangeKindCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct FoldingRangeKindCapabilities {
pub value_set: Option<Vec<String>>,
}
#[derive(Debug, Clone, Default)]
pub struct SelectionRangeCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct PublishDiagnosticsCapabilities {
pub related_information: Option<bool>,
pub tag_support: Option<TagSupport<DiagnosticTag>>,
pub version_support: Option<bool>,
pub code_description_support: Option<bool>,
pub data_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct CallHierarchyCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct SemanticTokensCapabilities {
pub dynamic_registration: Option<bool>,
pub requests: SemanticTokensRequestCapabilities,
pub token_types: Vec<String>,
pub token_modifiers: Vec<String>,
pub formats: Vec<String>,
pub overlapping_token_support: Option<bool>,
pub multiline_token_support: Option<bool>,
pub server_cancel_support: Option<bool>,
pub augments_syntax_tokens: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct SemanticTokensRequestCapabilities {
pub range: Option<bool>,
pub full: Option<SemanticTokensFullCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct SemanticTokensFullCapabilities {
pub delta: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct LinkedEditingRangeCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct MonikerCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct TypeHierarchyCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct InlineValueCapabilities {
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct InlayHintCapabilities {
pub dynamic_registration: Option<bool>,
pub resolve_support: Option<ResolveSupport>,
}
#[derive(Debug, Clone, Default)]
pub struct DiagnosticCapabilities {
pub dynamic_registration: Option<bool>,
pub related_document_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct WindowClientCapabilities {
pub work_done_progress: Option<bool>,
pub show_message: Option<ShowMessageRequestCapabilities>,
pub show_document: Option<ShowDocumentCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct ShowMessageRequestCapabilities {
pub message_action_item: Option<MessageActionItemCapabilities>,
}
#[derive(Debug, Clone, Default)]
pub struct MessageActionItemCapabilities {
pub additional_properties_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct ShowDocumentCapabilities {
pub support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct GeneralClientCapabilities {
pub stale_request_support: Option<StaleRequestSupport>,
pub regular_expressions: Option<RegularExpressionCapabilities>,
pub markdown: Option<MarkdownCapabilities>,
pub position_encodings: Option<Vec<String>>,
}
#[derive(Debug, Clone, Default)]
pub struct StaleRequestSupport {
pub cancel: bool,
pub retry_on_content_modified: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct RegularExpressionCapabilities {
pub engine: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct MarkdownCapabilities {
pub parser: String,
pub version: Option<String>,
pub allowed_tags: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct InitializeResult {
pub capabilities: ServerCapabilities,
pub server_info: Option<ServerInfo>,
}
#[derive(Debug, Clone)]
pub struct ServerInfo {
pub name: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ServerCapabilities {
pub text_document_sync: Option<TextDocumentSyncOptions>,
pub completion_provider: Option<CompletionOptions>,
pub hover_provider: Option<HoverOptions>,
pub signature_help_provider: Option<SignatureHelpOptions>,
pub declaration_provider: Option<DeclarationOptions>,
pub definition_provider: Option<DefinitionOptions>,
pub type_definition_provider: Option<TypeDefinitionOptions>,
pub implementation_provider: Option<ImplementationOptions>,
pub references_provider: Option<ReferenceOptions>,
pub document_highlight_provider: Option<DocumentHighlightOptions>,
pub document_symbol_provider: Option<DocumentSymbolOptions>,
pub code_action_provider: Option<CodeActionOptions>,
pub code_lens_provider: Option<CodeLensOptions>,
pub document_link_provider: Option<DocumentLinkOptions>,
pub color_provider: Option<ColorProviderOptions>,
pub document_formatting_provider: Option<DocumentFormattingOptions>,
pub document_range_formatting_provider: Option<DocumentRangeFormattingOptions>,
pub document_on_type_formatting_provider: Option<DocumentOnTypeFormattingOptions>,
pub rename_provider: Option<RenameOptions>,
pub folding_range_provider: Option<FoldingRangeOptions>,
pub selection_range_provider: Option<SelectionRangeOptions>,
pub execute_command_provider: Option<ExecuteCommandOptions>,
pub call_hierarchy_provider: Option<CallHierarchyOptions>,
pub linked_editing_range_provider: Option<LinkedEditingRangeOptions>,
pub semantic_tokens_provider: Option<SemanticTokensOptions>,
pub moniker_provider: Option<MonikerOptions>,
pub type_hierarchy_provider: Option<TypeHierarchyOptions>,
pub inline_value_provider: Option<InlineValueOptions>,
pub inlay_hint_provider: Option<InlayHintOptions>,
pub diagnostic_provider: Option<DiagnosticOptions>,
pub workspace_symbol_provider: Option<WorkspaceSymbolOptions>,
pub workspace: Option<WorkspaceOptions>,
pub experimental: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub enum TextDocumentSyncOptions {
None,
Full,
Incremental,
Kind(TextDocumentSyncKind),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TextDocumentSyncKind {
None,
Full,
Incremental,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionOptions {
pub trigger_characters: Option<Vec<String>>,
pub all_commit_characters: Option<Vec<String>>,
pub resolve_provider: Option<bool>,
pub completion_item: Option<CompletionItemOptions>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionItemOptions {
pub label_details_support: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkDoneProgressOptions {
pub work_done_progress: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct HoverOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct SignatureHelpOptions {
pub trigger_characters: Option<Vec<String>>,
pub retrigger_characters: Option<Vec<String>>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DeclarationOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DefinitionOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct TypeDefinitionOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct ImplementationOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct ReferenceOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentHighlightOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentSymbolOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
pub label: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeActionOptions {
pub code_action_kinds: Option<Vec<String>>,
pub resolve_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct CodeLensOptions {
pub resolve_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentLinkOptions {
pub resolve_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct ColorProviderOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentFormattingOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentRangeFormattingOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DocumentOnTypeFormattingOptions {
pub first_trigger_character: String,
pub more_trigger_character: Option<Vec<String>>,
}
#[derive(Debug, Clone, Default)]
pub struct RenameOptions {
pub prepare_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct FoldingRangeOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct SelectionRangeOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct ExecuteCommandOptions {
pub commands: Vec<String>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct CallHierarchyOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct LinkedEditingRangeOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct SemanticTokensOptions {
pub legend: SemanticTokensLegend,
pub range: Option<bool>,
pub full: Option<SemanticTokensFullOptions>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensLegend {
pub token_types: Vec<String>,
pub token_modifiers: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensFullOptions {
pub delta: Option<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct MonikerOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct TypeHierarchyOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct InlineValueOptions {
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct InlayHintOptions {
pub resolve_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct DiagnosticOptions {
pub identifier: Option<String>,
pub inter_file_dependencies: bool,
pub workspace_diagnostics: bool,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceSymbolOptions {
pub resolve_provider: Option<bool>,
pub work_done_progress_options: Option<WorkDoneProgressOptions>,
}
#[derive(Debug, Clone)]
pub struct WorkspaceOptions {
pub workspace_folders: Option<WorkspaceFoldersOptions>,
pub file_operations: Option<FileOperationOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceFoldersOptions {
pub supported: Option<bool>,
pub change_notifications: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationOptions {
pub did_create: Option<FileOperationRegistrationOptions>,
pub will_create: Option<FileOperationRegistrationOptions>,
pub did_rename: Option<FileOperationRegistrationOptions>,
pub will_rename: Option<FileOperationRegistrationOptions>,
pub did_delete: Option<FileOperationRegistrationOptions>,
pub will_delete: Option<FileOperationRegistrationOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationRegistrationOptions {
pub filters: Vec<FileOperationFilter>,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationFilter {
pub scheme: Option<String>,
pub pattern: FileOperationPattern,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationPattern {
pub glob: String,
pub matches: Option<String>,
pub options: Option<FileOperationPatternOptions>,
}
#[derive(Debug, Clone, Default)]
pub struct FileOperationPatternOptions {
pub ignore_case: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct DidOpenTextDocumentParams {
pub text_document: TextDocumentItem,
}
#[derive(Debug, Clone)]
pub struct TextDocumentItem {
pub uri: DocumentUri,
pub language_id: String,
pub version: LspInt,
pub text: String,
}
#[derive(Debug, Clone)]
pub struct DidChangeTextDocumentParams {
pub text_document: VersionedTextDocumentIdentifier,
pub content_changes: Vec<TextDocumentContentChangeEvent>,
}
#[derive(Debug, Clone)]
pub struct TextDocumentContentChangeEvent {
pub range: Option<Range>,
pub range_length: Option<LspUint>,
pub text: String,
}
#[derive(Debug, Clone)]
pub struct DidCloseTextDocumentParams {
pub text_document: TextDocumentIdentifier,
}
#[derive(Debug, Clone)]
pub struct DidSaveTextDocumentParams {
pub text_document: TextDocumentIdentifier,
pub text: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CompletionParams {
pub text_document: TextDocumentIdentifier,
pub position: Position,
pub context: Option<CompletionContext>,
}
#[derive(Debug, Clone)]
pub struct CompletionContext {
pub trigger_kind: CompletionTriggerKind,
pub trigger_character: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompletionTriggerKind {
Invoked,
TriggerCharacter,
TriggerForIncompleteCompletions,
}
#[derive(Debug, Clone)]
pub struct CompletionList {
pub is_incomplete: bool,
pub items: Vec<CompletionItem>,
pub item_defaults: Option<CompletionItemDefaults>,
}
#[derive(Debug, Clone, Default)]
pub struct CompletionItemDefaults {
pub commit_characters: Option<Vec<String>>,
pub edit_range: Option<EditRange>,
pub insert_text_format: Option<InsertTextFormat>,
pub insert_text_mode: Option<InsertTextMode>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub enum EditRange {
Range(Range),
InsertReplace { insert: Range, replace: Range },
}
#[derive(Debug, Clone)]
pub struct CompletionItem {
pub label: String,
pub label_details: Option<CompletionItemLabelDetails>,
pub kind: Option<CompletionItemKind>,
pub tags: Option<Vec<CompletionItemTag>>,
pub detail: Option<String>,
pub documentation: Option<MarkupContent>,
pub deprecated: Option<bool>,
pub preselect: Option<bool>,
pub sort_text: Option<String>,
pub filter_text: Option<String>,
pub insert_text: Option<String>,
pub insert_text_format: Option<InsertTextFormat>,
pub insert_text_mode: Option<InsertTextMode>,
pub text_edit: Option<TextEdit>,
pub text_edit_text: Option<String>,
pub additional_text_edits: Option<Vec<TextEdit>>,
pub commit_characters: Option<Vec<String>>,
pub command: Option<Command>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct CompletionItemLabelDetails {
pub detail: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompletionItemKind {
Text,
Method,
Function,
Constructor,
Field,
Variable,
Class,
Interface,
Module,
Property,
Unit,
Value,
Enum,
Keyword,
Snippet,
Color,
File,
Reference,
Folder,
EnumMember,
Constant,
Struct,
Event,
Operator,
TypeParameter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompletionItemTag {
Deprecated,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InsertTextFormat {
PlainText,
Snippet,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InsertTextMode {
AsIs,
AdjustIndentation,
}
#[derive(Debug, Clone)]
pub struct TextEdit {
pub range: Range,
pub new_text: String,
}
#[derive(Debug, Clone)]
pub struct MarkupContent {
pub kind: MarkupKind,
pub value: String,
}
#[derive(Debug, Clone)]
pub struct Command {
pub title: String,
pub command: String,
pub arguments: Option<Vec<serde_json::Value>>,
}
#[derive(Debug, Clone)]
pub struct Hover {
pub contents: MarkupContent,
pub range: Option<Range>,
}
pub type DefinitionResult = Vec<Location>;
#[derive(Debug, Clone)]
pub struct ReferenceParams {
pub text_document: TextDocumentIdentifier,
pub position: Position,
pub context: ReferenceContext,
}
#[derive(Debug, Clone)]
pub struct ReferenceContext {
pub include_declaration: bool,
}
#[derive(Debug, Clone)]
pub struct RenameParams {
pub text_document: TextDocumentIdentifier,
pub position: Position,
pub new_name: String,
}
#[derive(Debug, Clone)]
pub struct WorkspaceEdit {
pub changes: Option<HashMap<DocumentUri, Vec<TextEdit>>>,
pub document_changes: Option<Vec<TextDocumentEdit>>,
pub change_annotations: Option<HashMap<String, ChangeAnnotation>>,
}
#[derive(Debug, Clone)]
pub struct TextDocumentEdit {
pub text_document: OptionalVersionedTextDocumentIdentifier,
pub edits: Vec<TextEdit>,
}
#[derive(Debug, Clone)]
pub struct OptionalVersionedTextDocumentIdentifier {
pub uri: DocumentUri,
pub version: Option<LspInt>,
}
#[derive(Debug, Clone)]
pub struct ChangeAnnotation {
pub label: String,
pub needs_confirmation: Option<bool>,
pub description: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DocumentFormattingParams {
pub text_document: TextDocumentIdentifier,
pub options: FormattingOptions,
}
#[derive(Debug, Clone)]
pub struct FormattingOptions {
pub tab_size: LspUint,
pub insert_spaces: bool,
pub trim_trailing_whitespace: Option<bool>,
pub insert_final_newline: Option<bool>,
pub trim_final_newlines: Option<bool>,
pub additional_properties: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct CodeActionParams {
pub text_document: TextDocumentIdentifier,
pub range: Range,
pub context: CodeActionContext,
}
#[derive(Debug, Clone)]
pub struct CodeActionContext {
pub diagnostics: Vec<Diagnostic>,
pub only: Option<Vec<String>>,
pub trigger_kind: Option<CodeActionTriggerKind>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodeActionTriggerKind {
Invoked,
Automatic,
}
#[derive(Debug, Clone)]
pub enum CodeActionResponse {
Commands(Vec<Command>),
CodeActions(Vec<CodeAction>),
}
#[derive(Debug, Clone)]
pub struct CodeAction {
pub title: String,
pub kind: Option<String>,
pub diagnostics: Option<Vec<Diagnostic>>,
pub is_preferred: Option<bool>,
pub disabled: Option<CodeActionDisabled>,
pub edit: Option<WorkspaceEdit>,
pub command: Option<Command>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct CodeActionDisabled {
pub reason: String,
}
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub range: Range,
pub severity: Option<DiagnosticSeverity>,
pub code: Option<DiagnosticCode>,
pub code_description: Option<CodeDescription>,
pub source: Option<String>,
pub message: String,
pub tags: Option<Vec<DiagnosticTag>>,
pub related_information: Option<Vec<DiagnosticRelatedInformation>>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DiagnosticSeverity {
Error,
Warning,
Information,
Hint,
}
#[derive(Debug, Clone)]
pub enum DiagnosticCode {
String(String),
Number(LspInt),
}
#[derive(Debug, Clone)]
pub struct CodeDescription {
pub href: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DiagnosticTag {
Unnecessary,
Deprecated,
}
#[derive(Debug, Clone)]
pub struct DiagnosticRelatedInformation {
pub location: Location,
pub message: String,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensParams {
pub text_document: TextDocumentIdentifier,
}
#[derive(Debug, Clone)]
pub struct SemanticTokens {
pub result_id: Option<String>,
pub data: Vec<LspUint>,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensDeltaParams {
pub text_document: TextDocumentIdentifier,
pub previous_result_id: String,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensDelta {
pub result_id: Option<String>,
pub edits: Vec<SemanticTokensEdit>,
}
#[derive(Debug, Clone)]
pub struct SemanticTokensEdit {
pub start: LspUint,
pub delete_count: LspUint,
pub data: Option<Vec<LspUint>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SemanticTokenType {
Namespace,
Type,
Class,
Enum,
Interface,
Struct,
TypeParameter,
Parameter,
Variable,
Property,
EnumMember,
Event,
Function,
Method,
Macro,
Keyword,
Modifier,
Comment,
String,
Number,
Regexp,
Operator,
Decorator,
}
impl SemanticTokenType {
pub fn as_index(&self) -> u32 {
match self {
Self::Namespace => 0,
Self::Type => 1,
Self::Class => 2,
Self::Enum => 3,
Self::Interface => 4,
Self::Struct => 5,
Self::TypeParameter => 6,
Self::Parameter => 7,
Self::Variable => 8,
Self::Property => 9,
Self::EnumMember => 10,
Self::Event => 11,
Self::Function => 12,
Self::Method => 13,
Self::Macro => 14,
Self::Keyword => 15,
Self::Modifier => 16,
Self::Comment => 17,
Self::String => 18,
Self::Number => 19,
Self::Regexp => 20,
Self::Operator => 21,
Self::Decorator => 22,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SemanticTokenModifier {
Declaration,
Definition,
Readonly,
Static,
Deprecated,
Abstract,
Async,
Modification,
Documentation,
DefaultLibrary,
}
impl SemanticTokenModifier {
pub fn as_flag(&self) -> u32 {
match self {
Self::Declaration => 0,
Self::Definition => 1,
Self::Readonly => 2,
Self::Static => 3,
Self::Deprecated => 4,
Self::Abstract => 5,
Self::Async => 6,
Self::Modification => 7,
Self::Documentation => 8,
Self::DefaultLibrary => 9,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolKind {
File,
Module,
Namespace,
Package,
Class,
Method,
Property,
Field,
Constructor,
Enum,
Interface,
Function,
Variable,
Constant,
String,
Number,
Boolean,
Array,
Object,
Key,
Null,
EnumMember,
Struct,
Event,
Operator,
TypeParameter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolTag {
Deprecated,
}
#[derive(Debug, Clone)]
pub enum DocumentSymbolResponse {
Flat(Vec<SymbolInformation>),
Hierarchical(Vec<DocumentSymbol>),
}
#[derive(Debug, Clone)]
pub struct SymbolInformation {
pub name: String,
pub kind: SymbolKind,
pub tags: Option<Vec<SymbolTag>>,
pub deprecated: Option<bool>,
pub location: Location,
pub container_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DocumentSymbol {
pub name: String,
pub detail: Option<String>,
pub kind: SymbolKind,
pub tags: Option<Vec<SymbolTag>>,
pub deprecated: Option<bool>,
pub range: Range,
pub selection_range: Range,
pub children: Option<Vec<DocumentSymbol>>,
}
#[derive(Debug, Clone)]
pub struct WorkspaceSymbolParams {
pub query: String,
}
#[derive(Debug)]
pub struct ClangdServer {
pub workspace_root: PathBuf,
pub compilation_database: CompilationDatabase,
pub documents: HashMap<DocumentUri, TextDocument>,
pub index: CodeIndex,
pub diagnostics: HashMap<DocumentUri, Vec<Diagnostic>>,
pub config: ClangdConfig,
pub status: ServerStatus,
pub request_id: LspInt,
}
#[derive(Debug, Clone)]
pub struct ClangdConfig {
pub completion_style: CompletionStyle,
pub header_insertion: HeaderInsertionPolicy,
pub enable_auto_include: bool,
pub cross_file_rename: CrossFileRename,
pub enable_background_index: bool,
pub background_index_priority: IndexPriority,
pub clangd_tidy_enabled: bool,
pub clangd_tidy_checks: Vec<String>,
pub completion_all_scopes: bool,
pub hover_show_akas: bool,
pub inlay_hints_enabled: bool,
pub inlay_hints_parameters: bool,
pub inlay_hints_deduced_types: bool,
pub inlay_hints_designators: bool,
pub function_arg_placeholders: bool,
pub insert_argument_placeholders: bool,
pub limit_results: usize,
pub limit_references: usize,
pub use_dirty_headers: bool,
pub compile_args: Vec<String>,
pub fallback_flags: Vec<String>,
}
impl Default for ClangdConfig {
fn default() -> Self {
Self {
completion_style: CompletionStyle::Detailed,
header_insertion: HeaderInsertionPolicy::IWYU,
enable_auto_include: true,
cross_file_rename: CrossFileRename::Enabled,
enable_background_index: true,
background_index_priority: IndexPriority::Normal,
clangd_tidy_enabled: true,
clangd_tidy_checks: vec!["*".to_string(), "-modernize-use-trailing-return-type".to_string()],
completion_all_scopes: true,
hover_show_akas: true,
inlay_hints_enabled: true,
inlay_hints_parameters: true,
inlay_hints_deduced_types: true,
inlay_hints_designators: true,
function_arg_placeholders: true,
insert_argument_placeholders: true,
limit_results: 100,
limit_references: 1000,
use_dirty_headers: true,
compile_args: Vec::new(),
fallback_flags: vec![
"-std=c++17".to_string(),
"-xc++".to_string(),
],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompletionStyle {
Detailed,
Bundled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HeaderInsertionPolicy {
Never,
IWYU,
IncludeWhatYouUse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CrossFileRename {
None,
Minimal,
Enabled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IndexPriority {
Low,
Normal,
High,
Background,
Off,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ServerStatus {
Starting,
Initializing,
BuildingIndex,
Idle,
ShuttingDown,
Error(String),
}
#[derive(Debug, Clone)]
pub struct TextDocument {
pub uri: DocumentUri,
pub language_id: String,
pub version: LspInt,
pub text: String,
pub open: bool,
pub modified: bool,
pub parse_tree: Option<Box<serde_json::Value>>,
pub ast: Option<Box<serde_json::Value>>,
pub diagnostics: Vec<Diagnostic>,
}
impl TextDocument {
pub fn new(uri: DocumentUri, language_id: String, text: String) -> Self {
Self {
uri,
language_id,
version: 0,
text,
open: true,
modified: false,
parse_tree: None,
ast: None,
diagnostics: Vec::new(),
}
}
pub fn apply_changes(&mut self, changes: &[TextDocumentContentChangeEvent]) {
for change in changes {
match &change.range {
Some(range) => {
let start = Self::position_to_offset(&self.text, range.start);
let end = Self::position_to_offset(&self.text, range.end);
let mut new_text = String::with_capacity(
self.text.len() - (end - start) + change.text.len(),
);
new_text.push_str(&self.text[..start]);
new_text.push_str(&change.text);
new_text.push_str(&self.text[end..]);
self.text = new_text;
}
None => {
self.text = change.text.clone();
}
}
}
self.modified = true;
}
fn position_to_offset(text: &str, pos: Position) -> usize {
let mut offset = 0usize;
let mut current_line = 0u64;
for (i, ch) in text.char_indices() {
if current_line == pos.line && offset == 0 {
offset = i + pos.character as usize;
break;
}
if ch == '\n' {
current_line += 1;
if current_line == pos.line {
offset = i + 1 + pos.character as usize;
break;
}
}
}
offset.min(text.len())
}
pub fn get_text_in_range(&self, range: Range) -> String {
let start = Self::position_to_offset(&self.text, range.start);
let end = Self::position_to_offset(&self.text, range.end);
self.text[start..end].to_string()
}
pub fn get_line(&self, line: LspUint) -> Option<&str> {
self.text.lines().nth(line as usize)
}
pub fn line_count(&self) -> usize {
self.text.lines().count()
}
}
impl ClangdServer {
pub fn new(workspace_root: PathBuf) -> Self {
Self {
workspace_root: workspace_root.clone(),
compilation_database: CompilationDatabase::new(workspace_root.join("compile_commands.json")),
documents: HashMap::new(),
index: CodeIndex::new(),
diagnostics: HashMap::new(),
config: ClangdConfig::default(),
status: ServerStatus::Starting,
request_id: 0,
}
}
pub fn initialize(&mut self, params: &InitializeParams) -> InitializeResult {
self.status = ServerStatus::Initializing;
InitializeResult {
capabilities: ServerCapabilities {
text_document_sync: Some(TextDocumentSyncOptions::Kind(TextDocumentSyncKind::Incremental)),
completion_provider: Some(CompletionOptions {
trigger_characters: Some(vec![
".".to_string(), "->".to_string(), "::".to_string(),
">".to_string(), ":".to_string(), "\"".to_string(),
"/".to_string(), "<".to_string(),
]),
all_commit_characters: Some(vec![]),
resolve_provider: Some(true),
completion_item: Some(CompletionItemOptions { label_details_support: Some(true) }),
work_done_progress_options: Some(WorkDoneProgressOptions { work_done_progress: Some(false) }),
}),
hover_provider: Some(HoverOptions::default()),
definition_provider: Some(DefinitionOptions::default()),
references_provider: Some(ReferenceOptions::default()),
document_highlight_provider: Some(DocumentHighlightOptions::default()),
document_symbol_provider: Some(DocumentSymbolOptions::default()),
workspace_symbol_provider: Some(WorkspaceSymbolOptions::default()),
code_action_provider: Some(CodeActionOptions {
code_action_kinds: Some(vec![
"quickfix".to_string(),
"refactor".to_string(),
"refactor.extract".to_string(),
"refactor.inline".to_string(),
]),
resolve_provider: Some(false),
work_done_progress_options: Some(WorkDoneProgressOptions { work_done_progress: Some(false) }),
}),
document_formatting_provider: Some(DocumentFormattingOptions::default()),
document_range_formatting_provider: Some(DocumentRangeFormattingOptions::default()),
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
first_trigger_character: "}".to_string(),
more_trigger_character: Some(vec![
")".to_string(), "]".to_string(), ";".to_string(),
]),
}),
rename_provider: Some(RenameOptions {
prepare_provider: Some(true),
work_done_progress_options: Some(WorkDoneProgressOptions::default()),
}),
semantic_tokens_provider: Some(SemanticTokensOptions {
legend: SemanticTokensLegend {
token_types: vec![
"namespace".to_string(), "type".to_string(), "class".to_string(),
"enum".to_string(), "interface".to_string(), "struct".to_string(),
"typeParameter".to_string(), "parameter".to_string(), "variable".to_string(),
"property".to_string(), "enumMember".to_string(), "event".to_string(),
"function".to_string(), "method".to_string(), "macro".to_string(),
"keyword".to_string(), "modifier".to_string(), "comment".to_string(),
"string".to_string(), "number".to_string(), "regexp".to_string(),
"operator".to_string(), "decorator".to_string(),
],
token_modifiers: vec![
"declaration".to_string(), "definition".to_string(),
"readonly".to_string(), "static".to_string(),
"deprecated".to_string(), "abstract".to_string(),
"async".to_string(), "modification".to_string(),
"documentation".to_string(), "defaultLibrary".to_string(),
],
},
range: Some(true),
full: Some(SemanticTokensFullOptions { delta: Some(true) }),
work_done_progress_options: Some(WorkDoneProgressOptions::default()),
}),
call_hierarchy_provider: Some(CallHierarchyOptions::default()),
type_hierarchy_provider: Some(TypeHierarchyOptions::default()),
inlay_hint_provider: Some(InlayHintOptions {
resolve_provider: Some(false),
work_done_progress_options: Some(WorkDoneProgressOptions::default()),
}),
..Default::default()
},
server_info: Some(ServerInfo {
name: "clangd-native".to_string(),
version: Some("1.0.0".to_string()),
}),
}
}
pub fn did_open(&mut self, params: &DidOpenTextDocumentParams) {
let doc = TextDocument::new(
params.text_document.uri.clone(),
params.text_document.language_id.clone(),
params.text_document.text.clone(),
);
let uri = params.text_document.uri.clone();
self.documents.insert(uri.clone(), doc);
self.schedule_index(&uri);
}
pub fn did_change(&mut self, params: &DidChangeTextDocumentParams) {
let uri = params.text_document.uri.clone();
if let Some(doc) = self.documents.get_mut(&uri) {
doc.version = params.text_document.version;
doc.apply_changes(¶ms.content_changes);
}
}
pub fn did_close(&mut self, params: &DidCloseTextDocumentParams) {
let uri = params.text_document.uri.clone();
if let Some(doc) = self.documents.get_mut(&uri) {
doc.open = false;
}
}
pub fn completion(&self, params: &CompletionParams) -> CompletionList {
let uri = params.text_document.uri.clone();
let mut items = Vec::new();
if let Some(_doc) = self.documents.get(&uri) {
let keywords = vec![
"auto", "break", "case", "char", "const", "continue",
"default", "do", "double", "else", "enum", "extern",
"float", "for", "goto", "if", "int", "long", "register",
"return", "short", "signed", "sizeof", "static", "struct",
"switch", "typedef", "union", "unsigned", "void", "volatile",
"while", "class", "namespace", "template", "typename",
"public", "private", "protected", "virtual", "override",
"final", "noexcept", "constexpr", "consteval", "decltype",
];
for kw in keywords {
items.push(CompletionItem {
label: kw.to_string(),
kind: Some(CompletionItemKind::Keyword),
insert_text: Some(kw.to_string()),
insert_text_format: Some(InsertTextFormat::PlainText),
sort_text: Some(format!("0_{}", kw)),
..Default::default()
});
}
let indexed = self.index.find_completions(&uri, params.position);
items.extend(indexed);
}
CompletionList {
is_incomplete: true,
items,
item_defaults: None,
}
}
pub fn hover(&self, params: &TextDocumentPositionParams) -> Option<Hover> {
let uri = params.text_document.uri.clone();
self.index
.find_symbol_at_position(&uri, params.position)
.map(|sym| Hover {
contents: MarkupContent {
kind: MarkupKind::Markdown,
value: sym.hover_text(),
},
range: Some(sym.range),
})
}
pub fn definition(&self, params: &TextDocumentPositionParams) -> Option<DefinitionResult> {
let uri = params.text_document.uri.clone();
self.index
.find_definition(&uri, params.position)
.map(|def| vec![def])
}
pub fn references(&self, params: &ReferenceParams) -> Option<Vec<Location>> {
let uri = params.text_document.uri.clone();
Some(self.index.find_references(&uri, params.position))
}
pub fn rename(&self, params: &RenameParams) -> Option<WorkspaceEdit> {
let uri = params.text_document.uri.clone();
let refs = self.index.find_references(&uri, params.position);
if refs.is_empty() {
return None;
}
let mut changes: HashMap<DocumentUri, Vec<TextEdit>> = HashMap::new();
for loc in &refs {
let edit = TextEdit {
range: loc.range,
new_text: params.new_name.clone(),
};
changes.entry(loc.uri.clone()).or_default().push(edit);
}
Some(WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
})
}
pub fn formatting(&self, params: &DocumentFormattingParams) -> Option<Vec<TextEdit>> {
let uri = params.text_document.uri.clone();
if let Some(doc) = self.documents.get(&uri) {
let edits = self.format_document(doc, ¶ms.options);
return Some(edits);
}
None
}
pub fn code_action(&self, params: &CodeActionParams) -> Option<CodeActionResponse> {
let uri = params.text_document.uri.clone();
let mut actions = Vec::new();
if let Some(diags) = self.diagnostics.get(&uri) {
for diag in diags {
if let Some(fix) = self.generate_quick_fix(diag) {
actions.push(fix);
}
}
}
if actions.is_empty() {
None
} else {
Some(CodeActionResponse::CodeActions(actions))
}
}
pub fn semantic_tokens_full(&self, params: &SemanticTokensParams) -> Option<SemanticTokens> {
let uri = params.text_document.uri.clone();
if let Some(doc) = self.documents.get(&uri) {
let tokens = self.compute_semantic_tokens(doc);
Some(SemanticTokens {
result_id: Some(format!("{}", self.request_id)),
data: tokens,
})
} else {
None
}
}
pub fn document_symbols(&self, uri: &DocumentUri) -> Option<DocumentSymbolResponse> {
if let Some(_doc) = self.documents.get(uri) {
let symbols = self.index.get_document_symbols(uri);
if symbols.is_empty() {
None
} else {
Some(DocumentSymbolResponse::Hierarchical(symbols))
}
} else {
None
}
}
pub fn workspace_symbols(&self, query: &str) -> Option<Vec<SymbolInformation>> {
let results = self.index.search_workspace_symbols(query);
if results.is_empty() {
None
} else {
Some(results)
}
}
fn schedule_index(&mut self, _uri: &DocumentUri) {
if self.config.enable_background_index {
}
}
fn format_document(&self, _doc: &TextDocument, _options: &FormattingOptions) -> Vec<TextEdit> {
Vec::new()
}
fn generate_quick_fix(&self, _diag: &Diagnostic) -> Option<CodeAction> {
None
}
fn compute_semantic_tokens(&self, _doc: &TextDocument) -> Vec<LspUint> {
Vec::new()
}
}
impl CompletionItem {
fn default() -> Self {
Self {
label: String::new(),
label_details: None,
kind: None,
tags: None,
detail: None,
documentation: None,
deprecated: None,
preselect: None,
sort_text: None,
filter_text: None,
insert_text: None,
insert_text_format: None,
insert_text_mode: None,
text_edit: None,
text_edit_text: None,
additional_text_edits: None,
commit_characters: None,
command: None,
data: None,
}
}
}
#[derive(Debug)]
pub struct CXIndex {
pub exclude_declarations_from_pch: bool,
pub display_diagnostics: bool,
pub global_options: u32,
}
impl CXIndex {
pub fn new(exclude_pch: bool, display_diags: bool) -> Self {
Self {
exclude_declarations_from_pch: exclude_pch,
display_diagnostics: display_diags,
global_options: 0,
}
}
pub fn set_global_options(&mut self, options: u32) {
self.global_options = options;
}
}
#[derive(Debug)]
pub struct ClangTranslationUnit {
pub id: u64,
pub source_file: PathBuf,
pub clang_options: Vec<String>,
pub unsaved_files: Vec<UnsavedFile>,
pub diagnostics: Vec<ClangDiagnostic>,
pub cursor: Option<ClangCursor>,
pub tokens: Vec<ClangToken>,
}
#[derive(Debug, Clone)]
pub struct UnsavedFile {
pub filename: String,
pub contents: String,
pub length: usize,
}
#[derive(Debug, Clone)]
pub struct ClangCursor {
pub kind: ClangCursorKind,
pub spelling: String,
pub display_name: String,
pub usr: String,
pub location: ClangSourceLocation,
pub extent: ClangSourceRange,
pub cursor_type: ClangCxType,
pub linkage: ClangLinkageKind,
pub availability: ClangAvailabilityKind,
pub language: ClangLanguageKind,
pub is_definition: bool,
pub is_declaration: bool,
pub is_reference: bool,
pub is_expression: bool,
pub is_statement: bool,
pub is_attribute: bool,
pub is_invalid: bool,
pub is_translation_unit: bool,
pub is_preprocessing: bool,
pub is_unexposed: bool,
pub hash: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangCursorKind {
UnexposedDecl,
StructDecl,
UnionDecl,
ClassDecl,
EnumDecl,
FieldDecl,
EnumConstantDecl,
FunctionDecl,
VarDecl,
ParmDecl,
ObjCInterfaceDecl,
ObjCCategoryDecl,
ObjCProtocolDecl,
ObjCPropertyDecl,
ObjCIvarDecl,
ObjCInstanceMethodDecl,
ObjCClassMethodDecl,
ObjCImplementationDecl,
ObjCCategoryImplDecl,
TypedefDecl,
CXXMethod,
Namespace,
LinkageSpec,
Constructor,
Destructor,
ConversionFunction,
TemplateTypeParameter,
TemplateTemplateParameter,
TemplateNonTypeParameter,
FunctionTemplate,
ClassTemplate,
ClassTemplatePartialSpecialization,
NamespaceAlias,
UsingDirective,
UsingDeclaration,
TypeAliasDecl,
ObjCSynthesizeDecl,
ObjCDynamicDecl,
CXXAccessSpecifier,
ObjCSuperClassRef,
ObjCProtocolRef,
ObjCClassRef,
TypeRef,
CXXBaseSpecifier,
TemplateRef,
NamespaceRef,
MemberRef,
LabelRef,
OverloadedDeclRef,
VariableRef,
MacroDefinition,
MacroExpansion,
InclusionDirective,
UnexposedExpr,
DeclRefExpr,
MemberRefExpr,
CallExpr,
ObjCMessageExpr,
BlockExpr,
IntegerLiteral,
FloatingLiteral,
ImaginaryLiteral,
StringLiteral,
CharacterLiteral,
ParenExpr,
UnaryOperator,
ArraySubscriptExpr,
BinaryOperator,
CompoundAssignOperator,
ConditionalOperator,
CStyleCastExpr,
CompoundLiteralExpr,
InitListExpr,
AddrLabelExpr,
StmtExpr,
GenericSelectionExpr,
GNUNullExpr,
CXXStaticCastExpr,
CXXDynamicCastExpr,
CXXReinterpretCastExpr,
CXXConstCastExpr,
CXXFunctionalCastExpr,
CXXTypeidExpr,
CXXBoolLiteralExpr,
CXXNullPtrLiteralExpr,
CXXThisExpr,
CXXThrowExpr,
CXXNewExpr,
CXXDeleteExpr,
UnaryExpr,
ObjCStringLiteral,
ObjCEncodeExpr,
ObjCSelectorExpr,
ObjCProtocolExpr,
ObjCBridgedCastExpr,
PackExpansionExpr,
SizeOfPackExpr,
LambdaExpr,
ObjCBoolLiteralExpr,
ObjCSelfExpr,
OMPArraySectionExpr,
ObjCAvailabilityCheckExpr,
FixedPointLiteral,
UnexposedStmt,
LabelStmt,
CompoundStmt,
CaseStmt,
DefaultStmt,
IfStmt,
SwitchStmt,
WhileStmt,
DoStmt,
ForStmt,
GotoStmt,
IndirectGotoStmt,
ContinueStmt,
BreakStmt,
ReturnStmt,
AsmStmt,
ObjCAtTryStmt,
ObjCAtCatchStmt,
ObjCAtFinallyStmt,
ObjCAtThrowStmt,
ObjCAtSynchronizedStmt,
ObjCAutoreleasePoolStmt,
ObjCForCollectionStmt,
CXXCatchStmt,
CXXTryStmt,
CXXForRangeStmt,
SEHTryStmt,
SEHExceptStmt,
SEHFinallyStmt,
MSAsmStmt,
NullStmt,
DeclStmt,
OMPParallelDirective,
OMPSimdDirective,
OMPForDirective,
OMPSectionsDirective,
OMPSectionDirective,
OMPSingleDirective,
OMPMasterDirective,
OMPCriticalDirective,
OMPTaskDirective,
OMPParallelForDirective,
OMPParallelSectionsDirective,
OMPTaskyieldDirective,
OMPBarrierDirective,
OMPTaskwaitDirective,
OMPFlushDirective,
SEHLeaveStmt,
OMPOrderedDirective,
OMPAtomicDirective,
OMPForSimdDirective,
OMPParallelForSimdDirective,
OMPTargetDirective,
OMPTeamsDirective,
OMPTaskgroupDirective,
OMPCancellationPointDirective,
OMPCancelDirective,
OMPTargetDataDirective,
OMPTaskLoopDirective,
OMPTaskLoopSimdDirective,
OMPDistributeDirective,
OMPTargetEnterDataDirective,
OMPTargetExitDataDirective,
OMPTargetParallelDirective,
OMPTargetParallelForDirective,
OMPTargetUpdateDirective,
OMPDistributeParallelForDirective,
OMPDistributeParallelForSimdDirective,
OMPDistributeSimdDirective,
OMPTargetParallelForSimdDirective,
OMPTargetSimdDirective,
OMPTeamsDistributeDirective,
OMPTeamsDistributeSimdDirective,
OMPTeamsDistributeParallelForSimdDirective,
OMPTeamsDistributeParallelForDirective,
OMPTeamDirective,
TranslationUnit,
Attribute,
ExtraDecl,
OverloadCandidate,
}
#[derive(Debug, Clone)]
pub struct ClangCxType {
pub kind: ClangTypeKind,
pub spelling: String,
pub size: usize,
pub align: usize,
pub is_const_qualified: bool,
pub is_volatile_qualified: bool,
pub is_restrict_qualified: bool,
pub is_pod: bool,
pub is_function_variadic: bool,
pub is_null_ptr: bool,
pub is_transparent_tag_typedef: bool,
pub element_type: Option<Box<ClangCxType>>,
pub num_elements: i64,
pub result_type: Option<Box<ClangCxType>>,
pub num_template_arguments: i32,
pub template_argument_types: Vec<ClangCxType>,
pub array_size: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangTypeKind {
Invalid,
Unexposed,
Void,
Bool,
CharU,
UChar,
Char16,
Char32,
UShort,
UInt,
ULong,
ULongLong,
UInt128,
CharS,
SChar,
WChar,
Short,
Int,
Long,
LongLong,
Int128,
Float,
Double,
LongDouble,
NullPtr,
Overload,
Dependent,
ObjCId,
ObjCClass,
ObjCSel,
Float128,
Half,
Float16,
ShortAccum,
Accum,
LongAccum,
UShortAccum,
UAccum,
ULongAccum,
BlockPointer,
Pointer,
LValueReference,
RValueReference,
Record,
Enum,
Typedef,
ObjCInterface,
ObjCObjectPointer,
FunctionNoProto,
FunctionProto,
ConstantArray,
Vector,
IncompleteArray,
VariableArray,
DependentSizedArray,
MemberPointer,
Auto,
Elaborated,
Pipe,
OCLImage1DRO,
OCLImage1DArrayRO,
OCLImage1DBufferRO,
OCLImage2DRO,
OCLImage2DArrayRO,
OCLImage2DDepthRO,
OCLImage2DArrayDepthRO,
OCLImage2DMSAARO,
OCLImage2DArrayMSAARO,
OCLImage3DRO,
OCLImage1DWO,
OCLImage1DArrayWO,
OCLImage1DBufferWO,
OCLImage2DWO,
OCLImage2DArrayWO,
OCLImage2DDepthWO,
OCLImage2DArrayDepthWO,
OCLImage2DMSAAWO,
OCLImage2DArrayMSAAWO,
OCLImage3DWO,
OCLImage1DRW,
OCLImage1DArrayRW,
OCLImage1DBufferRW,
OCLImage2DRW,
OCLImage2DArrayRW,
OCLImage2DDepthRW,
OCLImage2DArrayDepthRW,
OCLImage2DMSAARW,
OCLImage2DArrayMSAARW,
OCLImage3DRW,
OCLSampler,
OCLEvent,
OCLQueue,
OCLReserveID,
ObjCObject,
ObjCTypeParam,
Attributed,
OCLIntelSubgroupAVCMcePayload,
OCLIntelSubgroupAVCImePayload,
OCLIntelSubgroupAVCRefPayload,
OCLIntelSubgroupAVCSicPayload,
OCLIntelSubgroupAVCMceResult,
OCLIntelSubgroupAVCImeResult,
OCLIntelSubgroupAVCRefResult,
OCLIntelSubgroupAVCSicResult,
OCLIntelSubgroupAVCImeResultSingleRefStreamOut,
OCLIntelSubgroupAVCImeResultDualRefStreamOut,
OCLIntelSubgroupAVCImeSingleRefStreamIn,
OCLIntelSubgroupAVCImeDualRefStreamIn,
ExtVector,
Atomic,
BTFTagAttributed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClangSourceLocation {
pub file: Option<&'static str>,
pub line: u32,
pub column: u32,
pub offset: u32,
pub is_in_system_header: bool,
pub is_from_main_file: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClangSourceRange {
pub start: ClangSourceLocation,
pub end: ClangSourceLocation,
}
#[derive(Debug, Clone)]
pub struct ClangDiagnostic {
pub severity: ClangDiagnosticSeverity,
pub message: String,
pub location: ClangSourceLocation,
pub spelling: String,
pub option: String,
pub category_text: String,
pub fixits: Vec<ClangFixIt>,
pub children: Vec<ClangDiagnostic>,
pub ranges: Vec<ClangSourceRange>,
pub format: ClangDiagnosticFormat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangDiagnosticSeverity {
Ignored,
Note,
Warning,
Error,
Fatal,
}
impl ClangDiagnosticSeverity {
pub fn as_str(&self) -> &'static str {
match self {
Self::Ignored => "ignored",
Self::Note => "note",
Self::Warning => "warning",
Self::Error => "error",
Self::Fatal => "fatal error",
}
}
}
#[derive(Debug, Clone)]
pub struct ClangFixIt {
pub range: ClangSourceRange,
pub replacement: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangDiagnosticFormat {
Clang,
MSVC,
Vi,
}
#[derive(Debug, Clone)]
pub struct ClangCodeCompleteResults {
pub results: Vec<ClangCompletionResult>,
pub num_diagnostics: u32,
pub diagnostics: Vec<ClangDiagnostic>,
pub context: u64,
}
#[derive(Debug, Clone)]
pub struct ClangCompletionResult {
pub kind: ClangCursorKind,
pub completion_string: ClangCompletionString,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct ClangCompletionString {
pub text: String,
pub kind: ClangCompletionChunkKind,
pub chunks: Vec<ClangCompletionChunk>,
pub availability: ClangAvailabilityKind,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct ClangCompletionChunk {
pub kind: ClangCompletionChunkKind,
pub text: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangCompletionChunkKind {
Optional,
TypedText,
Text,
Placeholder,
Informative,
CurrentParameter,
LeftParen,
RightParen,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
LeftAngle,
RightAngle,
Comma,
ResultType,
Colon,
SemiColon,
Equal,
HorizontalSpace,
VerticalSpace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangLinkageKind {
Invalid,
NoLinkage,
Internal,
UniqueExternal,
External,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangAvailabilityKind {
Available,
Deprecated,
NotAvailable,
NotAccessible,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangLanguageKind {
Invalid,
C,
CPlusPlus,
ObjC,
}
#[derive(Debug, Clone)]
pub struct ClangToken {
pub kind: ClangTokenKind,
pub spelling: String,
pub location: ClangSourceLocation,
pub extent: ClangSourceRange,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClangTokenKind {
Punctuation,
Keyword,
Identifier,
Literal,
Comment,
}
#[derive(Debug, Clone)]
pub struct ClangModule {
pub name: String,
pub full_name: String,
pub is_system: bool,
pub num_top_level_headers: u32,
pub top_level_headers: Vec<String>,
pub platform: Vec<String>,
pub parent: Option<Box<ClangModule>>,
pub ast_file: Option<PathBuf>,
}
#[derive(Debug, Clone)]
pub struct CompilationDatabase {
pub path: PathBuf,
pub commands: Vec<CompileCommand>,
pub last_modified: Option<SystemTime>,
pub watcher_active: bool,
}
#[derive(Debug, Clone)]
pub struct CompileCommand {
pub directory: PathBuf,
pub file: PathBuf,
pub arguments: Vec<String>,
pub output: Option<PathBuf>,
}
impl CompilationDatabase {
pub fn new(path: PathBuf) -> Self {
Self {
path,
commands: Vec::new(),
last_modified: None,
watcher_active: false,
}
}
pub fn load(&mut self) -> io::Result<()> {
use std::fs;
let content = fs::read_to_string(&self.path)?;
let json: serde_json::Value = serde_json::from_str(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let mut commands = Vec::new();
if let Some(arr) = json.as_array() {
for entry in arr {
let directory = entry["directory"]
.as_str()
.map(PathBuf::from)
.unwrap_or_default();
let file = entry["file"]
.as_str()
.map(PathBuf::from)
.unwrap_or_default();
let arguments: Vec<String> = if let Some(args) = entry["arguments"].as_array() {
args.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
} else if let Some(cmd) = entry["command"].as_str() {
cmd.split_whitespace().map(String::from).collect()
} else {
Vec::new()
};
let output = entry["output"].as_str().map(PathBuf::from);
commands.push(CompileCommand {
directory,
file,
arguments,
output,
});
}
}
self.commands = commands;
self.last_modified = std::fs::metadata(&self.path)
.ok()
.and_then(|m| m.modified().ok());
Ok(())
}
pub fn get_compile_args(&self, file: &PathBuf) -> Option<Vec<String>> {
self.commands
.iter()
.find(|cmd| {
cmd.file == *file
|| cmd.file.ends_with(file.file_name().unwrap_or_default())
})
.map(|cmd| cmd.arguments.clone())
}
pub fn watch(&mut self) -> io::Result<()> {
self.watcher_active = true;
Ok(())
}
pub fn reload_if_changed(&mut self) -> io::Result<bool> {
if let Ok(metadata) = std::fs::metadata(&self.path) {
if let Ok(modified) = metadata.modified() {
if self.last_modified != Some(modified) {
self.load()?;
return Ok(true);
}
}
}
Ok(false)
}
pub fn all_files(&self) -> Vec<PathBuf> {
self.commands.iter().map(|c| c.file.clone()).collect()
}
pub fn find_commands_for_file(&self, file: &str) -> Vec<&CompileCommand> {
self.commands
.iter()
.filter(|cmd| {
cmd.file.to_string_lossy().contains(file)
})
.collect()
}
}
#[derive(Debug)]
pub struct CodeIndex {
pub symbols: HashMap<DocumentUri, Vec<IndexedSymbol>>,
pub global_symbol_index: HashMap<String, Vec<IndexedSymbol>>,
pub file_hashes: HashMap<DocumentUri, u64>,
pub indexing_queue: VecDeque<IndexTask>,
pub is_indexing: bool,
}
#[derive(Debug, Clone)]
pub struct IndexedSymbol {
pub name: String,
pub qualified_name: String,
pub kind: SymbolKind,
pub range: Range,
pub selection_range: Range,
pub detail: Option<String>,
pub documentation: Option<String>,
pub parent: Option<String>,
pub children: Vec<String>,
pub usr: String,
pub is_definition: bool,
pub is_declaration: bool,
pub references: Vec<Location>,
pub definition_location: Option<Location>,
pub type_info: Option<IndexedTypeInfo>,
}
#[derive(Debug, Clone)]
pub struct IndexedTypeInfo {
pub type_name: String,
pub pointee_type: Option<String>,
pub return_type: Option<String>,
pub param_types: Vec<String>,
pub base_types: Vec<String>,
pub is_const: bool,
pub is_volatile: bool,
}
#[derive(Debug, Clone)]
pub struct IndexTask {
pub uri: DocumentUri,
pub priority: IndexPriority,
pub created_at: Instant,
}
impl IndexedSymbol {
pub fn hover_text(&self) -> String {
let mut text = String::new();
if let Some(detail) = &self.detail {
text.push_str(&format!("```cpp\n{}\n```\n\n", detail));
}
if let Some(type_info) = &self.type_info {
text.push_str(&format!("Type: `{}`\n\n", type_info.type_name));
}
if let Some(doc) = &self.documentation {
text.push_str(doc);
}
if self.is_definition {
text.push_str("\n\n*(Definition)*");
}
text
}
}
impl CodeIndex {
pub fn new() -> Self {
Self {
symbols: HashMap::new(),
global_symbol_index: HashMap::new(),
file_hashes: HashMap::new(),
indexing_queue: VecDeque::new(),
is_indexing: false,
}
}
pub fn index_file(&mut self, uri: &DocumentUri, _content: &str) {
let mut symbols = Vec::new();
let patterns = vec![
("class ", SymbolKind::Class),
("struct ", SymbolKind::Struct),
("enum ", SymbolKind::Enum),
("namespace ", SymbolKind::Namespace),
("void ", SymbolKind::Function),
("int ", SymbolKind::Function),
("bool ", SymbolKind::Function),
("auto ", SymbolKind::Function),
("float ", SymbolKind::Function),
("double ", SymbolKind::Function),
];
for (pattern, kind) in patterns {
if _content.contains(pattern) {
symbols.push(IndexedSymbol {
name: format!("{}_{}", kind, symbols.len()),
qualified_name: format!("{}::{}", uri, kind),
kind,
range: Range::from_coords(0, 0, 0, 0),
selection_range: Range::from_coords(0, 0, 0, 0),
detail: Some(format!("Found {} declaration", pattern.trim())),
documentation: None,
parent: None,
children: Vec::new(),
usr: format!("c:@F@{}#I#", symbols.len()),
is_definition: false,
is_declaration: true,
references: Vec::new(),
definition_location: None,
type_info: None,
});
}
}
self.symbols.insert(uri.clone(), symbols);
}
pub fn find_completions(&self, _uri: &DocumentUri, _pos: Position) -> Vec<CompletionItem> {
let mut items = Vec::new();
if let Some(symbols) = self.symbols.get(_uri) {
for sym in symbols {
items.push(CompletionItem {
label: sym.name.clone(),
kind: Some(sym.kind_to_completion_kind()),
detail: sym.detail.clone(),
insert_text: Some(sym.name.clone()),
sort_text: Some(format!("1_{}", sym.name)),
..Default::default()
});
}
}
items
}
pub fn find_symbol_at_position(&self, _uri: &DocumentUri, _pos: Position) -> Option<IndexedSymbol> {
if let Some(symbols) = self.symbols.get(_uri) {
symbols.iter().find(|s| s.range.covers(_pos)).cloned()
} else {
None
}
}
pub fn find_definition(&self, _uri: &DocumentUri, _pos: Position) -> Option<Location> {
self.find_symbol_at_position(_uri, _pos)
.and_then(|sym| sym.definition_location.clone())
}
pub fn find_references(&self, _uri: &DocumentUri, _pos: Position) -> Vec<Location> {
if let Some(sym) = self.find_symbol_at_position(_uri, _pos) {
sym.references.clone()
} else {
Vec::new()
}
}
pub fn get_document_symbols(&self, uri: &DocumentUri) -> Vec<DocumentSymbol> {
if let Some(symbols) = self.symbols.get(uri) {
symbols
.iter()
.map(|sym| DocumentSymbol {
name: sym.name.clone(),
detail: sym.detail.clone(),
kind: sym.kind,
tags: None,
deprecated: None,
range: sym.range,
selection_range: sym.selection_range,
children: None,
})
.collect()
} else {
Vec::new()
}
}
pub fn search_workspace_symbols(&self, query: &str) -> Vec<SymbolInformation> {
let query_lower = query.to_lowercase();
let mut results = Vec::new();
for (uri, symbols) in &self.symbols {
for sym in symbols {
if sym.name.to_lowercase().contains(&query_lower) {
results.push(SymbolInformation {
name: sym.name.clone(),
kind: sym.kind,
tags: None,
deprecated: None,
location: Location {
uri: uri.clone(),
range: sym.range,
},
container_name: sym.parent.clone(),
});
}
}
}
results.truncate(50);
results
}
pub fn enqueue_index_task(&mut self, task: IndexTask) {
self.indexing_queue.push_back(task);
}
pub fn process_index_queue(&mut self) -> usize {
let mut processed = 0;
while let Some(task) = self.indexing_queue.pop_front() {
processed += 1;
self.is_indexing = true;
}
self.is_indexing = false;
processed
}
}
impl SymbolKind {
pub fn kind_to_completion_kind(&self) -> CompletionItemKind {
match self {
Self::Class => CompletionItemKind::Class,
Self::Struct => CompletionItemKind::Struct,
Self::Enum => CompletionItemKind::Enum,
Self::EnumMember => CompletionItemKind::EnumMember,
Self::Function => CompletionItemKind::Function,
Self::Method => CompletionItemKind::Method,
Self::Property => CompletionItemKind::Property,
Self::Field => CompletionItemKind::Field,
Self::Variable => CompletionItemKind::Variable,
Self::Constant => CompletionItemKind::Constant,
Self::Interface => CompletionItemKind::Interface,
Self::Module => CompletionItemKind::Module,
Self::Namespace => CompletionItemKind::Module,
Self::Constructor => CompletionItemKind::Constructor,
Self::Operator => CompletionItemKind::Operator,
Self::TypeParameter => CompletionItemKind::TypeParameter,
_ => CompletionItemKind::Text,
}
}
}
impl fmt::Display for SymbolKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone)]
pub struct InlayHint {
pub position: Position,
pub label: InlayHintLabel,
pub kind: Option<InlayHintKind>,
pub text_edits: Option<Vec<TextEdit>>,
pub tooltip: Option<InlayHintTooltip>,
pub padding_left: Option<bool>,
pub padding_right: Option<bool>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub enum InlayHintLabel {
String(String),
Parts(Vec<InlayHintLabelPart>),
}
#[derive(Debug, Clone)]
pub struct InlayHintLabelPart {
pub value: String,
pub tooltip: Option<InlayHintTooltip>,
pub location: Option<Location>,
pub command: Option<Command>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InlayHintKind {
Type,
Parameter,
}
#[derive(Debug, Clone)]
pub enum InlayHintTooltip {
String(String),
MarkupContent(MarkupContent),
}
pub fn compute_inlay_hints(config: &ClangdConfig, _document: &TextDocument) -> Vec<InlayHint> {
let mut hints = Vec::new();
if !config.inlay_hints_enabled {
return hints;
}
if config.inlay_hints_parameters {
}
if config.inlay_hints_deduced_types {
}
if config.inlay_hints_designators {
}
hints
}
#[derive(Debug, Clone)]
pub struct CallHierarchyItem {
pub name: String,
pub kind: SymbolKind,
pub tags: Option<Vec<SymbolTag>>,
pub detail: Option<String>,
pub uri: DocumentUri,
pub range: Range,
pub selection_range: Range,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct CallHierarchyIncomingCall {
pub from: CallHierarchyItem,
pub from_ranges: Vec<Range>,
}
#[derive(Debug, Clone)]
pub struct CallHierarchyOutgoingCall {
pub to: CallHierarchyItem,
pub from_ranges: Vec<Range>,
}
#[derive(Debug, Clone)]
pub struct TypeHierarchyItem {
pub name: String,
pub kind: SymbolKind,
pub tags: Option<Vec<SymbolTag>>,
pub detail: Option<String>,
pub uri: DocumentUri,
pub range: Range,
pub selection_range: Range,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct TypeHierarchySubtype {
pub item: TypeHierarchyItem,
}
pub fn compute_call_hierarchy(_index: &CodeIndex, _uri: &DocumentUri, _position: Position) -> Vec<CallHierarchyItem> {
Vec::new()
}
pub fn call_hierarchy_incoming(_index: &CodeIndex, _item: &CallHierarchyItem) -> Vec<CallHierarchyIncomingCall> {
Vec::new()
}
pub fn call_hierarchy_outgoing(_index: &CodeIndex, _item: &CallHierarchyItem) -> Vec<CallHierarchyOutgoingCall> {
Vec::new()
}
pub fn compute_type_hierarchy(_index: &CodeIndex, _uri: &DocumentUri, _position: Position) -> Vec<TypeHierarchyItem> {
Vec::new()
}
pub fn type_hierarchy_supertypes(_index: &CodeIndex, _item: &TypeHierarchyItem) -> Vec<TypeHierarchySubtype> {
Vec::new()
}
pub fn type_hierarchy_subtypes(_index: &CodeIndex, _item: &TypeHierarchyItem) -> Vec<TypeHierarchySubtype> {
Vec::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_position_display() {
let pos = Position::new(10, 5);
assert_eq!(format!("{}", pos), "10:5");
}
#[test]
fn test_range_covers() {
let range = Range::from_coords(1, 0, 5, 10);
assert!(range.covers(Position::new(3, 5)));
assert!(!range.covers(Position::new(0, 0)));
assert!(!range.covers(Position::new(6, 0)));
assert!(!range.covers(Position::new(1, 0)));
assert!(range.covers(Position::new(1, 1)));
}
#[test]
fn test_document_position_to_offset() {
let doc = TextDocument::new(
"file:///test.cpp".to_string(),
"cpp".to_string(),
"line one\nline two\nline three".to_string(),
);
let pos = Position::new(1, 1);
let offset = TextDocument::position_to_offset(&doc.text, pos);
assert!(offset > 0);
}
#[test]
fn test_document_line_count() {
let doc = TextDocument::new(
"file:///test.cpp".to_string(),
"cpp".to_string(),
"line1\nline2\nline3".to_string(),
);
assert_eq!(doc.line_count(), 3);
}
#[test]
fn test_document_apply_changes_full() {
let mut doc = TextDocument::new(
"file:///test.cpp".to_string(),
"cpp".to_string(),
"old content".to_string(),
);
let changes = vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "new content".to_string(),
}];
doc.apply_changes(&changes);
assert_eq!(doc.text, "new content");
}
#[test]
fn test_document_apply_changes_incremental() {
let mut doc = TextDocument::new(
"file:///test.cpp".to_string(),
"cpp".to_string(),
"hello world".to_string(),
);
let changes = vec![TextDocumentContentChangeEvent {
range: Some(Range::from_coords(0, 6, 0, 11)),
range_length: Some(5),
text: "there".to_string(),
}];
doc.apply_changes(&changes);
assert!(doc.text.contains("hello there"));
}
#[test]
fn test_document_get_text_in_range() {
let doc = TextDocument::new(
"file:///test.cpp".to_string(),
"cpp".to_string(),
"hello world".to_string(),
);
let range = Range::from_coords(0, 0, 0, 5);
let text = doc.get_text_in_range(range);
assert_eq!(text, "hello");
}
#[test]
fn test_compilation_database() {
let mut db = CompilationDatabase::new(PathBuf::from("compile_commands.json"));
assert!(!db.watcher_active);
assert!(db.commands.is_empty());
}
#[test]
fn test_clangd_server_initialize() {
let server = ClangdServer::new(PathBuf::from("/tmp/test"));
assert!(matches!(server.status, ServerStatus::Starting));
}
#[test]
fn test_clangd_server_did_open() {
let mut server = ClangdServer::new(PathBuf::from("/tmp/test"));
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: "file:///test.cpp".to_string(),
language_id: "cpp".to_string(),
version: 1,
text: "int main() { return 0; }".to_string(),
},
};
server.did_open(¶ms);
assert!(server.documents.contains_key("file:///test.cpp"));
}
#[test]
fn test_clangd_server_completion() {
let mut server = ClangdServer::new(PathBuf::from("/tmp/test"));
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: "file:///test.cpp".to_string(),
language_id: "cpp".to_string(),
version: 1,
text: "int main() { return 0; }".to_string(),
},
};
server.did_open(¶ms);
let comp_params = CompletionParams {
text_document: TextDocumentIdentifier {
uri: "file:///test.cpp".to_string(),
},
position: Position::new(0, 0),
context: None,
};
let result = server.completion(&comp_params);
assert!(!result.items.is_empty());
assert!(result.is_incomplete);
}
#[test]
fn test_clangd_config_default() {
let config = ClangdConfig::default();
assert!(config.enable_background_index);
assert!(config.inlay_hints_enabled);
assert_eq!(config.limit_results, 100);
}
#[test]
fn test_initialize_result() {
let mut server = ClangdServer::new(PathBuf::from("/tmp/test"));
let params = InitializeParams {
process_id: None,
client_info: None,
locale: None,
root_path: None,
root_uri: Some("file:///tmp/test".to_string()),
initialization_options: None,
capabilities: ClientCapabilities::default(),
trace: Some(TraceValue::Off),
workspace_folders: None,
};
let result = server.initialize(¶ms);
assert!(result.server_info.is_some());
assert!(result.capabilities.completion_provider.is_some());
assert!(result.capabilities.semantic_tokens_provider.is_some());
}
#[test]
fn test_cursor_kind_enum() {
let kind = ClangCursorKind::FunctionDecl;
assert_ne!(kind, ClangCursorKind::ClassDecl);
}
#[test]
fn test_diagnostic_severity() {
assert_eq!(ClangDiagnosticSeverity::Error.as_str(), "error");
assert_eq!(ClangDiagnosticSeverity::Warning.as_str(), "warning");
assert_eq!(ClangDiagnosticSeverity::Note.as_str(), "note");
}
#[test]
fn test_completion_chunk_kind() {
let kind = ClangCompletionChunkKind::TypedText;
assert_ne!(kind, ClangCompletionChunkKind::Text);
}
#[test]
fn test_symbol_kind_to_completion_kind() {
assert_eq!(
SymbolKind::Function.kind_to_completion_kind(),
CompletionItemKind::Function
);
assert_eq!(
SymbolKind::Class.kind_to_completion_kind(),
CompletionItemKind::Class
);
assert_eq!(
SymbolKind::Variable.kind_to_completion_kind(),
CompletionItemKind::Variable
);
}
#[test]
fn test_code_index() {
let mut index = CodeIndex::new();
assert!(index.find_completions(&"test.cpp".to_string(), Position::new(0, 0)).is_empty());
index.index_file(&"test.cpp".to_string(), "int main() { return 0; }");
let syms = index.get_document_symbols(&"test.cpp".to_string());
assert!(!syms.is_empty());
}
#[test]
fn test_formatting_options() {
let opts = FormattingOptions {
tab_size: 4,
insert_spaces: true,
trim_trailing_whitespace: Some(true),
insert_final_newline: Some(true),
trim_final_newlines: Some(false),
additional_properties: HashMap::new(),
};
assert_eq!(opts.tab_size, 4);
assert!(opts.insert_spaces);
}
#[test]
fn test_inlay_hint_creation() {
let hint = InlayHint {
position: Position::new(5, 10),
label: InlayHintLabel::String("name:".to_string()),
kind: Some(InlayHintKind::Parameter),
text_edits: None,
tooltip: None,
padding_left: Some(true),
padding_right: Some(false),
data: None,
};
assert_eq!(hint.position.line, 5);
}
#[test]
fn test_compilation_database_watch() {
let mut db = CompilationDatabase::new(PathBuf::from("/tmp/nonexistent.json"));
assert!(!db.watcher_active);
let result = db.watch();
assert!(result.is_ok());
assert!(db.watcher_active);
}
#[test]
fn test_compilation_database_find_files() {
let mut db = CompilationDatabase::new(PathBuf::from("test.json"));
db.commands = vec![
CompileCommand {
directory: PathBuf::from("/src"),
file: PathBuf::from("/src/main.cpp"),
arguments: vec!["clang++".to_string(), "-std=c++17".to_string(), "main.cpp".to_string()],
output: Some(PathBuf::from("/src/main.o")),
},
CompileCommand {
directory: PathBuf::from("/src"),
file: PathBuf::from("/src/utils.cpp"),
arguments: vec!["clang++".to_string(), "-std=c++17".to_string(), "utils.cpp".to_string()],
output: None,
},
];
let files = db.all_files();
assert_eq!(files.len(), 2);
let cmds = db.find_commands_for_file("main");
assert_eq!(cmds.len(), 1);
}
#[test]
fn test_background_index_task() {
let task = IndexTask {
uri: "file:///test.cpp".to_string(),
priority: IndexPriority::High,
created_at: Instant::now(),
};
let mut index = CodeIndex::new();
index.enqueue_index_task(task);
assert_eq!(index.indexing_queue.len(), 1);
let processed = index.process_index_queue();
assert_eq!(processed, 1);
assert!(index.indexing_queue.is_empty());
}
#[test]
fn test_semantic_token_legend() {
let legend = SemanticTokensLegend {
token_types: vec!["namespace".to_string(), "type".to_string(), "function".to_string()],
token_modifiers: vec!["declaration".to_string(), "definition".to_string()],
};
assert_eq!(legend.token_types.len(), 3);
assert_eq!(legend.token_modifiers.len(), 2);
}
#[test]
fn test_workspace_edit() {
let mut changes = HashMap::new();
changes.insert(
"file:///test.cpp".to_string(),
vec![TextEdit {
range: Range::from_coords(0, 0, 0, 3),
new_text: "foo".to_string(),
}],
);
let edit = WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
};
assert!(edit.changes.is_some());
assert_eq!(edit.changes.unwrap().len(), 1);
}
}