tinymist_query/
selection_range.rs

1use typst_shim::syntax::LinkedNodeExt;
2
3use crate::{SyntaxRequest, prelude::*};
4
5/// The [`textDocument/selectionRange`] request is sent from the client to the
6/// server to return suggested selection ranges at an array of given positions.
7/// A selection range is a range around the cursor position which the user might
8/// be interested in selecting.
9///
10/// [`textDocument/selectionRange`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange
11///
12/// A selection range in the return array is for the position in the provided
13/// parameters at the same index. Therefore `params.positions[i]` must be
14/// contained in `result[i].range`.
15///
16/// # Compatibility
17///
18/// This request was introduced in specification version 3.15.0.
19#[derive(Debug, Clone)]
20pub struct SelectionRangeRequest {
21    /// The path of the document to get selection ranges for.
22    pub path: PathBuf,
23    /// The positions to get selection ranges for.
24    pub positions: Vec<LspPosition>,
25}
26
27impl SyntaxRequest for SelectionRangeRequest {
28    type Response = Vec<SelectionRange>;
29
30    fn request(
31        self,
32        source: &Source,
33        position_encoding: PositionEncoding,
34    ) -> Option<Self::Response> {
35        let mut ranges = Vec::new();
36        for position in self.positions {
37            let typst_offset = to_typst_position(position, position_encoding, source)?;
38            let tree = LinkedNode::new(source.root());
39            let leaf = tree.leaf_at_compat(typst_offset + 1)?;
40            ranges.push(range_for_node(source, position_encoding, &leaf));
41        }
42
43        Some(ranges)
44    }
45}
46
47fn range_for_node(
48    source: &Source,
49    position_encoding: PositionEncoding,
50    node: &LinkedNode,
51) -> SelectionRange {
52    let range = to_lsp_range(node.range(), source, position_encoding);
53    SelectionRange {
54        range,
55        parent: node
56            .parent()
57            .map(|node| Box::new(range_for_node(source, position_encoding, node))),
58    }
59}