use super::*;
use std::{ops::Range, sync::Arc};
use reovim_driver_syntax::{
Annotation, HighlightCategory, SyntaxDriver, SyntaxDriverFactory, SyntaxEdit,
};
struct TestDriver {
language: String,
parsed: bool,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl TestDriver {
fn new(language: &str) -> Self {
Self {
language: language.to_string(),
parsed: false,
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl SyntaxDriver for TestDriver {
fn language(&self) -> &str {
&self.language
}
fn parse(&mut self, _content: &str) {
self.parsed = true;
}
fn update(&mut self, _content: &str, _edit: &SyntaxEdit) {
}
fn highlights(&self, byte_range: Range<usize>) -> Vec<Annotation> {
if self.parsed {
vec![Annotation::new(
byte_range.start,
byte_range.end,
HighlightCategory::new("comment"),
)]
} else {
Vec::new()
}
}
fn is_parsed(&self) -> bool {
self.parsed
}
}
struct TestFactory;
#[cfg_attr(coverage_nightly, coverage(off))]
impl SyntaxDriverFactory for TestFactory {
fn create(&self, language_id: &str) -> Option<Box<dyn SyntaxDriver>> {
if language_id == "rust" {
Some(Box::new(TestDriver::new("rust")))
} else {
None
}
}
fn supported_languages(&self) -> Vec<&str> {
vec!["rust"]
}
fn supports(&self, language_id: &str) -> bool {
language_id == "rust"
}
}
fn buffer_id(n: usize) -> BufferId {
BufferId::from_raw(n)
}
#[test]
fn test_stream_state_new() {
let state = SyntaxStreamState::new();
assert_eq!(state.subscriber_count(), 0);
assert!(!state.has_subscribers());
}
#[test]
fn test_stream_state_session_extension() {
let state = SyntaxStreamState::create();
assert_eq!(state.subscriber_count(), 0);
}
#[test]
fn test_subscribe() {
let mut state = SyntaxStreamState::new();
assert_eq!(state.subscriber_count(), 0);
let _rx1 = state.subscribe();
assert_eq!(state.subscriber_count(), 1);
assert!(state.has_subscribers());
let _rx2 = state.subscribe();
assert_eq!(state.subscriber_count(), 2);
}
#[test]
fn test_broadcast() {
let mut state = SyntaxStreamState::new();
let mut rx = state.subscribe();
let update = TokenUpdate {
buffer_id: 1,
tokens: vec![],
start_line: 0,
end_line: 0,
full_refresh: false,
layer: "syntax".into(),
priority: 0,
};
state.broadcast(&update);
let received = rx.try_recv().expect("Should receive update");
assert_eq!(received.buffer_id, 1);
assert_eq!(received.layer, "syntax");
assert_eq!(received.priority, 0);
}
#[test]
fn test_broadcast_removes_disconnected() {
let mut state = SyntaxStreamState::new();
let rx = state.subscribe();
assert_eq!(state.subscriber_count(), 1);
drop(rx);
let update = TokenUpdate {
buffer_id: 1,
tokens: vec![],
start_line: 0,
end_line: 0,
full_refresh: false,
layer: "syntax".into(),
priority: 0,
};
state.broadcast(&update);
assert_eq!(state.subscriber_count(), 0);
}
#[tokio::test]
async fn test_notify_edit_with_subscriber() {
let mut syntax = SyntaxSessionState::new();
let mut stream = SyntaxStreamState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
syntax.get_mut(id).unwrap().parse("fn main() {}");
let mut rx = stream.subscribe();
assert_eq!(stream.subscriber_count(), 1);
let edit = SyntaxEdit::insert(0, 0, 0, 3, 0, 3);
stream.notify_edit(&mut syntax, id, "fn main() {}", &edit, 0, 0);
let update = rx.try_recv().expect("Should receive update");
assert_eq!(update.buffer_id, 1);
assert!(!update.full_refresh);
assert_eq!(update.layer, "syntax");
assert_eq!(update.priority, 0);
}
#[test]
fn test_notify_edit_no_driver() {
let mut syntax = SyntaxSessionState::new();
let mut stream = SyntaxStreamState::new();
let id = buffer_id(1);
let edit = SyntaxEdit::insert(0, 0, 0, 3, 0, 3);
stream.notify_edit(&mut syntax, id, "hello", &edit, 0, 0);
}
#[test]
fn test_send_full_refresh() {
let mut syntax = SyntaxSessionState::new();
let mut stream = SyntaxStreamState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
syntax.get_mut(id).unwrap().parse("fn main() {}");
let mut rx = stream.subscribe();
stream.send_full_refresh(&syntax, id, 10);
let update = rx.try_recv().expect("Should receive update");
assert_eq!(update.buffer_id, 1);
assert!(update.full_refresh);
assert_eq!(update.end_line, 9); assert_eq!(update.layer, "syntax");
assert_eq!(update.priority, 0);
}
#[test]
fn test_notify_edit_no_subscribers_skips_extraction() {
let mut syntax = SyntaxSessionState::new();
let mut stream = SyntaxStreamState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
let edit = SyntaxEdit {
start_byte: 0,
old_end_byte: 0,
new_end_byte: 5,
start_row: 0,
start_col: 0,
old_end_row: 0,
old_end_col: 0,
new_end_row: 0,
new_end_col: 5,
};
stream.notify_edit(&mut syntax, id, "hello", &edit, 0, 0);
}
#[test]
fn test_send_full_refresh_no_driver_returns_early() {
let syntax = SyntaxSessionState::new();
let mut stream = SyntaxStreamState::new();
let _rx = stream.subscribe(); let unknown = buffer_id(999);
stream.send_full_refresh(&syntax, unknown, 10);
}
#[test]
fn test_send_full_refresh_no_subscribers_returns_early() {
let mut syntax = SyntaxSessionState::new();
let stream = SyntaxStreamState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
let mut stream = stream;
stream.send_full_refresh(&syntax, id, 10);
}
#[test]
fn test_modification_to_syntax_edit_insert() {
use reovim_kernel::api::v1::events::kernel::Modification;
let modification = Modification::Insert {
start: (0, 5),
text: "hello".to_string(),
start_byte: 5,
};
let edit = modification_to_syntax_edit(&modification).unwrap();
assert_eq!(edit.start_byte, 5);
assert_eq!(edit.old_end_byte, 5); assert_eq!(edit.new_end_byte, 10); assert_eq!(edit.start_row, 0);
assert_eq!(edit.start_col, 5);
assert_eq!(edit.new_end_row, 0);
assert_eq!(edit.new_end_col, 10);
}
#[test]
fn test_modification_to_syntax_edit_insert_multiline() {
use reovim_kernel::api::v1::events::kernel::Modification;
let modification = Modification::Insert {
start: (1, 3),
text: "ab\ncd".to_string(),
start_byte: 10,
};
let edit = modification_to_syntax_edit(&modification).unwrap();
assert_eq!(edit.start_byte, 10);
assert_eq!(edit.new_end_byte, 15); assert_eq!(edit.new_end_row, 2); assert_eq!(edit.new_end_col, 2); }
#[test]
fn test_modification_to_syntax_edit_delete() {
use reovim_kernel::api::v1::events::kernel::Modification;
let modification = Modification::Delete {
start: (0, 0),
end: (0, 5),
text: "hello".to_string(),
start_byte: 0,
};
let edit = modification_to_syntax_edit(&modification).unwrap();
assert_eq!(edit.start_byte, 0);
assert_eq!(edit.old_end_byte, 5); assert_eq!(edit.new_end_byte, 0); assert_eq!(edit.old_end_row, 0);
assert_eq!(edit.old_end_col, 5);
}
#[test]
fn test_modification_to_syntax_edit_replace() {
use reovim_kernel::api::v1::events::kernel::Modification;
let modification = Modification::Replace {
start: (0, 0),
end: (0, 5),
old_text: "hello".to_string(),
new_text: "world!".to_string(),
start_byte: 0,
};
let edit = modification_to_syntax_edit(&modification).unwrap();
assert_eq!(edit.start_byte, 0);
assert_eq!(edit.old_end_byte, 5); assert_eq!(edit.new_end_byte, 6); assert_eq!(edit.new_end_row, 0);
assert_eq!(edit.new_end_col, 6);
}
#[test]
fn test_modification_to_syntax_edit_full_replace() {
use reovim_kernel::api::v1::events::kernel::Modification;
assert!(modification_to_syntax_edit(&Modification::FullReplace).is_none());
}
#[test]
fn test_compute_end_position_single_line() {
assert_eq!(compute_end_position(0, 0, "hello"), (0, 5));
assert_eq!(compute_end_position(2, 3, "abc"), (2, 6));
}
#[test]
fn test_compute_end_position_multi_line() {
assert_eq!(compute_end_position(0, 0, "ab\ncd"), (1, 2));
assert_eq!(compute_end_position(5, 10, "x\ny\nz"), (7, 1));
}
#[test]
fn test_compute_end_position_empty() {
assert_eq!(compute_end_position(3, 7, ""), (3, 7));
}
#[test]
fn test_compute_end_position_trailing_newline() {
assert_eq!(compute_end_position(0, 0, "abc\n"), (1, 0));
}
#[test]
fn test_debug_impl() {
let mut state = SyntaxStreamState::new();
let _rx = state.subscribe();
let debug = format!("{state:?}");
assert!(debug.contains("SyntaxStreamState"));
assert!(debug.contains("subscriber_count"));
}
#[test]
fn test_build_token_update_with_driver() {
let mut syntax = SyntaxSessionState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
syntax.get_mut(id).unwrap().parse("fn main() {}");
let update = build_token_update(&syntax, id, 10, true);
assert!(update.is_some());
let update = update.unwrap();
assert_eq!(update.buffer_id, 1);
assert!(update.full_refresh);
assert_eq!(update.start_line, 0);
assert_eq!(update.end_line, 9);
assert!(!update.tokens.is_empty());
assert_eq!(update.layer, "syntax");
assert_eq!(update.priority, 0);
}
#[test]
fn test_build_token_update_no_driver() {
let syntax = SyntaxSessionState::new();
let id = buffer_id(1);
let update = build_token_update(&syntax, id, 10, true);
assert!(update.is_none());
}
#[test]
fn test_build_token_update_incremental() {
let mut syntax = SyntaxSessionState::new();
let id = buffer_id(1);
syntax.set(id, Box::new(TestDriver::new("rust")));
syntax.get_mut(id).unwrap().parse("fn main() {}");
let update = build_token_update(&syntax, id, 5, false).unwrap();
assert!(!update.full_refresh);
assert_eq!(update.end_line, 4);
}
#[test]
fn test_syntax_session_state_reexport() {
let mut state = SyntaxSessionState::new();
let id = buffer_id(1);
state.set_factory(Arc::new(TestFactory));
assert!(state.ensure_driver(id, "rust", "fn main() {}"));
assert!(state.get(id).is_some());
}