microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.23.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/language_service/src/completion/ast_context.rs

327lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use qsc::{
5 ast::{
6 self, Attr, Block, CallableDecl, Expr, ExprKind, FieldAssign, FieldDef, FunctorExpr, Ident,
7 Idents, ImportKind, Item, ItemKind, Namespace, NodeId, Package, Pat, Path, QubitInit,
8 SpecDecl, Stmt, StructDecl, Ty, TyDef, TyKind,
9 visit::{self, Visitor},
10 },
11 parse::completion::PathKind,
12};
13use std::rc::Rc;
14
15/// Provides additional syntactic context for the cursor offset,
16/// for various special cases where it's needed.
17#[derive(Debug)]
18pub(super) struct AstContext<'a> {
19 path_qualifier: Option<Vec<&'a Ident>>,
20 context: Option<Context<'a>>,
21 offset: u32,
22}
23
24#[derive(Debug, Clone)]
25enum Context<'a> {
26 /// The cursor is on a path or incomplete path.
27 Path(PathKind),
28 /// The cursor is on a field access or a field assignment expression.
29 Field {
30 /// The type of this expression will be the record used for the field access.
31 record: &'a Expr,
32 },
33 /// The cursor is on an attribute.
34 AttrArg(Rc<str>),
35}
36
37impl<'a> AstContext<'a> {
38 pub fn init(offset: u32, package: &'a Package) -> Self {
39 let mut offset_visitor = OffsetVisitor {
40 offset,
41 visitor: AstContext {
42 offset,
43 context: None,
44 path_qualifier: None,
45 },
46 };
47
48 offset_visitor.visit_package(package);
49
50 offset_visitor.visitor
51 }
52
53 fn set_context(&mut self, context: Context<'a>) {
54 // Ignores update of context if the cursor is in an attribute argument
55 if matches!(self.context, Some(Context::AttrArg(_))) {
56 return;
57 }
58 self.context = Some(context);
59 }
60}
61
62impl<'a> Visitor<'a> for AstContext<'a> {
63 fn visit_item(&mut self, item: &'a Item) {
64 match &*item.kind {
65 ItemKind::Open(..) => self.set_context(Context::Path(PathKind::Namespace)),
66 ItemKind::ImportOrExport(decl) => {
67 self.set_context(Context::Path(PathKind::Import));
68 for item in &decl.items {
69 if ImportKind::Wildcard == item.kind && item.span.contains(self.offset) {
70 // Special case when the cursor falls *between* the
71 // `Path` and the glob asterisk,
72 // e.g. `foo.bar.|*` . In that case, the visitor
73 // will not visit the path since the cursor technically
74 // is not within the path.
75 self.visit_path_kind(&item.path);
76 }
77 }
78 }
79 _ => {}
80 }
81 }
82
83 fn visit_ty(&mut self, ty: &Ty) {
84 if let TyKind::Path(..) = *ty.kind {
85 self.set_context(Context::Path(PathKind::Ty));
86 }
87 }
88
89 fn visit_expr(&mut self, expr: &'a Expr) {
90 if let ExprKind::Path(..) = *expr.kind {
91 self.set_context(Context::Path(PathKind::Expr));
92 } else if let ExprKind::Struct(..) = expr.kind.as_ref() {
93 self.set_context(Context::Field { record: expr });
94 } else if let ExprKind::Field(record, _) = expr.kind.as_ref() {
95 self.set_context(Context::Field { record });
96 }
97 }
98
99 fn visit_path_kind(&mut self, path: &'a ast::PathKind) {
100 if let Some(Context::Field { .. }) = self.context {
101 self.set_context(Context::Path(PathKind::Struct));
102 }
103 self.path_qualifier = match path {
104 ast::PathKind::Ok(path) => Some(path.iter().collect()),
105 ast::PathKind::Err(Some(incomplete_path)) => {
106 Some(incomplete_path.segments.iter().collect())
107 }
108 ast::PathKind::Err(None) => None,
109 };
110 }
111
112 fn visit_attr(&mut self, attr: &'a Attr) {
113 if attr.arg.span.contains(self.offset) {
114 self.set_context(Context::AttrArg(attr.name.name.clone()));
115 }
116 }
117}
118
119impl AstContext<'_> {
120 /// Returns the path kind and the path qualifier before the cursor offset.
121 ///
122 /// Returns `None` if the cursor is not on a path.
123 pub fn path_segment_context(&self) -> Option<(PathKind, Vec<Rc<str>>)> {
124 let Some(Context::Path(path_kind)) = self.context else {
125 return None;
126 };
127
128 let qualifier = self.segments_before_offset();
129
130 if qualifier.is_empty() {
131 return None;
132 }
133
134 Some((path_kind, qualifier))
135 }
136
137 /// Returns the node ID of the node that the field access is being performed on, if any.
138 /// When this is a field assignment, returns the node ID of the struct expression.
139 ///
140 /// This node ID can then be used to look up the type to get field names.
141 ///
142 /// Returns `None` if the cursor is not on a field access expression or path.
143 pub fn field_access_context(&self) -> Option<NodeId> {
144 if let Some(Context::Field { record }) = &self.context {
145 // Unambiguously a field access expression, record node is an `Expr`
146 Some(record.id)
147 } else {
148 // A `Path` that may or may not be a field access expression,
149 // record node is the last identifier before the cursor
150 self.idents_before_cursor().last().map(|ident| ident.id)
151 }
152 }
153
154 /// Returns the name of the attribute, if the cursor is on an attribute argument.
155 /// If the cursor is not on an attribute argument, returns `None`.
156 pub fn get_name_of_attr_for_attr_arg(&self) -> Option<Rc<str>> {
157 if let Some(Context::AttrArg(name)) = &self.context {
158 Some(name.clone())
159 } else {
160 None
161 }
162 }
163
164 fn idents_before_cursor(&self) -> impl Iterator<Item = &Ident> {
165 self.path_qualifier
166 .iter()
167 .flatten()
168 .copied()
169 .take_while(|i| i.span.hi < self.offset)
170 }
171
172 fn segments_before_offset(&self) -> Vec<Rc<str>> {
173 self.idents_before_cursor()
174 .map(|i| i.name.clone())
175 .collect::<Vec<_>>()
176 }
177}
178
179/// A [`Visitor`] wrapper that only descends into a node
180/// if the given offset falls within that node.
181struct OffsetVisitor<T> {
182 offset: u32,
183 visitor: T,
184}
185
186impl<'a, T> Visitor<'a> for OffsetVisitor<T>
187where
188 T: Visitor<'a>,
189{
190 fn visit_namespace(&mut self, namespace: &'a Namespace) {
191 if namespace.span.touches(self.offset) {
192 self.visitor.visit_namespace(namespace);
193 visit::walk_namespace(self, namespace);
194 }
195 }
196
197 fn visit_item(&mut self, item: &'a Item) {
198 if item.span.touches(self.offset) {
199 self.visitor.visit_item(item);
200 visit::walk_item(self, item);
201 }
202 }
203
204 fn visit_attr(&mut self, attr: &'a Attr) {
205 if attr.span.touches(self.offset) {
206 self.visitor.visit_attr(attr);
207 visit::walk_attr(self, attr);
208 }
209 }
210
211 fn visit_ty_def(&mut self, def: &'a TyDef) {
212 if def.span.touches(self.offset) {
213 self.visitor.visit_ty_def(def);
214 visit::walk_ty_def(self, def);
215 }
216 }
217
218 fn visit_callable_decl(&mut self, decl: &'a CallableDecl) {
219 if decl.span.touches(self.offset) {
220 self.visitor.visit_callable_decl(decl);
221 visit::walk_callable_decl(self, decl);
222 }
223 }
224
225 fn visit_struct_decl(&mut self, decl: &'a StructDecl) {
226 if decl.span.touches(self.offset) {
227 self.visitor.visit_struct_decl(decl);
228 visit::walk_struct_decl(self, decl);
229 }
230 }
231
232 fn visit_field_def(&mut self, def: &'a FieldDef) {
233 if def.span.touches(self.offset) {
234 self.visitor.visit_field_def(def);
235 visit::walk_field_def(self, def);
236 }
237 }
238
239 fn visit_spec_decl(&mut self, decl: &'a SpecDecl) {
240 if decl.span.touches(self.offset) {
241 self.visitor.visit_spec_decl(decl);
242 visit::walk_spec_decl(self, decl);
243 }
244 }
245
246 fn visit_functor_expr(&mut self, expr: &'a FunctorExpr) {
247 if expr.span.touches(self.offset) {
248 self.visitor.visit_functor_expr(expr);
249 visit::walk_functor_expr(self, expr);
250 }
251 }
252
253 fn visit_ty(&mut self, ty: &'a Ty) {
254 if ty.span.touches(self.offset) {
255 self.visitor.visit_ty(ty);
256 visit::walk_ty(self, ty);
257 }
258 }
259
260 fn visit_block(&mut self, block: &'a Block) {
261 if block.span.touches(self.offset) {
262 self.visitor.visit_block(block);
263 visit::walk_block(self, block);
264 }
265 }
266
267 fn visit_stmt(&mut self, stmt: &'a Stmt) {
268 if stmt.span.touches(self.offset) {
269 self.visitor.visit_stmt(stmt);
270 visit::walk_stmt(self, stmt);
271 }
272 }
273
274 fn visit_expr(&mut self, expr: &'a Expr) {
275 if expr.span.touches(self.offset) {
276 self.visitor.visit_expr(expr);
277 visit::walk_expr(self, expr);
278 }
279 }
280
281 fn visit_field_assign(&mut self, assign: &'a FieldAssign) {
282 if assign.span.touches(self.offset) {
283 self.visitor.visit_field_assign(assign);
284 visit::walk_field_assign(self, assign);
285 }
286 }
287
288 fn visit_pat(&mut self, pat: &'a Pat) {
289 if pat.span.touches(self.offset) {
290 self.visitor.visit_pat(pat);
291 visit::walk_pat(self, pat);
292 }
293 }
294
295 fn visit_qubit_init(&mut self, init: &'a QubitInit) {
296 if init.span.touches(self.offset) {
297 self.visitor.visit_qubit_init(init);
298 visit::walk_qubit_init(self, init);
299 }
300 }
301
302 fn visit_path(&mut self, path: &'a Path) {
303 if path.span.touches(self.offset) {
304 self.visitor.visit_path(path);
305 visit::walk_path(self, path);
306 }
307 }
308
309 fn visit_path_kind(&mut self, path: &'a ast::PathKind) {
310 let span = match path {
311 ast::PathKind::Ok(path) => &path.span,
312 ast::PathKind::Err(Some(incomplete_path)) => &incomplete_path.span,
313 ast::PathKind::Err(None) => return,
314 };
315
316 if span.touches(self.offset) {
317 self.visitor.visit_path_kind(path);
318 visit::walk_path_kind(self, path);
319 }
320 }
321
322 fn visit_ident(&mut self, ident: &'a Ident) {
323 if ident.span.touches(self.offset) {
324 self.visitor.visit_ident(ident);
325 }
326 }
327}
328