turbo_static/
identifier.rs

1use std::{fs, path::PathBuf};
2
3use lsp_types::{CallHierarchyIncomingCall, CallHierarchyItem, Range};
4
5/// A task that references another, with the range of the reference
6#[derive(Hash, PartialEq, Eq, serde::Deserialize, serde::Serialize, Clone, Debug)]
7pub struct IdentifierReference {
8    pub identifier: Identifier,
9    pub references: Vec<Range>, // the places where this identifier is used
10}
11
12/// identifies a task by its file, and range in the file
13#[derive(Hash, PartialEq, Eq, serde::Deserialize, serde::Serialize, Clone)]
14pub struct Identifier {
15    pub path: String,
16    // technically you can derive this from the name and range but it's easier to just store it
17    pub name: String,
18    // post_transform_name: Option<String>,
19    pub range: lsp_types::Range,
20}
21
22impl Identifier {
23    /// check the span matches and the text matches
24    ///
25    /// `same_location` is used to check if the location of the identifier is
26    /// the same as the other
27    pub fn equals_ident(&self, other: &syn::Ident, match_location: bool) -> bool {
28        *other == self.name
29            && (!match_location
30                || (self.range.start.line == other.span().start().line as u32
31                    && self.range.start.character == other.span().start().column as u32))
32    }
33
34    /// We cannot use `item.name` here in all cases as, during testing, the name
35    /// does not always align with the exact text in the range.
36    fn get_name(item: &CallHierarchyItem) -> String {
37        // open file, find range inside, extract text
38        let file = fs::read_to_string(item.uri.path()).unwrap();
39        let start = item.selection_range.start;
40        let end = item.selection_range.end;
41        file.lines()
42            .nth(start.line as usize)
43            .unwrap()
44            .chars()
45            .skip(start.character as usize)
46            .take(end.character as usize - start.character as usize)
47            .collect()
48    }
49}
50
51impl From<(PathBuf, syn::Ident)> for Identifier {
52    fn from((path, ident): (PathBuf, syn::Ident)) -> Self {
53        Self {
54            path: path.display().to_string(),
55            name: ident.to_string(),
56            // post_transform_name: None,
57            range: Range {
58                start: lsp_types::Position {
59                    line: ident.span().start().line as u32 - 1,
60                    character: ident.span().start().column as u32,
61                },
62                end: lsp_types::Position {
63                    line: ident.span().end().line as u32 - 1,
64                    character: ident.span().end().column as u32,
65                },
66            },
67        }
68    }
69}
70
71impl From<CallHierarchyIncomingCall> for IdentifierReference {
72    fn from(item: CallHierarchyIncomingCall) -> Self {
73        Self {
74            identifier: Identifier {
75                name: Identifier::get_name(&item.from),
76                // post_transform_name: Some(item.from.name),
77                path: item.from.uri.path().to_owned(),
78                range: item.from.selection_range,
79            },
80            references: item.from_ranges,
81        }
82    }
83}
84
85impl std::fmt::Debug for Identifier {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        std::fmt::Display::fmt(self, f)
88    }
89}
90
91impl std::fmt::Display for Identifier {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}:{}#{}", self.path, self.range.start.line, self.name,)
94    }
95}