aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api_light/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api_light/src/lib.rs')
-rw-r--r--crates/ra_ide_api_light/src/lib.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs
new file mode 100644
index 000000000..40638eda8
--- /dev/null
+++ b/crates/ra_ide_api_light/src/lib.rs
@@ -0,0 +1,173 @@
1//! This crate provides thouse IDE features which use only a single file.
2//!
3//! This usually means functions which take sytnax tree as an input and produce
4//! an edit or some auxilarly info.
5
6pub mod assists;
7mod extend_selection;
8mod folding_ranges;
9mod line_index;
10mod line_index_utils;
11mod structure;
12#[cfg(test)]
13mod test_utils;
14mod typing;
15mod diagnostics;
16
17pub use self::{
18 assists::LocalEdit,
19 extend_selection::extend_selection,
20 folding_ranges::{folding_ranges, Fold, FoldKind},
21 line_index::{LineCol, LineIndex},
22 line_index_utils::translate_offset_with_edit,
23 structure::{file_structure, StructureNode},
24 typing::{join_lines, on_enter, on_dot_typed, on_eq_typed},
25 diagnostics::diagnostics
26};
27use ra_text_edit::TextEditBuilder;
28use ra_syntax::{
29 SourceFile, SyntaxNode, TextRange, TextUnit, Direction,
30 SyntaxKind::{self, *},
31 ast::{self, AstNode},
32 algo::find_leaf_at_offset,
33};
34use rustc_hash::FxHashSet;
35
36#[derive(Debug)]
37pub struct HighlightedRange {
38 pub range: TextRange,
39 pub tag: &'static str,
40}
41
42#[derive(Debug, Copy, Clone)]
43pub enum Severity {
44 Error,
45 WeakWarning,
46}
47
48#[derive(Debug)]
49pub struct Diagnostic {
50 pub range: TextRange,
51 pub msg: String,
52 pub severity: Severity,
53 pub fix: Option<LocalEdit>,
54}
55
56pub fn matching_brace(file: &SourceFile, offset: TextUnit) -> Option<TextUnit> {
57 const BRACES: &[SyntaxKind] = &[
58 L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE,
59 ];
60 let (brace_node, brace_idx) = find_leaf_at_offset(file.syntax(), offset)
61 .filter_map(|node| {
62 let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
63 Some((node, idx))
64 })
65 .next()?;
66 let parent = brace_node.parent()?;
67 let matching_kind = BRACES[brace_idx ^ 1];
68 let matching_node = parent
69 .children()
70 .find(|node| node.kind() == matching_kind)?;
71 Some(matching_node.range().start())
72}
73
74pub fn highlight(root: &SyntaxNode) -> Vec<HighlightedRange> {
75 // Visited nodes to handle highlighting priorities
76 let mut highlighted = FxHashSet::default();
77 let mut res = Vec::new();
78 for node in root.descendants() {
79 if highlighted.contains(&node) {
80 continue;
81 }
82 let tag = match node.kind() {
83 COMMENT => "comment",
84 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
85 ATTR => "attribute",
86 NAME_REF => "text",
87 NAME => "function",
88 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
89 LIFETIME => "parameter",
90 k if k.is_keyword() => "keyword",
91 _ => {
92 if let Some(macro_call) = ast::MacroCall::cast(node) {
93 if let Some(path) = macro_call.path() {
94 if let Some(segment) = path.segment() {
95 if let Some(name_ref) = segment.name_ref() {
96 highlighted.insert(name_ref.syntax());
97 let range_start = name_ref.syntax().range().start();
98 let mut range_end = name_ref.syntax().range().end();
99 for sibling in path.syntax().siblings(Direction::Next) {
100 match sibling.kind() {
101 EXCL | IDENT => range_end = sibling.range().end(),
102 _ => (),
103 }
104 }
105 res.push(HighlightedRange {
106 range: TextRange::from_to(range_start, range_end),
107 tag: "macro",
108 })
109 }
110 }
111 }
112 }
113 continue;
114 }
115 };
116 res.push(HighlightedRange {
117 range: node.range(),
118 tag,
119 })
120 }
121 res
122}
123
124pub fn syntax_tree(file: &SourceFile) -> String {
125 ::ra_syntax::utils::dump_tree(file.syntax())
126}
127
128#[cfg(test)]
129mod tests {
130 use ra_syntax::AstNode;
131
132 use crate::test_utils::{add_cursor, assert_eq_dbg, assert_eq_text, extract_offset};
133
134 use super::*;
135
136 #[test]
137 fn test_highlighting() {
138 let file = SourceFile::parse(
139 r#"
140// comment
141fn main() {}
142 println!("Hello, {}!", 92);
143"#,
144 );
145 let hls = highlight(file.syntax());
146 assert_eq_dbg(
147 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
148 HighlightedRange { range: [12; 14), tag: "keyword" },
149 HighlightedRange { range: [15; 19), tag: "function" },
150 HighlightedRange { range: [29; 37), tag: "macro" },
151 HighlightedRange { range: [38; 50), tag: "string" },
152 HighlightedRange { range: [52; 54), tag: "literal" }]"#,
153 &hls,
154 );
155 }
156
157 #[test]
158 fn test_matching_brace() {
159 fn do_check(before: &str, after: &str) {
160 let (pos, before) = extract_offset(before);
161 let file = SourceFile::parse(&before);
162 let new_pos = match matching_brace(&file, pos) {
163 None => pos,
164 Some(pos) => pos,
165 };
166 let actual = add_cursor(&before, new_pos);
167 assert_eq_text!(after, &actual);
168 }
169
170 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
171 }
172
173}