diff options
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/render.rs | 386 |
1 files changed, 178 insertions, 208 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 7118183fe..902df46ca 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -24,34 +24,34 @@ use crate::{ | |||
24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | pub(crate) fn render_field<'a>( | 27 | pub(crate) fn render_field( |
28 | ctx: RenderContext<'a>, | 28 | ctx: RenderContext<'_>, |
29 | receiver: Option<hir::Name>, | 29 | receiver: Option<hir::Name>, |
30 | field: hir::Field, | 30 | field: hir::Field, |
31 | ty: &hir::Type, | 31 | ty: &hir::Type, |
32 | ) -> CompletionItem { | 32 | ) -> CompletionItem { |
33 | Render::new(ctx).render_field(receiver, field, ty) | 33 | render_field_(ctx, receiver, field, ty) |
34 | } | 34 | } |
35 | 35 | ||
36 | pub(crate) fn render_tuple_field<'a>( | 36 | pub(crate) fn render_tuple_field( |
37 | ctx: RenderContext<'a>, | 37 | ctx: RenderContext<'_>, |
38 | receiver: Option<hir::Name>, | 38 | receiver: Option<hir::Name>, |
39 | field: usize, | 39 | field: usize, |
40 | ty: &hir::Type, | 40 | ty: &hir::Type, |
41 | ) -> CompletionItem { | 41 | ) -> CompletionItem { |
42 | Render::new(ctx).render_tuple_field(receiver, field, ty) | 42 | render_tuple_field_(ctx, receiver, field, ty) |
43 | } | 43 | } |
44 | 44 | ||
45 | pub(crate) fn render_resolution<'a>( | 45 | pub(crate) fn render_resolution( |
46 | ctx: RenderContext<'a>, | 46 | ctx: RenderContext<'_>, |
47 | local_name: hir::Name, | 47 | local_name: hir::Name, |
48 | resolution: &hir::ScopeDef, | 48 | resolution: &hir::ScopeDef, |
49 | ) -> Option<CompletionItem> { | 49 | ) -> Option<CompletionItem> { |
50 | Render::new(ctx).render_resolution(local_name, None, resolution) | 50 | render_resolution_(ctx, local_name, None, resolution) |
51 | } | 51 | } |
52 | 52 | ||
53 | pub(crate) fn render_resolution_with_import<'a>( | 53 | pub(crate) fn render_resolution_with_import( |
54 | ctx: RenderContext<'a>, | 54 | ctx: RenderContext<'_>, |
55 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
56 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
57 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); | 57 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); |
@@ -64,12 +64,10 @@ pub(crate) fn render_resolution_with_import<'a>( | |||
64 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), | 64 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), |
65 | _ => item_name(ctx.db(), import_edit.import.original_item)?, | 65 | _ => item_name(ctx.db(), import_edit.import.original_item)?, |
66 | }; | 66 | }; |
67 | Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( | 67 | render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| { |
68 | |mut item| { | 68 | item.completion_kind = CompletionKind::Magic; |
69 | item.completion_kind = CompletionKind::Magic; | 69 | item |
70 | item | 70 | }) |
71 | }, | ||
72 | ) | ||
73 | } | 71 | } |
74 | 72 | ||
75 | /// Interface for data and methods required for items rendering. | 73 | /// Interface for data and methods required for items rendering. |
@@ -121,216 +119,188 @@ impl<'a> RenderContext<'a> { | |||
121 | } | 119 | } |
122 | } | 120 | } |
123 | 121 | ||
124 | /// Generic renderer for completion items. | 122 | fn render_field_( |
125 | #[derive(Debug)] | 123 | ctx: RenderContext<'_>, |
126 | struct Render<'a> { | 124 | receiver: Option<hir::Name>, |
127 | ctx: RenderContext<'a>, | 125 | field: hir::Field, |
128 | } | 126 | ty: &hir::Type, |
129 | 127 | ) -> CompletionItem { | |
130 | impl<'a> Render<'a> { | 128 | let is_deprecated = ctx.is_deprecated(field); |
131 | fn new(ctx: RenderContext<'a>) -> Render<'a> { | 129 | let name = field.name(ctx.db()).to_string(); |
132 | Render { ctx } | 130 | let mut item = CompletionItem::new( |
131 | CompletionKind::Reference, | ||
132 | ctx.source_range(), | ||
133 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
134 | ); | ||
135 | item.kind(SymbolKind::Field) | ||
136 | .detail(ty.display(ctx.db()).to_string()) | ||
137 | .set_documentation(field.docs(ctx.db())) | ||
138 | .set_deprecated(is_deprecated); | ||
139 | |||
140 | item.set_relevance(CompletionRelevance { | ||
141 | type_match: compute_type_match(ctx.completion, ty), | ||
142 | exact_name_match: compute_exact_name_match(ctx.completion, &name), | ||
143 | ..CompletionRelevance::default() | ||
144 | }); | ||
145 | |||
146 | if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) { | ||
147 | // FIXME | ||
148 | // For now we don't properly calculate the edits for ref match | ||
149 | // completions on struct fields, so we've disabled them. See #8058. | ||
133 | } | 150 | } |
134 | 151 | ||
135 | fn render_field( | 152 | item.build() |
136 | &self, | 153 | } |
137 | receiver: Option<hir::Name>, | ||
138 | field: hir::Field, | ||
139 | ty: &hir::Type, | ||
140 | ) -> CompletionItem { | ||
141 | let is_deprecated = self.ctx.is_deprecated(field); | ||
142 | let name = field.name(self.ctx.db()).to_string(); | ||
143 | let mut item = CompletionItem::new( | ||
144 | CompletionKind::Reference, | ||
145 | self.ctx.source_range(), | ||
146 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
147 | ); | ||
148 | item.kind(SymbolKind::Field) | ||
149 | .detail(ty.display(self.ctx.db()).to_string()) | ||
150 | .set_documentation(field.docs(self.ctx.db())) | ||
151 | .set_deprecated(is_deprecated); | ||
152 | |||
153 | item.set_relevance(CompletionRelevance { | ||
154 | type_match: compute_type_match(self.ctx.completion, ty), | ||
155 | exact_name_match: compute_exact_name_match(self.ctx.completion, &name), | ||
156 | ..CompletionRelevance::default() | ||
157 | }); | ||
158 | |||
159 | if let Some(_ref_match) = compute_ref_match(self.ctx.completion, ty) { | ||
160 | // FIXME | ||
161 | // For now we don't properly calculate the edits for ref match | ||
162 | // completions on struct fields, so we've disabled them. See #8058. | ||
163 | } | ||
164 | 154 | ||
165 | item.build() | 155 | fn render_tuple_field_( |
166 | } | 156 | ctx: RenderContext<'_>, |
157 | receiver: Option<hir::Name>, | ||
158 | field: usize, | ||
159 | ty: &hir::Type, | ||
160 | ) -> CompletionItem { | ||
161 | let mut item = CompletionItem::new( | ||
162 | CompletionKind::Reference, | ||
163 | ctx.source_range(), | ||
164 | receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
165 | ); | ||
167 | 166 | ||
168 | fn render_tuple_field( | 167 | item.kind(SymbolKind::Field).detail(ty.display(ctx.db()).to_string()); |
169 | &self, | ||
170 | receiver: Option<hir::Name>, | ||
171 | field: usize, | ||
172 | ty: &hir::Type, | ||
173 | ) -> CompletionItem { | ||
174 | let mut item = CompletionItem::new( | ||
175 | CompletionKind::Reference, | ||
176 | self.ctx.source_range(), | ||
177 | receiver | ||
178 | .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
179 | ); | ||
180 | 168 | ||
181 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | 169 | item.build() |
170 | } | ||
182 | 171 | ||
183 | item.build() | 172 | fn render_resolution_( |
184 | } | 173 | ctx: RenderContext<'_>, |
174 | local_name: hir::Name, | ||
175 | import_to_add: Option<ImportEdit>, | ||
176 | resolution: &hir::ScopeDef, | ||
177 | ) -> Option<CompletionItem> { | ||
178 | let _p = profile::span("render_resolution"); | ||
179 | use hir::ModuleDef::*; | ||
185 | 180 | ||
186 | fn render_resolution( | 181 | let completion_kind = match resolution { |
187 | self, | 182 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, |
188 | local_name: hir::Name, | 183 | _ => CompletionKind::Reference, |
189 | import_to_add: Option<ImportEdit>, | 184 | }; |
190 | resolution: &hir::ScopeDef, | ||
191 | ) -> Option<CompletionItem> { | ||
192 | let _p = profile::span("render_resolution"); | ||
193 | use hir::ModuleDef::*; | ||
194 | |||
195 | let completion_kind = match resolution { | ||
196 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, | ||
197 | _ => CompletionKind::Reference, | ||
198 | }; | ||
199 | 185 | ||
200 | let kind = match resolution { | 186 | let kind = match resolution { |
201 | hir::ScopeDef::ModuleDef(Function(func)) => { | 187 | hir::ScopeDef::ModuleDef(Function(func)) => { |
202 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); | 188 | return render_fn(ctx, import_to_add, Some(local_name), *func); |
203 | } | 189 | } |
204 | hir::ScopeDef::ModuleDef(Variant(_)) | 190 | hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.is_pat_or_const.is_some() => { |
205 | if self.ctx.completion.is_pat_or_const.is_some() => | 191 | CompletionItemKind::SymbolKind(SymbolKind::Variant) |
206 | { | 192 | } |
207 | CompletionItemKind::SymbolKind(SymbolKind::Variant) | 193 | hir::ScopeDef::ModuleDef(Variant(var)) => { |
208 | } | 194 | let item = render_variant(ctx, import_to_add, Some(local_name), *var, None); |
209 | hir::ScopeDef::ModuleDef(Variant(var)) => { | 195 | return Some(item); |
210 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); | 196 | } |
211 | return Some(item); | 197 | hir::ScopeDef::MacroDef(mac) => { |
212 | } | 198 | let item = render_macro(ctx, import_to_add, local_name, *mac); |
213 | hir::ScopeDef::MacroDef(mac) => { | 199 | return item; |
214 | let item = render_macro(self.ctx, import_to_add, local_name, *mac); | 200 | } |
215 | return item; | ||
216 | } | ||
217 | 201 | ||
218 | hir::ScopeDef::ModuleDef(Module(..)) => { | 202 | hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), |
219 | CompletionItemKind::SymbolKind(SymbolKind::Module) | 203 | hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { |
220 | } | 204 | hir::Adt::Struct(_) => SymbolKind::Struct, |
221 | hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { | 205 | hir::Adt::Union(_) => SymbolKind::Union, |
222 | hir::Adt::Struct(_) => SymbolKind::Struct, | 206 | hir::Adt::Enum(_) => SymbolKind::Enum, |
223 | hir::Adt::Union(_) => SymbolKind::Union, | 207 | }), |
224 | hir::Adt::Enum(_) => SymbolKind::Enum, | 208 | hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), |
225 | }), | 209 | hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), |
226 | hir::ScopeDef::ModuleDef(Const(..)) => { | 210 | hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), |
227 | CompletionItemKind::SymbolKind(SymbolKind::Const) | 211 | hir::ScopeDef::ModuleDef(TypeAlias(..)) => { |
228 | } | 212 | CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) |
229 | hir::ScopeDef::ModuleDef(Static(..)) => { | 213 | } |
230 | CompletionItemKind::SymbolKind(SymbolKind::Static) | 214 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, |
231 | } | 215 | hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { |
232 | hir::ScopeDef::ModuleDef(Trait(..)) => { | 216 | hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, |
233 | CompletionItemKind::SymbolKind(SymbolKind::Trait) | 217 | hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, |
234 | } | 218 | hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, |
235 | hir::ScopeDef::ModuleDef(TypeAlias(..)) => { | 219 | }), |
236 | CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) | 220 | hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), |
237 | } | 221 | hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), |
238 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | 222 | hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { |
239 | hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { | 223 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
240 | hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, | 224 | } |
241 | hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, | 225 | hir::ScopeDef::Unknown => { |
242 | hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, | 226 | let mut item = CompletionItem::new( |
243 | }), | 227 | CompletionKind::Reference, |
244 | hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), | 228 | ctx.source_range(), |
245 | hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), | 229 | local_name.to_string(), |
246 | hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { | 230 | ); |
247 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 231 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
248 | } | 232 | return Some(item.build()); |
249 | hir::ScopeDef::Unknown => { | 233 | } |
250 | let mut item = CompletionItem::new( | 234 | }; |
251 | CompletionKind::Reference, | ||
252 | self.ctx.source_range(), | ||
253 | local_name.to_string(), | ||
254 | ); | ||
255 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); | ||
256 | return Some(item.build()); | ||
257 | } | ||
258 | }; | ||
259 | 235 | ||
260 | let local_name = local_name.to_string(); | 236 | let local_name = local_name.to_string(); |
261 | let mut item = | 237 | let mut item = CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone()); |
262 | CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); | 238 | if let hir::ScopeDef::Local(local) = resolution { |
263 | if let hir::ScopeDef::Local(local) = resolution { | 239 | let ty = local.ty(ctx.db()); |
264 | let ty = local.ty(self.ctx.db()); | 240 | if !ty.is_unknown() { |
265 | if !ty.is_unknown() { | 241 | item.detail(ty.display(ctx.db()).to_string()); |
266 | item.detail(ty.display(self.ctx.db()).to_string()); | 242 | } |
267 | } | ||
268 | 243 | ||
269 | item.set_relevance(CompletionRelevance { | 244 | item.set_relevance(CompletionRelevance { |
270 | type_match: compute_type_match(self.ctx.completion, &ty), | 245 | type_match: compute_type_match(ctx.completion, &ty), |
271 | exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name), | 246 | exact_name_match: compute_exact_name_match(ctx.completion, &local_name), |
272 | is_local: true, | 247 | is_local: true, |
273 | ..CompletionRelevance::default() | 248 | ..CompletionRelevance::default() |
274 | }); | 249 | }); |
275 | 250 | ||
276 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { | 251 | if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) { |
277 | item.ref_match(ref_match); | 252 | item.ref_match(ref_match); |
278 | } | 253 | } |
279 | }; | 254 | }; |
280 | 255 | ||
281 | // Add `<>` for generic types | 256 | // Add `<>` for generic types |
282 | if matches!( | 257 | if matches!( |
283 | self.ctx.completion.path_context, | 258 | ctx.completion.path_context, |
284 | Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. }) | 259 | Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. }) |
285 | ) && self.ctx.completion.config.add_call_parenthesis | 260 | ) && ctx.completion.config.add_call_parenthesis |
286 | { | 261 | { |
287 | if let Some(cap) = self.ctx.snippet_cap() { | 262 | if let Some(cap) = ctx.snippet_cap() { |
288 | let has_non_default_type_params = match resolution { | 263 | let has_non_default_type_params = match resolution { |
289 | hir::ScopeDef::ModuleDef(Adt(it)) => { | 264 | hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()), |
290 | it.has_non_default_type_params(self.ctx.db()) | 265 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()), |
291 | } | 266 | _ => false, |
292 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => { | 267 | }; |
293 | it.has_non_default_type_params(self.ctx.db()) | 268 | if has_non_default_type_params { |
294 | } | 269 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
295 | _ => false, | 270 | item.lookup_by(local_name.clone()) |
296 | }; | 271 | .label(format!("{}<…>", local_name)) |
297 | if has_non_default_type_params { | 272 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
298 | cov_mark::hit!(inserts_angle_brackets_for_generics); | ||
299 | item.lookup_by(local_name.clone()) | ||
300 | .label(format!("{}<…>", local_name)) | ||
301 | .insert_snippet(cap, format!("{}<$0>", local_name)); | ||
302 | } | ||
303 | } | 273 | } |
304 | } | 274 | } |
305 | item.kind(kind) | ||
306 | .add_import(import_to_add) | ||
307 | .set_documentation(self.docs(resolution)) | ||
308 | .set_deprecated(self.is_deprecated(resolution)); | ||
309 | Some(item.build()) | ||
310 | } | 275 | } |
276 | item.kind(kind) | ||
277 | .add_import(import_to_add) | ||
278 | .set_documentation(scope_def_docs(ctx.db(), resolution)) | ||
279 | .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); | ||
280 | Some(item.build()) | ||
281 | } | ||
311 | 282 | ||
312 | fn docs(&self, resolution: &hir::ScopeDef) -> Option<hir::Documentation> { | 283 | fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option<hir::Documentation> { |
313 | use hir::ModuleDef::*; | 284 | use hir::ModuleDef::*; |
314 | match resolution { | 285 | match resolution { |
315 | hir::ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), | 286 | hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db), |
316 | hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), | 287 | hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db), |
317 | hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()), | 288 | hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db), |
318 | hir::ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), | 289 | hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db), |
319 | hir::ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), | 290 | hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db), |
320 | hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), | 291 | hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db), |
321 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), | 292 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db), |
322 | _ => None, | 293 | _ => None, |
323 | } | ||
324 | } | 294 | } |
295 | } | ||
325 | 296 | ||
326 | fn is_deprecated(&self, resolution: &hir::ScopeDef) -> bool { | 297 | fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool { |
327 | match resolution { | 298 | match resolution { |
328 | hir::ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it), | 299 | hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it), |
329 | hir::ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it), | 300 | hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it), |
330 | hir::ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it), | 301 | hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it), |
331 | hir::ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it), | 302 | hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it), |
332 | _ => false, | 303 | _ => false, |
333 | } | ||
334 | } | 304 | } |
335 | } | 305 | } |
336 | 306 | ||