aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_config.rs27
-rw-r--r--crates/ra_assists/src/assist_context.rs257
-rw-r--r--crates/ra_assists/src/assist_ctx.rs257
-rw-r--r--crates/ra_assists/src/ast_transform.rs24
-rw-r--r--crates/ra_assists/src/doc_tests.rs35
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs65
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs37
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs50
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs44
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs355
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs42
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs341
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs71
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs134
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs17
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs109
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs961
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs96
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs55
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs237
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs559
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs23
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs11
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs24
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs95
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs140
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs45
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs38
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs34
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs18
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs54
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs54
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs47
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs10
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs39
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs54
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs24
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs61
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs60
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs16
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs512
-rw-r--r--crates/ra_assists/src/lib.rs307
-rw-r--r--crates/ra_assists/src/marks.rs12
-rw-r--r--crates/ra_assists/src/tests.rs153
-rw-r--r--crates/ra_assists/src/tests/generated.rs (renamed from crates/ra_assists/src/doc_tests/generated.rs)174
-rw-r--r--crates/ra_assists/src/utils.rs94
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs11
-rw-r--r--crates/ra_cfg/src/lib.rs7
-rw-r--r--crates/ra_db/src/fixture.rs60
-rw-r--r--crates/ra_flycheck/Cargo.toml10
-rw-r--r--crates/ra_flycheck/src/conv.rs341
-rw-r--r--crates/ra_flycheck/src/conv/test.rs1072
-rw-r--r--crates/ra_flycheck/src/lib.rs108
-rw-r--r--crates/ra_fmt/src/lib.rs1
-rw-r--r--crates/ra_hir/src/code_model.rs61
-rw-r--r--crates/ra_hir/src/semantics.rs19
-rw-r--r--crates/ra_hir/src/semantics/source_to_def.rs2
-rw-r--r--crates/ra_hir/src/source_analyzer.rs52
-rw-r--r--crates/ra_hir_def/src/adt.rs15
-rw-r--r--crates/ra_hir_def/src/attr.rs1
-rw-r--r--crates/ra_hir_def/src/body.rs11
-rw-r--r--crates/ra_hir_def/src/body/lower.rs63
-rw-r--r--crates/ra_hir_def/src/body/scope.rs4
-rw-r--r--crates/ra_hir_def/src/data.rs194
-rw-r--r--crates/ra_hir_def/src/db.rs22
-rw-r--r--crates/ra_hir_def/src/expr.rs4
-rw-r--r--crates/ra_hir_def/src/find_path.rs105
-rw-r--r--crates/ra_hir_def/src/item_scope.rs11
-rw-r--r--crates/ra_hir_def/src/lib.rs2
-rw-r--r--crates/ra_hir_def/src/marks.rs17
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs29
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs8
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs51
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs10
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs7
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs70
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--crates/ra_hir_def/src/path/lower.rs15
-rw-r--r--crates/ra_hir_def/src/path/lower/lower_use.rs4
-rw-r--r--crates/ra_hir_def/src/resolver.rs18
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs8
-rw-r--r--crates/ra_hir_expand/src/db.rs39
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs31
-rw-r--r--crates/ra_hir_expand/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/_match.rs68
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs28
-rw-r--r--crates/ra_hir_ty/src/display.rs239
-rw-r--r--crates/ra_hir_ty/src/infer.rs148
-rw-r--r--crates/ra_hir_ty/src/infer/coerce.rs36
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs116
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs35
-rw-r--r--crates/ra_hir_ty/src/infer/unify.rs8
-rw-r--r--crates/ra_hir_ty/src/lib.rs30
-rw-r--r--crates/ra_hir_ty/src/lower.rs2
-rw-r--r--crates/ra_hir_ty/src/marks.rs11
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs12
-rw-r--r--crates/ra_hir_ty/src/op.rs3
-rw-r--r--crates/ra_hir_ty/src/tests.rs62
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs50
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs50
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs32
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs46
-rw-r--r--crates/ra_hir_ty/src/tests/never_type.rs177
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs43
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs75
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs182
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs178
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs57
-rw-r--r--crates/ra_ide/src/assists.rs47
-rw-r--r--crates/ra_ide/src/call_info.rs8
-rw-r--r--crates/ra_ide/src/completion.rs5
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs300
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs256
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs63
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs46
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs29
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs14
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs7
-rw-r--r--crates/ra_ide/src/completion/presentation.rs82
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs2
-rw-r--r--crates/ra_ide/src/diagnostics.rs40
-rw-r--r--crates/ra_ide/src/display/function_signature.rs23
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs19
-rw-r--r--crates/ra_ide/src/folding_ranges.rs2
-rw-r--r--crates/ra_ide/src/goto_definition.rs51
-rw-r--r--crates/ra_ide/src/hover.rs66
-rw-r--r--crates/ra_ide/src/inlay_hints.rs74
-rw-r--r--crates/ra_ide/src/join_lines.rs33
-rw-r--r--crates/ra_ide/src/lib.rs65
-rw-r--r--crates/ra_ide/src/marks.rs14
-rw-r--r--crates/ra_ide/src/parent_module.rs8
-rw-r--r--crates/ra_ide/src/references.rs28
-rw-r--r--crates/ra_ide/src/references/rename.rs332
-rw-r--r--crates/ra_ide/src/runnables.rs113
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html4
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html23
-rw-r--r--crates/ra_ide/src/ssr.rs46
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs33
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs21
-rw-r--r--crates/ra_ide/src/syntax_tree.rs77
-rw-r--r--crates/ra_ide/src/test_utils.rs21
-rw-r--r--crates/ra_ide/src/typing.rs31
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs32
-rw-r--r--crates/ra_ide_db/src/defs.rs22
-rw-r--r--crates/ra_ide_db/src/lib.rs3
-rw-r--r--crates/ra_ide_db/src/line_index.rs29
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs302
-rw-r--r--crates/ra_ide_db/src/marks.rs11
-rw-r--r--crates/ra_ide_db/src/search.rs2
-rw-r--r--crates/ra_ide_db/src/source_change.rs (renamed from crates/ra_ide/src/source_change.rs)52
-rw-r--r--crates/ra_mbe/src/lib.rs2
-rw-r--r--crates/ra_mbe/src/mbe_expander/transcriber.rs33
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs2
-rw-r--r--crates/ra_mbe/src/tests.rs152
-rw-r--r--crates/ra_parser/src/grammar.rs4
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs26
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs74
-rw-r--r--crates/ra_parser/src/grammar/items.rs2
-rw-r--r--crates/ra_parser/src/grammar/type_args.rs2
-rw-r--r--crates/ra_parser/src/lib.rs4
-rw-r--r--crates/ra_parser/src/parser.rs2
-rw-r--r--crates/ra_parser/src/syntax_kind/generated.rs2
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml4
-rw-r--r--crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt21
-rw-r--r--crates/ra_proc_macro_srv/src/tests/mod.rs6
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs4
-rw-r--r--crates/ra_project_model/Cargo.toml5
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs35
-rw-r--r--crates/ra_project_model/src/lib.rs108
-rw-r--r--crates/ra_project_model/src/sysroot.rs41
-rw-r--r--crates/ra_syntax/src/algo.rs36
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/edit.rs78
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs33
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs6
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1621
-rw-r--r--crates/ra_syntax/src/ast/make.rs55
-rw-r--r--crates/ra_syntax/src/fuzz.rs6
-rw-r--r--crates/ra_syntax/src/lib.rs22
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs28
-rw-r--r--crates/ra_syntax/src/syntax_node.rs2
-rw-r--r--crates/ra_syntax/src/validation.rs2
-rw-r--r--crates/ra_syntax/src/validation/block.rs20
-rw-r--r--crates/ra_syntax/test_data/parser/err/0005_attribute_recover.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/err/0007_stray_curly_in_file.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0008_item_block_recovery.rast52
-rw-r--r--crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.rast63
-rw-r--r--crates/ra_syntax/test_data/parser/err/0014_where_no_bounds.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0016_missing_semi.rast61
-rw-r--r--crates/ra_syntax/test_data/parser/err/0017_incomplete_binexpr.rast45
-rw-r--r--crates/ra_syntax/test_data/parser/err/0018_incomplete_fn.rast165
-rw-r--r--crates/ra_syntax/test_data/parser/err/0019_let_recover.rast166
-rw-r--r--crates/ra_syntax/test_data/parser/err/0020_fn_recover.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0021_incomplete_param.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/err/0022_bad_exprs.rast229
-rw-r--r--crates/ra_syntax/test_data/parser/err/0023_mismatched_paren.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast430
-rw-r--r--crates/ra_syntax/test_data/parser/err/0025_nope.rast360
-rw-r--r--crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0029_field_completion.rast23
-rw-r--r--crates/ra_syntax/test_data/parser/err/0031_block_inner_attrs.rast214
-rw-r--r--crates/ra_syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast361
-rw-r--r--crates/ra_syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast103
-rw-r--r--crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.rast155
-rw-r--r--crates/ra_syntax/test_data/parser/err/0035_use_recover.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rast31
-rw-r--r--crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.rast121
-rw-r--r--crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rast71
-rw-r--r--crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rs6
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast25
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0006_unsafe_block_in_mod.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0007_async_without_semicolon.rast37
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0008_pub_expr.rast25
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast92
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.rast77
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast71
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0001_trait_item_list.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0005_function_type_params.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0006_self_param.rast25
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0008_path_part.rast169
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0009_loop_expr.rast26
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.rast95
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0015_continue_expr.rast48
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0018_arb_self_types.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0019_unary_expr.rast65
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0021_impl_item_list.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0024_slice_pat.rast59
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0026_tuple_pat_fields.rast185
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0027_ref_pat.rast75
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0029_cast_expr.rast155
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0030_cond.rast365
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rast149
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0034_break_expr.rast84
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0037_qual_paths.rast67
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0038_full_range_expr.rast33
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0039_type_arg.rast13
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0040_crate_keyword_vis.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0042_call_expr.rast271
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0044_block_items.rast34
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0048_path_type_with_bounds.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0053_path_expr.rast167
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0055_literal_pattern.rast129
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0056_where_clause.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0058_range_pat.rast141
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0059_match_arms_commas.rast94
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast225
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0062_mod_contents.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rast229
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rast279
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0070_stmt_bin_expr_ambiguity.rast79
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rast165
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0072_return_expr.rast33
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0074_stmt_postfix_expr_ambiguity.rast98
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0075_block.rast86
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0076_function_where_clause.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0077_try_expr.rast27
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rast167
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast85
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0085_expr_literals.rast247
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0086_function_ret_type.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0088_break_ambiguity.rast112
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0093_index_expr.rast43
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0095_placeholder_pat.rast33
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0096_no_semi_after_block.rast229
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0099_param_list.rast20
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0100_for_expr.rast46
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast241
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0103_array_expr.rast85
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0105_block_expr.rast43
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0105_block_expr.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0106_lambda_expr.rast251
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0107_method_call_expr.rast101
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0108_tuple_expr.rast53
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0109_label.rast108
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0111_tuple_pat.rast155
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rast231
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0113_nocontentexpr.rast90
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0118_match_guard.rast71
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast137
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast267
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast39
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast71
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast172
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast92
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rast231
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0130_try_block_expr.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0132_box_expr.rast155
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0134_nocontentexpr_after_item.rast102
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.rast115
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_expression_after_block.rast106
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0142_for_range_from.rast58
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast197
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0144_dot_dot_pat.rast887
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast127
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0146_as_precedence.rast61
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0155_closure_params.rast115
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_or_pattern.rast199
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.rast50
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.rast64
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast45
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0161_labeled_block.rast26
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0161_labeled_block.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0162_unsafe_block.rast23
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0162_unsafe_block.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0005_fn_item.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0008_mod_item.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0011_outer_attribute.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0012_visibility.rast25
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0017_attr_trailing_comma.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast21
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0025_extern_fn_in_block.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0026_const_fn_in_block.rast38
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0027_unsafe_fn_in_block.rast60
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0028_operator_binding_power.rast347
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0029_range_forms.rast279
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0030_string_suffixes.rast103
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0032_where_for.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0033_label_break.rast407
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0034_crate_path_in_call.rast59
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0035_weird_exprs.rast3912
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0036_fully_qualified.rast27
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0038_where_pred_type.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0039_raw_fn_item.rast7
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0041_raw_keywords.rast77
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0042_ufcs_call_list.rast97
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0043_complex_assignment.rast133
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0044_let_attrs.rast123
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0045_block_inner_attrs.rast196
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0047_minus_in_inner_pattern.rast495
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0048_compound_assignment.rast373
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0049_async_block.rast47
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0050_async_block_as_argument.rast56
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.rast55
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0052_for_range_block.rast135
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0054_qual_path_in_type_arg.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0055_dot_dot_dot.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0056_neq_in_type.rast106
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0057_loop_in_call.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0058_unary_expr_precedence.rast169
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0059_loops_in_parens.rast174
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0060_as_range.rast87
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rast29
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.rast205
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast20
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast20
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast5
-rw-r--r--crates/ra_text_edit/src/lib.rs174
-rw-r--r--crates/ra_text_edit/src/text_edit.rs102
-rw-r--r--crates/ra_toolchain/Cargo.toml8
-rw-r--r--crates/ra_toolchain/src/lib.rs63
-rw-r--r--crates/ra_tt/src/buffer.rs4
-rw-r--r--crates/ra_tt/src/lib.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-rw-r--r--crates/rust-analyzer/src/bin/main.rs7
-rw-r--r--crates/rust-analyzer/src/caps.rs18
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs26
-rw-r--r--crates/rust-analyzer/src/cli.rs1
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs2
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs125
-rw-r--r--crates/rust-analyzer/src/conv.rs727
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs11
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap)8
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap)8
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1399
-rw-r--r--crates/rust-analyzer/src/from_proto.rs42
-rw-r--r--crates/rust-analyzer/src/lib.rs5
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs (renamed from crates/rust-analyzer/src/req.rs)88
-rw-r--r--crates/rust-analyzer/src/main_loop.rs322
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs800
-rw-r--r--crates/rust-analyzer/src/to_proto.rs614
-rw-r--r--crates/rust-analyzer/src/world.rs46
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs251
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs7
-rw-r--r--crates/stdx/src/lib.rs22
-rw-r--r--crates/test_utils/src/lib.rs106
-rw-r--r--crates/test_utils/src/mark.rs (renamed from crates/test_utils/src/marks.rs)46
409 files changed, 23590 insertions, 17345 deletions
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs
new file mode 100644
index 000000000..c0a0226fb
--- /dev/null
+++ b/crates/ra_assists/src/assist_config.rs
@@ -0,0 +1,27 @@
1//! Settings for tweaking assists.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AssistConfig {
9 pub snippet_cap: Option<SnippetCap>,
10}
11
12impl AssistConfig {
13 pub fn allow_snippets(&mut self, yes: bool) {
14 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
15 }
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct SnippetCap {
20 _private: (),
21}
22
23impl Default for AssistConfig {
24 fn default() -> Self {
25 AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) }
26 }
27}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
new file mode 100644
index 000000000..f3af70a3e
--- /dev/null
+++ b/crates/ra_assists/src/assist_context.rs
@@ -0,0 +1,257 @@
1//! See `AssistContext`
2
3use algo::find_covering_element;
4use hir::Semantics;
5use ra_db::{FileId, FileRange};
6use ra_fmt::{leading_indent, reindent};
7use ra_ide_db::{
8 source_change::{SingleFileChange, SourceChange},
9 RootDatabase,
10};
11use ra_syntax::{
12 algo::{self, find_node_at_offset, SyntaxRewriter},
13 AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
14 TokenAtOffset,
15};
16use ra_text_edit::TextEditBuilder;
17
18use crate::{
19 assist_config::{AssistConfig, SnippetCap},
20 Assist, AssistId, GroupLabel, ResolvedAssist,
21};
22
23/// `AssistContext` allows to apply an assist or check if it could be applied.
24///
25/// Assists use a somewhat over-engineered approach, given the current needs.
26/// The assists workflow consists of two phases. In the first phase, a user asks
27/// for the list of available assists. In the second phase, the user picks a
28/// particular assist and it gets applied.
29///
30/// There are two peculiarities here:
31///
32/// * first, we ideally avoid computing more things then necessary to answer "is
33/// assist applicable" in the first phase.
34/// * second, when we are applying assist, we don't have a guarantee that there
35/// weren't any changes between the point when user asked for assists and when
36/// they applied a particular assist. So, when applying assist, we need to do
37/// all the checks from scratch.
38///
39/// To avoid repeating the same code twice for both "check" and "apply"
40/// functions, we use an approach reminiscent of that of Django's function based
41/// views dealing with forms. Each assist receives a runtime parameter,
42/// `resolve`. It first check if an edit is applicable (potentially computing
43/// info required to compute the actual edit). If it is applicable, and
44/// `resolve` is `true`, it then computes the actual edit.
45///
46/// So, to implement the original assists workflow, we can first apply each edit
47/// with `resolve = false`, and then applying the selected edit again, with
48/// `resolve = true` this time.
49///
50/// Note, however, that we don't actually use such two-phase logic at the
51/// moment, because the LSP API is pretty awkward in this place, and it's much
52/// easier to just compute the edit eagerly :-)
53pub(crate) struct AssistContext<'a> {
54 pub(crate) config: &'a AssistConfig,
55 pub(crate) sema: Semantics<'a, RootDatabase>,
56 pub(crate) db: &'a RootDatabase,
57 pub(crate) frange: FileRange,
58 source_file: SourceFile,
59}
60
61impl<'a> AssistContext<'a> {
62 pub(crate) fn new(
63 sema: Semantics<'a, RootDatabase>,
64 config: &'a AssistConfig,
65 frange: FileRange,
66 ) -> AssistContext<'a> {
67 let source_file = sema.parse(frange.file_id);
68 let db = sema.db;
69 AssistContext { config, sema, db, frange, source_file }
70 }
71
72 // NB, this ignores active selection.
73 pub(crate) fn offset(&self) -> TextSize {
74 self.frange.range.start()
75 }
76
77 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
78 self.source_file.syntax().token_at_offset(self.offset())
79 }
80 pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
81 self.token_at_offset().find(|it| it.kind() == kind)
82 }
83 pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
84 find_node_at_offset(self.source_file.syntax(), self.offset())
85 }
86 pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
87 self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
88 }
89 pub(crate) fn covering_element(&self) -> SyntaxElement {
90 find_covering_element(self.source_file.syntax(), self.frange.range)
91 }
92 // FIXME: remove
93 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
94 find_covering_element(self.source_file.syntax(), range)
95 }
96}
97
98pub(crate) struct Assists {
99 resolve: bool,
100 file: FileId,
101 buf: Vec<(Assist, Option<SourceChange>)>,
102}
103
104impl Assists {
105 pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
106 Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() }
107 }
108 pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
109 Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() }
110 }
111
112 pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
113 assert!(!self.resolve);
114 self.finish()
115 .into_iter()
116 .map(|(label, edit)| {
117 assert!(edit.is_none());
118 label
119 })
120 .collect()
121 }
122
123 pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
124 assert!(self.resolve);
125 self.finish()
126 .into_iter()
127 .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
128 .collect()
129 }
130
131 pub(crate) fn add(
132 &mut self,
133 id: AssistId,
134 label: impl Into<String>,
135 target: TextRange,
136 f: impl FnOnce(&mut AssistBuilder),
137 ) -> Option<()> {
138 let label = Assist::new(id, label.into(), None, target);
139 self.add_impl(label, f)
140 }
141 pub(crate) fn add_group(
142 &mut self,
143 group: &GroupLabel,
144 id: AssistId,
145 label: impl Into<String>,
146 target: TextRange,
147 f: impl FnOnce(&mut AssistBuilder),
148 ) -> Option<()> {
149 let label = Assist::new(id, label.into(), Some(group.clone()), target);
150 self.add_impl(label, f)
151 }
152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
153 let change_label = label.label.clone();
154 let source_change = if self.resolve {
155 let mut builder = AssistBuilder::new(self.file);
156 f(&mut builder);
157 Some(builder.finish(change_label))
158 } else {
159 None
160 };
161
162 self.buf.push((label, source_change));
163 Some(())
164 }
165
166 fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
167 self.buf.sort_by_key(|(label, _edit)| label.target.len());
168 self.buf
169 }
170}
171
172pub(crate) struct AssistBuilder {
173 edit: TextEditBuilder,
174 file: FileId,
175 is_snippet: bool,
176}
177
178impl AssistBuilder {
179 pub(crate) fn new(file: FileId) -> AssistBuilder {
180 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
181 }
182
183 /// Remove specified `range` of text.
184 pub(crate) fn delete(&mut self, range: TextRange) {
185 self.edit.delete(range)
186 }
187 /// Append specified `text` at the given `offset`
188 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
189 self.edit.insert(offset, text.into())
190 }
191 /// Append specified `snippet` at the given `offset`
192 pub(crate) fn insert_snippet(
193 &mut self,
194 _cap: SnippetCap,
195 offset: TextSize,
196 snippet: impl Into<String>,
197 ) {
198 self.is_snippet = true;
199 self.insert(offset, snippet);
200 }
201 /// Replaces specified `range` of text with a given string.
202 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
203 self.edit.replace(range, replace_with.into())
204 }
205 /// Replaces specified `range` of text with a given `snippet`.
206 pub(crate) fn replace_snippet(
207 &mut self,
208 _cap: SnippetCap,
209 range: TextRange,
210 snippet: impl Into<String>,
211 ) {
212 self.is_snippet = true;
213 self.replace(range, snippet);
214 }
215 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
216 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
217 }
218 /// Replaces specified `node` of text with a given string, reindenting the
219 /// string to maintain `node`'s existing indent.
220 // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
221 pub(crate) fn replace_node_and_indent(
222 &mut self,
223 node: &SyntaxNode,
224 replace_with: impl Into<String>,
225 ) {
226 let mut replace_with = replace_with.into();
227 if let Some(indent) = leading_indent(node) {
228 replace_with = reindent(&replace_with, &indent)
229 }
230 self.replace(node.text_range(), replace_with)
231 }
232 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
233 let node = rewriter.rewrite_root().unwrap();
234 let new = rewriter.rewrite(&node);
235 algo::diff(&node, &new).into_text_edit(&mut self.edit)
236 }
237
238 // FIXME: better API
239 pub(crate) fn set_file(&mut self, assist_file: FileId) {
240 self.file = assist_file;
241 }
242
243 // FIXME: kill this API
244 /// Get access to the raw `TextEditBuilder`.
245 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
246 &mut self.edit
247 }
248
249 fn finish(self, change_label: String) -> SourceChange {
250 let edit = self.edit.finish();
251 let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file);
252 if self.is_snippet {
253 res.is_snippet = true;
254 }
255 res
256 }
257}
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
deleted file mode 100644
index 2fe7c3de3..000000000
--- a/crates/ra_assists/src/assist_ctx.rs
+++ /dev/null
@@ -1,257 +0,0 @@
1//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2use hir::Semantics;
3use ra_db::FileRange;
4use ra_fmt::{leading_indent, reindent};
5use ra_ide_db::RootDatabase;
6use ra_syntax::{
7 algo::{self, find_covering_element, find_node_at_offset},
8 AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
9 TokenAtOffset,
10};
11use ra_text_edit::TextEditBuilder;
12
13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14use algo::SyntaxRewriter;
15
16#[derive(Clone, Debug)]
17pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
18
19#[derive(Clone, Debug)]
20pub(crate) struct AssistInfo {
21 pub(crate) label: AssistLabel,
22 pub(crate) group_label: Option<GroupLabel>,
23 pub(crate) action: Option<AssistAction>,
24}
25
26impl AssistInfo {
27 fn new(label: AssistLabel) -> AssistInfo {
28 AssistInfo { label, group_label: None, action: None }
29 }
30
31 fn resolved(self, action: AssistAction) -> AssistInfo {
32 AssistInfo { action: Some(action), ..self }
33 }
34
35 fn with_group(self, group_label: GroupLabel) -> AssistInfo {
36 AssistInfo { group_label: Some(group_label), ..self }
37 }
38
39 pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> {
40 let label = self.label;
41 let group_label = self.group_label;
42 self.action.map(|action| ResolvedAssist { label, group_label, action })
43 }
44}
45
46pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
47
48/// `AssistCtx` allows to apply an assist or check if it could be applied.
49///
50/// Assists use a somewhat over-engineered approach, given the current needs. The
51/// assists workflow consists of two phases. In the first phase, a user asks for
52/// the list of available assists. In the second phase, the user picks a
53/// particular assist and it gets applied.
54///
55/// There are two peculiarities here:
56///
57/// * first, we ideally avoid computing more things then necessary to answer
58/// "is assist applicable" in the first phase.
59/// * second, when we are applying assist, we don't have a guarantee that there
60/// weren't any changes between the point when user asked for assists and when
61/// they applied a particular assist. So, when applying assist, we need to do
62/// all the checks from scratch.
63///
64/// To avoid repeating the same code twice for both "check" and "apply"
65/// functions, we use an approach reminiscent of that of Django's function based
66/// views dealing with forms. Each assist receives a runtime parameter,
67/// `should_compute_edit`. It first check if an edit is applicable (potentially
68/// computing info required to compute the actual edit). If it is applicable,
69/// and `should_compute_edit` is `true`, it then computes the actual edit.
70///
71/// So, to implement the original assists workflow, we can first apply each edit
72/// with `should_compute_edit = false`, and then applying the selected edit
73/// again, with `should_compute_edit = true` this time.
74///
75/// Note, however, that we don't actually use such two-phase logic at the
76/// moment, because the LSP API is pretty awkward in this place, and it's much
77/// easier to just compute the edit eagerly :-)
78#[derive(Clone)]
79pub(crate) struct AssistCtx<'a> {
80 pub(crate) sema: &'a Semantics<'a, RootDatabase>,
81 pub(crate) db: &'a RootDatabase,
82 pub(crate) frange: FileRange,
83 source_file: SourceFile,
84 should_compute_edit: bool,
85}
86
87impl<'a> AssistCtx<'a> {
88 pub fn new(
89 sema: &'a Semantics<'a, RootDatabase>,
90 frange: FileRange,
91 should_compute_edit: bool,
92 ) -> AssistCtx<'a> {
93 let source_file = sema.parse(frange.file_id);
94 AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit }
95 }
96
97 pub(crate) fn add_assist(
98 self,
99 id: AssistId,
100 label: impl Into<String>,
101 f: impl FnOnce(&mut ActionBuilder),
102 ) -> Option<Assist> {
103 let label = AssistLabel::new(label.into(), id);
104
105 let mut info = AssistInfo::new(label);
106 if self.should_compute_edit {
107 let action = {
108 let mut edit = ActionBuilder::default();
109 f(&mut edit);
110 edit.build()
111 };
112 info = info.resolved(action)
113 };
114
115 Some(Assist(vec![info]))
116 }
117
118 pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> {
119 AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() }
120 }
121
122 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
123 self.source_file.syntax().token_at_offset(self.frange.range.start())
124 }
125
126 pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
127 self.token_at_offset().find(|it| it.kind() == kind)
128 }
129
130 pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
131 find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
132 }
133 pub(crate) fn covering_element(&self) -> SyntaxElement {
134 find_covering_element(self.source_file.syntax(), self.frange.range)
135 }
136 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
137 find_covering_element(self.source_file.syntax(), range)
138 }
139}
140
141pub(crate) struct AssistGroup<'a> {
142 ctx: AssistCtx<'a>,
143 group_name: String,
144 assists: Vec<AssistInfo>,
145}
146
147impl<'a> AssistGroup<'a> {
148 pub(crate) fn add_assist(
149 &mut self,
150 id: AssistId,
151 label: impl Into<String>,
152 f: impl FnOnce(&mut ActionBuilder),
153 ) {
154 let label = AssistLabel::new(label.into(), id);
155
156 let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone()));
157 if self.ctx.should_compute_edit {
158 let action = {
159 let mut edit = ActionBuilder::default();
160 f(&mut edit);
161 edit.build()
162 };
163 info = info.resolved(action)
164 };
165
166 self.assists.push(info)
167 }
168
169 pub(crate) fn finish(self) -> Option<Assist> {
170 if self.assists.is_empty() {
171 None
172 } else {
173 Some(Assist(self.assists))
174 }
175 }
176}
177
178#[derive(Default)]
179pub(crate) struct ActionBuilder {
180 edit: TextEditBuilder,
181 cursor_position: Option<TextSize>,
182 target: Option<TextRange>,
183 file: AssistFile,
184}
185
186impl ActionBuilder {
187 /// Replaces specified `range` of text with a given string.
188 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
189 self.edit.replace(range, replace_with.into())
190 }
191
192 /// Replaces specified `node` of text with a given string, reindenting the
193 /// string to maintain `node`'s existing indent.
194 // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
195 pub(crate) fn replace_node_and_indent(
196 &mut self,
197 node: &SyntaxNode,
198 replace_with: impl Into<String>,
199 ) {
200 let mut replace_with = replace_with.into();
201 if let Some(indent) = leading_indent(node) {
202 replace_with = reindent(&replace_with, &indent)
203 }
204 self.replace(node.text_range(), replace_with)
205 }
206
207 /// Remove specified `range` of text.
208 #[allow(unused)]
209 pub(crate) fn delete(&mut self, range: TextRange) {
210 self.edit.delete(range)
211 }
212
213 /// Append specified `text` at the given `offset`
214 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
215 self.edit.insert(offset, text.into())
216 }
217
218 /// Specify desired position of the cursor after the assist is applied.
219 pub(crate) fn set_cursor(&mut self, offset: TextSize) {
220 self.cursor_position = Some(offset)
221 }
222
223 /// Specify that the assist should be active withing the `target` range.
224 ///
225 /// Target ranges are used to sort assists: the smaller the target range,
226 /// the more specific assist is, and so it should be sorted first.
227 pub(crate) fn target(&mut self, target: TextRange) {
228 self.target = Some(target)
229 }
230
231 /// Get access to the raw `TextEditBuilder`.
232 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
233 &mut self.edit
234 }
235
236 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
237 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
238 }
239 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
240 let node = rewriter.rewrite_root().unwrap();
241 let new = rewriter.rewrite(&node);
242 algo::diff(&node, &new).into_text_edit(&mut self.edit)
243 }
244
245 pub(crate) fn set_file(&mut self, assist_file: AssistFile) {
246 self.file = assist_file
247 }
248
249 fn build(self) -> AssistAction {
250 AssistAction {
251 edit: self.edit.finish(),
252 cursor_position: self.cursor_position,
253 target: self.target,
254 file: self.file,
255 }
256 }
257}
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 9ac65ab39..3079a02a2 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -1,7 +1,7 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{PathResolution, SemanticsScope}; 4use hir::{HirDisplay, PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::SyntaxRewriter, 7 algo::SyntaxRewriter,
@@ -51,7 +51,27 @@ impl<'a> SubstituteTypeParams<'a> {
51 .into_iter() 51 .into_iter()
52 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky 52 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
53 .skip(1) 53 .skip(1)
54 .zip(substs.into_iter()) 54 // The actual list of trait type parameters may be longer than the one
55 // used in the `impl` block due to trailing default type parametrs.
56 // For that case we extend the `substs` with an empty iterator so we
57 // can still hit those trailing values and check if they actually have
58 // a default type. If they do, go for that type from `hir` to `ast` so
59 // the resulting change can be applied correctly.
60 .zip(substs.into_iter().map(Some).chain(std::iter::repeat(None)))
61 .filter_map(|(k, v)| match v {
62 Some(v) => Some((k, v)),
63 None => {
64 let default = k.default(source_scope.db)?;
65 Some((
66 k,
67 ast::make::type_ref(
68 &default
69 .display_source_code(source_scope.db, source_scope.module()?.into())
70 .ok()?,
71 ),
72 ))
73 }
74 })
55 .collect(); 75 .collect();
56 return SubstituteTypeParams { 76 return SubstituteTypeParams {
57 source_scope, 77 source_scope,
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
deleted file mode 100644
index c0f9bc1fb..000000000
--- a/crates/ra_assists/src/doc_tests.rs
+++ /dev/null
@@ -1,35 +0,0 @@
1//! Each assist definition has a special comment, which specifies docs and
2//! example.
3//!
4//! We collect all the example and write the as tests in this module.
5
6mod generated;
7
8use ra_db::FileRange;
9use test_utils::{assert_eq_text, extract_range_or_offset};
10
11use crate::resolved_assists;
12
13fn check(assist_id: &str, before: &str, after: &str) {
14 let (selection, before) = extract_range_or_offset(before);
15 let (db, file_id) = crate::helpers::with_single_file(&before);
16 let frange = FileRange { file_id, range: selection.into() };
17
18 let assist = resolved_assists(&db, frange)
19 .into_iter()
20 .find(|assist| assist.label.id.0 == assist_id)
21 .unwrap_or_else(|| {
22 panic!(
23 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
24 assist_id,
25 resolved_assists(&db, frange)
26 .into_iter()
27 .map(|assist| assist.label.id.0)
28 .collect::<Vec<_>>()
29 .join(", ")
30 )
31 });
32
33 let actual = assist.action.edit.apply(&before);
34 assert_eq_text!(after, &actual);
35}
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index 4ea26a550..fa70c8496 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -6,7 +6,10 @@ use ra_syntax::{
6}; 6};
7use stdx::SepBy; 7use stdx::SepBy;
8 8
9use crate::{Assist, AssistCtx, AssistId}; 9use crate::{
10 assist_context::{AssistContext, Assists},
11 AssistId,
12};
10 13
11// Assist: add_custom_impl 14// Assist: add_custom_impl
12// 15//
@@ -22,10 +25,10 @@ use crate::{Assist, AssistCtx, AssistId};
22// struct S; 25// struct S;
23// 26//
24// impl Debug for S { 27// impl Debug for S {
25// 28// $0
26// } 29// }
27// ``` 30// ```
28pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { 31pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 let input = ctx.find_node_at_offset::<ast::AttrInput>()?; 32 let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
30 let attr = input.syntax().parent().and_then(ast::Attr::cast)?; 33 let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
31 34
@@ -46,11 +49,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
46 let start_offset = annotated.syntax().parent()?.text_range().end(); 49 let start_offset = annotated.syntax().parent()?.text_range().end();
47 50
48 let label = 51 let label =
49 format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); 52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
50
51 ctx.add_assist(AssistId("add_custom_impl"), label, |edit| {
52 edit.target(attr.syntax().text_range());
53 53
54 let target = attr.syntax().text_range();
55 acc.add(AssistId("add_custom_impl"), label, target, |builder| {
54 let new_attr_input = input 56 let new_attr_input = input
55 .syntax() 57 .syntax()
56 .descendants_with_tokens() 58 .descendants_with_tokens()
@@ -61,20 +63,11 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
61 let has_more_derives = !new_attr_input.is_empty(); 63 let has_more_derives = !new_attr_input.is_empty();
62 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); 64 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
63 65
64 let mut buf = String::new(); 66 if has_more_derives {
65 buf.push_str("\n\nimpl "); 67 builder.replace(input.syntax().text_range(), new_attr_input);
66 buf.push_str(trait_token.text().as_str());
67 buf.push_str(" for ");
68 buf.push_str(annotated_name.as_str());
69 buf.push_str(" {\n");
70
71 let cursor_delta = if has_more_derives {
72 let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input);
73 edit.replace(input.syntax().text_range(), new_attr_input);
74 delta
75 } else { 68 } else {
76 let attr_range = attr.syntax().text_range(); 69 let attr_range = attr.syntax().text_range();
77 edit.delete(attr_range); 70 builder.delete(attr_range);
78 71
79 let line_break_range = attr 72 let line_break_range = attr
80 .syntax() 73 .syntax()
@@ -82,20 +75,30 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
82 .filter(|t| t.kind() == WHITESPACE) 75 .filter(|t| t.kind() == WHITESPACE)
83 .map(|t| t.text_range()) 76 .map(|t| t.text_range())
84 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); 77 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
85 edit.delete(line_break_range); 78 builder.delete(line_break_range);
86 79 }
87 attr_range.len() + line_break_range.len() 80
88 }; 81 match ctx.config.snippet_cap {
89 82 Some(cap) => {
90 edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); 83 builder.insert_snippet(
91 buf.push_str("\n}"); 84 cap,
92 edit.insert(start_offset, buf); 85 start_offset,
86 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name),
87 );
88 }
89 None => {
90 builder.insert(
91 start_offset,
92 format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name),
93 );
94 }
95 }
93 }) 96 })
94} 97}
95 98
96#[cfg(test)] 99#[cfg(test)]
97mod tests { 100mod tests {
98 use crate::helpers::{check_assist, check_assist_not_applicable}; 101 use crate::tests::{check_assist, check_assist_not_applicable};
99 102
100 use super::*; 103 use super::*;
101 104
@@ -115,7 +118,7 @@ struct Foo {
115} 118}
116 119
117impl Debug for Foo { 120impl Debug for Foo {
118<|> 121 $0
119} 122}
120 ", 123 ",
121 ) 124 )
@@ -137,7 +140,7 @@ pub struct Foo {
137} 140}
138 141
139impl Debug for Foo { 142impl Debug for Foo {
140<|> 143 $0
141} 144}
142 ", 145 ",
143 ) 146 )
@@ -156,7 +159,7 @@ struct Foo {}
156struct Foo {} 159struct Foo {}
157 160
158impl Debug for Foo { 161impl Debug for Foo {
159<|> 162 $0
160} 163}
161 ", 164 ",
162 ) 165 )
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index 6254eb7c4..b123b8498 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextSize, 4 TextSize,
5}; 5};
6 6
7use crate::{Assist, AssistCtx, AssistId}; 7use crate::{AssistContext, AssistId, Assists};
8 8
9// Assist: add_derive 9// Assist: add_derive
10// 10//
@@ -18,31 +18,37 @@ use crate::{Assist, AssistCtx, AssistId};
18// ``` 18// ```
19// -> 19// ->
20// ``` 20// ```
21// #[derive()] 21// #[derive($0)]
22// struct Point { 22// struct Point {
23// x: u32, 23// x: u32,
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> { 27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?;
28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
29 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
30 ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| { 31 let target = nominal.syntax().text_range();
32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| {
31 let derive_attr = nominal 33 let derive_attr = nominal
32 .attrs() 34 .attrs()
33 .filter_map(|x| x.as_simple_call()) 35 .filter_map(|x| x.as_simple_call())
34 .filter(|(name, _arg)| name == "derive") 36 .filter(|(name, _arg)| name == "derive")
35 .map(|(_name, arg)| arg) 37 .map(|(_name, arg)| arg)
36 .next(); 38 .next();
37 let offset = match derive_attr { 39 match derive_attr {
38 None => { 40 None => {
39 edit.insert(node_start, "#[derive()]\n"); 41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
40 node_start + TextSize::of("#[derive(") 42 }
43 Some(tt) => {
44 // Just move the cursor.
45 builder.insert_snippet(
46 cap,
47 tt.syntax().text_range().end() - TextSize::of(')'),
48 "$0",
49 )
41 } 50 }
42 Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'),
43 }; 51 };
44 edit.target(nominal.syntax().text_range());
45 edit.set_cursor(offset)
46 }) 52 })
47} 53}
48 54
@@ -57,20 +63,21 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> {
57 63
58#[cfg(test)] 64#[cfg(test)]
59mod tests { 65mod tests {
66 use crate::tests::{check_assist, check_assist_target};
67
60 use super::*; 68 use super::*;
61 use crate::helpers::{check_assist, check_assist_target};
62 69
63 #[test] 70 #[test]
64 fn add_derive_new() { 71 fn add_derive_new() {
65 check_assist( 72 check_assist(
66 add_derive, 73 add_derive,
67 "struct Foo { a: i32, <|>}", 74 "struct Foo { a: i32, <|>}",
68 "#[derive(<|>)]\nstruct Foo { a: i32, }", 75 "#[derive($0)]\nstruct Foo { a: i32, }",
69 ); 76 );
70 check_assist( 77 check_assist(
71 add_derive, 78 add_derive,
72 "struct Foo { <|> a: i32, }", 79 "struct Foo { <|> a: i32, }",
73 "#[derive(<|>)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
74 ); 81 );
75 } 82 }
76 83
@@ -79,7 +86,7 @@ mod tests {
79 check_assist( 86 check_assist(
80 add_derive, 87 add_derive,
81 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
82 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", 89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
83 ); 90 );
84 } 91 }
85 92
@@ -95,7 +102,7 @@ struct Foo { a: i32<|>, }
95 " 102 "
96/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
97/// It does stuff. 104/// It does stuff.
98#[derive(<|>)] 105#[derive($0)]
99struct Foo { a: i32, } 106struct Foo { a: i32, }
100 ", 107 ",
101 ); 108 );
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index bc313782b..ab20c6649 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextRange, 4 TextRange,
5}; 5};
6 6
7use crate::{Assist, AssistCtx, AssistId}; 7use crate::{AssistContext, AssistId, Assists};
8 8
9// Assist: add_explicit_type 9// Assist: add_explicit_type
10// 10//
@@ -21,12 +21,12 @@ use crate::{Assist, AssistCtx, AssistId};
21// let x: i32 = 92; 21// let x: i32 = 92;
22// } 22// }
23// ``` 23// ```
24pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { 24pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let stmt = ctx.find_node_at_offset::<LetStmt>()?; 25 let stmt = ctx.find_node_at_offset::<LetStmt>()?;
26 let module = ctx.sema.scope(stmt.syntax()).module()?;
26 let expr = stmt.initializer()?; 27 let expr = stmt.initializer()?;
27 let pat = stmt.pat()?;
28 // Must be a binding 28 // Must be a binding
29 let pat = match pat { 29 let pat = match stmt.pat()? {
30 ast::Pat::BindPat(bind_pat) => bind_pat, 30 ast::Pat::BindPat(bind_pat) => bind_pat,
31 _ => return None, 31 _ => return None,
32 }; 32 };
@@ -45,7 +45,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
45 // Assist not applicable if the type has already been specified 45 // Assist not applicable if the type has already been specified
46 // and it has no placeholders 46 // and it has no placeholders
47 let ascribed_ty = stmt.ascribed_type(); 47 let ascribed_ty = stmt.ascribed_type();
48 if let Some(ref ty) = ascribed_ty { 48 if let Some(ty) = &ascribed_ty {
49 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { 49 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
50 return None; 50 return None;
51 } 51 }
@@ -57,17 +57,17 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
57 return None; 57 return None;
58 } 58 }
59 59
60 let db = ctx.db; 60 let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?;
61 let new_type_string = ty.display_truncated(db, None).to_string(); 61 acc.add(
62 ctx.add_assist(
63 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type"),
64 format!("Insert explicit type '{}'", new_type_string), 63 format!("Insert explicit type `{}`", inferred_type),
65 |edit| { 64 pat_range,
66 edit.target(pat_range); 65 |builder| match ascribed_ty {
67 if let Some(ascribed_ty) = ascribed_ty { 66 Some(ascribed_ty) => {
68 edit.replace(ascribed_ty.syntax().text_range(), new_type_string); 67 builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
69 } else { 68 }
70 edit.insert(name_range.end(), format!(": {}", new_type_string)); 69 None => {
70 builder.insert(name_range.end(), format!(": {}", inferred_type));
71 } 71 }
72 }, 72 },
73 ) 73 )
@@ -77,7 +77,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
77mod tests { 77mod tests {
78 use super::*; 78 use super::*;
79 79
80 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 80 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
81 81
82 #[test] 82 #[test]
83 fn add_explicit_type_target() { 83 fn add_explicit_type_target() {
@@ -86,11 +86,7 @@ mod tests {
86 86
87 #[test] 87 #[test]
88 fn add_explicit_type_works_for_simple_expr() { 88 fn add_explicit_type_works_for_simple_expr() {
89 check_assist( 89 check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }");
90 add_explicit_type,
91 "fn f() { let a<|> = 1; }",
92 "fn f() { let a<|>: i32 = 1; }",
93 );
94 } 90 }
95 91
96 #[test] 92 #[test]
@@ -98,7 +94,7 @@ mod tests {
98 check_assist( 94 check_assist(
99 add_explicit_type, 95 add_explicit_type,
100 "fn f() { let a<|>: _ = 1; }", 96 "fn f() { let a<|>: _ = 1; }",
101 "fn f() { let a<|>: i32 = 1; }", 97 "fn f() { let a: i32 = 1; }",
102 ); 98 );
103 } 99 }
104 100
@@ -122,7 +118,7 @@ mod tests {
122 } 118 }
123 119
124 fn f() { 120 fn f() {
125 let a<|>: Option<i32> = Option::Some(1); 121 let a: Option<i32> = Option::Some(1);
126 }"#, 122 }"#,
127 ); 123 );
128 } 124 }
@@ -132,7 +128,7 @@ mod tests {
132 check_assist( 128 check_assist(
133 add_explicit_type, 129 add_explicit_type,
134 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", 130 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
135 r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", 131 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
136 ); 132 );
137 } 133 }
138 134
@@ -140,8 +136,8 @@ mod tests {
140 fn add_explicit_type_works_for_macro_call_recursive() { 136 fn add_explicit_type_works_for_macro_call_recursive() {
141 check_assist( 137 check_assist(
142 add_explicit_type, 138 add_explicit_type,
143 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", 139 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#,
144 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", 140 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
145 ); 141 );
146 } 142 }
147 143
@@ -208,7 +204,7 @@ struct Test<K, T = u8> {
208} 204}
209 205
210fn main() { 206fn main() {
211 let test<|>: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23, k: 33 };
212}"#, 208}"#,
213 ); 209 );
214 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 49deb6701..6a675e812 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -1,12 +1,8 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::{ 2use ra_syntax::ast::{self, AstNode, NameOwner};
3 ast::{self, AstNode, NameOwner}, 3use test_utils::mark;
4 TextSize,
5};
6use stdx::format_to;
7 4
8use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
9use test_utils::tested_by;
10 6
11// Assist add_from_impl_for_enum 7// Assist add_from_impl_for_enum
12// 8//
@@ -25,7 +21,7 @@ use test_utils::tested_by;
25// } 21// }
26// } 22// }
27// ``` 23// ```
28pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { 24pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; 25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
30 let variant_name = variant.name()?; 26 let variant_name = variant.name()?;
31 let enum_name = variant.parent_enum().name()?; 27 let enum_name = variant.parent_enum().name()?;
@@ -38,23 +34,23 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
38 } 34 }
39 let field_type = field_list.fields().next()?.type_ref()?; 35 let field_type = field_list.fields().next()?.type_ref()?;
40 let path = match field_type { 36 let path = match field_type {
41 ast::TypeRef::PathType(p) => p, 37 ast::TypeRef::PathType(it) => it,
42 _ => return None, 38 _ => return None,
43 }; 39 };
44 40
45 if existing_from_impl(ctx.sema, &variant).is_some() { 41 if existing_from_impl(&ctx.sema, &variant).is_some() {
46 tested_by!(test_add_from_impl_already_exists); 42 mark::hit!(test_add_from_impl_already_exists);
47 return None; 43 return None;
48 } 44 }
49 45
50 ctx.add_assist( 46 let target = variant.syntax().text_range();
47 acc.add(
51 AssistId("add_from_impl_for_enum"), 48 AssistId("add_from_impl_for_enum"),
52 "Add From impl for this enum variant", 49 "Add From impl for this enum variant",
50 target,
53 |edit| { 51 |edit| {
54 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
55 let mut buf = String::new(); 53 let buf = format!(
56 format_to!(
57 buf,
58 r#" 54 r#"
59 55
60impl From<{0}> for {1} {{ 56impl From<{0}> for {1} {{
@@ -67,7 +63,6 @@ impl From<{0}> for {1} {{
67 variant_name 63 variant_name
68 ); 64 );
69 edit.insert(start_offset, buf); 65 edit.insert(start_offset, buf);
70 edit.set_cursor(start_offset + TextSize::of("\n\n"));
71 }, 66 },
72 ) 67 )
73} 68}
@@ -95,10 +90,11 @@ fn existing_from_impl(
95 90
96#[cfg(test)] 91#[cfg(test)]
97mod tests { 92mod tests {
98 use super::*; 93 use test_utils::mark;
94
95 use crate::tests::{check_assist, check_assist_not_applicable};
99 96
100 use crate::helpers::{check_assist, check_assist_not_applicable}; 97 use super::*;
101 use test_utils::covers;
102 98
103 #[test] 99 #[test]
104 fn test_add_from_impl_for_enum() { 100 fn test_add_from_impl_for_enum() {
@@ -107,7 +103,7 @@ mod tests {
107 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
108 r#"enum A { One(u32) } 104 r#"enum A { One(u32) }
109 105
110<|>impl From<u32> for A { 106impl From<u32> for A {
111 fn from(v: u32) -> Self { 107 fn from(v: u32) -> Self {
112 A::One(v) 108 A::One(v)
113 } 109 }
@@ -119,10 +115,10 @@ mod tests {
119 fn test_add_from_impl_for_enum_complicated_path() { 115 fn test_add_from_impl_for_enum_complicated_path() {
120 check_assist( 116 check_assist(
121 add_from_impl_for_enum, 117 add_from_impl_for_enum,
122 "enum A { <|>One(foo::bar::baz::Boo) }", 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
123 r#"enum A { One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
124 120
125<|>impl From<foo::bar::baz::Boo> for A { 121impl From<foo::bar::baz::Boo> for A {
126 fn from(v: foo::bar::baz::Boo) -> Self { 122 fn from(v: foo::bar::baz::Boo) -> Self {
127 A::One(v) 123 A::One(v)
128 } 124 }
@@ -153,7 +149,7 @@ mod tests {
153 149
154 #[test] 150 #[test]
155 fn test_add_from_impl_already_exists() { 151 fn test_add_from_impl_already_exists() {
156 covers!(test_add_from_impl_already_exists); 152 mark::check!(test_add_from_impl_already_exists);
157 check_not_applicable( 153 check_not_applicable(
158 r#" 154 r#"
159enum A { <|>One(u32), } 155enum A { <|>One(u32), }
@@ -184,7 +180,7 @@ pub trait From<T> {
184}"#, 180}"#,
185 r#"enum A { One(u32), Two(String), } 181 r#"enum A { One(u32), Two(String), }
186 182
187<|>impl From<u32> for A { 183impl From<u32> for A {
188 fn from(v: u32) -> Self { 184 fn from(v: u32) -> Self {
189 A::One(v) 185 A::One(v)
190 } 186 }
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 6c7456579..24f931a85 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -1,13 +1,21 @@
1use hir::HirDisplay;
2use ra_db::FileId;
1use ra_syntax::{ 3use ra_syntax::{
2 ast::{self, AstNode}, 4 ast::{
5 self,
6 edit::{AstNodeEdit, IndentLevel},
7 make, ArgListOwner, AstNode, ModuleItemOwner,
8 },
3 SyntaxKind, SyntaxNode, TextSize, 9 SyntaxKind, SyntaxNode, TextSize,
4}; 10};
5
6use crate::{Assist, AssistCtx, AssistFile, AssistId};
7use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner};
8use hir::HirDisplay;
9use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap, FxHashSet};
10 12
13use crate::{
14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists,
17};
18
11// Assist: add_function 19// Assist: add_function
12// 20//
13// Adds a stub function with a signature matching the function under the cursor. 21// Adds a stub function with a signature matching the function under the cursor.
@@ -29,11 +37,11 @@ use rustc_hash::{FxHashMap, FxHashSet};
29// } 37// }
30// 38//
31// fn bar(arg: &str, baz: Baz) { 39// fn bar(arg: &str, baz: Baz) {
32// todo!() 40// ${0:todo!()}
33// } 41// }
34// 42//
35// ``` 43// ```
36pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { 44pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
37 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; 45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
38 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
39 let path = path_expr.path()?; 47 let path = path_expr.path()?;
@@ -43,36 +51,49 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
43 return None; 51 return None;
44 } 52 }
45 53
46 let target_module = if let Some(qualifier) = path.qualifier() { 54 let target_module = match path.qualifier() {
47 if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = 55 Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
48 ctx.sema.resolve_path(&qualifier) 56 Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module),
49 { 57 _ => return None,
50 Some(module.definition_source(ctx.sema.db)) 58 },
51 } else { 59 None => None,
52 return None;
53 }
54 } else {
55 None
56 }; 60 };
57 61
58 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
59 63
60 ctx.add_assist(AssistId("add_function"), "Add function", |edit| { 64 let target = call.syntax().text_range();
61 edit.target(call.syntax().text_range()); 65 acc.add(AssistId("add_function"), "Add function", target, |builder| {
62 66 let function_template = function_builder.render();
63 if let Some(function_template) = function_builder.render() { 67 builder.set_file(function_template.file);
64 edit.set_file(function_template.file); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
65 edit.set_cursor(function_template.cursor_offset); 69 match ctx.config.snippet_cap {
66 edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); 70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
71 None => builder.insert(function_template.insert_offset, new_fn),
67 } 72 }
68 }) 73 })
69} 74}
70 75
71struct FunctionTemplate { 76struct FunctionTemplate {
72 insert_offset: TextSize, 77 insert_offset: TextSize,
73 cursor_offset: TextSize, 78 placeholder_expr: ast::MacroCall,
74 fn_def: ast::SourceFile, 79 leading_ws: String,
75 file: AssistFile, 80 fn_def: ast::FnDef,
81 trailing_ws: String,
82 file: FileId,
83}
84
85impl FunctionTemplate {
86 fn to_string(&self, cap: Option<SnippetCap>) -> String {
87 let f = match cap {
88 Some(cap) => render_snippet(
89 cap,
90 self.fn_def.syntax(),
91 Cursor::Replace(self.placeholder_expr.syntax()),
92 ),
93 None => self.fn_def.to_string(),
94 };
95 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
96 }
76} 97}
77 98
78struct FunctionBuilder { 99struct FunctionBuilder {
@@ -80,68 +101,73 @@ struct FunctionBuilder {
80 fn_name: ast::Name, 101 fn_name: ast::Name,
81 type_params: Option<ast::TypeParamList>, 102 type_params: Option<ast::TypeParamList>,
82 params: ast::ParamList, 103 params: ast::ParamList,
83 file: AssistFile, 104 file: FileId,
84 needs_pub: bool, 105 needs_pub: bool,
85} 106}
86 107
87impl FunctionBuilder { 108impl FunctionBuilder {
88 /// Prepares a generated function that matches `call` in `generate_in` 109 /// Prepares a generated function that matches `call`.
89 /// (or as close to `call` as possible, if `generate_in` is `None`) 110 /// The function is generated in `target_module` or next to `call`
90 fn from_call( 111 fn from_call(
91 ctx: &AssistCtx, 112 ctx: &AssistContext,
92 call: &ast::CallExpr, 113 call: &ast::CallExpr,
93 path: &ast::Path, 114 path: &ast::Path,
94 target_module: Option<hir::InFile<hir::ModuleSource>>, 115 target_module: Option<hir::Module>,
95 ) -> Option<Self> { 116 ) -> Option<Self> {
96 let needs_pub = target_module.is_some(); 117 let mut file = ctx.frange.file_id;
97 let mut file = AssistFile::default(); 118 let target = match &target_module {
98 let target = if let Some(target_module) = target_module { 119 Some(target_module) => {
99 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; 120 let module_source = target_module.definition_source(ctx.db);
100 file = in_file; 121 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
101 target 122 file = in_file;
102 } else { 123 target
103 next_space_for_fn_after_call_site(&call)? 124 }
125 None => next_space_for_fn_after_call_site(&call)?,
104 }; 126 };
127 let needs_pub = target_module.is_some();
128 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
105 let fn_name = fn_name(&path)?; 129 let fn_name = fn_name(&path)?;
106 let (type_params, params) = fn_args(ctx, &call)?; 130 let (type_params, params) = fn_args(ctx, target_module, &call)?;
131
107 Some(Self { target, fn_name, type_params, params, file, needs_pub }) 132 Some(Self { target, fn_name, type_params, params, file, needs_pub })
108 } 133 }
109 134
110 fn render(self) -> Option<FunctionTemplate> { 135 fn render(self) -> FunctionTemplate {
111 let placeholder_expr = ast::make::expr_todo(); 136 let placeholder_expr = make::expr_todo();
112 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 137 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
113 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 138 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
114 if self.needs_pub { 139 let mut fn_def =
115 fn_def = ast::make::add_pub_crate_modifier(fn_def); 140 make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body);
116 } 141 let leading_ws;
142 let trailing_ws;
117 143
118 let (fn_def, insert_offset) = match self.target { 144 let insert_offset = match self.target {
119 GeneratedFunctionTarget::BehindItem(it) => { 145 GeneratedFunctionTarget::BehindItem(it) => {
120 let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); 146 let indent = IndentLevel::from_node(&it);
121 let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); 147 leading_ws = format!("\n\n{}", indent);
122 (indented, it.text_range().end()) 148 fn_def = fn_def.indent(indent);
149 trailing_ws = String::new();
150 it.text_range().end()
123 } 151 }
124 GeneratedFunctionTarget::InEmptyItemList(it) => { 152 GeneratedFunctionTarget::InEmptyItemList(it) => {
125 let indent_once = IndentLevel(1);
126 let indent = IndentLevel::from_node(it.syntax()); 153 let indent = IndentLevel::from_node(it.syntax());
127 154 leading_ws = format!("\n{}", indent + 1);
128 let fn_def = ast::make::add_leading_newlines(1, fn_def); 155 fn_def = fn_def.indent(indent + 1);
129 let fn_def = indent_once.increase_indent(fn_def); 156 trailing_ws = format!("\n{}", indent);
130 let fn_def = ast::make::add_trailing_newlines(1, fn_def); 157 it.syntax().text_range().start() + TextSize::of('{')
131 let fn_def = indent.increase_indent(fn_def);
132 (fn_def, it.syntax().text_range().start() + TextSize::of('{'))
133 } 158 }
134 }; 159 };
135 160
136 let cursor_offset_from_fn_start = fn_def 161 let placeholder_expr =
137 .syntax() 162 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
138 .descendants() 163 FunctionTemplate {
139 .find_map(ast::MacroCall::cast)? 164 insert_offset,
140 .syntax() 165 placeholder_expr,
141 .text_range() 166 leading_ws,
142 .start(); 167 fn_def,
143 let cursor_offset = insert_offset + cursor_offset_from_fn_start; 168 trailing_ws,
144 Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) 169 file: self.file,
170 }
145 } 171 }
146} 172}
147 173
@@ -150,32 +176,41 @@ enum GeneratedFunctionTarget {
150 InEmptyItemList(ast::ItemList), 176 InEmptyItemList(ast::ItemList),
151} 177}
152 178
179impl GeneratedFunctionTarget {
180 fn syntax(&self) -> &SyntaxNode {
181 match self {
182 GeneratedFunctionTarget::BehindItem(it) => it,
183 GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(),
184 }
185 }
186}
187
153fn fn_name(call: &ast::Path) -> Option<ast::Name> { 188fn fn_name(call: &ast::Path) -> Option<ast::Name> {
154 let name = call.segment()?.syntax().to_string(); 189 let name = call.segment()?.syntax().to_string();
155 Some(ast::make::name(&name)) 190 Some(make::name(&name))
156} 191}
157 192
158/// Computes the type variables and arguments required for the generated function 193/// Computes the type variables and arguments required for the generated function
159fn fn_args( 194fn fn_args(
160 ctx: &AssistCtx, 195 ctx: &AssistContext,
196 target_module: hir::Module,
161 call: &ast::CallExpr, 197 call: &ast::CallExpr,
162) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { 198) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
163 let mut arg_names = Vec::new(); 199 let mut arg_names = Vec::new();
164 let mut arg_types = Vec::new(); 200 let mut arg_types = Vec::new();
165 for arg in call.arg_list()?.args() { 201 for arg in call.arg_list()?.args() {
166 let arg_name = match fn_arg_name(&arg) { 202 arg_names.push(match fn_arg_name(&arg) {
167 Some(name) => name, 203 Some(name) => name,
168 None => String::from("arg"), 204 None => String::from("arg"),
169 }; 205 });
170 arg_names.push(arg_name); 206 arg_types.push(match fn_arg_type(ctx, target_module, &arg) {
171 arg_types.push(match fn_arg_type(ctx, &arg) {
172 Some(ty) => ty, 207 Some(ty) => ty,
173 None => String::from("()"), 208 None => String::from("()"),
174 }); 209 });
175 } 210 }
176 deduplicate_arg_names(&mut arg_names); 211 deduplicate_arg_names(&mut arg_names);
177 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); 212 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty));
178 Some((None, ast::make::param_list(params))) 213 Some((None, make::param_list(params)))
179} 214}
180 215
181/// Makes duplicate argument names unique by appending incrementing numbers. 216/// Makes duplicate argument names unique by appending incrementing numbers.
@@ -224,12 +259,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
224 } 259 }
225} 260}
226 261
227fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> { 262fn fn_arg_type(
263 ctx: &AssistContext,
264 target_module: hir::Module,
265 fn_arg: &ast::Expr,
266) -> Option<String> {
228 let ty = ctx.sema.type_of_expr(fn_arg)?; 267 let ty = ctx.sema.type_of_expr(fn_arg)?;
229 if ty.is_unknown() { 268 if ty.is_unknown() {
230 return None; 269 return None;
231 } 270 }
232 Some(ty.display(ctx.sema.db).to_string()) 271
272 if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) {
273 Some(rendered)
274 } else {
275 None
276 }
233} 277}
234 278
235/// Returns the position inside the current mod or file 279/// Returns the position inside the current mod or file
@@ -258,11 +302,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu
258 302
259fn next_space_for_fn_in_module( 303fn next_space_for_fn_in_module(
260 db: &dyn hir::db::AstDatabase, 304 db: &dyn hir::db::AstDatabase,
261 module: hir::InFile<hir::ModuleSource>, 305 module_source: &hir::InFile<hir::ModuleSource>,
262) -> Option<(AssistFile, GeneratedFunctionTarget)> { 306) -> Option<(FileId, GeneratedFunctionTarget)> {
263 let file = module.file_id.original_file(db); 307 let file = module_source.file_id.original_file(db);
264 let assist_file = AssistFile::TargetFile(file); 308 let assist_item = match &module_source.value {
265 let assist_item = match module.value {
266 hir::ModuleSource::SourceFile(it) => { 309 hir::ModuleSource::SourceFile(it) => {
267 if let Some(last_item) = it.items().last() { 310 if let Some(last_item) = it.items().last() {
268 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) 311 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
@@ -278,12 +321,12 @@ fn next_space_for_fn_in_module(
278 } 321 }
279 } 322 }
280 }; 323 };
281 Some((assist_file, assist_item)) 324 Some((file, assist_item))
282} 325}
283 326
284#[cfg(test)] 327#[cfg(test)]
285mod tests { 328mod tests {
286 use crate::helpers::{check_assist, check_assist_not_applicable}; 329 use crate::tests::{check_assist, check_assist_not_applicable};
287 330
288 use super::*; 331 use super::*;
289 332
@@ -302,7 +345,7 @@ fn foo() {
302} 345}
303 346
304fn bar() { 347fn bar() {
305 <|>todo!() 348 ${0:todo!()}
306} 349}
307", 350",
308 ) 351 )
@@ -329,7 +372,7 @@ impl Foo {
329} 372}
330 373
331fn bar() { 374fn bar() {
332 <|>todo!() 375 ${0:todo!()}
333} 376}
334", 377",
335 ) 378 )
@@ -353,7 +396,7 @@ fn foo1() {
353} 396}
354 397
355fn bar() { 398fn bar() {
356 <|>todo!() 399 ${0:todo!()}
357} 400}
358 401
359fn foo2() {} 402fn foo2() {}
@@ -379,7 +422,7 @@ mod baz {
379 } 422 }
380 423
381 fn bar() { 424 fn bar() {
382 <|>todo!() 425 ${0:todo!()}
383 } 426 }
384} 427}
385", 428",
@@ -405,7 +448,7 @@ fn foo() {
405} 448}
406 449
407fn bar(baz: Baz) { 450fn bar(baz: Baz) {
408 <|>todo!() 451 ${0:todo!()}
409} 452}
410", 453",
411 ); 454 );
@@ -438,7 +481,7 @@ impl Baz {
438} 481}
439 482
440fn bar(baz: Baz) { 483fn bar(baz: Baz) {
441 <|>todo!() 484 ${0:todo!()}
442} 485}
443", 486",
444 ) 487 )
@@ -459,7 +502,7 @@ fn foo() {
459} 502}
460 503
461fn bar(arg: &str) { 504fn bar(arg: &str) {
462 <|>todo!() 505 ${0:todo!()}
463} 506}
464"#, 507"#,
465 ) 508 )
@@ -480,7 +523,7 @@ fn foo() {
480} 523}
481 524
482fn bar(arg: char) { 525fn bar(arg: char) {
483 <|>todo!() 526 ${0:todo!()}
484} 527}
485"#, 528"#,
486 ) 529 )
@@ -501,7 +544,7 @@ fn foo() {
501} 544}
502 545
503fn bar(arg: i32) { 546fn bar(arg: i32) {
504 <|>todo!() 547 ${0:todo!()}
505} 548}
506", 549",
507 ) 550 )
@@ -522,7 +565,7 @@ fn foo() {
522} 565}
523 566
524fn bar(arg: u8) { 567fn bar(arg: u8) {
525 <|>todo!() 568 ${0:todo!()}
526} 569}
527", 570",
528 ) 571 )
@@ -547,7 +590,7 @@ fn foo() {
547} 590}
548 591
549fn bar(x: u8) { 592fn bar(x: u8) {
550 <|>todo!() 593 ${0:todo!()}
551} 594}
552", 595",
553 ) 596 )
@@ -570,7 +613,7 @@ fn foo() {
570} 613}
571 614
572fn bar(worble: ()) { 615fn bar(worble: ()) {
573 <|>todo!() 616 ${0:todo!()}
574} 617}
575", 618",
576 ) 619 )
@@ -599,15 +642,40 @@ fn baz() {
599} 642}
600 643
601fn bar(foo: impl Foo) { 644fn bar(foo: impl Foo) {
602 <|>todo!() 645 ${0:todo!()}
646}
647",
648 )
649 }
650
651 #[test]
652 fn borrowed_arg() {
653 check_assist(
654 add_function,
655 r"
656struct Baz;
657fn baz() -> Baz { todo!() }
658
659fn foo() {
660 bar<|>(&baz())
661}
662",
663 r"
664struct Baz;
665fn baz() -> Baz { todo!() }
666
667fn foo() {
668 bar(&baz())
669}
670
671fn bar(baz: &Baz) {
672 ${0:todo!()}
603} 673}
604", 674",
605 ) 675 )
606 } 676 }
607 677
608 #[test] 678 #[test]
609 #[ignore]
610 // FIXME print paths properly to make this test pass
611 fn add_function_with_qualified_path_arg() { 679 fn add_function_with_qualified_path_arg() {
612 check_assist( 680 check_assist(
613 add_function, 681 add_function,
@@ -616,10 +684,8 @@ mod Baz {
616 pub struct Bof; 684 pub struct Bof;
617 pub fn baz() -> Bof { Bof } 685 pub fn baz() -> Bof { Bof }
618} 686}
619mod Foo { 687fn foo() {
620 fn foo() { 688 <|>bar(Baz::baz())
621 <|>bar(super::Baz::baz())
622 }
623} 689}
624", 690",
625 r" 691 r"
@@ -627,14 +693,12 @@ mod Baz {
627 pub struct Bof; 693 pub struct Bof;
628 pub fn baz() -> Bof { Bof } 694 pub fn baz() -> Bof { Bof }
629} 695}
630mod Foo { 696fn foo() {
631 fn foo() { 697 bar(Baz::baz())
632 bar(super::Baz::baz()) 698}
633 }
634 699
635 fn bar(baz: super::Baz::Bof) { 700fn bar(baz: Baz::Bof) {
636 <|>todo!() 701 ${0:todo!()}
637 }
638} 702}
639", 703",
640 ) 704 )
@@ -657,7 +721,7 @@ fn foo<T>(t: T) {
657} 721}
658 722
659fn bar<T>(t: T) { 723fn bar<T>(t: T) {
660 <|>todo!() 724 ${0:todo!()}
661} 725}
662", 726",
663 ) 727 )
@@ -688,7 +752,7 @@ fn foo() {
688} 752}
689 753
690fn bar(arg: fn() -> Baz) { 754fn bar(arg: fn() -> Baz) {
691 <|>todo!() 755 ${0:todo!()}
692} 756}
693", 757",
694 ) 758 )
@@ -713,7 +777,7 @@ fn foo() {
713} 777}
714 778
715fn bar(closure: impl Fn(i64) -> i64) { 779fn bar(closure: impl Fn(i64) -> i64) {
716 <|>todo!() 780 ${0:todo!()}
717} 781}
718", 782",
719 ) 783 )
@@ -734,7 +798,7 @@ fn foo() {
734} 798}
735 799
736fn bar(baz: ()) { 800fn bar(baz: ()) {
737 <|>todo!() 801 ${0:todo!()}
738} 802}
739", 803",
740 ) 804 )
@@ -759,7 +823,7 @@ fn foo() {
759} 823}
760 824
761fn bar(baz_1: Baz, baz_2: Baz) { 825fn bar(baz_1: Baz, baz_2: Baz) {
762 <|>todo!() 826 ${0:todo!()}
763} 827}
764", 828",
765 ) 829 )
@@ -784,7 +848,7 @@ fn foo() {
784} 848}
785 849
786fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { 850fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
787 <|>todo!() 851 ${0:todo!()}
788} 852}
789"#, 853"#,
790 ) 854 )
@@ -804,7 +868,7 @@ fn foo() {
804 r" 868 r"
805mod bar { 869mod bar {
806 pub(crate) fn my_fn() { 870 pub(crate) fn my_fn() {
807 <|>todo!() 871 ${0:todo!()}
808 } 872 }
809} 873}
810 874
@@ -816,6 +880,40 @@ fn foo() {
816 } 880 }
817 881
818 #[test] 882 #[test]
883 #[ignore]
884 // Ignored until local imports are supported.
885 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
886 fn qualified_path_uses_correct_scope() {
887 check_assist(
888 add_function,
889 "
890mod foo {
891 pub struct Foo;
892}
893fn bar() {
894 use foo::Foo;
895 let foo = Foo;
896 baz<|>(foo)
897}
898",
899 "
900mod foo {
901 pub struct Foo;
902}
903fn bar() {
904 use foo::Foo;
905 let foo = Foo;
906 baz(foo)
907}
908
909fn baz(foo: foo::Foo) {
910 ${0:todo!()}
911}
912",
913 )
914 }
915
916 #[test]
819 fn add_function_in_module_containing_other_items() { 917 fn add_function_in_module_containing_other_items() {
820 check_assist( 918 check_assist(
821 add_function, 919 add_function,
@@ -833,7 +931,7 @@ mod bar {
833 fn something_else() {} 931 fn something_else() {}
834 932
835 pub(crate) fn my_fn() { 933 pub(crate) fn my_fn() {
836 <|>todo!() 934 ${0:todo!()}
837 } 935 }
838} 936}
839 937
@@ -861,7 +959,7 @@ fn foo() {
861mod bar { 959mod bar {
862 mod baz { 960 mod baz {
863 pub(crate) fn my_fn() { 961 pub(crate) fn my_fn() {
864 <|>todo!() 962 ${0:todo!()}
865 } 963 }
866 } 964 }
867} 965}
@@ -890,7 +988,7 @@ fn main() {
890 988
891 989
892pub(crate) fn bar() { 990pub(crate) fn bar() {
893 <|>todo!() 991 ${0:todo!()}
894}", 992}",
895 ) 993 )
896 } 994 }
@@ -927,21 +1025,6 @@ fn bar(baz: ()) {}
927 } 1025 }
928 1026
929 #[test] 1027 #[test]
930 fn add_function_not_applicable_if_function_path_not_singleton() {
931 // In the future this assist could be extended to generate functions
932 // if the path is in the same crate (or even the same workspace).
933 // For the beginning, I think this is fine.
934 check_assist_not_applicable(
935 add_function,
936 r"
937fn foo() {
938 other_crate::bar<|>();
939}
940 ",
941 )
942 }
943
944 #[test]
945 #[ignore] 1028 #[ignore]
946 fn create_method_with_no_args() { 1029 fn create_method_with_no_args() {
947 check_assist( 1030 check_assist(
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index d26f8b93d..eceba7d0a 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -1,10 +1,7 @@
1use ra_syntax::{ 1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2 ast::{self, AstNode, NameOwner, TypeParamsOwner},
3 TextSize,
4};
5use stdx::{format_to, SepBy}; 2use stdx::{format_to, SepBy};
6 3
7use crate::{Assist, AssistCtx, AssistId}; 4use crate::{AssistContext, AssistId, Assists};
8 5
9// Assist: add_impl 6// Assist: add_impl
10// 7//
@@ -12,24 +9,24 @@ use crate::{Assist, AssistCtx, AssistId};
12// 9//
13// ``` 10// ```
14// struct Ctx<T: Clone> { 11// struct Ctx<T: Clone> {
15// data: T,<|> 12// data: T,<|>
16// } 13// }
17// ``` 14// ```
18// -> 15// ->
19// ``` 16// ```
20// struct Ctx<T: Clone> { 17// struct Ctx<T: Clone> {
21// data: T, 18// data: T,
22// } 19// }
23// 20//
24// impl<T: Clone> Ctx<T> { 21// impl<T: Clone> Ctx<T> {
25// 22// $0
26// } 23// }
27// ``` 24// ```
28pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> { 25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let name = nominal.name()?; 27 let name = nominal.name()?;
31 ctx.add_assist(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), |edit| { 28 let target = nominal.syntax().text_range();
32 edit.target(nominal.syntax().text_range()); 29 acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
33 let type_params = nominal.type_param_list(); 30 let type_params = nominal.type_param_list();
34 let start_offset = nominal.syntax().text_range().end(); 31 let start_offset = nominal.syntax().text_range().end();
35 let mut buf = String::new(); 32 let mut buf = String::new();
@@ -50,30 +47,37 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
50 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
51 format_to!(buf, "<{}>", generic_params) 48 format_to!(buf, "<{}>", generic_params)
52 } 49 }
53 buf.push_str(" {\n"); 50 match ctx.config.snippet_cap {
54 edit.set_cursor(start_offset + TextSize::of(&buf)); 51 Some(cap) => {
55 buf.push_str("\n}"); 52 buf.push_str(" {\n $0\n}");
56 edit.insert(start_offset, buf); 53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
57 }) 60 })
58} 61}
59 62
60#[cfg(test)] 63#[cfg(test)]
61mod tests { 64mod tests {
65 use crate::tests::{check_assist, check_assist_target};
66
62 use super::*; 67 use super::*;
63 use crate::helpers::{check_assist, check_assist_target};
64 68
65 #[test] 69 #[test]
66 fn test_add_impl() { 70 fn test_add_impl() {
67 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n"); 71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
68 check_assist( 72 check_assist(
69 add_impl, 73 add_impl,
70 "struct Foo<T: Clone> {<|>}", 74 "struct Foo<T: Clone> {<|>}",
71 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", 75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
72 ); 76 );
73 check_assist( 77 check_assist(
74 add_impl, 78 add_impl,
75 "struct Foo<'a, T: Foo<'a>> {<|>}", 79 "struct Foo<'a, T: Foo<'a>> {<|>}",
76 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", 80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
77 ); 81 );
78 } 82 }
79 83
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 2d6d44980..abacd4065 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -1,13 +1,18 @@
1use hir::HasSource; 1use hir::HasSource;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, edit, make, AstNode, NameOwner}, 3 ast::{
4 self,
5 edit::{self, AstNodeEdit, IndentLevel},
6 make, AstNode, NameOwner,
7 },
4 SmolStr, 8 SmolStr,
5}; 9};
6 10
7use crate::{ 11use crate::{
12 assist_context::{AssistContext, Assists},
8 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
9 utils::{get_missing_impl_items, resolve_target_trait}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
10 Assist, AssistCtx, AssistId, 15 AssistId,
11}; 16};
12 17
13#[derive(PartialEq)] 18#[derive(PartialEq)]
@@ -40,12 +45,15 @@ enum AddMissingImplMembersMode {
40// } 45// }
41// 46//
42// impl Trait<u32> for () { 47// impl Trait<u32> for () {
43// fn foo(&self) -> u32 { todo!() } 48// fn foo(&self) -> u32 {
49// ${0:todo!()}
50// }
44// 51//
45// } 52// }
46// ``` 53// ```
47pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> { 54pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
48 add_missing_impl_members_inner( 55 add_missing_impl_members_inner(
56 acc,
49 ctx, 57 ctx,
50 AddMissingImplMembersMode::NoDefaultMethods, 58 AddMissingImplMembersMode::NoDefaultMethods,
51 "add_impl_missing_members", 59 "add_impl_missing_members",
@@ -81,12 +89,13 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> {
81// impl Trait for () { 89// impl Trait for () {
82// Type X = (); 90// Type X = ();
83// fn foo(&self) {} 91// fn foo(&self) {}
84// fn bar(&self) {} 92// $0fn bar(&self) {}
85// 93//
86// } 94// }
87// ``` 95// ```
88pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> { 96pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
89 add_missing_impl_members_inner( 97 add_missing_impl_members_inner(
98 acc,
90 ctx, 99 ctx,
91 AddMissingImplMembersMode::DefaultMethodsOnly, 100 AddMissingImplMembersMode::DefaultMethodsOnly,
92 "add_impl_default_members", 101 "add_impl_default_members",
@@ -95,36 +104,37 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
95} 104}
96 105
97fn add_missing_impl_members_inner( 106fn add_missing_impl_members_inner(
98 ctx: AssistCtx, 107 acc: &mut Assists,
108 ctx: &AssistContext,
99 mode: AddMissingImplMembersMode, 109 mode: AddMissingImplMembersMode,
100 assist_id: &'static str, 110 assist_id: &'static str,
101 label: &'static str, 111 label: &'static str,
102) -> Option<Assist> { 112) -> Option<()> {
103 let _p = ra_prof::profile("add_missing_impl_members_inner"); 113 let _p = ra_prof::profile("add_missing_impl_members_inner");
104 let impl_node = ctx.find_node_at_offset::<ast::ImplDef>()?; 114 let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?;
105 let impl_item_list = impl_node.item_list()?; 115 let impl_item_list = impl_def.item_list()?;
106 116
107 let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; 117 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
108 118
109 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { 119 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
110 match item { 120 match item {
111 ast::ImplItem::FnDef(def) => def.name(), 121 ast::AssocItem::FnDef(def) => def.name(),
112 ast::ImplItem::TypeAliasDef(def) => def.name(), 122 ast::AssocItem::TypeAliasDef(def) => def.name(),
113 ast::ImplItem::ConstDef(def) => def.name(), 123 ast::AssocItem::ConstDef(def) => def.name(),
114 } 124 }
115 .map(|it| it.text().clone()) 125 .map(|it| it.text().clone())
116 }; 126 };
117 127
118 let missing_items = get_missing_impl_items(&ctx.sema, &impl_node) 128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
119 .iter() 129 .iter()
120 .map(|i| match i { 130 .map(|i| match i {
121 hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), 131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value),
122 hir::AssocItem::TypeAlias(i) => ast::ImplItem::TypeAliasDef(i.source(ctx.db).value), 132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value),
123 hir::AssocItem::Const(i) => ast::ImplItem::ConstDef(i.source(ctx.db).value), 133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value),
124 }) 134 })
125 .filter(|t| def_name(&t).is_some()) 135 .filter(|t| def_name(&t).is_some())
126 .filter(|t| match t { 136 .filter(|t| match t {
127 ast::ImplItem::FnDef(def) => match mode { 137 ast::AssocItem::FnDef(def) => match mode {
128 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), 138 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
129 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), 139 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
130 }, 140 },
@@ -136,44 +146,59 @@ fn add_missing_impl_members_inner(
136 return None; 146 return None;
137 } 147 }
138 148
139 let sema = ctx.sema; 149 let target = impl_def.syntax().text_range();
140 150 acc.add(AssistId(assist_id), label, target, |builder| {
141 ctx.add_assist(AssistId(assist_id), label, |edit| { 151 let n_existing_items = impl_item_list.assoc_items().count();
142 let n_existing_items = impl_item_list.impl_items().count(); 152 let source_scope = ctx.sema.scope_for_def(trait_);
143 let source_scope = sema.scope_for_def(trait_); 153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
144 let target_scope = sema.scope(impl_item_list.syntax());
145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 154 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_node)); 155 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
147 let items = missing_items 156 let items = missing_items
148 .into_iter() 157 .into_iter()
149 .map(|it| ast_transform::apply(&*ast_transform, it)) 158 .map(|it| ast_transform::apply(&*ast_transform, it))
150 .map(|it| match it { 159 .map(|it| match it {
151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), 160 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)),
152 _ => it, 161 _ => it,
153 }) 162 })
154 .map(|it| edit::remove_attrs_and_docs(&it)); 163 .map(|it| edit::remove_attrs_and_docs(&it));
155 let new_impl_item_list = impl_item_list.append_items(items); 164 let new_impl_item_list = impl_item_list.append_items(items);
156 let cursor_position = { 165 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); 166
158 first_new_item.syntax().text_range().start() 167 let original_range = impl_item_list.syntax().text_range();
168 match ctx.config.snippet_cap {
169 None => builder.replace(original_range, new_impl_item_list.to_string()),
170 Some(cap) => {
171 let mut cursor = Cursor::Before(first_new_item.syntax());
172 let placeholder;
173 if let ast::AssocItem::FnDef(func) = &first_new_item {
174 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
175 if m.syntax().text() == "todo!()" {
176 placeholder = m;
177 cursor = Cursor::Replace(placeholder.syntax());
178 }
179 }
180 }
181 builder.replace_snippet(
182 cap,
183 original_range,
184 render_snippet(cap, new_impl_item_list.syntax(), cursor),
185 )
186 }
159 }; 187 };
160
161 edit.replace_ast(impl_item_list, new_impl_item_list);
162 edit.set_cursor(cursor_position);
163 }) 188 })
164} 189}
165 190
166fn add_body(fn_def: ast::FnDef) -> ast::FnDef { 191fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
167 if fn_def.body().is_none() { 192 if fn_def.body().is_some() {
168 fn_def.with_body(make::block_from_expr(make::expr_todo())) 193 return fn_def;
169 } else {
170 fn_def
171 } 194 }
195 let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1));
196 fn_def.with_body(body)
172} 197}
173 198
174#[cfg(test)] 199#[cfg(test)]
175mod tests { 200mod tests {
176 use crate::helpers::{check_assist, check_assist_not_applicable}; 201 use crate::tests::{check_assist, check_assist_not_applicable};
177 202
178 use super::*; 203 use super::*;
179 204
@@ -181,7 +206,7 @@ mod tests {
181 fn test_add_missing_impl_members() { 206 fn test_add_missing_impl_members() {
182 check_assist( 207 check_assist(
183 add_missing_impl_members, 208 add_missing_impl_members,
184 " 209 r#"
185trait Foo { 210trait Foo {
186 type Output; 211 type Output;
187 212
@@ -197,8 +222,8 @@ struct S;
197impl Foo for S { 222impl Foo for S {
198 fn bar(&self) {} 223 fn bar(&self) {}
199<|> 224<|>
200}", 225}"#,
201 " 226 r#"
202trait Foo { 227trait Foo {
203 type Output; 228 type Output;
204 229
@@ -213,12 +238,16 @@ struct S;
213 238
214impl Foo for S { 239impl Foo for S {
215 fn bar(&self) {} 240 fn bar(&self) {}
216 <|>type Output; 241 $0type Output;
217 const CONST: usize = 42; 242 const CONST: usize = 42;
218 fn foo(&self) { todo!() } 243 fn foo(&self) {
219 fn baz(&self) { todo!() } 244 todo!()
245 }
246 fn baz(&self) {
247 todo!()
248 }
220 249
221}", 250}"#,
222 ); 251 );
223 } 252 }
224 253
@@ -226,7 +255,7 @@ impl Foo for S {
226 fn test_copied_overriden_members() { 255 fn test_copied_overriden_members() {
227 check_assist( 256 check_assist(
228 add_missing_impl_members, 257 add_missing_impl_members,
229 " 258 r#"
230trait Foo { 259trait Foo {
231 fn foo(&self); 260 fn foo(&self);
232 fn bar(&self) -> bool { true } 261 fn bar(&self) -> bool { true }
@@ -238,8 +267,8 @@ struct S;
238impl Foo for S { 267impl Foo for S {
239 fn bar(&self) {} 268 fn bar(&self) {}
240<|> 269<|>
241}", 270}"#,
242 " 271 r#"
243trait Foo { 272trait Foo {
244 fn foo(&self); 273 fn foo(&self);
245 fn bar(&self) -> bool { true } 274 fn bar(&self) -> bool { true }
@@ -250,9 +279,11 @@ struct S;
250 279
251impl Foo for S { 280impl Foo for S {
252 fn bar(&self) {} 281 fn bar(&self) {}
253 <|>fn foo(&self) { todo!() } 282 fn foo(&self) {
283 ${0:todo!()}
284 }
254 285
255}", 286}"#,
256 ); 287 );
257 } 288 }
258 289
@@ -260,16 +291,18 @@ impl Foo for S {
260 fn test_empty_impl_def() { 291 fn test_empty_impl_def() {
261 check_assist( 292 check_assist(
262 add_missing_impl_members, 293 add_missing_impl_members,
263 " 294 r#"
264trait Foo { fn foo(&self); } 295trait Foo { fn foo(&self); }
265struct S; 296struct S;
266impl Foo for S { <|> }", 297impl Foo for S { <|> }"#,
267 " 298 r#"
268trait Foo { fn foo(&self); } 299trait Foo { fn foo(&self); }
269struct S; 300struct S;
270impl Foo for S { 301impl Foo for S {
271 <|>fn foo(&self) { todo!() } 302 fn foo(&self) {
272}", 303 ${0:todo!()}
304 }
305}"#,
273 ); 306 );
274 } 307 }
275 308
@@ -277,16 +310,18 @@ impl Foo for S {
277 fn fill_in_type_params_1() { 310 fn fill_in_type_params_1() {
278 check_assist( 311 check_assist(
279 add_missing_impl_members, 312 add_missing_impl_members,
280 " 313 r#"
281trait Foo<T> { fn foo(&self, t: T) -> &T; } 314trait Foo<T> { fn foo(&self, t: T) -> &T; }
282struct S; 315struct S;
283impl Foo<u32> for S { <|> }", 316impl Foo<u32> for S { <|> }"#,
284 " 317 r#"
285trait Foo<T> { fn foo(&self, t: T) -> &T; } 318trait Foo<T> { fn foo(&self, t: T) -> &T; }
286struct S; 319struct S;
287impl Foo<u32> for S { 320impl Foo<u32> for S {
288 <|>fn foo(&self, t: u32) -> &u32 { todo!() } 321 fn foo(&self, t: u32) -> &u32 {
289}", 322 ${0:todo!()}
323 }
324}"#,
290 ); 325 );
291 } 326 }
292 327
@@ -294,16 +329,18 @@ impl Foo<u32> for S {
294 fn fill_in_type_params_2() { 329 fn fill_in_type_params_2() {
295 check_assist( 330 check_assist(
296 add_missing_impl_members, 331 add_missing_impl_members,
297 " 332 r#"
298trait Foo<T> { fn foo(&self, t: T) -> &T; } 333trait Foo<T> { fn foo(&self, t: T) -> &T; }
299struct S; 334struct S;
300impl<U> Foo<U> for S { <|> }", 335impl<U> Foo<U> for S { <|> }"#,
301 " 336 r#"
302trait Foo<T> { fn foo(&self, t: T) -> &T; } 337trait Foo<T> { fn foo(&self, t: T) -> &T; }
303struct S; 338struct S;
304impl<U> Foo<U> for S { 339impl<U> Foo<U> for S {
305 <|>fn foo(&self, t: U) -> &U { todo!() } 340 fn foo(&self, t: U) -> &U {
306}", 341 ${0:todo!()}
342 }
343}"#,
307 ); 344 );
308 } 345 }
309 346
@@ -311,16 +348,18 @@ impl<U> Foo<U> for S {
311 fn test_cursor_after_empty_impl_def() { 348 fn test_cursor_after_empty_impl_def() {
312 check_assist( 349 check_assist(
313 add_missing_impl_members, 350 add_missing_impl_members,
314 " 351 r#"
315trait Foo { fn foo(&self); } 352trait Foo { fn foo(&self); }
316struct S; 353struct S;
317impl Foo for S {}<|>", 354impl Foo for S {}<|>"#,
318 " 355 r#"
319trait Foo { fn foo(&self); } 356trait Foo { fn foo(&self); }
320struct S; 357struct S;
321impl Foo for S { 358impl Foo for S {
322 <|>fn foo(&self) { todo!() } 359 fn foo(&self) {
323}", 360 ${0:todo!()}
361 }
362}"#,
324 ) 363 )
325 } 364 }
326 365
@@ -328,22 +367,24 @@ impl Foo for S {
328 fn test_qualify_path_1() { 367 fn test_qualify_path_1() {
329 check_assist( 368 check_assist(
330 add_missing_impl_members, 369 add_missing_impl_members,
331 " 370 r#"
332mod foo { 371mod foo {
333 pub struct Bar; 372 pub struct Bar;
334 trait Foo { fn foo(&self, bar: Bar); } 373 trait Foo { fn foo(&self, bar: Bar); }
335} 374}
336struct S; 375struct S;
337impl foo::Foo for S { <|> }", 376impl foo::Foo for S { <|> }"#,
338 " 377 r#"
339mod foo { 378mod foo {
340 pub struct Bar; 379 pub struct Bar;
341 trait Foo { fn foo(&self, bar: Bar); } 380 trait Foo { fn foo(&self, bar: Bar); }
342} 381}
343struct S; 382struct S;
344impl foo::Foo for S { 383impl foo::Foo for S {
345 <|>fn foo(&self, bar: foo::Bar) { todo!() } 384 fn foo(&self, bar: foo::Bar) {
346}", 385 ${0:todo!()}
386 }
387}"#,
347 ); 388 );
348 } 389 }
349 390
@@ -351,22 +392,24 @@ impl foo::Foo for S {
351 fn test_qualify_path_generic() { 392 fn test_qualify_path_generic() {
352 check_assist( 393 check_assist(
353 add_missing_impl_members, 394 add_missing_impl_members,
354 " 395 r#"
355mod foo { 396mod foo {
356 pub struct Bar<T>; 397 pub struct Bar<T>;
357 trait Foo { fn foo(&self, bar: Bar<u32>); } 398 trait Foo { fn foo(&self, bar: Bar<u32>); }
358} 399}
359struct S; 400struct S;
360impl foo::Foo for S { <|> }", 401impl foo::Foo for S { <|> }"#,
361 " 402 r#"
362mod foo { 403mod foo {
363 pub struct Bar<T>; 404 pub struct Bar<T>;
364 trait Foo { fn foo(&self, bar: Bar<u32>); } 405 trait Foo { fn foo(&self, bar: Bar<u32>); }
365} 406}
366struct S; 407struct S;
367impl foo::Foo for S { 408impl foo::Foo for S {
368 <|>fn foo(&self, bar: foo::Bar<u32>) { todo!() } 409 fn foo(&self, bar: foo::Bar<u32>) {
369}", 410 ${0:todo!()}
411 }
412}"#,
370 ); 413 );
371 } 414 }
372 415
@@ -374,22 +417,24 @@ impl foo::Foo for S {
374 fn test_qualify_path_and_substitute_param() { 417 fn test_qualify_path_and_substitute_param() {
375 check_assist( 418 check_assist(
376 add_missing_impl_members, 419 add_missing_impl_members,
377 " 420 r#"
378mod foo { 421mod foo {
379 pub struct Bar<T>; 422 pub struct Bar<T>;
380 trait Foo<T> { fn foo(&self, bar: Bar<T>); } 423 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
381} 424}
382struct S; 425struct S;
383impl foo::Foo<u32> for S { <|> }", 426impl foo::Foo<u32> for S { <|> }"#,
384 " 427 r#"
385mod foo { 428mod foo {
386 pub struct Bar<T>; 429 pub struct Bar<T>;
387 trait Foo<T> { fn foo(&self, bar: Bar<T>); } 430 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
388} 431}
389struct S; 432struct S;
390impl foo::Foo<u32> for S { 433impl foo::Foo<u32> for S {
391 <|>fn foo(&self, bar: foo::Bar<u32>) { todo!() } 434 fn foo(&self, bar: foo::Bar<u32>) {
392}", 435 ${0:todo!()}
436 }
437}"#,
393 ); 438 );
394 } 439 }
395 440
@@ -398,15 +443,15 @@ impl foo::Foo<u32> for S {
398 // when substituting params, the substituted param should not be qualified! 443 // when substituting params, the substituted param should not be qualified!
399 check_assist( 444 check_assist(
400 add_missing_impl_members, 445 add_missing_impl_members,
401 " 446 r#"
402mod foo { 447mod foo {
403 trait Foo<T> { fn foo(&self, bar: T); } 448 trait Foo<T> { fn foo(&self, bar: T); }
404 pub struct Param; 449 pub struct Param;
405} 450}
406struct Param; 451struct Param;
407struct S; 452struct S;
408impl foo::Foo<Param> for S { <|> }", 453impl foo::Foo<Param> for S { <|> }"#,
409 " 454 r#"
410mod foo { 455mod foo {
411 trait Foo<T> { fn foo(&self, bar: T); } 456 trait Foo<T> { fn foo(&self, bar: T); }
412 pub struct Param; 457 pub struct Param;
@@ -414,8 +459,10 @@ mod foo {
414struct Param; 459struct Param;
415struct S; 460struct S;
416impl foo::Foo<Param> for S { 461impl foo::Foo<Param> for S {
417 <|>fn foo(&self, bar: Param) { todo!() } 462 fn foo(&self, bar: Param) {
418}", 463 ${0:todo!()}
464 }
465}"#,
419 ); 466 );
420 } 467 }
421 468
@@ -423,15 +470,15 @@ impl foo::Foo<Param> for S {
423 fn test_qualify_path_associated_item() { 470 fn test_qualify_path_associated_item() {
424 check_assist( 471 check_assist(
425 add_missing_impl_members, 472 add_missing_impl_members,
426 " 473 r#"
427mod foo { 474mod foo {
428 pub struct Bar<T>; 475 pub struct Bar<T>;
429 impl Bar<T> { type Assoc = u32; } 476 impl Bar<T> { type Assoc = u32; }
430 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); } 477 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
431} 478}
432struct S; 479struct S;
433impl foo::Foo for S { <|> }", 480impl foo::Foo for S { <|> }"#,
434 " 481 r#"
435mod foo { 482mod foo {
436 pub struct Bar<T>; 483 pub struct Bar<T>;
437 impl Bar<T> { type Assoc = u32; } 484 impl Bar<T> { type Assoc = u32; }
@@ -439,8 +486,10 @@ mod foo {
439} 486}
440struct S; 487struct S;
441impl foo::Foo for S { 488impl foo::Foo for S {
442 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { todo!() } 489 fn foo(&self, bar: foo::Bar<u32>::Assoc) {
443}", 490 ${0:todo!()}
491 }
492}"#,
444 ); 493 );
445 } 494 }
446 495
@@ -448,15 +497,15 @@ impl foo::Foo for S {
448 fn test_qualify_path_nested() { 497 fn test_qualify_path_nested() {
449 check_assist( 498 check_assist(
450 add_missing_impl_members, 499 add_missing_impl_members,
451 " 500 r#"
452mod foo { 501mod foo {
453 pub struct Bar<T>; 502 pub struct Bar<T>;
454 pub struct Baz; 503 pub struct Baz;
455 trait Foo { fn foo(&self, bar: Bar<Baz>); } 504 trait Foo { fn foo(&self, bar: Bar<Baz>); }
456} 505}
457struct S; 506struct S;
458impl foo::Foo for S { <|> }", 507impl foo::Foo for S { <|> }"#,
459 " 508 r#"
460mod foo { 509mod foo {
461 pub struct Bar<T>; 510 pub struct Bar<T>;
462 pub struct Baz; 511 pub struct Baz;
@@ -464,8 +513,10 @@ mod foo {
464} 513}
465struct S; 514struct S;
466impl foo::Foo for S { 515impl foo::Foo for S {
467 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { todo!() } 516 fn foo(&self, bar: foo::Bar<foo::Baz>) {
468}", 517 ${0:todo!()}
518 }
519}"#,
469 ); 520 );
470 } 521 }
471 522
@@ -473,22 +524,24 @@ impl foo::Foo for S {
473 fn test_qualify_path_fn_trait_notation() { 524 fn test_qualify_path_fn_trait_notation() {
474 check_assist( 525 check_assist(
475 add_missing_impl_members, 526 add_missing_impl_members,
476 " 527 r#"
477mod foo { 528mod foo {
478 pub trait Fn<Args> { type Output; } 529 pub trait Fn<Args> { type Output; }
479 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } 530 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
480} 531}
481struct S; 532struct S;
482impl foo::Foo for S { <|> }", 533impl foo::Foo for S { <|> }"#,
483 " 534 r#"
484mod foo { 535mod foo {
485 pub trait Fn<Args> { type Output; } 536 pub trait Fn<Args> { type Output; }
486 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } 537 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
487} 538}
488struct S; 539struct S;
489impl foo::Foo for S { 540impl foo::Foo for S {
490 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { todo!() } 541 fn foo(&self, bar: dyn Fn(u32) -> i32) {
491}", 542 ${0:todo!()}
543 }
544}"#,
492 ); 545 );
493 } 546 }
494 547
@@ -496,10 +549,10 @@ impl foo::Foo for S {
496 fn test_empty_trait() { 549 fn test_empty_trait() {
497 check_assist_not_applicable( 550 check_assist_not_applicable(
498 add_missing_impl_members, 551 add_missing_impl_members,
499 " 552 r#"
500trait Foo; 553trait Foo;
501struct S; 554struct S;
502impl Foo for S { <|> }", 555impl Foo for S { <|> }"#,
503 ) 556 )
504 } 557 }
505 558
@@ -507,13 +560,13 @@ impl Foo for S { <|> }",
507 fn test_ignore_unnamed_trait_members_and_default_methods() { 560 fn test_ignore_unnamed_trait_members_and_default_methods() {
508 check_assist_not_applicable( 561 check_assist_not_applicable(
509 add_missing_impl_members, 562 add_missing_impl_members,
510 " 563 r#"
511trait Foo { 564trait Foo {
512 fn (arg: u32); 565 fn (arg: u32);
513 fn valid(some: u32) -> bool { false } 566 fn valid(some: u32) -> bool { false }
514} 567}
515struct S; 568struct S;
516impl Foo for S { <|> }", 569impl Foo for S { <|> }"#,
517 ) 570 )
518 } 571 }
519 572
@@ -543,8 +596,10 @@ trait Foo {
543} 596}
544struct S; 597struct S;
545impl Foo for S { 598impl Foo for S {
546 <|>type Output; 599 $0type Output;
547 fn foo(&self) { todo!() } 600 fn foo(&self) {
601 todo!()
602 }
548}"#, 603}"#,
549 ) 604 )
550 } 605 }
@@ -553,7 +608,7 @@ impl Foo for S {
553 fn test_default_methods() { 608 fn test_default_methods() {
554 check_assist( 609 check_assist(
555 add_missing_default_members, 610 add_missing_default_members,
556 " 611 r#"
557trait Foo { 612trait Foo {
558 type Output; 613 type Output;
559 614
@@ -563,8 +618,8 @@ trait Foo {
563 fn foo(some: u32) -> bool; 618 fn foo(some: u32) -> bool;
564} 619}
565struct S; 620struct S;
566impl Foo for S { <|> }", 621impl Foo for S { <|> }"#,
567 " 622 r#"
568trait Foo { 623trait Foo {
569 type Output; 624 type Output;
570 625
@@ -575,8 +630,58 @@ trait Foo {
575} 630}
576struct S; 631struct S;
577impl Foo for S { 632impl Foo for S {
578 <|>fn valid(some: u32) -> bool { false } 633 $0fn valid(some: u32) -> bool { false }
579}", 634}"#,
635 )
636 }
637
638 #[test]
639 fn test_generic_single_default_parameter() {
640 check_assist(
641 add_missing_impl_members,
642 r#"
643trait Foo<T = Self> {
644 fn bar(&self, other: &T);
645}
646
647struct S;
648impl Foo for S { <|> }"#,
649 r#"
650trait Foo<T = Self> {
651 fn bar(&self, other: &T);
652}
653
654struct S;
655impl Foo for S {
656 fn bar(&self, other: &Self) {
657 ${0:todo!()}
658 }
659}"#,
660 )
661 }
662
663 #[test]
664 fn test_generic_default_parameter_is_second() {
665 check_assist(
666 add_missing_impl_members,
667 r#"
668trait Foo<T1, T2 = Self> {
669 fn bar(&self, this: &T1, that: &T2);
670}
671
672struct S<T>;
673impl Foo<T> for S<T> { <|> }"#,
674 r#"
675trait Foo<T1, T2 = Self> {
676 fn bar(&self, this: &T1, that: &T2);
677}
678
679struct S<T>;
680impl Foo<T> for S<T> {
681 fn bar(&self, this: &T, that: &Self) {
682 ${0:todo!()}
683 }
684}"#,
580 ) 685 )
581 } 686 }
582} 687}
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index 0f9174a29..837aa8377 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -3,11 +3,11 @@ use ra_syntax::{
3 ast::{ 3 ast::{
4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, 4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
5 }, 5 },
6 TextSize, T, 6 T,
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
10use crate::{Assist, AssistCtx, AssistId}; 10use crate::{AssistContext, AssistId, Assists};
11 11
12// Assist: add_new 12// Assist: add_new
13// 13//
@@ -25,11 +25,11 @@ use crate::{Assist, AssistCtx, AssistId};
25// } 25// }
26// 26//
27// impl<T: Clone> Ctx<T> { 27// impl<T: Clone> Ctx<T> {
28// fn new(data: T) -> Self { Self { data } } 28// fn $0new(data: T) -> Self { Self { data } }
29// } 29// }
30// 30//
31// ``` 31// ```
32pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> { 32pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; 33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
34 34
35 // We want to only apply this to non-union structs with named fields 35 // We want to only apply this to non-union structs with named fields
@@ -41,33 +41,27 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
41 // Return early if we've found an existing new fn 41 // Return early if we've found an existing new fn
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43