aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-24 17:05:16 +0100
committerGitHub <[email protected]>2020-04-24 17:05:16 +0100
commit5d97667f8dd467e7382885fcae40bcdbac63ae4f (patch)
tree5fd1a5271033937b50f78bcbb1b62a3ade027d33 /crates
parentedd6f91cab77c4fabf449a50f4f65e4761c98ca4 (diff)
parent0dab5d58790d46e28d738c0d1d96e833a61495a1 (diff)
Merge #4105
4105: Adds attribute completions (#3941) r=matklad a=jrvidal I've hacked together some basic completions for a few builtin attributes. * I have no idea what `CompletionItemKind` we should set for them. * There's plenty of fancy stuff we could do by detecting the item the attribute is being added to. Nothing of that here. * Maybe we could complete as soon as you type `#` or `#!`? I'm not sure what info exactly we can dump on `CompletionContext` and what we should query during the completion calculation itself. I've assumed adding an `is_attribute` field is OK, since it allows to discard the completion quickly if it doesn't apply. Later we inspect the AST to find out whether it is an inner attribute or not. Co-authored-by: Roberto Vidal <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/completion.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs587
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs3
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs2
-rw-r--r--crates/rust-analyzer/src/conv.rs1
5 files changed, 595 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index f0e02180b..4ca0fdf4f 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -5,6 +5,7 @@ mod completion_item;
5mod completion_context; 5mod completion_context;
6mod presentation; 6mod presentation;
7 7
8mod complete_attribute;
8mod complete_dot; 9mod complete_dot;
9mod complete_record; 10mod complete_record;
10mod complete_pattern; 11mod complete_pattern;
@@ -78,6 +79,7 @@ pub(crate) fn completions(
78 complete_postfix::complete_postfix(&mut acc, &ctx); 79 complete_postfix::complete_postfix(&mut acc, &ctx);
79 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 80 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
80 complete_trait_impl::complete_trait_impl(&mut acc, &ctx); 81 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
82 complete_attribute::complete_attribute(&mut acc, &ctx);
81 83
82 Some(acc) 84 Some(acc)
83} 85}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
new file mode 100644
index 000000000..b405042e8
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -0,0 +1,587 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use super::completion_context::CompletionContext;
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions};
8use ra_syntax::{
9 ast::{Attr, AttrKind},
10 AstNode,
11};
12
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
14 if !ctx.is_attribute {
15 return;
16 }
17
18 let is_inner = ctx
19 .original_token
20 .ancestors()
21 .find_map(Attr::cast)
22 .map(|attr| attr.kind() == AttrKind::Inner)
23 .unwrap_or(false);
24
25 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new(
27 CompletionKind::Attribute,
28 ctx.source_range(),
29 attr_completion.label,
30 )
31 .kind(CompletionItemKind::Attribute);
32
33 match (attr_completion.snippet, ctx.config.snippet_cap) {
34 (Some(snippet), Some(cap)) => {
35 item = item.insert_snippet(cap, snippet);
36 }
37 _ => {}
38 }
39
40 if is_inner || !attr_completion.should_be_inner {
41 acc.add(item);
42 }
43 }
44}
45
46struct AttrCompletion {
47 label: &'static str,
48 snippet: Option<&'static str>,
49 should_be_inner: bool,
50}
51
52const ATTRIBUTES: &[AttrCompletion] = &[
53 AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false },
54 AttrCompletion {
55 label: "cfg_attr",
56 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"),
57 should_be_inner: false,
58 },
59 AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false },
60 AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false },
61 AttrCompletion {
62 label: "deprecated",
63 snippet: Some(r#"deprecated = "${0:reason}""#),
64 should_be_inner: false,
65 },
66 AttrCompletion {
67 label: "derive",
68 snippet: Some(r#"derive(${0:Debug})"#),
69 should_be_inner: false,
70 },
71 AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
72 AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
73 AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false },
74 // FIXME: resolve through macro resolution?
75 AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true },
76 AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false },
77 AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false },
78 AttrCompletion {
79 label: "link_name",
80 snippet: Some(r#"link_name = "${0:symbol_name}""#),
81 should_be_inner: false,
82 },
83 AttrCompletion { label: "link", snippet: None, should_be_inner: false },
84 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false },
85 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false },
86 AttrCompletion {
87 label: "must_use",
88 snippet: Some(r#"must_use = "${0:reason}""#),
89 should_be_inner: false,
90 },
91 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false },
92 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true },
93 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false },
94 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true },
95 AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false },
96 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false },
97 AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false },
98 AttrCompletion {
99 label: "proc_macro_derive",
100 snippet: Some("proc_macro_derive(${0:Trait})"),
101 should_be_inner: false,
102 },
103 AttrCompletion {
104 label: "recursion_limit",
105 snippet: Some("recursion_limit = ${0:128}"),
106 should_be_inner: true,
107 },
108 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
109 AttrCompletion {
110 label: "should_panic",
111 snippet: Some(r#"expected = "${0:reason}""#),
112 should_be_inner: false,
113 },
114 AttrCompletion {
115 label: "target_feature",
116 snippet: Some("target_feature = \"${0:feature}\""),
117 should_be_inner: false,
118 },
119 AttrCompletion { label: "test", snippet: None, should_be_inner: false },
120 AttrCompletion { label: "used", snippet: None, should_be_inner: false },
121 AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
122 AttrCompletion {
123 label: "windows_subsystem",
124 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
125 should_be_inner: true,
126 },
127];
128
129#[cfg(test)]
130mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
132 use insta::assert_debug_snapshot;
133
134 fn do_attr_completion(code: &str) -> Vec<CompletionItem> {
135 do_completion(code, CompletionKind::Attribute)
136 }
137
138 #[test]
139 fn test_attribute_completion() {
140 assert_debug_snapshot!(
141 do_attr_completion(
142 r"
143 #[<|>]
144 ",
145 ),
146 @r###"
147 [
148 CompletionItem {
149 label: "allow",
150 source_range: [19; 19),
151 delete: [19; 19),
152 insert: "allow(${0:lint})",
153 kind: Attribute,
154 },
155 CompletionItem {
156 label: "cfg",
157 source_range: [19; 19),
158 delete: [19; 19),
159 insert: "cfg(${0:predicate})",
160 kind: Attribute,
161 },
162 CompletionItem {
163 label: "cfg_attr",
164 source_range: [19; 19),
165 delete: [19; 19),
166 insert: "cfg_attr(${1:predicate}, ${0:attr})",
167 kind: Attribute,
168 },
169 CompletionItem {
170 label: "deny",
171 source_range: [19; 19),
172 delete: [19; 19),
173 insert: "deny(${0:lint})",
174 kind: Attribute,
175 },
176 CompletionItem {
177 label: "deprecated",
178 source_range: [19; 19),
179 delete: [19; 19),
180 insert: "deprecated = \"${0:reason}\"",
181 kind: Attribute,
182 },
183 CompletionItem {
184 label: "derive",
185 source_range: [19; 19),
186 delete: [19; 19),
187 insert: "derive(${0:Debug})",
188 kind: Attribute,
189 },
190 CompletionItem {
191 label: "doc",
192 source_range: [19; 19),
193 delete: [19; 19),
194 insert: "doc = \"${0:docs}\"",
195 kind: Attribute,
196 },
197 CompletionItem {
198 label: "forbid",
199 source_range: [19; 19),
200 delete: [19; 19),
201 insert: "forbid(${0:lint})",
202 kind: Attribute,
203 },
204 CompletionItem {
205 label: "ignore",
206 source_range: [19; 19),
207 delete: [19; 19),
208 insert: "ignore(${0:lint})",
209 kind: Attribute,
210 },
211 CompletionItem {
212 label: "inline",
213 source_range: [19; 19),
214 delete: [19; 19),
215 insert: "inline(${0:lint})",
216 kind: Attribute,
217 },
218 CompletionItem {
219 label: "link",
220 source_range: [19; 19),
221 delete: [19; 19),
222 insert: "link",
223 kind: Attribute,
224 },
225 CompletionItem {
226 label: "link_name",
227 source_range: [19; 19),
228 delete: [19; 19),
229 insert: "link_name = \"${0:symbol_name}\"",
230 kind: Attribute,
231 },
232 CompletionItem {
233 label: "macro_export",
234 source_range: [19; 19),
235 delete: [19; 19),
236 insert: "macro_export",
237 kind: Attribute,
238 },
239 CompletionItem {
240 label: "macro_use",
241 source_range: [19; 19),
242 delete: [19; 19),
243 insert: "macro_use",
244 kind: Attribute,
245 },
246 CompletionItem {
247 label: "must_use",
248 source_range: [19; 19),
249 delete: [19; 19),
250 insert: "must_use = \"${0:reason}\"",
251 kind: Attribute,
252 },
253 CompletionItem {
254 label: "no_mangle",
255 source_range: [19; 19),
256 delete: [19; 19),
257 insert: "no_mangle",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "non_exhaustive",
262 source_range: [19; 19),
263 delete: [19; 19),
264 insert: "non_exhaustive",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "path",
269 source_range: [19; 19),
270 delete: [19; 19),
271 insert: "path =\"${0:path}\"",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "proc_macro",
276 source_range: [19; 19),
277 delete: [19; 19),
278 insert: "proc_macro",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "proc_macro_attribute",
283 source_range: [19; 19),
284 delete: [19; 19),
285 insert: "proc_macro_attribute",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "proc_macro_derive",
290 source_range: [19; 19),
291 delete: [19; 19),
292 insert: "proc_macro_derive(${0:Trait})",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "repr",
297 source_range: [19; 19),
298 delete: [19; 19),
299 insert: "repr(${0:C})",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "should_panic",
304 source_range: [19; 19),
305 delete: [19; 19),
306 insert: "expected = \"${0:reason}\"",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "target_feature",
311 source_range: [19; 19),
312 delete: [19; 19),
313 insert: "target_feature = \"${0:feature}\"",
314 kind: Attribute,
315 },
316 CompletionItem {
317 label: "test",
318 source_range: [19; 19),
319 delete: [19; 19),
320 insert: "test",
321 kind: Attribute,
322 },
323 CompletionItem {
324 label: "used",
325 source_range: [19; 19),
326 delete: [19; 19),
327 insert: "used",
328 kind: Attribute,
329 },
330 CompletionItem {
331 label: "warn",
332 source_range: [19; 19),
333 delete: [19; 19),
334 insert: "warn(${0:lint})",
335 kind: Attribute,
336 },
337 ]
338 "###
339 );
340 }
341
342 #[test]
343 fn test_inner_attribute_completion() {
344 assert_debug_snapshot!(
345 do_attr_completion(
346 r"
347 #![<|>]
348 ",
349 ),
350 @r###"
351 [
352 CompletionItem {
353 label: "allow",
354 source_range: [20; 20),
355 delete: [20; 20),
356 insert: "allow(${0:lint})",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "cfg",
361 source_range: [20; 20),
362 delete: [20; 20),
363 insert: "cfg(${0:predicate})",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "cfg_attr",
368 source_range: [20; 20),
369 delete: [20; 20),
370 insert: "cfg_attr(${1:predicate}, ${0:attr})",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "deny",
375 source_range: [20; 20),
376 delete: [20; 20),
377 insert: "deny(${0:lint})",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "deprecated",
382 source_range: [20; 20),
383 delete: [20; 20),
384 insert: "deprecated = \"${0:reason}\"",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "derive",
389 source_range: [20; 20),
390 delete: [20; 20),
391 insert: "derive(${0:Debug})",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "doc",
396 source_range: [20; 20),
397 delete: [20; 20),
398 insert: "doc = \"${0:docs}\"",
399 kind: Attribute,
400 },
401 CompletionItem {
402 label: "feature",
403 source_range: [20; 20),
404 delete: [20; 20),
405 insert: "feature(${0:flag})",
406 kind: Attribute,
407 },
408 CompletionItem {
409 label: "forbid",
410 source_range: [20; 20),
411 delete: [20; 20),
412 insert: "forbid(${0:lint})",
413 kind: Attribute,
414 },
415 CompletionItem {
416 label: "global_allocator",
417 source_range: [20; 20),
418 delete: [20; 20),
419 insert: "global_allocator",
420 kind: Attribute,
421 },
422 CompletionItem {
423 label: "ignore",
424 source_range: [20; 20),
425 delete: [20; 20),
426 insert: "ignore(${0:lint})",
427 kind: Attribute,
428 },
429 CompletionItem {
430 label: "inline",
431 source_range: [20; 20),
432 delete: [20; 20),
433 insert: "inline(${0:lint})",
434 kind: Attribute,
435 },
436 CompletionItem {
437 label: "link",
438 source_range: [20; 20),
439 delete: [20; 20),
440 insert: "link",
441 kind: Attribute,
442 },
443 CompletionItem {
444 label: "link_name",
445 source_range: [20; 20),
446 delete: [20; 20),
447 insert: "link_name = \"${0:symbol_name}\"",
448 kind: Attribute,
449 },
450 CompletionItem {
451 label: "macro_export",
452 source_range: [20; 20),
453 delete: [20; 20),
454 insert: "macro_export",
455 kind: Attribute,
456 },
457 CompletionItem {
458 label: "macro_use",
459 source_range: [20; 20),
460 delete: [20; 20),
461 insert: "macro_use",
462 kind: Attribute,
463 },
464 CompletionItem {
465 label: "must_use",
466 source_range: [20; 20),
467 delete: [20; 20),
468 insert: "must_use = \"${0:reason}\"",
469 kind: Attribute,
470 },
471 CompletionItem {
472 label: "no_mangle",
473 source_range: [20; 20),
474 delete: [20; 20),
475 insert: "no_mangle",
476 kind: Attribute,
477 },
478 CompletionItem {
479 label: "no_std",
480 source_range: [20; 20),
481 delete: [20; 20),
482 insert: "no_std",
483 kind: Attribute,
484 },
485 CompletionItem {
486 label: "non_exhaustive",
487 source_range: [20; 20),
488 delete: [20; 20),
489 insert: "non_exhaustive",
490 kind: Attribute,
491 },
492 CompletionItem {
493 label: "panic_handler",
494 source_range: [20; 20),
495 delete: [20; 20),
496 insert: "panic_handler",
497 kind: Attribute,
498 },
499 CompletionItem {
500 label: "path",
501 source_range: [20; 20),
502 delete: [20; 20),
503 insert: "path =\"${0:path}\"",
504 kind: Attribute,
505 },
506 CompletionItem {
507 label: "proc_macro",
508 source_range: [20; 20),
509 delete: [20; 20),
510 insert: "proc_macro",
511 kind: Attribute,
512 },
513 CompletionItem {
514 label: "proc_macro_attribute",
515 source_range: [20; 20),
516 delete: [20; 20),
517 insert: "proc_macro_attribute",
518 kind: Attribute,
519 },
520 CompletionItem {
521 label: "proc_macro_derive",
522 source_range: [20; 20),
523 delete: [20; 20),
524 insert: "proc_macro_derive(${0:Trait})",
525 kind: Attribute,
526 },
527 CompletionItem {
528 label: "recursion_limit",
529 source_range: [20; 20),
530 delete: [20; 20),
531 insert: "recursion_limit = ${0:128}",
532 kind: Attribute,
533 },
534 CompletionItem {
535 label: "repr",
536 source_range: [20; 20),
537 delete: [20; 20),
538 insert: "repr(${0:C})",
539 kind: Attribute,
540 },
541 CompletionItem {
542 label: "should_panic",
543 source_range: [20; 20),
544 delete: [20; 20),
545 insert: "expected = \"${0:reason}\"",
546 kind: Attribute,
547 },
548 CompletionItem {
549 label: "target_feature",
550 source_range: [20; 20),
551 delete: [20; 20),
552 insert: "target_feature = \"${0:feature}\"",
553 kind: Attribute,
554 },
555 CompletionItem {
556 label: "test",
557 source_range: [20; 20),
558 delete: [20; 20),
559 insert: "test",
560 kind: Attribute,
561 },
562 CompletionItem {
563 label: "used",
564 source_range: [20; 20),
565 delete: [20; 20),
566 insert: "used",
567 kind: Attribute,
568 },
569 CompletionItem {
570 label: "warn",
571 source_range: [20; 20),
572 delete: [20; 20),
573 insert: "warn(${0:lint})",
574 kind: Attribute,
575 },
576 CompletionItem {
577 label: "windows_subsystem",
578 source_range: [20; 20),
579 delete: [20; 20),
580 insert: "windows_subsystem = \"${0:subsystem}\"",
581 kind: Attribute,
582 },
583 ]
584 "###
585 );
586 }
587}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index a76d1ce45..37880448a 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -57,6 +57,7 @@ pub(crate) struct CompletionContext<'a> {
57 pub(super) is_macro_call: bool, 57 pub(super) is_macro_call: bool,
58 pub(super) is_path_type: bool, 58 pub(super) is_path_type: bool,
59 pub(super) has_type_args: bool, 59 pub(super) has_type_args: bool,
60 pub(super) is_attribute: bool,
60} 61}
61 62
62impl<'a> CompletionContext<'a> { 63impl<'a> CompletionContext<'a> {
@@ -113,6 +114,7 @@ impl<'a> CompletionContext<'a> {
113 is_path_type: false, 114 is_path_type: false,
114 has_type_args: false, 115 has_type_args: false,
115 dot_receiver_is_ambiguous_float_literal: false, 116 dot_receiver_is_ambiguous_float_literal: false,
117 is_attribute: false,
116 }; 118 };
117 119
118 let mut original_file = original_file.syntax().clone(); 120 let mut original_file = original_file.syntax().clone();
@@ -306,6 +308,7 @@ impl<'a> CompletionContext<'a> {
306 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 308 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
307 .is_some(); 309 .is_some();
308 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 310 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
311 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
309 312
310 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 313 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
311 self.has_type_args = segment.type_arg_list().is_some(); 314 self.has_type_args = segment.type_arg_list().is_some();
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index fb06cc125..5936fb8f7 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -121,6 +121,7 @@ pub enum CompletionItemKind {
121 Method, 121 Method,
122 TypeParam, 122 TypeParam,
123 Macro, 123 Macro,
124 Attribute,
124} 125}
125 126
126#[derive(Debug, PartialEq, Eq, Copy, Clone)] 127#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -134,6 +135,7 @@ pub(crate) enum CompletionKind {
134 Snippet, 135 Snippet,
135 Postfix, 136 Postfix,
136 BuiltinType, 137 BuiltinType,
138 Attribute,
137} 139}
138 140
139#[derive(Debug, PartialEq, Eq, Copy, Clone)] 141#[derive(Debug, PartialEq, Eq, Copy, Clone)]
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 098ee369c..2285cb1d3 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -100,6 +100,7 @@ impl Conv for CompletionItemKind {
100 CompletionItemKind::Method => Method, 100 CompletionItemKind::Method => Method,
101 CompletionItemKind::TypeParam => TypeParameter, 101 CompletionItemKind::TypeParam => TypeParameter,
102 CompletionItemKind::Macro => Method, 102 CompletionItemKind::Macro => Method,
103 CompletionItemKind::Attribute => EnumMember,
103 } 104 }
104 } 105 }
105} 106}