use super::*; #[test] fn native_definitions_preserve_snapshot_identity_and_utf16_until_navigation() { let source = Source { buffer_id: 7, revision: 3, row: 0, column: 0, request_id: 11, }; let path = Path::new("/vamp-immutable-snapshot/Before/renamed/λ.rs"); let span = json!({ "start": {"line": 4, "character": 5}, "end": {"line": 4, "character": 8} }); for location in [ json!({"uri": file_uri(path).unwrap(), "range": span}), json!({"targetUri": file_uri(path).unwrap(), "targetRange": span, "targetSelectionRange": span}), ] { let effects = Runtime::definition_effects(&source, &location).unwrap(); let expected_target_range = location.get("targetUri").map(|_| Utf16Range { start: (4, 5), end: (4, 8), }); assert!(matches!( effects.as_slice(), [UpdateEffect::NavigateUtf16 { buffer_id: 7, revision: 3, path: target, range: Utf16Range { start: (4, 5), end: (4, 8) }, target_range, }] if target == path && *target_range == expected_target_range )); } for invalid in [ json!({"start": {"line": 4, "character": 8}, "end": {"line": 4, "character": 5}}), json!({"start": {"line": -1, "character": 0}, "end": {"line": 0, "character": 0}}), ] { for location in [ json!({"uri": file_uri(path).unwrap(), "range": invalid}), json!({"targetUri": file_uri(path).unwrap(), "targetRange": invalid, "targetSelectionRange": span}), json!({"targetUri": file_uri(path).unwrap(), "targetRange": span, "targetSelectionRange": invalid}), ] { assert!(Runtime::definition_effects(&source, &location).is_err()); } } assert!( serde_json::from_value::(json!({ "type": "navigate_utf16", "buffer_id": 7, "revision": 3, "path": path, "range": {"start": [4,5], "end": [4,8]}, "target_range": null })) .is_err() ); } #[test] fn semantic_decoder_validates_raw_coordinates_before_grapheme_rounding() { let options = semantic_options(Some(&json!({ "full": true, "legend": {"tokenTypes": ["custom", "function"], "tokenModifiers": ["custom", "readonly", "declaration", "async"]} }))) .unwrap() .unwrap(); let tokens = decode_semantic_tokens( "A😀e\u{301}Z\r\n\r\nx", &json!({"data": [ 0, 1, 2, 1, 2, 0, 2, 1, 1, 12, 0, 1, 1, 1, 0, 0, 1, 999, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 100, 1, 0 ]}), &options, ) .unwrap() .unwrap(); assert_eq!(tokens.len(), 4); assert_eq!(tokens[0].start, TextPosition { row: 0, column: 1 }); assert_eq!(tokens[0].end, TextPosition { row: 0, column: 2 }); assert_eq!(tokens[0].modifiers, SemanticTokenModifier::Readonly as u16); assert_eq!(tokens[1].start, TextPosition { row: 0, column: 2 }); assert_eq!(tokens[1].end, TextPosition { row: 0, column: 3 }); assert_eq!( tokens[1].modifiers, SemanticTokenModifier::Declaration as u16 | SemanticTokenModifier::Async as u16 ); assert_eq!(tokens[2].start, tokens[1].start); assert_eq!(tokens[3].end, TextPosition { row: 2, column: 1 }); for data in [ json!([0, 0, 1, 1]), json!([-1, 0, 1, 1, 0]), json!([0, 0.5, 1, 1, 0]), json!([0, 0, 1, 1, 2147483648u64]), json!([0, 0, 1, 65536, 0]), json!([0, 0, 1, 2, 0]), json!([0, 0, 1, 1, 16]), json!([0, 2, 1, 0, 0]), json!([0, 1, 1, 1, 0]), json!([0, 7, 1, 1, 0]), json!([2, 0, 1, 1, 0]), json!([0, 1, 2, 1, 0, 0, 1, 1, 1, 0]), ] { assert!( decode_semantic_tokens("A😀e\u{301}Z\n", &json!({"data": data}), &options).is_err(), "{data}" ); } let count = MAX_BODY / std::mem::size_of::() + 1; assert!(decode_semantic_tokens("x", &json!({"data": vec![0; count * 5]}), &options).is_err()); } #[test] fn unicode_positions_and_local_uri_boundaries() { let text = "A😀e\u{301}Z\n"; let (start, end) = range( text, &json!({"start": {"line": 0, "character": 2}, "end": {"line": 0, "character": 4}}), ) .unwrap(); assert_eq!(start, TextPosition { row: 0, column: 1 }); assert_eq!(end, TextPosition { row: 0, column: 3 }); assert_eq!( outbound_position(text, 0, 3).unwrap(), json!({"line": 0, "character": 5}) ); assert_eq!( position(text, &json!({"line": 1, "character": 999}), false).unwrap(), TextPosition { row: 1, column: 0 } ); assert!( range( text, &json!({"start": {"line": 0, "character": 3}, "end": {"line": 0, "character": 2}}) ) .is_err() ); let path = Path::new("/tmp/source space λ😀.rs"); assert_eq!(uri_path(&file_uri(path).unwrap()).unwrap(), path); assert_eq!( uri_path("file://localhost/tmp/a%23b").unwrap(), Path::new("/tmp/a#b") ); for uri in [ "https://example.invalid/a", "file://remote/tmp/a", "file:///tmp/%", "file:///tmp/%00", "file:///tmp/%FF", "file:///tmp/a?query", ] { assert!(uri_path(uri).is_err(), "{uri}"); } } #[test] fn hover_contents_keep_lines_and_reject_malformed_values() { let source = "A😀e\u{301}Z\r\n"; for kind in ["plaintext", "markdown"] { assert_eq!( hover_text( &json!({ "contents": {"kind": kind, "value": "😀e\u{301}\r\n\tcode\rnext\u{1b}\u{85}"}, "range": {"start": {"line": 0, "character": 2}, "end": {"line": 0, "character": 4}} }), source, ) .unwrap(), Some("😀e\u{301}\n code\nnext ".into()), ); } for contents in [ json!("line\ncode"), json!({"language": "rust", "value": "line\ncode"}), ] { assert_eq!( hover_text(&json!({"contents": contents}), source).unwrap(), Some("line\ncode".into()), ); } assert_eq!( hover_text( &json!({"contents": ["first", {"language": "rust", "value": "fn main() {\n}"}]}), source, ) .unwrap(), Some("first\n\nfn main() {\n}".into()), ); for value in [ Value::Null, json!({"contents": ""}), json!({"contents": []}), ] { assert_eq!(hover_text(&value, source).unwrap(), None); } for value in [ json!({}), json!({"contents": {"kind": "html", "value": "x"}}), json!({"contents": {"language": "rust", "value": false}}), json!({"contents": [{"kind": "markdown", "value": "x"}]}), json!({"contents": "x", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 0, "character": 0}}}), json!({"contents": "\t".repeat(MAX_BODY / 4 + 1)}), ] { assert!(hover_text(&value, source).is_err()); } } #[test] fn completion_text_limit_counts_the_atomic_batch_not_sort_metadata() { let source = Source { buffer_id: 1, revision: 0, row: 0, column: 0, request_id: 1, }; let at_limit = json!([{"label": "x", "insertText": "x".repeat(MAX_BODY - 1)}]); assert_eq!( decode_completion("", &at_limit, &source, None).unwrap()[0].edits[0] .text .len(), MAX_BODY - 1 ); let over_limit = json!([ {"label": "x", "insertText": "x".repeat(MAX_BODY - 1)}, {"label": "y"} ]); assert!( decode_completion("", &over_limit, &source, None) .unwrap_err() .contains("4 MiB") ); }