diff options
author | Marcus Klaas de Vries <[email protected]> | 2019-01-10 12:54:58 +0000 |
---|---|---|
committer | Marcus Klaas de Vries <[email protected]> | 2019-01-14 12:52:55 +0000 |
commit | a6146d35b1615cf5fb908b29f34e58bfde3bf96d (patch) | |
tree | 70613ee98eee67c1df6aff1e663be75a33c348f4 | |
parent | 8caff4e03475c20392f13e8c6ad469bd01a4b4ce (diff) |
Implement type inference for literals (WIP)
-rw-r--r-- | crates/ra_hir/src/expr.rs | 78 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 33 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 20 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/basics.txt | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/literals.txt | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/syntax_text.rs | 11 |
8 files changed, 166 insertions, 5 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index f0936e9f3..e07725d05 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -5,7 +5,10 @@ use rustc_hash::FxHashMap; | |||
5 | 5 | ||
6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
7 | use ra_db::{LocalSyntaxPtr, Cancelable}; | 7 | use ra_db::{LocalSyntaxPtr, Cancelable}; |
8 | use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner}; | 8 | use ra_syntax::{ |
9 | SyntaxKind, | ||
10 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner} | ||
11 | }; | ||
9 | 12 | ||
10 | use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; | 13 | use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; |
11 | 14 | ||
@@ -104,6 +107,19 @@ impl BodySyntaxMapping { | |||
104 | } | 107 | } |
105 | 108 | ||
106 | #[derive(Debug, Clone, Eq, PartialEq)] | 109 | #[derive(Debug, Clone, Eq, PartialEq)] |
110 | pub enum Literal { | ||
111 | String(String), | ||
112 | ByteString(Vec<u8>), | ||
113 | Char(char), | ||
114 | Bool(bool), | ||
115 | Byte(u8), | ||
116 | Int, // this and float need additional information | ||
117 | Float, | ||
118 | Tuple { values: Vec<ExprId> }, | ||
119 | Array { values: Vec<ExprId> }, | ||
120 | } | ||
121 | |||
122 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
107 | pub enum Expr { | 123 | pub enum Expr { |
108 | /// This is produced if syntax tree does not have a required expression piece. | 124 | /// This is produced if syntax tree does not have a required expression piece. |
109 | Missing, | 125 | Missing, |
@@ -186,6 +202,7 @@ pub enum Expr { | |||
186 | Tuple { | 202 | Tuple { |
187 | exprs: Vec<ExprId>, | 203 | exprs: Vec<ExprId>, |
188 | }, | 204 | }, |
205 | Literal(Literal), | ||
189 | } | 206 | } |
190 | 207 | ||
191 | pub use ra_syntax::ast::PrefixOp as UnaryOp; | 208 | pub use ra_syntax::ast::PrefixOp as UnaryOp; |
@@ -305,6 +322,20 @@ impl Expr { | |||
305 | f(*expr); | 322 | f(*expr); |
306 | } | 323 | } |
307 | } | 324 | } |
325 | Expr::Literal(l) => match l { | ||
326 | Literal::Array { values } | Literal::Tuple { values } => { | ||
327 | for &val in values { | ||
328 | f(val); | ||
329 | } | ||
330 | } | ||
331 | Literal::String(..) | ||
332 | | Literal::ByteString(..) | ||
333 | | Literal::Byte(..) | ||
334 | | Literal::Bool(..) | ||
335 | | Literal::Char(..) | ||
336 | | Literal::Int | ||
337 | | Literal::Float => {} | ||
338 | }, | ||
308 | } | 339 | } |
309 | } | 340 | } |
310 | } | 341 | } |
@@ -633,13 +664,56 @@ impl ExprCollector { | |||
633 | let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); | 664 | let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); |
634 | self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) | 665 | self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) |
635 | } | 666 | } |
667 | ast::ExprKind::Literal(e) => { | ||
668 | let child = e.syntax().children().next(); | ||
669 | |||
670 | if let Some(c) = child { | ||
671 | let lit = match c.kind() { | ||
672 | SyntaxKind::INT_NUMBER => Literal::Int, | ||
673 | SyntaxKind::FLOAT_NUMBER => Literal::Float, | ||
674 | SyntaxKind::STRING => { | ||
675 | // FIXME: this likely includes the " characters | ||
676 | let text = c.text().to_string(); | ||
677 | Literal::String(text) | ||
678 | } | ||
679 | SyntaxKind::ARRAY_EXPR => { | ||
680 | // TODO: recursively call to self | ||
681 | Literal::Array { values: vec![] } | ||
682 | } | ||
683 | SyntaxKind::PAREN_EXPR => { | ||
684 | // TODO: recursively call to self | ||
685 | Literal::Tuple { values: vec![] } | ||
686 | } | ||
687 | SyntaxKind::TRUE_KW => Literal::Bool(true), | ||
688 | SyntaxKind::FALSE_KW => Literal::Bool(false), | ||
689 | SyntaxKind::BYTE_STRING => { | ||
690 | // FIXME: this is completely incorrect for a variety | ||
691 | // of reasons, but at least it gives the right type | ||
692 | let bytes = c.text().to_string().into_bytes(); | ||
693 | Literal::ByteString(bytes) | ||
694 | } | ||
695 | SyntaxKind::CHAR => { | ||
696 | let character = c.text().char_at(1).unwrap_or('X'); | ||
697 | Literal::Char(character) | ||
698 | } | ||
699 | SyntaxKind::BYTE => { | ||
700 | let character = c.text().char_at(1).unwrap_or('X'); | ||
701 | Literal::Byte(character as u8) | ||
702 | } | ||
703 | _ => return self.alloc_expr(Expr::Missing, syntax_ptr), | ||
704 | }; | ||
705 | |||
706 | self.alloc_expr(Expr::Literal(lit), syntax_ptr) | ||
707 | } else { | ||
708 | self.alloc_expr(Expr::Missing, syntax_ptr) | ||
709 | } | ||
710 | } | ||
636 | 711 | ||
637 | // TODO implement HIR for these: | 712 | // TODO implement HIR for these: |
638 | ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | 713 | ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), |
639 | ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | 714 | ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), |
640 | ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | 715 | ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), |
641 | ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | 716 | ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), |
642 | ast::ExprKind::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
643 | } | 717 | } |
644 | } | 718 | } |
645 | 719 | ||
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index fa46ddfe9..0baa205a1 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -38,7 +38,7 @@ use crate::{ | |||
38 | db::HirDatabase, | 38 | db::HirDatabase, |
39 | type_ref::{TypeRef, Mutability}, | 39 | type_ref::{TypeRef, Mutability}, |
40 | name::KnownName, | 40 | name::KnownName, |
41 | expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, | 41 | expr::{Body, Expr, Literal, ExprId, PatId, UnaryOp, BinaryOp, Statement}, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | fn transpose<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> { | 44 | fn transpose<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> { |
@@ -1067,6 +1067,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1067 | 1067 | ||
1068 | Ty::Tuple(Arc::from(ty_vec)) | 1068 | Ty::Tuple(Arc::from(ty_vec)) |
1069 | } | 1069 | } |
1070 | Expr::Literal(lit) => match lit { | ||
1071 | Literal::Bool(..) => Ty::Bool, | ||
1072 | Literal::String(..) => Ty::Ref(Arc::new(Ty::Str), Mutability::Shared), | ||
1073 | Literal::ByteString(..) => { | ||
1074 | let byte_type = Arc::new(Ty::Uint(primitive::UintTy::U8)); | ||
1075 | let slice_type = Arc::new(Ty::Slice(byte_type)); | ||
1076 | Ty::Ref(slice_type, Mutability::Shared) | ||
1077 | } | ||
1078 | Literal::Byte(..) => Ty::Uint(primitive::UintTy::U8), | ||
1079 | Literal::Char(..) => Ty::Char, | ||
1080 | Literal::Tuple { values } => { | ||
1081 | let mut inner_tys = Vec::new(); | ||
1082 | for &expr in values { | ||
1083 | let inner_ty = self.infer_expr(expr, &Expectation::none())?; | ||
1084 | inner_tys.push(inner_ty); | ||
1085 | } | ||
1086 | Ty::Tuple(Arc::from(inner_tys)) | ||
1087 | } | ||
1088 | Literal::Array { values } => { | ||
1089 | // simply take the type of the first element for now | ||
1090 | let inner_ty = match values.get(0) { | ||
1091 | Some(&expr) => self.infer_expr(expr, &Expectation::none())?, | ||
1092 | None => Ty::Unknown, | ||
1093 | }; | ||
1094 | // TODO: we should return a Ty::Array when it becomes | ||
1095 | // available | ||
1096 | Ty::Slice(Arc::new(inner_ty)) | ||
1097 | } | ||
1098 | // TODO | ||
1099 | Literal::Int | Literal::Float => Ty::Unknown, | ||
1100 | }, | ||
1070 | }; | 1101 | }; |
1071 | // use a new type variable if we got Ty::Unknown here | 1102 | // use a new type variable if we got Ty::Unknown here |
1072 | let ty = self.insert_type_vars_shallow(ty); | 1103 | let ty = self.insert_type_vars_shallow(ty); |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 920188fc9..97d3d222f 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -133,6 +133,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | |||
133 | } | 133 | } |
134 | 134 | ||
135 | #[test] | 135 | #[test] |
136 | fn infer_literals() { | ||
137 | check_inference( | ||
138 | r#" | ||
139 | fn test() { | ||
140 | 5i32; | ||
141 | "hello"; | ||
142 | b"bytes"; | ||
143 | 'c'; | ||
144 | b'b'; | ||
145 | 3.14; | ||
146 | 5000; | ||
147 | (0u32, -5isize); | ||
148 | [true, true, false] | ||
149 | } | ||
150 | "#, | ||
151 | "literals.txt", | ||
152 | ); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
136 | fn infer_backwards() { | 156 | fn infer_backwards() { |
137 | check_inference( | 157 | check_inference( |
138 | r#" | 158 | r#" |
diff --git a/crates/ra_hir/src/ty/tests/data/basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index ac7faae0a..e02947ba8 100644 --- a/crates/ra_hir/src/ty/tests/data/basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt | |||
@@ -9,5 +9,5 @@ | |||
9 | [69; 70) 'd': &str | 9 | [69; 70) 'd': &str |
10 | [76; 82) '1usize': [unknown] | 10 | [76; 82) '1usize': [unknown] |
11 | [88; 94) '1isize': [unknown] | 11 | [88; 94) '1isize': [unknown] |
12 | [100; 106) '"test"': [unknown] | 12 | [100; 106) '"test"': &str |
13 | [112; 118) '1.0f32': [unknown] | 13 | [112; 118) '1.0f32': [unknown] |
diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt new file mode 100644 index 000000000..8b22eee31 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/literals.txt | |||
@@ -0,0 +1,10 @@ | |||
1 | [11; 135) '{ ...lse] }': () | ||
2 | [17; 21) '5i32': [unknown] | ||
3 | [27; 34) '"hello"': &str | ||
4 | [40; 48) 'b"bytes"': &[u8] | ||
5 | [54; 57) ''c'': char | ||
6 | [63; 67) 'b'b'': u8 | ||
7 | [73; 77) '3.14': [unknown] | ||
8 | [83; 87) '5000': [unknown] | ||
9 | [93; 108) '(0u32, -5isize)': [unknown] | ||
10 | [114; 133) '[true,...false]': () | ||
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 2a095817a..6181df9d7 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -59,24 +59,29 @@ impl SourceFile { | |||
59 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); | 59 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); |
60 | TreeArc::cast(root) | 60 | TreeArc::cast(root) |
61 | } | 61 | } |
62 | |||
62 | pub fn parse(text: &str) -> TreeArc<SourceFile> { | 63 | pub fn parse(text: &str) -> TreeArc<SourceFile> { |
63 | let tokens = tokenize(&text); | 64 | let tokens = tokenize(&text); |
64 | let (green, errors) = | 65 | let (green, errors) = |
65 | parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root); | 66 | parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root); |
66 | SourceFile::new(green, errors) | 67 | SourceFile::new(green, errors) |
67 | } | 68 | } |
69 | |||
68 | pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { | 70 | pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { |
69 | self.incremental_reparse(edit) | 71 | self.incremental_reparse(edit) |
70 | .unwrap_or_else(|| self.full_reparse(edit)) | 72 | .unwrap_or_else(|| self.full_reparse(edit)) |
71 | } | 73 | } |
74 | |||
72 | pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> { | 75 | pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> { |
73 | reparsing::incremental_reparse(self.syntax(), edit, self.errors()) | 76 | reparsing::incremental_reparse(self.syntax(), edit, self.errors()) |
74 | .map(|(green_node, errors)| SourceFile::new(green_node, errors)) | 77 | .map(|(green_node, errors)| SourceFile::new(green_node, errors)) |
75 | } | 78 | } |
79 | |||
76 | fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { | 80 | fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { |
77 | let text = edit.apply(self.syntax().text().to_string()); | 81 | let text = edit.apply(self.syntax().text().to_string()); |
78 | SourceFile::parse(&text) | 82 | SourceFile::parse(&text) |
79 | } | 83 | } |
84 | |||
80 | pub fn errors(&self) -> Vec<SyntaxError> { | 85 | pub fn errors(&self) -> Vec<SyntaxError> { |
81 | let mut errors = self.syntax.root_data().clone(); | 86 | let mut errors = self.syntax.root_data().clone(); |
82 | errors.extend(validation::validate(self)); | 87 | errors.extend(validation::validate(self)); |
diff --git a/crates/ra_syntax/src/yellow.rs b/crates/ra_syntax/src/yellow.rs index 93621d08a..03df00fc6 100644 --- a/crates/ra_syntax/src/yellow.rs +++ b/crates/ra_syntax/src/yellow.rs | |||
@@ -128,40 +128,52 @@ impl SyntaxNode { | |||
128 | pub(crate) fn root_data(&self) -> &Vec<SyntaxError> { | 128 | pub(crate) fn root_data(&self) -> &Vec<SyntaxError> { |
129 | self.0.root_data() | 129 | self.0.root_data() |
130 | } | 130 | } |
131 | |||
131 | pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode { | 132 | pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode { |
132 | self.0.replace_self(replacement) | 133 | self.0.replace_self(replacement) |
133 | } | 134 | } |
135 | |||
134 | pub fn to_owned(&self) -> TreeArc<SyntaxNode> { | 136 | pub fn to_owned(&self) -> TreeArc<SyntaxNode> { |
135 | let ptr = TreeArc(self.0.to_owned()); | 137 | let ptr = TreeArc(self.0.to_owned()); |
136 | TreeArc::cast(ptr) | 138 | TreeArc::cast(ptr) |
137 | } | 139 | } |
140 | |||
138 | pub fn kind(&self) -> SyntaxKind { | 141 | pub fn kind(&self) -> SyntaxKind { |
139 | self.0.kind() | 142 | self.0.kind() |
140 | } | 143 | } |
144 | |||
141 | pub fn range(&self) -> TextRange { | 145 | pub fn range(&self) -> TextRange { |
142 | self.0.range() | 146 | self.0.range() |
143 | } | 147 | } |
148 | |||
144 | pub fn text(&self) -> SyntaxText { | 149 | pub fn text(&self) -> SyntaxText { |
145 | SyntaxText::new(self) | 150 | SyntaxText::new(self) |
146 | } | 151 | } |
152 | |||
147 | pub fn is_leaf(&self) -> bool { | 153 | pub fn is_leaf(&self) -> bool { |
148 | self.0.is_leaf() | 154 | self.0.is_leaf() |
149 | } | 155 | } |
156 | |||
150 | pub fn parent(&self) -> Option<&SyntaxNode> { | 157 | pub fn parent(&self) -> Option<&SyntaxNode> { |
151 | self.0.parent().map(SyntaxNode::from_repr) | 158 | self.0.parent().map(SyntaxNode::from_repr) |
152 | } | 159 | } |
160 | |||
153 | pub fn first_child(&self) -> Option<&SyntaxNode> { | 161 | pub fn first_child(&self) -> Option<&SyntaxNode> { |
154 | self.0.first_child().map(SyntaxNode::from_repr) | 162 | self.0.first_child().map(SyntaxNode::from_repr) |
155 | } | 163 | } |
164 | |||
156 | pub fn last_child(&self) -> Option<&SyntaxNode> { | 165 | pub fn last_child(&self) -> Option<&SyntaxNode> { |
157 | self.0.last_child().map(SyntaxNode::from_repr) | 166 | self.0.last_child().map(SyntaxNode::from_repr) |
158 | } | 167 | } |
168 | |||
159 | pub fn next_sibling(&self) -> Option<&SyntaxNode> { | 169 | pub fn next_sibling(&self) -> Option<&SyntaxNode> { |
160 | self.0.next_sibling().map(SyntaxNode::from_repr) | 170 | self.0.next_sibling().map(SyntaxNode::from_repr) |
161 | } | 171 | } |
172 | |||
162 | pub fn prev_sibling(&self) -> Option<&SyntaxNode> { | 173 | pub fn prev_sibling(&self) -> Option<&SyntaxNode> { |
163 | self.0.prev_sibling().map(SyntaxNode::from_repr) | 174 | self.0.prev_sibling().map(SyntaxNode::from_repr) |
164 | } | 175 | } |
176 | |||
165 | pub fn children(&self) -> SyntaxNodeChildren { | 177 | pub fn children(&self) -> SyntaxNodeChildren { |
166 | SyntaxNodeChildren(self.0.children()) | 178 | SyntaxNodeChildren(self.0.children()) |
167 | } | 179 | } |
diff --git a/crates/ra_syntax/src/yellow/syntax_text.rs b/crates/ra_syntax/src/yellow/syntax_text.rs index 08dbe57a2..378cd1b2e 100644 --- a/crates/ra_syntax/src/yellow/syntax_text.rs +++ b/crates/ra_syntax/src/yellow/syntax_text.rs | |||
@@ -15,6 +15,7 @@ impl<'a> SyntaxText<'a> { | |||
15 | range: node.range(), | 15 | range: node.range(), |
16 | } | 16 | } |
17 | } | 17 | } |
18 | |||
18 | pub fn chunks(&self) -> impl Iterator<Item = &'a str> { | 19 | pub fn chunks(&self) -> impl Iterator<Item = &'a str> { |
19 | let range = self.range; | 20 | let range = self.range; |
20 | self.node.descendants().filter_map(move |node| { | 21 | self.node.descendants().filter_map(move |node| { |
@@ -24,15 +25,19 @@ impl<'a> SyntaxText<'a> { | |||
24 | Some(&text[range]) | 25 | Some(&text[range]) |
25 | }) | 26 | }) |
26 | } | 27 | } |
28 | |||
27 | pub fn push_to(&self, buf: &mut String) { | 29 | pub fn push_to(&self, buf: &mut String) { |
28 | self.chunks().for_each(|it| buf.push_str(it)); | 30 | self.chunks().for_each(|it| buf.push_str(it)); |
29 | } | 31 | } |
32 | |||
30 | pub fn to_string(&self) -> String { | 33 | pub fn to_string(&self) -> String { |
31 | self.chunks().collect() | 34 | self.chunks().collect() |
32 | } | 35 | } |
36 | |||
33 | pub fn contains(&self, c: char) -> bool { | 37 | pub fn contains(&self, c: char) -> bool { |
34 | self.chunks().any(|it| it.contains(c)) | 38 | self.chunks().any(|it| it.contains(c)) |
35 | } | 39 | } |
40 | |||
36 | pub fn find(&self, c: char) -> Option<TextUnit> { | 41 | pub fn find(&self, c: char) -> Option<TextUnit> { |
37 | let mut acc: TextUnit = 0.into(); | 42 | let mut acc: TextUnit = 0.into(); |
38 | for chunk in self.chunks() { | 43 | for chunk in self.chunks() { |
@@ -44,9 +49,11 @@ impl<'a> SyntaxText<'a> { | |||
44 | } | 49 | } |
45 | None | 50 | None |
46 | } | 51 | } |
52 | |||
47 | pub fn len(&self) -> TextUnit { | 53 | pub fn len(&self) -> TextUnit { |
48 | self.range.len() | 54 | self.range.len() |
49 | } | 55 | } |
56 | |||
50 | pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { | 57 | pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { |
51 | let range = range.restrict(self.range).unwrap_or_else(|| { | 58 | let range = range.restrict(self.range).unwrap_or_else(|| { |
52 | panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) | 59 | panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) |
@@ -56,8 +63,10 @@ impl<'a> SyntaxText<'a> { | |||
56 | range, | 63 | range, |
57 | } | 64 | } |
58 | } | 65 | } |
59 | pub fn char_at(&self, offset: TextUnit) -> Option<char> { | 66 | |
67 | pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> { | ||
60 | let mut start: TextUnit = 0.into(); | 68 | let mut start: TextUnit = 0.into(); |
69 | let offset = offset.into(); | ||
61 | for chunk in self.chunks() { | 70 | for chunk in self.chunks() { |
62 | let end = start + TextUnit::of_str(chunk); | 71 | let end = start + TextUnit::of_str(chunk); |
63 | if start <= offset && offset < end { | 72 | if start <= offset && offset < end { |