use {
laburnum::{
Laburnum,
protocol::lsp::{
DidChangeTextDocumentParams,
DidCloseTextDocumentParams,
DidOpenTextDocumentParams,
DidSaveTextDocumentParams,
InitializeParams,
Position,
Range,
TextDocumentContentChangeEvent,
TextDocumentIdentifier,
TextDocumentItem,
VersionedTextDocumentIdentifier,
},
},
macro_rules_attribute::apply,
smol_macros::test,
};
mod common;
use common::TestPartitions;
#[apply(test!)]
#[test_log::test]
async fn test_did_open() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri,
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
};
let result = client.did_open_text_document(params).await;
assert!(result.is_ok(), "did_open should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_open_invalid_uri_empty() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri_result = "".parse::<laburnum::Uri>();
if let Ok(uri) = uri_result {
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri,
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
};
client.did_open_text_document(params).await.ok();
}
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_open_invalid_uri_missing_scheme() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri_result = "path/to/file.txt".parse::<laburnum::Uri>();
if let Ok(uri) = uri_result {
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri,
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
};
client.did_open_text_document(params).await.ok();
}
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_open_invalid_uri_wrong_scheme() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "http://example.com/file.txt".parse().unwrap();
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri,
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
};
client.did_open_text_document(params).await.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_open_invalid_uri_malformed() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri_result = "file://".parse::<laburnum::Uri>();
if let Ok(uri) = uri_result {
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri,
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
};
client.did_open_text_document(params).await.ok();
}
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_unopened_file() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///never_opened.txt".parse().unwrap();
let params = DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier { uri, version: 1.into() },
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "trying to change unopened file".to_string(),
}],
};
client.did_change_text_document(params).await.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_invalid_uri() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri_result = "not-a-valid-uri".parse::<laburnum::Uri>();
if let Ok(uri) = uri_result {
let params = DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier { uri, version: 1.into() },
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "change text".to_string(),
}],
};
client.did_change_text_document(params).await.ok();
}
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_save_before_open() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///never_opened.txt".parse().unwrap();
let params = DidSaveTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
text: Some("trying to save unopened file".to_string()),
};
client.did_save_text_document(params).await.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_close_without_open() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///never_opened.txt".parse().unwrap();
let params = DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
};
client.did_close_text_document(params).await.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_multiple_did_open_same_uri() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "first open".to_string(),
},
})
.await
.ok();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "second open".to_string(),
},
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_multiple_did_close_same_uri() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "content".to_string(),
},
})
.await
.ok();
client
.did_close_text_document(DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
})
.await
.ok();
client
.did_close_text_document(DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_version_too_old() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "initial".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 5.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 5".to_string(),
}],
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 3.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 3 (old)".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_version_jump() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "initial".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 1.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 1".to_string(),
}],
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 5.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 5 (jumped)".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_version_reset_after_reopen() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "initial".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 5.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 5".to_string(),
}],
})
.await
.ok();
client
.did_close_text_document(DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
})
.await
.ok();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 0.into(),
text: "reopened".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 1.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "version 1 after reopen".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_invalid_range_out_of_bounds() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "line1\nline2\nline3".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 2.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: Some(Range {
start: Position {
line: 10,
character: 0,
},
end: Position {
line: 20,
character: 0,
},
}),
range_length: None,
text: "out of bounds edit".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_mismatched_range_length() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "Hello World".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 2.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: Some(Range {
start: Position {
line: 0,
character: 0,
},
end: Position {
line: 0,
character: 5,
},
}),
range_length: Some(10),
text: "Hi".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_full_sync_empty_text() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "some content here".to_string(),
},
})
.await
.ok();
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 2.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "".to_string(),
}],
})
.await
.ok();
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_full_sync() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
})
.await
.ok();
let params = DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier { uri, version: 2.into() },
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "goodbye world".to_string(),
}],
};
let result = client.did_change_text_document(params).await;
assert!(result.is_ok(), "did_change should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_change_incremental_sync() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
})
.await
.ok();
let params = DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier { uri, version: 2.into() },
content_changes: vec![TextDocumentContentChangeEvent {
range: Some(Range {
start: Position {
line: 0,
character: 6,
},
end: Position {
line: 0,
character: 11,
},
}),
range_length: None,
text: "rust".to_string(),
}],
};
let result = client.did_change_text_document(params).await;
assert!(result.is_ok(), "did_change should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_close() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
})
.await
.ok();
let params = DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
};
let result = client.did_close_text_document(params).await;
assert!(result.is_ok(), "did_close should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_save_without_text() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
})
.await
.ok();
let params = DidSaveTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
text: None,
};
let result = client.did_save_text_document(params).await;
assert!(result.is_ok(), "did_save should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_did_save_with_text() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "hello world".to_string(),
},
})
.await
.ok();
let params = DidSaveTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
text: Some("saved content".to_string()),
};
let result = client.did_save_text_document(params).await;
assert!(result.is_ok(), "did_save should succeed: {:?}", result);
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}
#[apply(test!)]
#[test_log::test]
async fn test_text_document_lifecycle() {
let (server, client) = Laburnum::<TestPartitions, _>::example().build_test();
let mut snapshot = ferrotype::Ferrotype::new();
client.start(InitializeParams::default()).await.ok();
let uri: laburnum::Uri = "file:///test.txt".parse().unwrap();
client
.did_open_text_document(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "plaintext".to_string(),
version: 1.into(),
text: "initial".to_string(),
},
})
.await
.expect("did_open failed");
client
.did_change_text_document(DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: 2.into(),
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "modified".to_string(),
}],
})
.await
.expect("did_change failed");
client
.did_save_text_document(DidSaveTextDocumentParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
text: Some("saved".to_string()),
})
.await
.expect("did_save failed");
client
.did_close_text_document(DidCloseTextDocumentParams {
text_document: TextDocumentIdentifier { uri },
})
.await
.expect("did_close failed");
client.stop_test(&mut snapshot).await.ok();
server.close().expect("Failed to close server");
ferrotype::assert!(snapshot);
}