use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{IntoOption, Meta, SessionId};
pub(crate) const NES_START_METHOD_NAME: &str = "nes/start";
pub(crate) const NES_SUGGEST_METHOD_NAME: &str = "nes/suggest";
pub(crate) const NES_ACCEPT_METHOD_NAME: &str = "nes/accept";
pub(crate) const NES_REJECT_METHOD_NAME: &str = "nes/reject";
pub(crate) const NES_CLOSE_METHOD_NAME: &str = "nes/close";
pub(crate) const DOCUMENT_DID_OPEN_METHOD_NAME: &str = "document/didOpen";
pub(crate) const DOCUMENT_DID_CHANGE_METHOD_NAME: &str = "document/didChange";
pub(crate) const DOCUMENT_DID_CLOSE_METHOD_NAME: &str = "document/didClose";
pub(crate) const DOCUMENT_DID_SAVE_METHOD_NAME: &str = "document/didSave";
pub(crate) const DOCUMENT_DID_FOCUS_METHOD_NAME: &str = "document/didFocus";
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub enum PositionEncodingKind {
#[serde(rename = "utf-16")]
Utf16,
#[serde(rename = "utf-32")]
Utf32,
#[serde(rename = "utf-8")]
Utf8,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Position {
pub line: u32,
pub character: u32,
}
impl Position {
#[must_use]
pub fn new(line: u32, character: u32) -> Self {
Self { line, character }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Range {
pub start: Position,
pub end: Position,
}
impl Range {
#[must_use]
pub fn new(start: Position, end: Position) -> Self {
Self { start, end }
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub events: Option<NesEventCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<NesContextCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn events(mut self, events: impl IntoOption<NesEventCapabilities>) -> Self {
self.events = events.into_option();
self
}
#[must_use]
pub fn context(mut self, context: impl IntoOption<NesContextCapabilities>) -> Self {
self.context = context.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesEventCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub document: Option<NesDocumentEventCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesEventCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn document(mut self, document: impl IntoOption<NesDocumentEventCapabilities>) -> Self {
self.document = document.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentEventCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub did_open: Option<NesDocumentDidOpenCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub did_change: Option<NesDocumentDidChangeCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub did_close: Option<NesDocumentDidCloseCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub did_save: Option<NesDocumentDidSaveCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub did_focus: Option<NesDocumentDidFocusCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentEventCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn did_open(mut self, did_open: impl IntoOption<NesDocumentDidOpenCapabilities>) -> Self {
self.did_open = did_open.into_option();
self
}
#[must_use]
pub fn did_change(
mut self,
did_change: impl IntoOption<NesDocumentDidChangeCapabilities>,
) -> Self {
self.did_change = did_change.into_option();
self
}
#[must_use]
pub fn did_close(
mut self,
did_close: impl IntoOption<NesDocumentDidCloseCapabilities>,
) -> Self {
self.did_close = did_close.into_option();
self
}
#[must_use]
pub fn did_save(mut self, did_save: impl IntoOption<NesDocumentDidSaveCapabilities>) -> Self {
self.did_save = did_save.into_option();
self
}
#[must_use]
pub fn did_focus(
mut self,
did_focus: impl IntoOption<NesDocumentDidFocusCapabilities>,
) -> Self {
self.did_focus = did_focus.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentDidOpenCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentDidOpenCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentDidChangeCapabilities {
pub sync_kind: TextDocumentSyncKind,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentDidChangeCapabilities {
#[must_use]
pub fn new(sync_kind: TextDocumentSyncKind) -> Self {
Self {
sync_kind,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub enum TextDocumentSyncKind {
#[serde(rename = "full")]
Full,
#[serde(rename = "incremental")]
Incremental,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentDidCloseCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentDidCloseCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentDidSaveCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentDidSaveCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDocumentDidFocusCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDocumentDidFocusCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesContextCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub recent_files: Option<NesRecentFilesCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub related_snippets: Option<NesRelatedSnippetsCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub edit_history: Option<NesEditHistoryCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_actions: Option<NesUserActionsCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_files: Option<NesOpenFilesCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<NesDiagnosticsCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesContextCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn recent_files(
mut self,
recent_files: impl IntoOption<NesRecentFilesCapabilities>,
) -> Self {
self.recent_files = recent_files.into_option();
self
}
#[must_use]
pub fn related_snippets(
mut self,
related_snippets: impl IntoOption<NesRelatedSnippetsCapabilities>,
) -> Self {
self.related_snippets = related_snippets.into_option();
self
}
#[must_use]
pub fn edit_history(
mut self,
edit_history: impl IntoOption<NesEditHistoryCapabilities>,
) -> Self {
self.edit_history = edit_history.into_option();
self
}
#[must_use]
pub fn user_actions(
mut self,
user_actions: impl IntoOption<NesUserActionsCapabilities>,
) -> Self {
self.user_actions = user_actions.into_option();
self
}
#[must_use]
pub fn open_files(mut self, open_files: impl IntoOption<NesOpenFilesCapabilities>) -> Self {
self.open_files = open_files.into_option();
self
}
#[must_use]
pub fn diagnostics(mut self, diagnostics: impl IntoOption<NesDiagnosticsCapabilities>) -> Self {
self.diagnostics = diagnostics.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRecentFilesCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesRecentFilesCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRelatedSnippetsCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesRelatedSnippetsCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesEditHistoryCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesEditHistoryCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesUserActionsCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesUserActionsCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesOpenFilesCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesOpenFilesCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDiagnosticsCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesDiagnosticsCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ClientNesCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub jump: Option<NesJumpCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rename: Option<NesRenameCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_and_replace: Option<NesSearchAndReplaceCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl ClientNesCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn jump(mut self, jump: impl IntoOption<NesJumpCapabilities>) -> Self {
self.jump = jump.into_option();
self
}
#[must_use]
pub fn rename(mut self, rename: impl IntoOption<NesRenameCapabilities>) -> Self {
self.rename = rename.into_option();
self
}
#[must_use]
pub fn search_and_replace(
mut self,
search_and_replace: impl IntoOption<NesSearchAndReplaceCapabilities>,
) -> Self {
self.search_and_replace = search_and_replace.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesJumpCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesJumpCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRenameCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesRenameCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesSearchAndReplaceCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesSearchAndReplaceCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_OPEN_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DidOpenDocumentNotification {
pub session_id: SessionId,
pub uri: String,
pub language_id: String,
pub version: i64,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl DidOpenDocumentNotification {
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
uri: impl Into<String>,
language_id: impl Into<String>,
version: i64,
text: impl Into<String>,
) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
language_id: language_id.into(),
version,
text: text.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CHANGE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DidChangeDocumentNotification {
pub session_id: SessionId,
pub uri: String,
pub version: i64,
pub content_changes: Vec<TextDocumentContentChangeEvent>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl DidChangeDocumentNotification {
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
uri: impl Into<String>,
version: i64,
content_changes: Vec<TextDocumentContentChangeEvent>,
) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
version,
content_changes,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct TextDocumentContentChangeEvent {
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<Range>,
pub text: String,
}
impl TextDocumentContentChangeEvent {
#[must_use]
pub fn full(text: impl Into<String>) -> Self {
Self {
range: None,
text: text.into(),
}
}
#[must_use]
pub fn incremental(range: Range, text: impl Into<String>) -> Self {
Self {
range: Some(range),
text: text.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CLOSE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DidCloseDocumentNotification {
pub session_id: SessionId,
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl DidCloseDocumentNotification {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, uri: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_SAVE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DidSaveDocumentNotification {
pub session_id: SessionId,
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl DidSaveDocumentNotification {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, uri: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_FOCUS_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DidFocusDocumentNotification {
pub session_id: SessionId,
pub uri: String,
pub version: i64,
pub position: Position,
pub visible_range: Range,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl DidFocusDocumentNotification {
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
uri: impl Into<String>,
version: i64,
position: Position,
visible_range: Range,
) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
version,
position,
visible_range,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct StartNesRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_folders: Option<Vec<WorkspaceFolder>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub repository: Option<NesRepository>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl StartNesRequest {
#[must_use]
pub fn new() -> Self {
Self {
workspace_uri: None,
workspace_folders: None,
repository: None,
meta: None,
}
}
#[must_use]
pub fn workspace_uri(mut self, workspace_uri: impl IntoOption<String>) -> Self {
self.workspace_uri = workspace_uri.into_option();
self
}
#[must_use]
pub fn workspace_folders(
mut self,
workspace_folders: impl IntoOption<Vec<WorkspaceFolder>>,
) -> Self {
self.workspace_folders = workspace_folders.into_option();
self
}
#[must_use]
pub fn repository(mut self, repository: impl IntoOption<NesRepository>) -> Self {
self.repository = repository.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
impl Default for StartNesRequest {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct WorkspaceFolder {
pub uri: String,
pub name: String,
}
impl WorkspaceFolder {
#[must_use]
pub fn new(uri: impl Into<String>, name: impl Into<String>) -> Self {
Self {
uri: uri.into(),
name: name.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRepository {
pub name: String,
pub owner: String,
pub remote_url: String,
}
impl NesRepository {
#[must_use]
pub fn new(
name: impl Into<String>,
owner: impl Into<String>,
remote_url: impl Into<String>,
) -> Self {
Self {
name: name.into(),
owner: owner.into(),
remote_url: remote_url.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct StartNesResponse {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl StartNesResponse {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CloseNesRequest {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl CloseNesRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CloseNesResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl CloseNesResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub enum NesTriggerKind {
#[serde(rename = "automatic")]
Automatic,
#[serde(rename = "diagnostic")]
Diagnostic,
#[serde(rename = "manual")]
Manual,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SuggestNesRequest {
pub session_id: SessionId,
pub uri: String,
pub version: i64,
pub position: Position,
#[serde(skip_serializing_if = "Option::is_none")]
pub selection: Option<Range>,
pub trigger_kind: NesTriggerKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<NesSuggestContext>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SuggestNesRequest {
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
uri: impl Into<String>,
version: i64,
position: Position,
trigger_kind: NesTriggerKind,
) -> Self {
Self {
session_id: session_id.into(),
uri: uri.into(),
version,
position,
selection: None,
trigger_kind,
context: None,
meta: None,
}
}
#[must_use]
pub fn selection(mut self, selection: impl IntoOption<Range>) -> Self {
self.selection = selection.into_option();
self
}
#[must_use]
pub fn context(mut self, context: impl IntoOption<NesSuggestContext>) -> Self {
self.context = context.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesSuggestContext {
#[serde(skip_serializing_if = "Option::is_none")]
pub recent_files: Option<Vec<NesRecentFile>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub related_snippets: Option<Vec<NesRelatedSnippet>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub edit_history: Option<Vec<NesEditHistoryEntry>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_actions: Option<Vec<NesUserAction>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_files: Option<Vec<NesOpenFile>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<Vec<NesDiagnostic>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NesSuggestContext {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn recent_files(mut self, recent_files: impl IntoOption<Vec<NesRecentFile>>) -> Self {
self.recent_files = recent_files.into_option();
self
}
#[must_use]
pub fn related_snippets(
mut self,
related_snippets: impl IntoOption<Vec<NesRelatedSnippet>>,
) -> Self {
self.related_snippets = related_snippets.into_option();
self
}
#[must_use]
pub fn edit_history(mut self, edit_history: impl IntoOption<Vec<NesEditHistoryEntry>>) -> Self {
self.edit_history = edit_history.into_option();
self
}
#[must_use]
pub fn user_actions(mut self, user_actions: impl IntoOption<Vec<NesUserAction>>) -> Self {
self.user_actions = user_actions.into_option();
self
}
#[must_use]
pub fn open_files(mut self, open_files: impl IntoOption<Vec<NesOpenFile>>) -> Self {
self.open_files = open_files.into_option();
self
}
#[must_use]
pub fn diagnostics(mut self, diagnostics: impl IntoOption<Vec<NesDiagnostic>>) -> Self {
self.diagnostics = diagnostics.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRecentFile {
pub uri: String,
pub language_id: String,
pub text: String,
}
impl NesRecentFile {
#[must_use]
pub fn new(
uri: impl Into<String>,
language_id: impl Into<String>,
text: impl Into<String>,
) -> Self {
Self {
uri: uri.into(),
language_id: language_id.into(),
text: text.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRelatedSnippet {
pub uri: String,
pub excerpts: Vec<NesExcerpt>,
}
impl NesRelatedSnippet {
#[must_use]
pub fn new(uri: impl Into<String>, excerpts: Vec<NesExcerpt>) -> Self {
Self {
uri: uri.into(),
excerpts,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesExcerpt {
pub start_line: u32,
pub end_line: u32,
pub text: String,
}
impl NesExcerpt {
#[must_use]
pub fn new(start_line: u32, end_line: u32, text: impl Into<String>) -> Self {
Self {
start_line,
end_line,
text: text.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesEditHistoryEntry {
pub uri: String,
pub diff: String,
}
impl NesEditHistoryEntry {
#[must_use]
pub fn new(uri: impl Into<String>, diff: impl Into<String>) -> Self {
Self {
uri: uri.into(),
diff: diff.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesUserAction {
pub action: String,
pub uri: String,
pub position: Position,
pub timestamp_ms: u64,
}
impl NesUserAction {
#[must_use]
pub fn new(
action: impl Into<String>,
uri: impl Into<String>,
position: Position,
timestamp_ms: u64,
) -> Self {
Self {
action: action.into(),
uri: uri.into(),
position,
timestamp_ms,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesOpenFile {
pub uri: String,
pub language_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub visible_range: Option<Range>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_focused_ms: Option<u64>,
}
impl NesOpenFile {
#[must_use]
pub fn new(uri: impl Into<String>, language_id: impl Into<String>) -> Self {
Self {
uri: uri.into(),
language_id: language_id.into(),
visible_range: None,
last_focused_ms: None,
}
}
#[must_use]
pub fn visible_range(mut self, visible_range: impl IntoOption<Range>) -> Self {
self.visible_range = visible_range.into_option();
self
}
#[must_use]
pub fn last_focused_ms(mut self, last_focused_ms: impl IntoOption<u64>) -> Self {
self.last_focused_ms = last_focused_ms.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesDiagnostic {
pub uri: String,
pub range: Range,
pub severity: NesDiagnosticSeverity,
pub message: String,
}
impl NesDiagnostic {
#[must_use]
pub fn new(
uri: impl Into<String>,
range: Range,
severity: NesDiagnosticSeverity,
message: impl Into<String>,
) -> Self {
Self {
uri: uri.into(),
range,
severity,
message: message.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub enum NesDiagnosticSeverity {
#[serde(rename = "error")]
Error,
#[serde(rename = "warning")]
Warning,
#[serde(rename = "information")]
Information,
#[serde(rename = "hint")]
Hint,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SuggestNesResponse {
pub suggestions: Vec<NesSuggestion>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SuggestNesResponse {
#[must_use]
pub fn new(suggestions: Vec<NesSuggestion>) -> Self {
Self {
suggestions,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "kind", rename_all = "camelCase")]
#[schemars(extend("discriminator" = {"propertyName": "kind"}))]
#[non_exhaustive]
pub enum NesSuggestion {
Edit(NesEditSuggestion),
Jump(NesJumpSuggestion),
Rename(NesRenameSuggestion),
SearchAndReplace(NesSearchAndReplaceSuggestion),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesEditSuggestion {
pub id: String,
pub uri: String,
pub edits: Vec<NesTextEdit>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor_position: Option<Position>,
}
impl NesEditSuggestion {
#[must_use]
pub fn new(id: impl Into<String>, uri: impl Into<String>, edits: Vec<NesTextEdit>) -> Self {
Self {
id: id.into(),
uri: uri.into(),
edits,
cursor_position: None,
}
}
#[must_use]
pub fn cursor_position(mut self, cursor_position: impl IntoOption<Position>) -> Self {
self.cursor_position = cursor_position.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesTextEdit {
pub range: Range,
pub new_text: String,
}
impl NesTextEdit {
#[must_use]
pub fn new(range: Range, new_text: impl Into<String>) -> Self {
Self {
range,
new_text: new_text.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesJumpSuggestion {
pub id: String,
pub uri: String,
pub position: Position,
}
impl NesJumpSuggestion {
#[must_use]
pub fn new(id: impl Into<String>, uri: impl Into<String>, position: Position) -> Self {
Self {
id: id.into(),
uri: uri.into(),
position,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesRenameSuggestion {
pub id: String,
pub uri: String,
pub position: Position,
pub new_name: String,
}
impl NesRenameSuggestion {
#[must_use]
pub fn new(
id: impl Into<String>,
uri: impl Into<String>,
position: Position,
new_name: impl Into<String>,
) -> Self {
Self {
id: id.into(),
uri: uri.into(),
position,
new_name: new_name.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NesSearchAndReplaceSuggestion {
pub id: String,
pub uri: String,
pub search: String,
pub replace: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_regex: Option<bool>,
}
impl NesSearchAndReplaceSuggestion {
#[must_use]
pub fn new(
id: impl Into<String>,
uri: impl Into<String>,
search: impl Into<String>,
replace: impl Into<String>,
) -> Self {
Self {
id: id.into(),
uri: uri.into(),
search: search.into(),
replace: replace.into(),
is_regex: None,
}
}
#[must_use]
pub fn is_regex(mut self, is_regex: impl IntoOption<bool>) -> Self {
self.is_regex = is_regex.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_ACCEPT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AcceptNesNotification {
pub session_id: SessionId,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl AcceptNesNotification {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, id: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
id: id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = NES_REJECT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct RejectNesNotification {
pub session_id: SessionId,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<NesRejectReason>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl RejectNesNotification {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, id: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
id: id.into(),
reason: None,
meta: None,
}
}
#[must_use]
pub fn reason(mut self, reason: impl IntoOption<NesRejectReason>) -> Self {
self.reason = reason.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub enum NesRejectReason {
#[serde(rename = "rejected")]
Rejected,
#[serde(rename = "ignored")]
Ignored,
#[serde(rename = "replaced")]
Replaced,
#[serde(rename = "cancelled")]
Cancelled,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_position_encoding_kind_serialization() {
assert_eq!(
serde_json::to_value(&PositionEncodingKind::Utf16).unwrap(),
json!("utf-16")
);
assert_eq!(
serde_json::to_value(&PositionEncodingKind::Utf32).unwrap(),
json!("utf-32")
);
assert_eq!(
serde_json::to_value(&PositionEncodingKind::Utf8).unwrap(),
json!("utf-8")
);
assert_eq!(
serde_json::from_value::<PositionEncodingKind>(json!("utf-16")).unwrap(),
PositionEncodingKind::Utf16
);
assert_eq!(
serde_json::from_value::<PositionEncodingKind>(json!("utf-32")).unwrap(),
PositionEncodingKind::Utf32
);
assert_eq!(
serde_json::from_value::<PositionEncodingKind>(json!("utf-8")).unwrap(),
PositionEncodingKind::Utf8
);
}
#[test]
fn test_agent_nes_capabilities_serialization() {
let caps = NesCapabilities::new()
.events(
NesEventCapabilities::new().document(
NesDocumentEventCapabilities::new()
.did_open(NesDocumentDidOpenCapabilities::default())
.did_change(NesDocumentDidChangeCapabilities::new(
TextDocumentSyncKind::Incremental,
))
.did_close(NesDocumentDidCloseCapabilities::default())
.did_save(NesDocumentDidSaveCapabilities::default())
.did_focus(NesDocumentDidFocusCapabilities::default()),
),
)
.context(
NesContextCapabilities::new()
.recent_files(NesRecentFilesCapabilities {
max_count: Some(10),
meta: None,
})
.related_snippets(NesRelatedSnippetsCapabilities::default())
.edit_history(NesEditHistoryCapabilities {
max_count: Some(6),
meta: None,
})
.user_actions(NesUserActionsCapabilities {
max_count: Some(16),
meta: None,
})
.open_files(NesOpenFilesCapabilities::default())
.diagnostics(NesDiagnosticsCapabilities::default()),
);
let json = serde_json::to_value(&caps).unwrap();
assert_eq!(
json,
json!({
"events": {
"document": {
"didOpen": {},
"didChange": {
"syncKind": "incremental"
},
"didClose": {},
"didSave": {},
"didFocus": {}
}
},
"context": {
"recentFiles": {
"maxCount": 10
},
"relatedSnippets": {},
"editHistory": {
"maxCount": 6
},
"userActions": {
"maxCount": 16
},
"openFiles": {},
"diagnostics": {}
}
})
);
let deserialized: NesCapabilities = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, caps);
}
#[test]
fn test_client_nes_capabilities_serialization() {
let caps = ClientNesCapabilities::new()
.jump(NesJumpCapabilities::default())
.rename(NesRenameCapabilities::default())
.search_and_replace(NesSearchAndReplaceCapabilities::default());
let json = serde_json::to_value(&caps).unwrap();
assert_eq!(
json,
json!({
"jump": {},
"rename": {},
"searchAndReplace": {}
})
);
let deserialized: ClientNesCapabilities = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, caps);
}
#[test]
fn test_document_did_open_serialization() {
let notification = DidOpenDocumentNotification::new(
"session_123",
"file:///path/to/file.rs",
"rust",
1,
"fn main() {\n println!(\"hello\");\n}\n",
);
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({
"sessionId": "session_123",
"uri": "file:///path/to/file.rs",
"languageId": "rust",
"version": 1,
"text": "fn main() {\n println!(\"hello\");\n}\n"
})
);
let deserialized: DidOpenDocumentNotification = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, notification);
}
#[test]
fn test_document_did_change_incremental_serialization() {
let notification = DidChangeDocumentNotification::new(
"session_123",
"file:///path/to/file.rs",
2,
vec![TextDocumentContentChangeEvent::incremental(
Range::new(Position::new(1, 4), Position::new(1, 4)),
"let x = 42;\n ",
)],
);
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({
"sessionId": "session_123",
"uri": "file:///path/to/file.rs",
"version": 2,
"contentChanges": [
{
"range": {
"start": { "line": 1, "character": 4 },
"end": { "line": 1, "character": 4 }
},
"text": "let x = 42;\n "
}
]
})
);
}
#[test]
fn test_document_did_change_full_serialization() {
let notification = DidChangeDocumentNotification::new(
"session_123",
"file:///path/to/file.rs",
2,
vec![TextDocumentContentChangeEvent::full(
"fn main() {\n let x = 42;\n println!(\"hello\");\n}\n",
)],
);
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({
"sessionId": "session_123",
"uri": "file:///path/to/file.rs",
"version": 2,
"contentChanges": [
{
"text": "fn main() {\n let x = 42;\n println!(\"hello\");\n}\n"
}
]
})
);
}
#[test]
fn test_document_did_close_serialization() {
let notification =
DidCloseDocumentNotification::new("session_123", "file:///path/to/file.rs");
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" })
);
}
#[test]
fn test_document_did_save_serialization() {
let notification =
DidSaveDocumentNotification::new("session_123", "file:///path/to/file.rs");
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" })
);
}
#[test]
fn test_document_did_focus_serialization() {
let notification = DidFocusDocumentNotification::new(
"session_123",
"file:///path/to/file.rs",
2,
Position::new(5, 12),
Range::new(Position::new(0, 0), Position::new(45, 0)),
);
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({
"sessionId": "session_123",
"uri": "file:///path/to/file.rs",
"version": 2,
"position": { "line": 5, "character": 12 },
"visibleRange": {
"start": { "line": 0, "character": 0 },
"end": { "line": 45, "character": 0 }
}
})
);
}
#[test]
fn test_nes_suggestion_edit_serialization() {
let suggestion = NesSuggestion::Edit(
NesEditSuggestion::new(
"sugg_001",
"file:///path/to/other_file.rs",
vec![NesTextEdit::new(
Range::new(Position::new(5, 0), Position::new(5, 10)),
"let result = helper();",
)],
)
.cursor_position(Position::new(5, 22)),
);
let json = serde_json::to_value(&suggestion).unwrap();
assert_eq!(
json,
json!({
"kind": "edit",
"id": "sugg_001",
"uri": "file:///path/to/other_file.rs",
"edits": [
{
"range": {
"start": { "line": 5, "character": 0 },
"end": { "line": 5, "character": 10 }
},
"newText": "let result = helper();"
}
],
"cursorPosition": { "line": 5, "character": 22 }
})
);
let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, suggestion);
}
#[test]
fn test_nes_suggestion_jump_serialization() {
let suggestion = NesSuggestion::Jump(NesJumpSuggestion::new(
"sugg_002",
"file:///path/to/other_file.rs",
Position::new(15, 4),
));
let json = serde_json::to_value(&suggestion).unwrap();
assert_eq!(
json,
json!({
"kind": "jump",
"id": "sugg_002",
"uri": "file:///path/to/other_file.rs",
"position": { "line": 15, "character": 4 }
})
);
let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, suggestion);
}
#[test]
fn test_nes_suggestion_rename_serialization() {
let suggestion = NesSuggestion::Rename(NesRenameSuggestion::new(
"sugg_003",
"file:///path/to/file.rs",
Position::new(5, 10),
"calculateTotal",
));
let json = serde_json::to_value(&suggestion).unwrap();
assert_eq!(
json,
json!({
"kind": "rename",
"id": "sugg_003",
"uri": "file:///path/to/file.rs",
"position": { "line": 5, "character": 10 },
"newName": "calculateTotal"
})
);
let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, suggestion);
}
#[test]
fn test_nes_suggestion_search_and_replace_serialization() {
let suggestion = NesSuggestion::SearchAndReplace(
NesSearchAndReplaceSuggestion::new(
"sugg_004",
"file:///path/to/file.rs",
"oldFunction",
"newFunction",
)
.is_regex(false),
);
let json = serde_json::to_value(&suggestion).unwrap();
assert_eq!(
json,
json!({
"kind": "searchAndReplace",
"id": "sugg_004",
"uri": "file:///path/to/file.rs",
"search": "oldFunction",
"replace": "newFunction",
"isRegex": false
})
);
let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, suggestion);
}
#[test]
fn test_nes_start_request_serialization() {
let request = StartNesRequest::new()
.workspace_uri("file:///Users/alice/projects/my-app")
.workspace_folders(vec![WorkspaceFolder::new(
"file:///Users/alice/projects/my-app",
"my-app",
)])
.repository(NesRepository::new(
"my-app",
"alice",
"https://github.com/alice/my-app.git",
));
let json = serde_json::to_value(&request).unwrap();
assert_eq!(
json,
json!({
"workspaceUri": "file:///Users/alice/projects/my-app",
"workspaceFolders": [
{
"uri": "file:///Users/alice/projects/my-app",
"name": "my-app"
}
],
"repository": {
"name": "my-app",
"owner": "alice",
"remoteUrl": "https://github.com/alice/my-app.git"
}
})
);
}
#[test]
fn test_nes_start_response_serialization() {
let response = StartNesResponse::new("session_abc123");
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json, json!({ "sessionId": "session_abc123" }));
}
#[test]
fn test_nes_trigger_kind_serialization() {
assert_eq!(
serde_json::to_value(&NesTriggerKind::Automatic).unwrap(),
json!("automatic")
);
assert_eq!(
serde_json::to_value(&NesTriggerKind::Diagnostic).unwrap(),
json!("diagnostic")
);
assert_eq!(
serde_json::to_value(&NesTriggerKind::Manual).unwrap(),
json!("manual")
);
}
#[test]
fn test_nes_reject_reason_serialization() {
assert_eq!(
serde_json::to_value(&NesRejectReason::Rejected).unwrap(),
json!("rejected")
);
assert_eq!(
serde_json::to_value(&NesRejectReason::Ignored).unwrap(),
json!("ignored")
);
assert_eq!(
serde_json::to_value(&NesRejectReason::Replaced).unwrap(),
json!("replaced")
);
assert_eq!(
serde_json::to_value(&NesRejectReason::Cancelled).unwrap(),
json!("cancelled")
);
}
#[test]
fn test_nes_accept_notification_serialization() {
let notification = AcceptNesNotification::new("session_123", "sugg_001");
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({ "sessionId": "session_123", "id": "sugg_001" })
);
}
#[test]
fn test_nes_reject_notification_serialization() {
let notification =
RejectNesNotification::new("session_123", "sugg_001").reason(NesRejectReason::Rejected);
let json = serde_json::to_value(¬ification).unwrap();
assert_eq!(
json,
json!({ "sessionId": "session_123", "id": "sugg_001", "reason": "rejected" })
);
}
#[test]
fn test_nes_suggest_request_with_context_serialization() {
let request = SuggestNesRequest::new(
"session_123",
"file:///path/to/file.rs",
2,
Position::new(5, 12),
NesTriggerKind::Automatic,
)
.selection(Range::new(Position::new(5, 4), Position::new(5, 12)))
.context(
NesSuggestContext::new()
.recent_files(vec![NesRecentFile::new(
"file:///path/to/utils.rs",
"rust",
"pub fn helper() -> i32 { 42 }\n",
)])
.diagnostics(vec![NesDiagnostic::new(
"file:///path/to/file.rs",
Range::new(Position::new(5, 0), Position::new(5, 10)),
NesDiagnosticSeverity::Error,
"cannot find value `foo` in this scope",
)]),
);
let json = serde_json::to_value(&request).unwrap();
assert_eq!(json["sessionId"], "session_123");
assert_eq!(json["uri"], "file:///path/to/file.rs");
assert_eq!(json["version"], 2);
assert_eq!(json["triggerKind"], "automatic");
assert_eq!(
json["context"]["recentFiles"][0]["uri"],
"file:///path/to/utils.rs"
);
assert_eq!(json["context"]["diagnostics"][0]["severity"], "error");
}
#[test]
fn test_text_document_sync_kind_serialization() {
assert_eq!(
serde_json::to_value(&TextDocumentSyncKind::Full).unwrap(),
json!("full")
);
assert_eq!(
serde_json::to_value(&TextDocumentSyncKind::Incremental).unwrap(),
json!("incremental")
);
}
#[test]
fn test_document_did_change_capabilities_requires_sync_kind() {
assert!(serde_json::from_value::<NesDocumentDidChangeCapabilities>(json!({})).is_err());
}
#[test]
fn test_nes_suggest_response_serialization() {
let response = SuggestNesResponse::new(vec![
NesSuggestion::Edit(NesEditSuggestion::new(
"sugg_001",
"file:///path/to/file.rs",
vec![NesTextEdit::new(
Range::new(Position::new(5, 0), Position::new(5, 10)),
"let result = helper();",
)],
)),
NesSuggestion::Jump(NesJumpSuggestion::new(
"sugg_002",
"file:///path/to/other.rs",
Position::new(10, 0),
)),
]);
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["suggestions"].as_array().unwrap().len(), 2);
assert_eq!(json["suggestions"][0]["kind"], "edit");
assert_eq!(json["suggestions"][1]["kind"], "jump");
}
}