use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PositionParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
#[schemars(description = "Line number (1-based).")]
pub line: u32,
#[schemars(description = "Character/column number (1-based).")]
pub character: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RangeParams {
#[schemars(description = "Start line (1-based).")]
pub start_line: u32,
#[schemars(description = "Start character (1-based).")]
pub start_character: u32,
#[schemars(description = "End line (1-based).")]
pub end_line: u32,
#[schemars(description = "End character (1-based).")]
pub end_character: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for finding all references to a symbol.")]
pub struct ReferencesParams {
#[serde(flatten)]
pub position: PositionParams,
#[schemars(description = "Whether to include the declaration in the results.")]
#[serde(default)]
pub include_declaration: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for getting diagnostics (errors, warnings) for a file.")]
pub struct DiagnosticsParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for renaming a symbol across the workspace.")]
pub struct RenameParams {
#[serde(flatten)]
pub position: PositionParams,
#[schemars(description = "New name for the symbol.")]
pub new_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for getting code completion suggestions.")]
pub struct CompletionsParams {
#[serde(flatten)]
pub position: PositionParams,
#[schemars(description = "Optional trigger character (e.g., '.', ':', '->').")]
pub trigger: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for getting all symbols in a document.")]
pub struct DocumentSymbolsParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for formatting a document.")]
pub struct FormatDocumentParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
#[schemars(description = "Tab size for formatting (default: 4).")]
#[serde(default = "default_tab_size")]
pub tab_size: u32,
#[schemars(description = "Whether to use spaces instead of tabs (default: true).")]
#[serde(default = "default_insert_spaces")]
pub insert_spaces: bool,
}
const fn default_tab_size() -> u32 {
4
}
const fn default_insert_spaces() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for searching symbols across the workspace.")]
pub struct WorkspaceSymbolParams {
#[schemars(description = "Search query for symbol names (supports partial matching).")]
pub query: String,
#[schemars(description = "Optional filter by symbol kind (function, class, variable, etc.).")]
#[serde(skip_serializing_if = "Option::is_none")]
pub kind_filter: Option<String>,
#[schemars(description = "Maximum results to return (default: 100).")]
#[serde(default = "default_max_results")]
pub limit: u32,
}
const fn default_max_results() -> u32 {
100
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(
description = "Parameters for getting available code actions (quick fixes, refactorings) for a range."
)]
pub struct CodeActionsParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
#[serde(flatten)]
pub range: RangeParams,
#[schemars(description = "Optional filter by action kind (quickfix, refactor, source, etc.).")]
#[serde(skip_serializing_if = "Option::is_none")]
pub kind_filter: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(
description = "Parameters for getting incoming or outgoing calls for a call hierarchy item."
)]
pub struct CallHierarchyCallsParams {
#[schemars(description = "The call hierarchy item to get calls for (from prepare response).")]
pub item: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(
description = "Parameters for getting cached diagnostics from LSP server notifications."
)]
pub struct CachedDiagnosticsParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for getting recent LSP server log messages.")]
pub struct ServerLogsParams {
#[schemars(description = "Maximum number of log entries to return (default: 50).")]
#[serde(default = "default_log_limit")]
pub limit: usize,
#[schemars(description = "Minimum log level to include: error, warning, info, debug.")]
#[serde(skip_serializing_if = "Option::is_none")]
pub min_level: Option<String>,
}
const fn default_log_limit() -> usize {
50
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(
description = "Parameters for getting recent LSP server messages (showMessage notifications)."
)]
pub struct ServerMessagesParams {
#[schemars(description = "Maximum number of messages to return (default: 20).")]
#[serde(default = "default_message_limit")]
pub limit: usize,
}
const fn default_message_limit() -> usize {
20
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Parameters for getting inlay hints in a range.")]
pub struct InlayHintsParams {
#[schemars(description = "Absolute path to the file.")]
pub file_path: String,
#[serde(flatten)]
pub range: RangeParams,
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn flattened_params_serialize_to_flat_json() {
let references = ReferencesParams {
position: PositionParams {
file_path: "/a.rs".to_string(),
line: 1,
character: 2,
},
include_declaration: true,
};
let json = serde_json::to_value(&references).unwrap();
assert_eq!(
json,
serde_json::json!({
"file_path": "/a.rs",
"line": 1,
"character": 2,
"include_declaration": true,
})
);
let inlay = InlayHintsParams {
file_path: "/b.rs".to_string(),
range: RangeParams {
start_line: 1,
start_character: 2,
end_line: 3,
end_character: 4,
},
};
let json = serde_json::to_value(&inlay).unwrap();
assert_eq!(
json,
serde_json::json!({
"file_path": "/b.rs",
"start_line": 1,
"start_character": 2,
"end_line": 3,
"end_character": 4,
})
);
}
#[test]
fn flat_json_deserializes_into_flattened_params() {
let json = serde_json::json!({"file_path": "/a.rs", "line": 1, "character": 2});
let references: ReferencesParams = serde_json::from_value(json).unwrap();
assert_eq!(references.position.file_path, "/a.rs");
assert_eq!(references.position.line, 1);
assert_eq!(references.position.character, 2);
assert!(!references.include_declaration);
}
#[test]
fn generated_schema_exposes_flattened_fields_at_top_level() {
let schema = schemars::schema_for!(ReferencesParams);
let properties = schema
.as_object()
.unwrap()
.get("properties")
.unwrap()
.as_object()
.unwrap();
assert!(properties.contains_key("file_path"));
assert!(properties.contains_key("line"));
assert!(properties.contains_key("character"));
assert!(properties.contains_key("include_declaration"));
assert!(!properties.contains_key("position"));
let schema = schemars::schema_for!(InlayHintsParams);
let properties = schema
.as_object()
.unwrap()
.get("properties")
.unwrap()
.as_object()
.unwrap();
assert!(properties.contains_key("file_path"));
assert!(properties.contains_key("start_line"));
assert!(properties.contains_key("start_character"));
assert!(properties.contains_key("end_line"));
assert!(properties.contains_key("end_character"));
assert!(!properties.contains_key("range"));
}
}