1 use cancellation::CancellationToken;
2 use lsp_types::{DocumentHighlight, DocumentHighlightKind, DocumentHighlightParams};
3
4 use crate::{
5 features::cursor::CursorContext,
6 syntax::{latex, CstNode},
7 LineIndexExt,
8 };
9
find_label_highlights( context: &CursorContext<DocumentHighlightParams>, cancellation_token: &CancellationToken, ) -> Option<Vec<DocumentHighlight>>10 pub fn find_label_highlights(
11 context: &CursorContext<DocumentHighlightParams>,
12 cancellation_token: &CancellationToken,
13 ) -> Option<Vec<DocumentHighlight>> {
14 let (name_text, _) = context.find_label_name_key()?;
15
16 let main_document = context.request.main_document();
17 let data = main_document.data.as_latex()?;
18
19 let mut highlights = Vec::new();
20 for node in data.root.descendants() {
21 cancellation_token.result().ok()?;
22
23 if let Some(label_name) = latex::LabelDefinition::cast(node)
24 .and_then(|label| label.name())
25 .and_then(|label_name| label_name.key())
26 .filter(|label_name| label_name.to_string() == name_text)
27 {
28 let range = main_document
29 .line_index
30 .line_col_lsp_range(label_name.small_range());
31
32 highlights.push(DocumentHighlight {
33 range,
34 kind: Some(DocumentHighlightKind::Write),
35 });
36 } else if let Some(label) = latex::LabelReference::cast(node) {
37 for label_name in label
38 .name_list()
39 .into_iter()
40 .flat_map(|name| name.keys())
41 .filter(|label_name| label_name.to_string() == name_text)
42 {
43 let range = main_document
44 .line_index
45 .line_col_lsp_range(label_name.small_range());
46
47 highlights.push(DocumentHighlight {
48 range,
49 kind: Some(DocumentHighlightKind::Read),
50 });
51 }
52 } else if let Some(label) = latex::LabelReferenceRange::cast(node) {
53 if let Some(label_name) = label
54 .from()
55 .and_then(|label_name| label_name.key())
56 .filter(|label_name| label_name.to_string() == name_text)
57 {
58 let range = main_document
59 .line_index
60 .line_col_lsp_range(label_name.small_range());
61
62 highlights.push(DocumentHighlight {
63 range,
64 kind: Some(DocumentHighlightKind::Read),
65 });
66 }
67
68 if let Some(label_name) = label
69 .to()
70 .and_then(|label_name| label_name.key())
71 .filter(|label_name| label_name.to_string() == name_text)
72 {
73 let range = main_document
74 .line_index
75 .line_col_lsp_range(label_name.small_range());
76
77 highlights.push(DocumentHighlight {
78 range,
79 kind: Some(DocumentHighlightKind::Read),
80 });
81 }
82 }
83 }
84
85 Some(highlights)
86 }
87
88 #[cfg(test)]
89 mod tests {
90 use lsp_types::Range;
91
92 use crate::{features::testing::FeatureTester, RangeExt};
93
94 use super::*;
95
96 #[test]
test_empty_latex_document()97 fn test_empty_latex_document() {
98 let request = FeatureTester::builder()
99 .files(vec![("main.tex", "")])
100 .main("main.tex")
101 .line(0)
102 .character(0)
103 .build()
104 .highlight();
105 let context = CursorContext::new(request);
106
107 let actual_links = find_label_highlights(&context, CancellationToken::none());
108
109 assert!(actual_links.is_none());
110 }
111
112 #[test]
test_empty_bibtex_document()113 fn test_empty_bibtex_document() {
114 let request = FeatureTester::builder()
115 .files(vec![("main.bib", "")])
116 .main("main.bib")
117 .line(0)
118 .character(0)
119 .build()
120 .highlight();
121 let context = CursorContext::new(request);
122
123 let actual_links = find_label_highlights(&context, CancellationToken::none());
124
125 assert!(actual_links.is_none());
126 }
127
128 #[test]
test_label()129 fn test_label() {
130 let tester = FeatureTester::builder()
131 .files(vec![("main.tex", "\\label{foo}\n\\ref{foo}\\label{bar}")])
132 .main("main.tex")
133 .line(0)
134 .character(7)
135 .build();
136 let request = tester.highlight();
137 let context = CursorContext::new(request);
138
139 let actual_highlights = find_label_highlights(&context, CancellationToken::none()).unwrap();
140
141 let expected_highlights = vec![
142 DocumentHighlight {
143 range: Range::new_simple(0, 7, 0, 10),
144 kind: Some(DocumentHighlightKind::Write),
145 },
146 DocumentHighlight {
147 range: Range::new_simple(1, 5, 1, 8),
148 kind: Some(DocumentHighlightKind::Read),
149 },
150 ];
151
152 assert_eq!(actual_highlights, expected_highlights);
153 }
154 }
155