turbo_static/
identifier.rs

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