aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config18
-rw-r--r--.travis.yml3
-rw-r--r--Cargo.lock130
-rw-r--r--crates/gen_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_analysis/Cargo.toml3
-rw-r--r--crates/ra_analysis/src/completion.rs19
-rw-r--r--crates/ra_analysis/src/completion/complete_keyword.rs67
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_analysis/src/db.rs16
-rw-r--r--crates/ra_analysis/src/extend_selection.rs29
-rw-r--r--crates/ra_analysis/src/imp.rs462
-rw-r--r--crates/ra_analysis/src/lib.rs222
-rw-r--r--crates/ra_analysis/src/macros.rs75
-rw-r--r--crates/ra_analysis/src/runnables.rs86
-rw-r--r--crates/ra_analysis/src/symbol_index.rs155
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs42
-rw-r--r--crates/ra_analysis/tests/test/main.rs (renamed from crates/ra_analysis/tests/tests.rs)34
-rw-r--r--crates/ra_analysis/tests/test/runnables.rs109
-rw-r--r--crates/ra_analysis/tests/test/type_of.rs77
-rw-r--r--crates/ra_cli/src/main.rs2
-rw-r--r--crates/ra_db/src/syntax_ptr.rs4
-rw-r--r--crates/ra_editor/src/assists.rs185
-rw-r--r--crates/ra_editor/src/assists/add_derive.rs84
-rw-r--r--crates/ra_editor/src/assists/add_impl.rs66
-rw-r--r--crates/ra_editor/src/assists/change_visibility.rs88
-rw-r--r--crates/ra_editor/src/assists/flip_comma.rs31
-rw-r--r--crates/ra_editor/src/assists/introduce_variable.rs144
-rw-r--r--crates/ra_editor/src/code_actions.rs415
-rw-r--r--crates/ra_editor/src/diagnostics.rs6
-rw-r--r--crates/ra_editor/src/extend_selection.rs36
-rw-r--r--crates/ra_editor/src/lib.rs76
-rw-r--r--crates/ra_editor/src/structure.rs129
-rw-r--r--crates/ra_editor/src/symbols.rs246
-rw-r--r--crates/ra_editor/src/typing.rs8
-rw-r--r--crates/ra_hir/src/adt.rs3
-rw-r--r--crates/ra_hir/src/db.rs19
-rw-r--r--crates/ra_hir/src/ids.rs287
-rw-r--r--crates/ra_hir/src/krate.rs5
-rw-r--r--crates/ra_hir/src/lib.rs169
-rw-r--r--crates/ra_hir/src/macros.rs199
-rw-r--r--crates/ra_hir/src/mock.rs10
-rw-r--r--crates/ra_hir/src/module.rs30
-rw-r--r--crates/ra_hir/src/module/imp.rs6
-rw-r--r--crates/ra_hir/src/module/nameres.rs91
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs84
-rw-r--r--crates/ra_hir/src/query_definitions.rs73
-rw-r--r--crates/ra_hir/src/source_binder.rs51
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/caps.rs2
-rw-r--r--crates/ra_lsp_server/src/conv.rs61
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs73
-rw-r--r--crates/ra_lsp_server/src/req.rs22
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs23
-rw-r--r--crates/ra_syntax/fuzz/Cargo.lock6
-rw-r--r--crates/ra_syntax/src/ast.rs36
-rw-r--r--crates/ra_syntax/src/ast/generated.rs47
-rw-r--r--crates/ra_syntax/src/grammar.ron15
-rw-r--r--crates/ra_syntax/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/validation/byte.rs4
-rw-r--r--crates/ra_syntax/src/validation/char.rs13
-rw-r--r--crates/ra_syntax/src/yellow/syntax_text.rs6
-rw-r--r--crates/ra_syntax/tests/data/parser/fuzz-failures/0003.rs1
-rw-r--r--crates/ra_syntax/tests/data/parser/fuzz-failures/0004.rs1
-rw-r--r--crates/ra_text_edit/src/text_edit.rs5
-rw-r--r--crates/ra_vfs/src/lib.rs2
-rw-r--r--crates/test_utils/Cargo.toml2
-rw-r--r--crates/tools/src/lib.rs17
-rw-r--r--crates/tools/src/main.rs7
-rw-r--r--editors/code/package-lock.json872
-rw-r--r--editors/code/package.json62
-rw-r--r--editors/code/src/commands/apply_source_change.ts43
72 files changed, 3335 insertions, 2088 deletions
diff --git a/.cargo/config b/.cargo/config
index c319d33f2..b9db30c96 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -1,10 +1,16 @@
1[alias] 1[alias]
2# Automatically generates the ast and syntax kinds files 2# Automatically generates the ast and syntax kinds files
3gen-syntax = "run --package tools --bin tools -- gen-syntax" 3gen-syntax = "run --package tools --bin tools -- gen-syntax"
4gen-tests = "run --package tools --bin tools -- gen-tests" 4# Extracts the tests from
5gen-tests = "run --package tools --bin tools -- gen-tests"
6# Installs the visual studio code extension
5install-code = "run --package tools --bin tools -- install-code" 7install-code = "run --package tools --bin tools -- install-code"
6format = "run --package tools --bin tools -- format" 8# Formats the full repository or installs the git hook to do it automatically.
7format-hook = "run --package tools --bin tools -- format-hook" 9format = "run --package tools --bin tools -- format"
10format-hook = "run --package tools --bin tools -- format-hook"
11# Runs the fuzzing test suite (currently only parser)
12fuzz-tests = "run --package tools --bin tools -- fuzz-tests"
8 13
9render-test = "run --package ra_cli -- render-test" 14render-test = "run --package ra_cli -- render-test"
10parse = "run --package ra_cli -- parse" 15# Parse a file. This should be piped the file contents
16parse = "run --package ra_cli -- parse"
diff --git a/.travis.yml b/.travis.yml
index 04bb3be37..86bce93d6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,6 +10,7 @@ build: &rust_build
10 script: 10 script:
11 - cargo gen-tests --verify 11 - cargo gen-tests --verify
12 - cargo gen-syntax --verify 12 - cargo gen-syntax --verify
13 - cargo test --no-run # let's measure compile time separately
13 - cargo test 14 - cargo test
14 env: 15 env:
15 - RUSTFLAGS="-D warnings", CARGO_INCREMENTAL=0 16 - RUSTFLAGS="-D warnings", CARGO_INCREMENTAL=0
@@ -25,7 +26,7 @@ matrix:
25 - cd editors/code && npm ci && npm run travis 26 - cd editors/code && npm ci && npm run travis
26 27
27 - os: windows 28 - os: windows
28 if: branch = master 29 if: branch = master AND type = push
29 before_script: 30 before_script:
30 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.txt 31 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.txt
31 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.rs 32 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.rs
diff --git a/Cargo.lock b/Cargo.lock
index f06da0050..5f35f2872 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -48,7 +48,7 @@ version = "0.3.13"
48source = "registry+https://github.com/rust-lang/crates.io-index" 48source = "registry+https://github.com/rust-lang/crates.io-index"
49dependencies = [ 49dependencies = [
50 "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 50 "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
51 "backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 51 "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
52 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 52 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
53 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 53 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
54 "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 54 "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -57,10 +57,10 @@ dependencies = [
57 57
58[[package]] 58[[package]]
59name = "backtrace-sys" 59name = "backtrace-sys"
60version = "0.1.26" 60version = "0.1.28"
61source = "registry+https://github.com/rust-lang/crates.io-index" 61source = "registry+https://github.com/rust-lang/crates.io-index"
62dependencies = [ 62dependencies = [
63 "cc 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", 63 "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
64 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 64 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
65] 65]
66 66
@@ -117,14 +117,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
117dependencies = [ 117dependencies = [
118 "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 118 "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
119 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 119 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
120 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 120 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
121 "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 121 "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
122 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 122 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
123] 123]
124 124
125[[package]] 125[[package]]
126name = "cc" 126name = "cc"
127version = "1.0.27" 127version = "1.0.28"
128source = "registry+https://github.com/rust-lang/crates.io-index" 128source = "registry+https://github.com/rust-lang/crates.io-index"
129 129
130[[package]] 130[[package]]
@@ -340,8 +340,8 @@ dependencies = [
340 "failure 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 340 "failure 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
341 "languageserver-types 0.53.1 (registry+https://github.com/rust-lang/crates.io-index)", 341 "languageserver-types 0.53.1 (registry+https://github.com/rust-lang/crates.io-index)",
342 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 342 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
343 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 343 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
344 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 344 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
345] 345]
346 346
347[[package]] 347[[package]]
@@ -425,9 +425,9 @@ dependencies = [
425 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 425 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
426 "num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 426 "num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
427 "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 427 "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
428 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 428 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
429 "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 429 "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
430 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 430 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
431 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 431 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
432 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 432 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
433] 433]
@@ -587,7 +587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
587 587
588[[package]] 588[[package]]
589name = "pest" 589name = "pest"
590version = "2.0.2" 590version = "2.1.0"
591source = "registry+https://github.com/rust-lang/crates.io-index" 591source = "registry+https://github.com/rust-lang/crates.io-index"
592dependencies = [ 592dependencies = [
593 "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 593 "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -595,32 +595,32 @@ dependencies = [
595 595
596[[package]] 596[[package]]
597name = "pest_derive" 597name = "pest_derive"
598version = "2.0.1" 598version = "2.1.0"
599source = "registry+https://github.com/rust-lang/crates.io-index" 599source = "registry+https://github.com/rust-lang/crates.io-index"
600dependencies = [ 600dependencies = [
601 "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 601 "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
602 "pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 602 "pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
603] 603]
604 604
605[[package]] 605[[package]]
606name = "pest_generator" 606name = "pest_generator"
607version = "2.0.0" 607version = "2.1.0"
608source = "registry+https://github.com/rust-lang/crates.io-index" 608source = "registry+https://github.com/rust-lang/crates.io-index"
609dependencies = [ 609dependencies = [
610 "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 610 "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
611 "pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 611 "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
612 "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 612 "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
613 "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 613 "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
614 "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", 614 "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
615] 615]
616 616
617[[package]] 617[[package]]
618name = "pest_meta" 618name = "pest_meta"
619version = "2.0.3" 619version = "2.1.0"
620source = "registry+https://github.com/rust-lang/crates.io-index" 620source = "registry+https://github.com/rust-lang/crates.io-index"
621dependencies = [ 621dependencies = [
622 "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 622 "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
623 "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 623 "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
624 "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 624 "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
625] 625]
626 626
@@ -667,6 +667,7 @@ name = "ra_analysis"
667version = "0.1.0" 667version = "0.1.0"
668dependencies = [ 668dependencies = [
669 "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 669 "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
670 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
670 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 671 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
671 "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 672 "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
672 "ra_db 0.1.0", 673 "ra_db 0.1.0",
@@ -679,6 +680,7 @@ dependencies = [
679 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 680 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
680 "salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)", 681 "salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)",
681 "test_utils 0.1.0", 682 "test_utils 0.1.0",
683 "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
682] 684]
683 685
684[[package]] 686[[package]]
@@ -762,8 +764,8 @@ dependencies = [
762 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 764 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
763 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 765 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
764 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 766 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
765 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 767 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
766 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 768 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
767 "smol_str 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 769 "smol_str 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
768 "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 770 "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
769 "test_utils 0.1.0", 771 "test_utils 0.1.0",
@@ -921,7 +923,7 @@ dependencies = [
921 923
922[[package]] 924[[package]]
923name = "redox_syscall" 925name = "redox_syscall"
924version = "0.1.44" 926version = "0.1.50"
925source = "registry+https://github.com/rust-lang/crates.io-index" 927source = "registry+https://github.com/rust-lang/crates.io-index"
926 928
927[[package]] 929[[package]]
@@ -929,7 +931,7 @@ name = "redox_termios"
929version = "0.1.1" 931version = "0.1.1"
930source = "registry+https://github.com/rust-lang/crates.io-index" 932source = "registry+https://github.com/rust-lang/crates.io-index"
931dependencies = [ 933dependencies = [
932 "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 934 "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)",
933] 935]
934 936
935[[package]] 937[[package]]
@@ -972,7 +974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
972dependencies = [ 974dependencies = [
973 "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 975 "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
974 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 976 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
975 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 977 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
976] 978]
977 979
978[[package]] 980[[package]]
@@ -1060,7 +1062,7 @@ version = "0.9.0"
1060source = "registry+https://github.com/rust-lang/crates.io-index" 1062source = "registry+https://github.com/rust-lang/crates.io-index"
1061dependencies = [ 1063dependencies = [
1062 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1064 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
1063 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1065 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1064] 1066]
1065 1067
1066[[package]] 1068[[package]]
@@ -1070,15 +1072,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1070 1072
1071[[package]] 1073[[package]]
1072name = "serde" 1074name = "serde"
1073version = "1.0.83" 1075version = "1.0.84"
1074source = "registry+https://github.com/rust-lang/crates.io-index" 1076source = "registry+https://github.com/rust-lang/crates.io-index"
1075dependencies = [ 1077dependencies = [
1076 "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 1078 "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1077] 1079]
1078 1080
1079[[package]] 1081[[package]]
1080name = "serde_derive" 1082name = "serde_derive"
1081version = "1.0.82" 1083version = "1.0.84"
1082source = "registry+https://github.com/rust-lang/crates.io-index" 1084source = "registry+https://github.com/rust-lang/crates.io-index"
1083dependencies = [ 1085dependencies = [
1084 "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 1086 "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1088,12 +1090,12 @@ dependencies = [
1088 1090
1089[[package]] 1091[[package]]
1090name = "serde_json" 1092name = "serde_json"
1091version = "1.0.33" 1093version = "1.0.34"
1092source = "registry+https://github.com/rust-lang/crates.io-index" 1094source = "registry+https://github.com/rust-lang/crates.io-index"
1093dependencies = [ 1095dependencies = [
1094 "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 1096 "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
1095 "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 1097 "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
1096 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1098 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1097] 1099]
1098 1100
1099[[package]] 1101[[package]]
@@ -1128,7 +1130,7 @@ name = "smol_str"
1128version = "0.1.8" 1130version = "0.1.8"
1129source = "registry+https://github.com/rust-lang/crates.io-index" 1131source = "registry+https://github.com/rust-lang/crates.io-index"
1130dependencies = [ 1132dependencies = [
1131 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1133 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1132] 1134]
1133 1135
1134[[package]] 1136[[package]]
@@ -1148,16 +1150,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1148 1150
1149[[package]] 1151[[package]]
1150name = "syn" 1152name = "syn"
1151version = "0.14.9"
1152source = "registry+https://github.com/rust-lang/crates.io-index"
1153dependencies = [
1154 "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
1155 "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
1156 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1157]
1158
1159[[package]]
1160name = "syn"
1161version = "0.15.23" 1153version = "0.15.23"
1162source = "registry+https://github.com/rust-lang/crates.io-index" 1154source = "registry+https://github.com/rust-lang/crates.io-index"
1163dependencies = [ 1155dependencies = [
@@ -1185,7 +1177,7 @@ dependencies = [
1185 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 1177 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
1186 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1178 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
1187 "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 1179 "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
1188 "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1180 "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)",
1189 "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1181 "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
1190 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1182 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
1191] 1183]
@@ -1200,11 +1192,11 @@ dependencies = [
1200 "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 1192 "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
1201 "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1193 "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1202 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1194 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
1203 "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1195 "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1204 "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1196 "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1205 "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1197 "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1206 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1198 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1207 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 1199 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
1208 "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 1200 "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
1209 "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1201 "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
1210 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 1202 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1227,7 +1219,7 @@ version = "1.5.1"
1227source = "registry+https://github.com/rust-lang/crates.io-index" 1219source = "registry+https://github.com/rust-lang/crates.io-index"
1228dependencies = [ 1220dependencies = [
1229 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1221 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
1230 "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1222 "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)",
1231 "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1223 "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
1232] 1224]
1233 1225
@@ -1237,7 +1229,7 @@ version = "0.1.0"
1237dependencies = [ 1229dependencies = [
1238 "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1230 "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
1239 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1231 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
1240 "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 1232 "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
1241 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1233 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
1242] 1234]
1243 1235
@@ -1246,7 +1238,7 @@ name = "text_unit"
1246version = "0.1.5" 1238version = "0.1.5"
1247source = "registry+https://github.com/rust-lang/crates.io-index" 1239source = "registry+https://github.com/rust-lang/crates.io-index"
1248dependencies = [ 1240dependencies = [
1249 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1241 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1250] 1242]
1251 1243
1252[[package]] 1244[[package]]
@@ -1288,7 +1280,7 @@ version = "0.1.41"
1288source = "registry+https://github.com/rust-lang/crates.io-index" 1280source = "registry+https://github.com/rust-lang/crates.io-index"
1289dependencies = [ 1281dependencies = [
1290 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1282 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
1291 "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1283 "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)",
1292 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1284 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
1293] 1285]
1294 1286
@@ -1363,6 +1355,14 @@ dependencies = [
1363] 1355]
1364 1356
1365[[package]] 1357[[package]]
1358name = "unicase"
1359version = "2.2.0"
1360source = "registry+https://github.com/rust-lang/crates.io-index"
1361dependencies = [
1362 "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
1363]
1364
1365[[package]]
1366name = "unicode-bidi" 1366name = "unicode-bidi"
1367version = "0.3.4" 1367version = "0.3.4"
1368source = "registry+https://github.com/rust-lang/crates.io-index" 1368source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1413,7 +1413,7 @@ name = "url_serde"
1413version = "0.2.0" 1413version = "0.2.0"
1414source = "registry+https://github.com/rust-lang/crates.io-index" 1414source = "registry+https://github.com/rust-lang/crates.io-index"
1415dependencies = [ 1415dependencies = [
1416 "serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)", 1416 "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
1417 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 1417 "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
1418] 1418]
1419 1419
@@ -1490,7 +1490,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1490"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 1490"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
1491"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" 1491"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
1492"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" 1492"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
1493"checksum backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3fcce89e5ad5c8949caa9434501f7b55415b3e7ad5270cb88c75a8d35e8f1279" 1493"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
1494"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" 1494"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
1495"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a" 1495"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a"
1496"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf" 1496"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"
@@ -1499,7 +1499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1499"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 1499"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
1500"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 1500"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
1501"checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe" 1501"checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe"
1502"checksum cc 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "155ed195f7bd722d1dfeb30365b9d0c1f6a078fa7ca4014497e5935d90993d6f" 1502"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
1503"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" 1503"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
1504"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 1504"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
1505"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" 1505"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
@@ -1557,10 +1557,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1557"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" 1557"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
1558"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" 1558"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
1559"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1559"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
1560"checksum pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a677051ad923732bb5c70f2d45f8985a96e3eee2e2bff86697e3b11b0c3fcfde" 1560"checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3"
1561"checksum pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b76f477146419bc539a63f4ef40e902166cb43b3e51cecc71d9136fd12c567e7" 1561"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
1562"checksum pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebee4e9680be4fd162e6f3394ae4192a6b60b1e4d17d845e631f0c68d1a3386" 1562"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646"
1563"checksum pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6d5f6f0e6082578c86af197d780dc38328e3f768cec06aac9bc46d714e8221" 1563"checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e"
1564"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 1564"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
1565"checksum proptest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "926d0604475349f463fe44130aae73f2294b5309ab2ca0310b998bd334ef191f" 1565"checksum proptest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "926d0604475349f463fe44130aae73f2294b5309ab2ca0310b998bd334ef191f"
1566"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" 1566"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
@@ -1576,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1576"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" 1576"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
1577"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" 1577"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473"
1578"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" 1578"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
1579"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" 1579"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2"
1580"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 1580"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
1581"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" 1581"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
1582"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" 1582"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
@@ -1595,9 +1595,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1595"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1595"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
1596"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1596"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1597"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1597"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1598"checksum serde 1.0.83 (registry+https://github.com/rust-lang/crates.io-index)" = "157e12af46859e968da75dea9845530e13d03bcab2009a41b9b7bb3cf4eb3ec2" 1598"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7"
1599"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154" 1599"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b"
1600"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" 1600"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545"
1601"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" 1601"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded"
1602"checksum slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" 1602"checksum slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373"
1603"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" 1603"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db"
@@ -1605,7 +1605,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1605"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 1605"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
1606"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 1606"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
1607"checksum superslice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b50b13d42370e0f5fc62eafdd5c2d20065eaf5458dab215ff3e20e63eea96b30" 1607"checksum superslice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b50b13d42370e0f5fc62eafdd5c2d20065eaf5458dab215ff3e20e63eea96b30"
1608"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
1609"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" 1608"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
1610"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 1609"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
1611"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" 1610"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2"
@@ -1626,6 +1625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1626"checksum unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ca47cbb09fb5fcd066b5867d11dc528302fa465277882797d6a836e1ee6f9e" 1625"checksum unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ca47cbb09fb5fcd066b5867d11dc528302fa465277882797d6a836e1ee6f9e"
1627"checksum unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48f1a08ce0409a9e391b88d1930118eec48af12742fc538bcec55f775865776e" 1626"checksum unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48f1a08ce0409a9e391b88d1930118eec48af12742fc538bcec55f775865776e"
1628"checksum unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1f5e6c6c53c2d0ece4a5964bc55fcff8602153063cb4fab20958ff32998ff6" 1627"checksum unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1f5e6c6c53c2d0ece4a5964bc55fcff8602153063cb4fab20958ff32998ff6"
1628"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90"
1629"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1629"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
1630"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" 1630"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
1631"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" 1631"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml
index ff475f196..a421a3e0b 100644
--- a/crates/gen_lsp_server/Cargo.toml
+++ b/crates/gen_lsp_server/Cargo.toml
@@ -11,6 +11,6 @@ description = "Generic LSP server scaffold."
11languageserver-types = "0.53.1" 11languageserver-types = "0.53.1"
12log = "0.4.3" 12log = "0.4.3"
13failure = "0.1.4" 13failure = "0.1.4"
14serde_json = "1.0.24" 14serde_json = "1.0.34"
15serde = { version = "1.0.83", features = ["derive"] } 15serde = { version = "1.0.83", features = ["derive"] }
16crossbeam-channel = "0.3.5" 16crossbeam-channel = "0.3.5"
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml
index a5d4f65ab..7a4fdaed9 100644
--- a/crates/ra_analysis/Cargo.toml
+++ b/crates/ra_analysis/Cargo.toml
@@ -5,6 +5,7 @@ version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8itertools = "0.8.0"
8log = "0.4.5" 9log = "0.4.5"
9relative-path = "0.4.0" 10relative-path = "0.4.0"
10rayon = "1.0.2" 11rayon = "1.0.2"
@@ -12,6 +13,8 @@ fst = "0.3.1"
12salsa = "0.9.0" 13salsa = "0.9.0"
13rustc-hash = "1.0" 14rustc-hash = "1.0"
14parking_lot = "0.7.0" 15parking_lot = "0.7.0"
16unicase = "2.2.0"
17
15ra_syntax = { path = "../ra_syntax" } 18ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 19ra_editor = { path = "../ra_editor" }
17ra_text_edit = { path = "../ra_text_edit" } 20ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index fe580700f..ce777a771 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -28,7 +28,21 @@ pub use crate::completion::completion_item::{CompletionItem, InsertText, Complet
28/// incomplete and can look really weird. 28/// incomplete and can look really weird.
29/// 29///
30/// Once the context is collected, we run a series of completion routines which 30/// Once the context is collected, we run a series of completion routines which
31/// look at the context and produce completion items. 31/// look at the context and produce completion items. One subtelty about this
32/// phase is that completion engine should not filter by the substring which is
33/// already present, it should give all possible variants for the identifier at
34/// the caret. In other words, for
35///
36/// ```no-run
37/// fn f() {
38/// let foo = 92;
39/// let _ = bar<|>
40/// }
41/// ```
42///
43/// `foo` *should* be present among the completion variants. Filtering by
44/// identifier prefix/fuzzy match should be done higher in the stack, together
45/// with ordering of completions (currently this is done by the client).
32pub(crate) fn completions( 46pub(crate) fn completions(
33 db: &db::RootDatabase, 47 db: &db::RootDatabase,
34 position: FilePosition, 48 position: FilePosition,
@@ -40,6 +54,7 @@ pub(crate) fn completions(
40 54
41 complete_fn_param::complete_fn_param(&mut acc, &ctx); 55 complete_fn_param::complete_fn_param(&mut acc, &ctx);
42 complete_keyword::complete_expr_keyword(&mut acc, &ctx); 56 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
57 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
43 complete_snippet::complete_expr_snippet(&mut acc, &ctx); 58 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
44 complete_snippet::complete_item_snippet(&mut acc, &ctx); 59 complete_snippet::complete_item_snippet(&mut acc, &ctx);
45 complete_path::complete_path(&mut acc, &ctx)?; 60 complete_path::complete_path(&mut acc, &ctx)?;
@@ -57,6 +72,6 @@ fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind
57 } else { 72 } else {
58 single_file_with_position(code) 73 single_file_with_position(code)
59 }; 74 };
60 let completions = completions(&analysis.imp.db, position).unwrap().unwrap(); 75 let completions = completions(&analysis.db, position).unwrap().unwrap();
61 completions.assert_match(expected_completions, kind); 76 completions.assert_match(expected_completions, kind);
62} 77}
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
index d70fdaada..28194c908 100644
--- a/crates/ra_analysis/src/completion/complete_keyword.rs
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -7,6 +7,38 @@ use ra_syntax::{
7 7
8use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind}; 8use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};
9 9
10pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 // complete keyword "crate" in use stmt
12 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
13 (Some(_), None) => {
14 CompletionItem::new(CompletionKind::Keyword, "crate")
15 .kind(CompletionItemKind::Keyword)
16 .lookup_by("crate")
17 .snippet("crate::")
18 .add_to(acc);
19 CompletionItem::new(CompletionKind::Keyword, "self")
20 .kind(CompletionItemKind::Keyword)
21 .lookup_by("self")
22 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, "super")
24 .kind(CompletionItemKind::Keyword)
25 .lookup_by("super")
26 .add_to(acc);
27 }
28 (Some(_), Some(_)) => {
29 CompletionItem::new(CompletionKind::Keyword, "self")
30 .kind(CompletionItemKind::Keyword)
31 .lookup_by("self")
32 .add_to(acc);
33 CompletionItem::new(CompletionKind::Keyword, "super")
34 .kind(CompletionItemKind::Keyword)
35 .lookup_by("super")
36 .add_to(acc);
37 }
38 _ => {}
39 }
40}
41
10fn keyword(kw: &str, snippet: &str) -> CompletionItem { 42fn keyword(kw: &str, snippet: &str) -> CompletionItem {
11 CompletionItem::new(CompletionKind::Keyword, kw) 43 CompletionItem::new(CompletionKind::Keyword, kw)
12 .kind(CompletionItemKind::Keyword) 44 .kind(CompletionItemKind::Keyword)
@@ -18,6 +50,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
18 if !ctx.is_trivial_path { 50 if !ctx.is_trivial_path {
19 return; 51 return;
20 } 52 }
53
21 let fn_def = match ctx.function_syntax { 54 let fn_def = match ctx.function_syntax {
22 Some(it) => it, 55 Some(it) => it,
23 None => return, 56 None => return,
@@ -80,6 +113,40 @@ mod tests {
80 } 113 }
81 114
82 #[test] 115 #[test]
116 fn completes_keywords_in_use_stmt() {
117 check_keyword_completion(
118 r"
119 use <|>
120 ",
121 r#"
122 crate "crate" "crate::"
123 self "self"
124 super "super"
125 "#,
126 );
127
128 check_keyword_completion(
129 r"
130 use a::<|>
131 ",
132 r#"
133 self "self"
134 super "super"
135 "#,
136 );
137
138 check_keyword_completion(
139 r"
140 use a::{b, <|>}
141 ",
142 r#"
143 self "self"
144 super "super"
145 "#,
146 );
147 }
148
149 #[test]
83 fn completes_various_keywords_in_function() { 150 fn completes_various_keywords_in_function() {
84 check_keyword_completion( 151 check_keyword_completion(
85 r" 152 r"
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index daf666505..4dead3689 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
27 match res.import { 27 match res.import {
28 None => true, 28 None => true,
29 Some(import) => { 29 Some(import) => {
30 let range = import.range(ctx.db, module.source().file_id()); 30 let range = import.range(ctx.db, module.file_id());
31 !range.is_subrange(&ctx.leaf.range()) 31 !range.is_subrange(&ctx.leaf.range())
32 } 32 }
33 } 33 }
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index b072a5eba..d7740f0c4 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,7 +1,6 @@
1use std::{fmt, sync::Arc}; 1use std::{fmt, sync::Arc};
2use salsa::{self, Database}; 2use salsa::{self, Database};
3use ra_db::{LocationIntener, BaseDatabase}; 3use ra_db::{LocationIntener, BaseDatabase};
4use hir::{self, DefId, DefLoc};
5 4
6use crate::{ 5use crate::{
7 symbol_index, 6 symbol_index,
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase {
15 14
16#[derive(Default)] 15#[derive(Default)]
17struct IdMaps { 16struct IdMaps {
18 defs: LocationIntener<DefLoc, DefId>, 17 defs: LocationIntener<hir::DefLoc, hir::DefId>,
18 macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>,
19} 19}
20 20
21impl fmt::Debug for IdMaps { 21impl fmt::Debug for IdMaps {
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase {
59 59
60impl BaseDatabase for RootDatabase {} 60impl BaseDatabase for RootDatabase {}
61 61
62impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { 62impl AsRef<LocationIntener<hir::DefLoc, hir::DefId>> for RootDatabase {
63 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { 63 fn as_ref(&self) -> &LocationIntener<hir::DefLoc, hir::DefId> {
64 &self.id_maps.defs 64 &self.id_maps.defs
65 } 65 }
66} 66}
67 67
68impl AsRef<LocationIntener<hir::MacroCallLoc, hir::MacroCallId>> for RootDatabase {
69 fn as_ref(&self) -> &LocationIntener<hir::MacroCallLoc, hir::MacroCallId> {
70 &self.id_maps.macros
71 }
72}
73
68salsa::database_storage! { 74salsa::database_storage! {
69 pub(crate) struct RootDatabaseStorage for RootDatabase { 75 pub(crate) struct RootDatabaseStorage for RootDatabase {
70 impl ra_db::FilesDatabase { 76 impl ra_db::FilesDatabase {
@@ -85,6 +91,8 @@ salsa::database_storage! {
85 fn library_symbols() for symbol_index::LibrarySymbolsQuery; 91 fn library_symbols() for symbol_index::LibrarySymbolsQuery;
86 } 92 }
87 impl hir::db::HirDatabase { 93 impl hir::db::HirDatabase {
94 fn hir_source_file() for hir::db::HirSourceFileQuery;
95 fn expand_macro_invocation() for hir::db::ExpandMacroCallQuery;
88 fn module_tree() for hir::db::ModuleTreeQuery; 96 fn module_tree() for hir::db::ModuleTreeQuery;
89 fn fn_scopes() for hir::db::FnScopesQuery; 97 fn fn_scopes() for hir::db::FnScopesQuery;
90 fn file_items() for hir::db::SourceFileItemsQuery; 98 fn file_items() for hir::db::SourceFileItemsQuery;
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
index cde6ee101..f1b77f981 100644
--- a/crates/ra_analysis/src/extend_selection.rs
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -1,6 +1,6 @@
1use ra_db::SyntaxDatabase; 1use ra_db::SyntaxDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNodeRef, AstNode, 3 SyntaxNodeRef, AstNode, SourceFileNode,
4 ast, algo::find_covering_node, 4 ast, algo::find_covering_node,
5}; 5};
6 6
@@ -11,18 +11,23 @@ use crate::{
11 11
12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
13 let source_file = db.source_file(frange.file_id); 13 let source_file = db.source_file(frange.file_id);
14 if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { 14 if let Some(range) = extend_selection_in_macro(db, &source_file, frange) {
15 if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { 15 return range;
16 if let Some(dst_range) = exp.map_range_forward(frange.range) {
17 if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) {
18 if let Some(src_range) = exp.map_range_back(dst_range) {
19 return src_range;
20 }
21 }
22 }
23 }
24 } 16 }
25 ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) 17 ra_editor::extend_selection(source_file.syntax(), frange.range).unwrap_or(frange.range)
18}
19
20fn extend_selection_in_macro(
21 _db: &RootDatabase,
22 source_file: &SourceFileNode,
23 frange: FileRange,
24) -> Option<TextRange> {
25 let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
26 let (off, exp) = hir::MacroDef::ast_expand(macro_call)?;
27 let dst_range = exp.map_range_forward(frange.range - off)?;
28 let dst_range = ra_editor::extend_selection(exp.syntax().borrowed(), dst_range)?;
29 let src_range = exp.map_range_back(dst_range)? + off;
30 Some(src_range)
26} 31}
27 32
28fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> { 33fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> {
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 5ed374c79..eae73c2c4 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -1,59 +1,42 @@
1use std::{ 1use std::sync::Arc;
2 fmt,
3 sync::Arc,
4};
5 2
6use rayon::prelude::*; 3use salsa::Database;
7use salsa::{Database, ParallelDatabase};
8 4
9use hir::{ 5use hir::{
10 self, FnSignatureInfo, Problem, source_binder, 6 self, FnSignatureInfo, Problem, source_binder,
11}; 7};
12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 8use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
13use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; 9use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity};
14use ra_syntax::{ 10use ra_syntax::{
15 algo::find_covering_node, 11 algo::{find_covering_node, visit::{visitor, Visitor}},
16 ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, 12 ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
17 AstNode, SourceFileNode, 13 AstNode, SourceFileNode,
18 SyntaxKind::*, 14 SyntaxKind::*,
19 SyntaxNodeRef, TextRange, TextUnit, 15 SyntaxNode, SyntaxNodeRef, TextRange, TextUnit,
20}; 16};
21 17
22use crate::{ 18use crate::{
23 AnalysisChange, 19 AnalysisChange,
24 Cancelable, 20 Cancelable, NavigationTarget,
25 completion::{CompletionItem, completions},
26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, 21 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 22 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, 23 symbol_index::{LibrarySymbolsQuery, FileSymbol},
29}; 24};
30 25
31#[derive(Debug, Default)] 26impl db::RootDatabase {
32pub(crate) struct AnalysisHostImpl { 27 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
33 db: db::RootDatabase,
34}
35
36impl AnalysisHostImpl {
37 pub fn analysis(&self) -> AnalysisImpl {
38 AnalysisImpl {
39 db: self.db.snapshot(),
40 }
41 }
42 pub fn apply_change(&mut self, change: AnalysisChange) {
43 log::info!("apply_change {:?}", change); 28 log::info!("apply_change {:?}", change);
44 // self.gc_syntax_trees(); 29 // self.gc_syntax_trees();
45 if !change.new_roots.is_empty() { 30 if !change.new_roots.is_empty() {
46 let mut local_roots = Vec::clone(&self.db.local_roots()); 31 let mut local_roots = Vec::clone(&self.local_roots());
47 for (root_id, is_local) in change.new_roots { 32 for (root_id, is_local) in change.new_roots {
48 self.db 33 self.query_mut(ra_db::SourceRootQuery)
49 .query_mut(ra_db::SourceRootQuery)
50 .set(root_id, Default::default()); 34 .set(root_id, Default::default());
51 if is_local { 35 if is_local {
52 local_roots.push(root_id); 36 local_roots.push(root_id);
53 } 37 }
54 } 38 }
55 self.db 39 self.query_mut(ra_db::LocalRootsQuery)
56 .query_mut(ra_db::LocalRootsQuery)
57 .set((), Arc::new(local_roots)); 40 .set((), Arc::new(local_roots));
58 } 41 }
59 42
@@ -61,53 +44,44 @@ impl AnalysisHostImpl {
61 self.apply_root_change(root_id, root_change); 44 self.apply_root_change(root_id, root_change);
62 } 45 }
63 for (file_id, text) in change.files_changed { 46 for (file_id, text) in change.files_changed {
64 self.db.query_mut(ra_db::FileTextQuery).set(file_id, text) 47 self.query_mut(ra_db::FileTextQuery).set(file_id, text)
65 } 48 }
66 if !change.libraries_added.is_empty() { 49 if !change.libraries_added.is_empty() {
67 let mut libraries = Vec::clone(&self.db.library_roots()); 50 let mut libraries = Vec::clone(&self.library_roots());
68 for library in change.libraries_added { 51 for library in change.libraries_added {
69 libraries.push(library.root_id); 52 libraries.push(library.root_id);
70 self.db 53 self.query_mut(ra_db::SourceRootQuery)
71 .query_mut(ra_db::SourceRootQuery)
72 .set(library.root_id, Default::default()); 54 .set(library.root_id, Default::default());
73 self.db 55 self.query_mut(LibrarySymbolsQuery)
74 .query_mut(LibrarySymbolsQuery)
75 .set_constant(library.root_id, Arc::new(library.symbol_index)); 56 .set_constant(library.root_id, Arc::new(library.symbol_index));
76 self.apply_root_change(library.root_id, library.root_change); 57 self.apply_root_change(library.root_id, library.root_change);
77 } 58 }
78 self.db 59 self.query_mut(ra_db::LibraryRootsQuery)
79 .query_mut(ra_db::LibraryRootsQuery)
80 .set((), Arc::new(libraries)); 60 .set((), Arc::new(libraries));
81 } 61 }
82 if let Some(crate_graph) = change.crate_graph { 62 if let Some(crate_graph) = change.crate_graph {
83 self.db 63 self.query_mut(ra_db::CrateGraphQuery)
84 .query_mut(ra_db::CrateGraphQuery)
85 .set((), Arc::new(crate_graph)) 64 .set((), Arc::new(crate_graph))
86 } 65 }
87 } 66 }
88 67
89 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 68 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
90 let mut source_root = SourceRoot::clone(&self.db.source_root(root_id)); 69 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
91 for add_file in root_change.added { 70 for add_file in root_change.added {
92 self.db 71 self.query_mut(ra_db::FileTextQuery)
93 .query_mut(ra_db::FileTextQuery)
94 .set(add_file.file_id, add_file.text); 72 .set(add_file.file_id, add_file.text);
95 self.db 73 self.query_mut(ra_db::FileRelativePathQuery)
96 .query_mut(ra_db::FileRelativePathQuery)
97 .set(add_file.file_id, add_file.path.clone()); 74 .set(add_file.file_id, add_file.path.clone());
98 self.db 75 self.query_mut(ra_db::FileSourceRootQuery)
99 .query_mut(ra_db::FileSourceRootQuery)
100 .set(add_file.file_id, root_id); 76 .set(add_file.file_id, root_id);
101 source_root.files.insert(add_file.path, add_file.file_id); 77 source_root.files.insert(add_file.path, add_file.file_id);
102 } 78 }
103 for remove_file in root_change.removed { 79 for remove_file in root_change.removed {
104 self.db 80 self.query_mut(ra_db::FileTextQuery)
105 .query_mut(ra_db::FileTextQuery)
106 .set(remove_file.file_id, Default::default()); 81 .set(remove_file.file_id, Default::default());
107 source_root.files.remove(&remove_file.path); 82 source_root.files.remove(&remove_file.path);
108 } 83 }
109 self.db 84 self.query_mut(ra_db::SourceRootQuery)
110 .query_mut(ra_db::SourceRootQuery)
111 .set(root_id, Arc::new(source_root)); 85 .set(root_id, Arc::new(source_root));
112 } 86 }
113 87
@@ -116,142 +90,86 @@ impl AnalysisHostImpl {
116 /// syntax trees. However, if we actually do that, everything is recomputed 90 /// syntax trees. However, if we actually do that, everything is recomputed
117 /// for some reason. Needs investigation. 91 /// for some reason. Needs investigation.
118 fn gc_syntax_trees(&mut self) { 92 fn gc_syntax_trees(&mut self) {
119 self.db 93 self.query(ra_db::SourceFileQuery)
120 .query(ra_db::SourceFileQuery)
121 .sweep(salsa::SweepStrategy::default().discard_values()); 94 .sweep(salsa::SweepStrategy::default().discard_values());
122 self.db 95 self.query(hir::db::SourceFileItemsQuery)
123 .query(hir::db::SourceFileItemsQuery)
124 .sweep(salsa::SweepStrategy::default().discard_values()); 96 .sweep(salsa::SweepStrategy::default().discard_values());
125 self.db 97 self.query(hir::db::FileItemQuery)
126 .query(hir::db::FileItemQuery)
127 .sweep(salsa::SweepStrategy::default().discard_values()); 98 .sweep(salsa::SweepStrategy::default().discard_values());
128 } 99 }
129} 100}
130 101
131pub(crate) struct AnalysisImpl { 102impl db::RootDatabase {
132 pub(crate) db: salsa::Snapshot<db::RootDatabase>,
133}
134
135impl fmt::Debug for AnalysisImpl {
136 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
137 let db: &db::RootDatabase = &self.db;
138 fmt.debug_struct("AnalysisImpl").field("db", db).finish()
139 }
140}
141
142impl AnalysisImpl {
143 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
144 self.db.file_text(file_id)
145 }
146 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
147 self.db.source_file(file_id)
148 }
149 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
150 self.db.file_lines(file_id)
151 }
152 pub fn world_symbols(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> {
153 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
154 struct Snap(salsa::Snapshot<db::RootDatabase>);
155 impl Clone for Snap {
156 fn clone(&self) -> Snap {
157 Snap(self.0.snapshot())
158 }
159 }
160
161 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
162 let snap = Snap(self.db.snapshot());
163 self.db
164 .library_roots()
165 .par_iter()
166 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
167 .collect()
168 } else {
169 let mut files = Vec::new();
170 for &root in self.db.local_roots().iter() {
171 let sr = self.db.source_root(root);
172 files.extend(sr.files.values().map(|&it| it))
173 }
174
175 let snap = Snap(self.db.snapshot());
176 files
177 .par_iter()
178 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
179 .filter_map(|it| it.ok())
180 .collect()
181 };
182 Ok(query.search(&buf))
183 }
184 /// This returns `Vec` because a module may be included from several places. We 103 /// This returns `Vec` because a module may be included from several places. We
185 /// don't handle this case yet though, so the Vec has length at most one. 104 /// don't handle this case yet though, so the Vec has length at most one.
186 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 105 pub(crate) fn parent_module(
187 let descr = match source_binder::module_from_position(&*self.db, position)? { 106 &self,
107 position: FilePosition,
108 ) -> Cancelable<Vec<NavigationTarget>> {
109 let descr = match source_binder::module_from_position(self, position)? {
188 None => return Ok(Vec::new()), 110 None => return Ok(Vec::new()),
189 Some(it) => it, 111 Some(it) => it,
190 }; 112 };
191 let (file_id, decl) = match descr.parent_link_source(&*self.db) { 113 let (file_id, decl) = match descr.parent_link_source(self) {
192 None => return Ok(Vec::new()), 114 None => return Ok(Vec::new()),
193 Some(it) => it, 115 Some(it) => it,
194 }; 116 };
195 let decl = decl.borrowed(); 117 let decl = decl.borrowed();
196 let decl_name = decl.name().unwrap(); 118 let decl_name = decl.name().unwrap();
197 let sym = FileSymbol { 119 Ok(vec![NavigationTarget {
120 file_id,
198 name: decl_name.text(), 121 name: decl_name.text(),
199 node_range: decl_name.syntax().range(), 122 range: decl_name.syntax().range(),
200 kind: MODULE, 123 kind: MODULE,
201 }; 124 ptr: None,
202 Ok(vec![(file_id, sym)]) 125 }])
203 } 126 }
204 /// Returns `Vec` for the same reason as `parent_module` 127 /// Returns `Vec` for the same reason as `parent_module`
205 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 128 pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
206 let descr = match source_binder::module_from_file_id(&*self.db, file_id)? { 129 let descr = match source_binder::module_from_file_id(self, file_id)? {
207 None => return Ok(Vec::new()), 130 None => return Ok(Vec::new()),
208 Some(it) => it, 131 Some(it) => it,
209 }; 132 };
210 let root = descr.crate_root(); 133 let root = descr.crate_root();
211 let file_id = root.source().file_id(); 134 let file_id = root.file_id();
212 135
213 let crate_graph = self.db.crate_graph(); 136 let crate_graph = self.crate_graph();
214 let crate_id = crate_graph.crate_id_for_crate_root(file_id); 137 let crate_id = crate_graph.crate_id_for_crate_root(file_id);
215 Ok(crate_id.into_iter().collect()) 138 Ok(crate_id.into_iter().collect())
216 } 139 }
217 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 140 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId {
218 self.db.crate_graph().crate_root(crate_id) 141 self.crate_graph().crate_root(crate_id)
219 } 142 }
220 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 143 pub(crate) fn approximately_resolve_symbol(
221 let completions = completions(&self.db, position)?;
222 Ok(completions.map(|it| it.into()))
223 }
224 pub fn approximately_resolve_symbol(
225 &self, 144 &self,
226 position: FilePosition, 145 position: FilePosition,
227 ) -> Cancelable<Option<ReferenceResolution>> { 146 ) -> Cancelable<Option<ReferenceResolution>> {
228 let file = self.db.source_file(position.file_id); 147 let file = self.source_file(position.file_id);
229 let syntax = file.syntax(); 148 let syntax = file.syntax();
230 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 149 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
231 let mut rr = ReferenceResolution::new(name_ref.syntax().range()); 150 let mut rr = ReferenceResolution::new(name_ref.syntax().range());
232 if let Some(fn_descr) = source_binder::function_from_child_node( 151 if let Some(fn_descr) =
233 &*self.db, 152 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())?
234 position.file_id, 153 {
235 name_ref.syntax(), 154 let scope = fn_descr.scopes(self);
236 )? {
237 let scope = fn_descr.scopes(&*self.db);
238 // First try to resolve the symbol locally 155 // First try to resolve the symbol locally
239 if let Some(entry) = scope.resolve_local_name(name_ref) { 156 if let Some(entry) = scope.resolve_local_name(name_ref) {
240 rr.add_resolution( 157 rr.resolves_to.push(NavigationTarget {
241 position.file_id, 158 file_id: position.file_id,
242 FileSymbol { 159 name: entry.name().to_string().into(),
243 name: entry.name().to_string().into(), 160 range: entry.ptr().range(),
244 node_range: entry.ptr().range(), 161 kind: NAME,
245 kind: NAME, 162 ptr: None,
246 }, 163 });
247 );
248 return Ok(Some(rr)); 164 return Ok(Some(rr));
249 }; 165 };
250 } 166 }
251 // If that fails try the index based approach. 167 // If that fails try the index based approach.
252 for (file_id, symbol) in self.index_resolve(name_ref)? { 168 rr.resolves_to.extend(
253 rr.add_resolution(file_id, symbol); 169 self.index_resolve(name_ref)?
254 } 170 .into_iter()
171 .map(NavigationTarget::from_symbol),
172 );
255 return Ok(Some(rr)); 173 return Ok(Some(rr));
256 } 174 }
257 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 175 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
@@ -259,19 +177,21 @@ impl AnalysisImpl {
259 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 177 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
260 if module.has_semi() { 178 if module.has_semi() {
261 if let Some(child_module) = 179 if let Some(child_module) =
262 source_binder::module_from_declaration(&*self.db, position.file_id, module)? 180 source_binder::module_from_declaration(self, position.file_id, module)?
263 { 181 {
264 let file_id = child_module.source().file_id(); 182 let file_id = child_module.file_id();
265 let name = match child_module.name() { 183 let name = match child_module.name() {
266 Some(name) => name.to_string().into(), 184 Some(name) => name.to_string().into(),
267 None => "".into(), 185 None => "".into(),
268 }; 186 };
269 let symbol = FileSymbol { 187 let symbol = NavigationTarget {
188 file_id,
270 name, 189 name,
271 node_range: TextRange::offset_len(0.into(), 0.into()), 190 range: TextRange::offset_len(0.into(), 0.into()),
272 kind: MODULE, 191 kind: MODULE,
192 ptr: None,
273 }; 193 };
274 rr.add_resolution(file_id, symbol); 194 rr.resolves_to.push(symbol);
275 return Ok(Some(rr)); 195 return Ok(Some(rr));
276 } 196 }
277 } 197 }
@@ -280,10 +200,13 @@ impl AnalysisImpl {
280 Ok(None) 200 Ok(None)
281 } 201 }
282 202
283 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 203 pub(crate) fn find_all_refs(
284 let file = self.db.source_file(position.file_id); 204 &self,
205 position: FilePosition,
206 ) -> Cancelable<Vec<(FileId, TextRange)>> {
207 let file = self.source_file(position.file_id);
285 // Find the binding associated with the offset 208 // Find the binding associated with the offset
286 let (binding, descr) = match find_binding(&self.db, &file, position)? { 209 let (binding, descr) = match find_binding(self, &file, position)? {
287 None => return Ok(Vec::new()), 210 None => return Ok(Vec::new()),
288 Some(it) => it, 211 Some(it) => it,
289 }; 212 };
@@ -295,7 +218,7 @@ impl AnalysisImpl {
295 .collect::<Vec<_>>(); 218 .collect::<Vec<_>>();
296 ret.extend( 219 ret.extend(
297 descr 220 descr
298 .scopes(&*self.db) 221 .scopes(self)
299 .find_all_refs(binding) 222 .find_all_refs(binding)
300 .into_iter() 223 .into_iter()
301 .map(|ref_desc| (position.file_id, ref_desc.range)), 224 .map(|ref_desc| (position.file_id, ref_desc.range)),
@@ -333,9 +256,8 @@ impl AnalysisImpl {
333 Ok(Some((binding, descr))) 256 Ok(Some((binding, descr)))
334 } 257 }
335 } 258 }
336 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 259 pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
337 let file = self.db.source_file(file_id); 260 let result = match (nav.description(self), nav.docs(self)) {
338 let result = match (symbol.description(&file), symbol.docs(&file)) {
339 (Some(desc), Some(docs)) => { 261 (Some(desc), Some(docs)) => {
340 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) 262 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
341 } 263 }
@@ -347,8 +269,8 @@ impl AnalysisImpl {
347 Ok(result) 269 Ok(result)
348 } 270 }
349 271
350 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 272 pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
351 let syntax = self.db.source_file(file_id); 273 let syntax = self.source_file(file_id);
352 274
353 let mut res = ra_editor::diagnostics(&syntax) 275 let mut res = ra_editor::diagnostics(&syntax)
354 .into_iter() 276 .into_iter()
@@ -359,9 +281,9 @@ impl AnalysisImpl {
359 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), 281 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
360 }) 282 })
361 .collect::<Vec<_>>(); 283 .collect::<Vec<_>>();
362 if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { 284 if let Some(m) = source_binder::module_from_file_id(self, file_id)? {
363 for (name_node, problem) in m.problems(&*self.db) { 285 for (name_node, problem) in m.problems(self) {
364 let source_root = self.db.file_source_root(file_id); 286 let source_root = self.file_source_root(file_id);
365 let diag = match problem { 287 let diag = match problem {
366 Problem::UnresolvedModule { candidate } => { 288 Problem::UnresolvedModule { candidate } => {
367 let create_file = FileSystemEdit::CreateFile { 289 let create_file = FileSystemEdit::CreateFile {
@@ -411,29 +333,19 @@ impl AnalysisImpl {
411 Ok(res) 333 Ok(res)
412 } 334 }
413 335
414 pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> { 336 pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
415 let file = self.file_syntax(frange.file_id); 337 let file = self.source_file(frange.file_id);
416 let offset = frange.range.start(); 338 assists::assists(&file, frange.range)
417 let actions = vec![
418 ra_editor::flip_comma(&file, offset).map(|f| f()),
419 ra_editor::add_derive(&file, offset).map(|f| f()),
420 ra_editor::add_impl(&file, offset).map(|f| f()),
421 ra_editor::make_pub_crate(&file, offset).map(|f| f()),
422 ra_editor::introduce_variable(&file, frange.range).map(|f| f()),
423 ];
424 actions
425 .into_iter() 339 .into_iter()
426 .filter_map(|local_edit| { 340 .map(|local_edit| SourceChange::from_local_edit(frange.file_id, local_edit))
427 Some(SourceChange::from_local_edit(frange.file_id, local_edit?))
428 })
429 .collect() 341 .collect()
430 } 342 }
431 343
432 pub fn resolve_callable( 344 pub(crate) fn resolve_callable(
433 &self, 345 &self,
434 position: FilePosition, 346 position: FilePosition,
435 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 347 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
436 let file = self.db.source_file(position.file_id); 348 let file = self.source_file(position.file_id);
437 let syntax = file.syntax(); 349 let syntax = file.syntax();
438 350
439 // Find the calling expression and it's NameRef 351 // Find the calling expression and it's NameRef
@@ -442,53 +354,55 @@ impl AnalysisImpl {
442 354
443 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 355 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
444 let file_symbols = self.index_resolve(name_ref)?; 356 let file_symbols = self.index_resolve(name_ref)?;
445 for (fn_file_id, fs) in file_symbols { 357 for symbol in file_symbols {
446 if fs.kind == FN_DEF { 358 if symbol.ptr.kind() == FN_DEF {
447 let fn_file = self.db.source_file(fn_file_id); 359 let fn_file = self.source_file(symbol.file_id);
448 if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { 360 let fn_def = symbol.ptr.resolve(&fn_file);
449 let descr = ctry!(source_binder::function_from_source( 361 let fn_def = ast::FnDef::cast(fn_def.borrowed()).unwrap();
450 &*self.db, fn_file_id, fn_def 362 let descr = ctry!(source_binder::function_from_source(
451 )?); 363 self,
452 if let Some(descriptor) = descr.signature_info(&*self.db) { 364 symbol.file_id,
453 // If we have a calling expression let's find which argument we are on 365 fn_def
454 let mut current_parameter = None; 366 )?);
455 367 if let Some(descriptor) = descr.signature_info(self) {
456 let num_params = descriptor.params.len(); 368 // If we have a calling expression let's find which argument we are on
457 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 369 let mut current_parameter = None;
458 370
459 if num_params == 1 { 371 let num_params = descriptor.params.len();
460 if !has_self { 372 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
461 current_parameter = Some(0); 373
462 } 374 if num_params == 1 {
463 } else if num_params > 1 { 375 if !has_self {
464 // Count how many parameters into the call we are. 376 current_parameter = Some(0);
465 // TODO: This is best effort for now and should be fixed at some point.
466 // It may be better to see where we are in the arg_list and then check
467 // where offset is in that list (or beyond).
468 // Revisit this after we get documentation comments in.
469 if let Some(ref arg_list) = calling_node.arg_list() {
470 let start = arg_list.syntax().range().start();
471
472 let range_search = TextRange::from_to(start, position.offset);
473 let mut commas: usize = arg_list
474 .syntax()
475 .text()
476 .slice(range_search)
477 .to_string()
478 .matches(',')
479 .count();
480
481 // If we have a method call eat the first param since it's just self.
482 if has_self {
483 commas += 1;
484 }
485
486 current_parameter = Some(commas);
487 }
488 } 377 }
378 } else if num_params > 1 {
379 // Count how many parameters into the call we are.
380 // TODO: This is best effort for now and should be fixed at some point.
381 // It may be better to see where we are in the arg_list and then check
382 // where offset is in that list (or beyond).
383 // Revisit this after we get documentation comments in.
384 if let Some(ref arg_list) = calling_node.arg_list() {
385 let start = arg_list.syntax().range().start();
386
387 let range_search = TextRange::from_to(start, position.offset);
388 let mut commas: usize = arg_list
389 .syntax()
390 .text()
391 .slice(range_search)
392 .to_string()
393 .matches(',')
394 .count();
395
396 // If we have a method call eat the first param since it's just self.
397 if has_self {
398 commas += 1;
399 }
489 400
490 return Ok(Some((descriptor, current_parameter))); 401 current_parameter = Some(commas);
402 }
491 } 403 }
404
405 return Ok(Some((descriptor, current_parameter)));
492 } 406 }
493 } 407 }
494 } 408 }
@@ -496,20 +410,20 @@ impl AnalysisImpl {
496 Ok(None) 410 Ok(None)
497 } 411 }
498 412
499 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 413 pub(crate) fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
500 let file = self.db.source_file(frange.file_id); 414 let file = self.source_file(frange.file_id);
501 let syntax = file.syntax(); 415 let syntax = file.syntax();
502 let node = find_covering_node(syntax, frange.range); 416 let node = find_covering_node(syntax, frange.range);
503 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); 417 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast));
504 let function = ctry!(source_binder::function_from_source( 418 let function = ctry!(source_binder::function_from_source(
505 &*self.db, 419 self,
506 frange.file_id, 420 frange.file_id,
507 parent_fn 421 parent_fn
508 )?); 422 )?);
509 let infer = function.infer(&*self.db)?; 423 let infer = function.infer(self)?;
510 Ok(infer.type_of_node(node).map(|t| t.to_string())) 424 Ok(infer.type_of_node(node).map(|t| t.to_string()))
511 } 425 }
512 pub fn rename( 426 pub(crate) fn rename(
513 &self, 427 &self,
514 position: FilePosition, 428 position: FilePosition,
515 new_name: &str, 429 new_name: &str,
@@ -520,7 +434,7 @@ impl AnalysisImpl {
520 .map(|(file_id, text_range)| SourceFileEdit { 434 .map(|(file_id, text_range)| SourceFileEdit {
521 file_id: *file_id, 435 file_id: *file_id,
522 edit: { 436 edit: {
523 let mut builder = ra_text_edit::TextEditBuilder::new(); 437 let mut builder = ra_text_edit::TextEditBuilder::default();
524 builder.replace(*text_range, new_name.into()); 438 builder.replace(*text_range, new_name.into());
525 builder.finish() 439 builder.finish()
526 }, 440 },
@@ -528,12 +442,12 @@ impl AnalysisImpl {
528 .collect::<Vec<_>>(); 442 .collect::<Vec<_>>();
529 Ok(res) 443 Ok(res)
530 } 444 }
531 fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<(FileId, FileSymbol)>> { 445 fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<FileSymbol>> {
532 let name = name_ref.text(); 446 let name = name_ref.text();
533 let mut query = Query::new(name.to_string()); 447 let mut query = Query::new(name.to_string());
534 query.exact(); 448 query.exact();
535 query.limit(4); 449 query.limit(4);
536 self.world_symbols(query) 450 crate::symbol_index::world_symbols(self, query)
537 } 451 }
538} 452}
539 453
@@ -592,3 +506,91 @@ impl<'a> FnCallNode<'a> {
592 } 506 }
593 } 507 }
594} 508}
509
510impl NavigationTarget {
511 fn node(&self, db: &db::RootDatabase) -> Option<SyntaxNode> {
512 let source_file = db.source_file(self.file_id);
513 let source_file = source_file.syntax();
514 let node = source_file
515 .descendants()
516 .find(|node| node.kind() == self.kind && node.range() == self.range)?
517 .owned();
518 Some(node)
519 }
520
521 fn docs(&self, db: &db::RootDatabase) -> Option<String> {
522 let node = self.node(db)?;
523 let node = node.borrowed();
524 fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> {
525 let comments = node.doc_comment_text();
526 if comments.is_empty() {
527 None
528 } else {
529 Some(comments)
530 }
531 }
532
533 visitor()
534 .visit(doc_comments::<ast::FnDef>)
535 .visit(doc_comments::<ast::StructDef>)
536 .visit(doc_comments::<ast::EnumDef>)
537 .visit(doc_comments::<ast::TraitDef>)
538 .visit(doc_comments::<ast::Module>)
539 .visit(doc_comments::<ast::TypeDef>)
540 .visit(doc_comments::<ast::ConstDef>)
541 .visit(doc_comments::<ast::StaticDef>)
542 .accept(node)?
543 }
544
545 /// Get a description of this node.
546 ///
547 /// e.g. `struct Name`, `enum Name`, `fn Name`
548 fn description(&self, db: &db::RootDatabase) -> Option<String> {
549 // TODO: After type inference is done, add type information to improve the output
550 let node = self.node(db)?;
551 let node = node.borrowed();
552 // TODO: Refactor to be have less repetition
553 visitor()
554 .visit(|node: ast::FnDef| {
555 let mut string = "fn ".to_string();
556 node.name()?.syntax().text().push_to(&mut string);
557 Some(string)
558 })
559 .visit(|node: ast::StructDef| {
560 let mut string = "struct ".to_string();
561 node.name()?.syntax().text().push_to(&mut string);
562 Some(string)
563 })
564 .visit(|node: ast::EnumDef| {
565 let mut string = "enum ".to_string();
566 node.name()?.syntax().text().push_to(&mut string);
567 Some(string)
568 })
569 .visit(|node: ast::TraitDef| {
570 let mut string = "trait ".to_string();
571 node.name()?.syntax().text().push_to(&mut string);
572 Some(string)
573 })
574 .visit(|node: ast::Module| {
575 let mut string = "mod ".to_string();
576 node.name()?.syntax().text().push_to(&mut string);
577 Some(string)
578 })
579 .visit(|node: ast::TypeDef| {
580 let mut string = "type ".to_string();
581 node.name()?.syntax().text().push_to(&mut string);
582 Some(string)
583 })
584 .visit(|node: ast::ConstDef| {
585 let mut string = "const ".to_string();
586 node.name()?.syntax().text().push_to(&mut string);
587 Some(string)
588 })
589 .visit(|node: ast::StaticDef| {
590 let mut string = "static ".to_string();
591 node.name()?.syntax().text().push_to(&mut string);
592 Some(string)
593 })
594 .accept(node)?
595 }
596}
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index e56168510..ab935954c 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -1,6 +1,8 @@
1//! ra_analyzer crate is the brain of Rust analyzer. It relies on the `salsa` 1//! ra_analyzer crate provides "ide-centric" APIs for the rust-analyzer. What
2//! crate, which provides and incremental on-demand database of facts. 2//! powers this API are the `RootDatabase` struct, which defines a `salsa`
3 3//! database, and the `ra_hir` crate, where majority of the analysis happens.
4//! However, IDE specific bits of the analysis (most notably completion) happen
5//! in this crate.
4macro_rules! ctry { 6macro_rules! ctry {
5 ($expr:expr) => { 7 ($expr:expr) => {
6 match $expr { 8 match $expr {
@@ -15,34 +17,34 @@ mod imp;
15mod completion; 17mod completion;
16mod symbol_index; 18mod symbol_index;
17pub mod mock_analysis; 19pub mod mock_analysis;
20mod runnables;
18 21
19mod extend_selection; 22mod extend_selection;
20mod syntax_highlighting; 23mod syntax_highlighting;
21mod macros;
22 24
23use std::{fmt, sync::Arc}; 25use std::{fmt, sync::Arc};
24 26
25use rustc_hash::FxHashMap; 27use rustc_hash::FxHashMap;
26use ra_syntax::{SourceFileNode, TextRange, TextUnit}; 28use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
28use rayon::prelude::*; 30use rayon::prelude::*;
29use relative_path::RelativePathBuf; 31use relative_path::RelativePathBuf;
32use salsa::ParallelDatabase;
30 33
31use crate::{ 34use crate::symbol_index::{SymbolIndex, FileSymbol};
32 imp::{AnalysisHostImpl, AnalysisImpl},
33 symbol_index::SymbolIndex,
34};
35 35
36pub use crate::completion::{CompletionItem, CompletionItemKind, InsertText}; 36pub use crate::{
37 completion::{CompletionItem, CompletionItemKind, InsertText},
38 runnables::{Runnable, RunnableKind},
39};
37pub use ra_editor::{ 40pub use ra_editor::{
38 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, 41 Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
39 Severity
40}; 42};
41pub use hir::FnSignatureInfo; 43pub use hir::FnSignatureInfo;
42 44
43pub use ra_db::{ 45pub use ra_db::{
44 Canceled, Cancelable, FilePosition, FileRange, 46 Canceled, Cancelable, FilePosition, FileRange, LocalSyntaxPtr,
45 CrateGraph, CrateId, SourceRootId, FileId 47 CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase
46}; 48};
47 49
48#[derive(Default)] 50#[derive(Default)]
@@ -148,27 +150,6 @@ impl AnalysisChange {
148 } 150 }
149} 151}
150 152
151/// `AnalysisHost` stores the current state of the world.
152#[derive(Debug, Default)]
153pub struct AnalysisHost {
154 imp: AnalysisHostImpl,
155}
156
157impl AnalysisHost {
158 /// Returns a snapshot of the current state, which you can query for
159 /// semantic information.
160 pub fn analysis(&self) -> Analysis {
161 Analysis {
162 imp: self.imp.analysis(),
163 }
164 }
165 /// Applies changes to the current state of the world. If there are
166 /// outstanding snapshots, they will be canceled.
167 pub fn apply_change(&mut self, change: AnalysisChange) {
168 self.imp.apply_change(change)
169 }
170}
171
172#[derive(Debug)] 153#[derive(Debug)]
173pub struct SourceChange { 154pub struct SourceChange {
174 pub label: String, 155 pub label: String,
@@ -240,6 +221,45 @@ impl Query {
240 } 221 }
241} 222}
242 223
224/// `NavigationTarget` represents and element in the editor's UI whihc you can
225/// click on to navigate to a particular piece of code.
226///
227/// Typically, a `NavigationTarget` corresponds to some element in the source
228/// code, like a function or a struct, but this is not strictly required.
229#[derive(Debug)]
230pub struct NavigationTarget {
231 file_id: FileId,
232 name: SmolStr,
233 kind: SyntaxKind,
234 range: TextRange,
235 // Should be DefId ideally
236 ptr: Option<LocalSyntaxPtr>,
237}
238
239impl NavigationTarget {
240 fn from_symbol(symbol: FileSymbol) -> NavigationTarget {
241 NavigationTarget {
242 file_id: symbol.file_id,
243 name: symbol.name.clone(),
244 kind: symbol.ptr.kind(),
245 range: symbol.ptr.range(),
246 ptr: Some(symbol.ptr.clone()),
247 }
248 }
249 pub fn name(&self) -> &SmolStr {
250 &self.name
251 }
252 pub fn kind(&self) -> SyntaxKind {
253 self.kind
254 }
255 pub fn file_id(&self) -> FileId {
256 self.file_id
257 }
258 pub fn range(&self) -> TextRange {
259 self.range
260 }
261}
262
243/// Result of "goto def" query. 263/// Result of "goto def" query.
244#[derive(Debug)] 264#[derive(Debug)]
245pub struct ReferenceResolution { 265pub struct ReferenceResolution {
@@ -248,7 +268,7 @@ pub struct ReferenceResolution {
248 /// client where the reference was. 268 /// client where the reference was.
249 pub reference_range: TextRange, 269 pub reference_range: TextRange,
250 /// What this reference resolves to. 270 /// What this reference resolves to.
251 pub resolves_to: Vec<(FileId, FileSymbol)>, 271 pub resolves_to: Vec<NavigationTarget>,
252} 272}
253 273
254impl ReferenceResolution { 274impl ReferenceResolution {
@@ -258,9 +278,26 @@ impl ReferenceResolution {
258 resolves_to: Vec::new(), 278 resolves_to: Vec::new(),
259 } 279 }
260 } 280 }
281}
282
283/// `AnalysisHost` stores the current state of the world.
284#[derive(Debug, Default)]
285pub struct AnalysisHost {
286 db: db::RootDatabase,
287}
261 288
262 fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { 289impl AnalysisHost {
263 self.resolves_to.push((file_id, symbol)) 290 /// Returns a snapshot of the current state, which you can query for
291 /// semantic information.
292 pub fn analysis(&self) -> Analysis {
293 Analysis {
294 db: self.db.snapshot(),
295 }
296 }
297 /// Applies changes to the current state of the world. If there are
298 /// outstanding snapshots, they will be canceled.
299 pub fn apply_change(&mut self, change: AnalysisChange) {
300 self.db.apply_change(change)
264 } 301 }
265} 302}
266 303
@@ -270,109 +307,146 @@ impl ReferenceResolution {
270/// `Analysis` are canceled (most method return `Err(Canceled)`). 307/// `Analysis` are canceled (most method return `Err(Canceled)`).
271#[derive(Debug)] 308#[derive(Debug)]
272pub struct Analysis { 309pub struct Analysis {
273 pub(crate) imp: AnalysisImpl, 310 db: salsa::Snapshot<db::RootDatabase>,
274} 311}
275 312
276impl Analysis { 313impl Analysis {
314 /// Gets the text of the source file.
277 pub fn file_text(&self, file_id: FileId) -> Arc<String> { 315 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
278 self.imp.file_text(file_id) 316 self.db.file_text(file_id)
279 } 317 }
318 /// Gets the syntax tree of the file.
280 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 319 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
281 self.imp.file_syntax(file_id).clone() 320 self.db.source_file(file_id).clone()
282 } 321 }
322 /// Gets the file's `LineIndex`: data structure to convert between absolute
323 /// offsets and line/column representation.
283 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 324 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
284 self.imp.file_line_index(file_id) 325 self.db.file_lines(file_id)
285 } 326 }
327 /// Selects the next syntactic nodes encopasing the range.
286 pub fn extend_selection(&self, frange: FileRange) -> TextRange { 328 pub fn extend_selection(&self, frange: FileRange) -> TextRange {
287 extend_selection::extend_selection(&self.imp.db, frange) 329 extend_selection::extend_selection(&self.db, frange)
288 } 330 }
331 /// Returns position of the mathcing brace (all types of braces are
332 /// supported).
289 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 333 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
290 ra_editor::matching_brace(file, offset) 334 ra_editor::matching_brace(file, offset)
291 } 335 }
336 /// Returns a syntax tree represented as `String`, for debug purposes.
337 // FIXME: use a better name here.
292 pub fn syntax_tree(&self, file_id: FileId) -> String { 338 pub fn syntax_tree(&self, file_id: FileId) -> String {
293 let file = self.imp.file_syntax(file_id); 339 let file = self.db.source_file(file_id);
294 ra_editor::syntax_tree(&file) 340 ra_editor::syntax_tree(&file)
295 } 341 }
342 /// Returns an edit to remove all newlines in the range, cleaning up minor
343 /// stuff like trailing commas.
296 pub fn join_lines(&self, frange: FileRange) -> SourceChange { 344 pub fn join_lines(&self, frange: FileRange) -> SourceChange {
297 let file = self.imp.file_syntax(frange.file_id); 345 let file = self.db.source_file(frange.file_id);
298 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range)) 346 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range))
299 } 347 }
348 /// Returns an edit which should be applied when opening a new line, fixing
349 /// up minor stuff like continuing the comment.
300 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 350 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
301 let file = self.imp.file_syntax(position.file_id); 351 let file = self.db.source_file(position.file_id);
302 let edit = ra_editor::on_enter(&file, position.offset)?; 352 let edit = ra_editor::on_enter(&file, position.offset)?;
303 let res = SourceChange::from_local_edit(position.file_id, edit); 353 Some(SourceChange::from_local_edit(position.file_id, edit))
304 Some(res)
305 } 354 }
355 /// Returns an edit which should be applied after `=` was typed. Primaraly,
356 /// this works when adding `let =`.
357 // FIXME: use a snippet completion instead of this hack here.
306 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { 358 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
307 let file = self.imp.file_syntax(position.file_id); 359 let file = self.db.source_file(position.file_id);
308 Some(SourceChange::from_local_edit( 360 let edit = ra_editor::on_eq_typed(&file, position.offset)?;
309 position.file_id, 361 Some(SourceChange::from_local_edit(position.file_id, edit))
310 ra_editor::on_eq_typed(&file, position.offset)?,
311 ))
312 } 362 }
363 /// Returns a tree representation of symbols in the file. Useful to draw a
364 /// file outline.
313 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 365 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
314 let file = self.imp.file_syntax(file_id); 366 let file = self.db.source_file(file_id);
315 ra_editor::file_structure(&file) 367 ra_editor::file_structure(&file)
316 } 368 }
369 /// Returns the set of folding ranges.
317 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { 370 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
318 let file = self.imp.file_syntax(file_id); 371 let file = self.db.source_file(file_id);
319 ra_editor::folding_ranges(&file) 372 ra_editor::folding_ranges(&file)
320 } 373 }
321 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { 374 /// Fuzzy searches for a symbol.
322 self.imp.world_symbols(query) 375 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
376 let res = symbol_index::world_symbols(&*self.db, query)?
377 .into_iter()
378 .map(NavigationTarget::from_symbol)
379 .collect();
380 Ok(res)
323 } 381 }
382 /// Resolves reference to definition, but does not gurantee correctness.
324 pub fn approximately_resolve_symbol( 383 pub fn approximately_resolve_symbol(
325 &self, 384 &self,
326 position: FilePosition, 385 position: FilePosition,
327 ) -> Cancelable<Option<ReferenceResolution>> { 386 ) -> Cancelable<Option<ReferenceResolution>> {
328 self.imp.approximately_resolve_symbol(position) 387 self.db.approximately_resolve_symbol(position)
329 } 388 }
389 /// Finds all usages of the reference at point.
330 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 390 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
331 self.imp.find_all_refs(position) 391 self.db.find_all_refs(position)
332 } 392 }
333 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 393 /// Returns documentation string for a given target.
334 self.imp.doc_text_for(file_id, symbol) 394 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
395 self.db.doc_text_for(nav)
335 } 396 }
336 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 397 /// Returns a `mod name;` declaration whihc created the current module.
337 self.imp.parent_module(position) 398 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
399 self.db.parent_module(position)
338 } 400 }
401 /// Returns crates this file belongs too.
339 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 402 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
340 self.imp.crate_for(file_id) 403 self.db.crate_for(file_id)
341 } 404 }
405 /// Returns the root file of the given crate.
342 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 406 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
343 Ok(self.imp.crate_root(crate_id)) 407 Ok(self.db.crate_root(crate_id))
344 } 408 }
409 /// Returns the set of possible targets to run for the current file.
345 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 410 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
346 let file = self.imp.file_syntax(file_id); 411 runnables::runnables(&*self.db, file_id)
347 Ok(ra_editor::runnables(&file))
348 } 412 }
413 /// Computes syntax highlighting for the given file.
349 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 414 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
350 syntax_highlighting::highlight(&*self.imp.db, file_id) 415 syntax_highlighting::highlight(&*self.db, file_id)
351 } 416 }
417 /// Computes completions at the given position.
352 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 418 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
353 self.imp.completions(position) 419 let completions = completion::completions(&self.db, position)?;
420 Ok(completions.map(|it| it.into()))
354 } 421 }
422 /// Computes assists (aks code actons aka intentions) for the given
423 /// position.
355 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { 424 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
356 Ok(self.imp.assists(frange)) 425 Ok(self.db.assists(frange))
357 } 426 }
427 /// Computes the set of diagnostics for the given file.
358 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 428 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
359 self.imp.diagnostics(file_id) 429 self.db.diagnostics(file_id)
360 } 430 }
431 /// Computes parameter information for the given call expression.
361 pub fn resolve_callable( 432 pub fn resolve_callable(
362 &self, 433 &self,
363 position: FilePosition, 434 position: FilePosition,
364 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 435 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
365 self.imp.resolve_callable(position) 436 self.db.resolve_callable(position)
366 } 437 }
438 /// Computes the type of the expression at the given position.
367 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 439 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
368 self.imp.type_of(frange) 440 self.db.type_of(frange)
369 } 441 }
442 /// Returns the edit required to rename reference at the position to the new
443 /// name.
370 pub fn rename( 444 pub fn rename(
371 &self, 445 &self,
372 position: FilePosition, 446 position: FilePosition,
373 new_name: &str, 447 new_name: &str,
374 ) -> Cancelable<Vec<SourceFileEdit>> { 448 ) -> Cancelable<Vec<SourceFileEdit>> {
375 self.imp.rename(position, new_name) 449 self.db.rename(position, new_name)
376 } 450 }
377} 451}
378 452
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs
deleted file mode 100644
index b9feb7fad..000000000
--- a/crates/ra_analysis/src/macros.rs
+++ /dev/null
@@ -1,75 +0,0 @@
1/// Begining of macro expansion.
2///
3/// This code should be moved out of ra_analysis into hir (?) ideally.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(crate) fn expand(
9 _db: &RootDatabase,
10 _file_id: FileId,
11 macro_call: ast::MacroCall,
12) -> Option<MacroExpansion> {
13 let path = macro_call.path()?;
14 if path.qualifier().is_some() {
15 return None;
16 }
17 let name_ref = path.segment()?.name_ref()?;
18 if name_ref.text() != "ctry" {
19 return None;
20 }
21
22 let arg = macro_call.token_tree()?;
23 let text = format!(
24 r"
25 fn dummy() {{
26 match {} {{
27 None => return Ok(None),
28 Some(it) => it,
29 }}
30 }}",
31 arg.syntax().text()
32 );
33 let file = SourceFileNode::parse(&text);
34 let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
35 let match_arg = match_expr.expr()?;
36 let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())];
37 let res = MacroExpansion {
38 source_file: file,
39 ranges_map,
40 };
41 Some(res)
42}
43
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl MacroExpansion {
50 pub(crate) fn source_file(&self) -> &SourceFileNode {
51 &self.source_file
52 }
53 pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
54 for (s_range, t_range) in self.ranges_map.iter() {
55 if tgt_range.is_subrange(&t_range) {
56 let tgt_at_zero_range = tgt_range - tgt_range.start();
57 let tgt_range_offset = tgt_range.start() - t_range.start();
58 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
59 return Some(src_range);
60 }
61 }
62 None
63 }
64 pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
65 for (s_range, t_range) in self.ranges_map.iter() {
66 if src_range.is_subrange(&s_range) {
67 let src_at_zero_range = src_range - src_range.start();
68 let src_range_offset = src_range.start() - s_range.start();
69 let src_range = src_at_zero_range + src_range_offset + t_range.start();
70 return Some(src_range);
71 }
72 }
73 None
74 }
75}
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
new file mode 100644
index 000000000..474267605
--- /dev/null
+++ b/crates/ra_analysis/src/runnables.rs
@@ -0,0 +1,86 @@
1use itertools::Itertools;
2use ra_syntax::{
3 ast::{self, AstNode, NameOwner, ModuleItemOwner},
4 TextRange, SyntaxNodeRef,
5};
6use ra_db::{Cancelable, SyntaxDatabase};
7
8use crate::{db::RootDatabase, FileId};
9
10#[derive(Debug)]
11pub struct Runnable {
12 pub range: TextRange,
13 pub kind: RunnableKind,
14}
15
16#[derive(Debug)]
17pub enum RunnableKind {
18 Test { name: String },
19 TestMod { path: String },
20 Bin,
21}
22
23pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Runnable>> {
24 let source_file = db.source_file(file_id);
25 let res = source_file
26 .syntax()
27 .descendants()
28 .filter_map(|i| runnable(db, file_id, i))
29 .collect();
30 Ok(res)
31}
32
33fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNodeRef) -> Option<Runnable> {
34 if let Some(fn_def) = ast::FnDef::cast(item) {
35 runnable_fn(fn_def)
36 } else if let Some(m) = ast::Module::cast(item) {
37 runnable_mod(db, file_id, m)
38 } else {
39 None
40 }
41}
42
43fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
44 let name = fn_def.name()?.text();
45 let kind = if name == "main" {
46 RunnableKind::Bin
47 } else if fn_def.has_atom_attr("test") {
48 RunnableKind::Test {
49 name: name.to_string(),
50 }
51 } else {
52 return None;
53 };
54 Some(Runnable {
55 range: fn_def.syntax().range(),
56 kind,
57 })
58}
59
60fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
61 let has_test_function = module
62 .item_list()?
63 .items()
64 .filter_map(|it| match it {
65 ast::ModuleItem::FnDef(it) => Some(it),
66 _ => None,
67 })
68 .any(|f| f.has_atom_attr("test"));
69 if !has_test_function {
70 return None;
71 }
72 let range = module.syntax().range();
73 let module =
74 hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??;
75 let path = module
76 .path_to_root()
77 .into_iter()
78 .rev()
79 .into_iter()
80 .filter_map(|it| it.name().map(Clone::clone))
81 .join("::");
82 Some(Runnable {
83 range,
84 kind: RunnableKind::TestMod { path },
85 })
86}
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index e5bdf0aa1..e2b1c88fe 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -1,24 +1,48 @@
1//! This module handles fuzzy-searching of functions, structs and other symbols
2//! by name across the whole workspace and dependencies.
3//!
4//! It works by building an incrementally-updated text-search index of all
5//! symbols. The backbone of the index is the **awesome** `fst` crate by
6//! @BurntSushi.
7//!
8//! In a nutshell, you give a set of strings to the `fst`, and it builds a
9//! finite state machine describing this set of strtings. The strings which
10//! could fuzzy-match a pattern can also be described by a finite state machine.
11//! What is freakingly cool is that you can now traverse both state machines in
12//! lock-step to enumerate the strings which are both in the input set and
13//! fuzz-match the query. Or, more formally, given two langauges described by
14//! fsts, one can build an product fst which describes the intersection of the
15//! languages.
16//!
17//! `fst` does not support cheap updating of the index, but it supports unioning
18//! of state machines. So, to account for changing source code, we build an fst
19//! for each library (which is assumed to never change) and an fst for each rust
20//! file in the current workspace, and run a query aginst the union of all
21//! thouse fsts.
1use std::{ 22use std::{
23 cmp::Ordering,
2 hash::{Hash, Hasher}, 24 hash::{Hash, Hasher},
3 sync::Arc, 25 sync::Arc,
4}; 26};
5 27
6use fst::{self, Streamer}; 28use fst::{self, Streamer};
7use ra_editor::{self, FileSymbol};
8use ra_syntax::{ 29use ra_syntax::{
9 SourceFileNode, 30 SyntaxNodeRef, SourceFileNode, SmolStr,
31 algo::{visit::{visitor, Visitor}, find_covering_node},
10 SyntaxKind::{self, *}, 32 SyntaxKind::{self, *},
33 ast::{self, NameOwner},
11}; 34};
12use ra_db::{SyntaxDatabase, SourceRootId}; 35use ra_db::{SourceRootId, FilesDatabase, LocalSyntaxPtr};
36use salsa::ParallelDatabase;
13use rayon::prelude::*; 37use rayon::prelude::*;
14 38
15use crate::{ 39use crate::{
16 Cancelable, 40 Cancelable, FileId, Query,
17 FileId, Query, 41 db::RootDatabase,
18}; 42};
19 43
20salsa::query_group! { 44salsa::query_group! {
21 pub(crate) trait SymbolsDatabase: SyntaxDatabase { 45 pub(crate) trait SymbolsDatabase: hir::db::HirDatabase {
22 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { 46 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
23 type FileSymbolsQuery; 47 type FileSymbolsQuery;
24 } 48 }
@@ -29,15 +53,60 @@ salsa::query_group! {
29 } 53 }
30} 54}
31 55
32fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { 56fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
33 db.check_canceled()?; 57 db.check_canceled()?;
34 let syntax = db.source_file(file_id); 58 let source_file = db.source_file(file_id);
35 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) 59 let mut symbols = source_file
60 .syntax()
61 .descendants()
62 .filter_map(to_symbol)
63 .map(move |(name, ptr)| FileSymbol { name, ptr, file_id })
64 .collect::<Vec<_>>();
65
66 for (name, text_range) in hir::source_binder::macro_symbols(db, file_id)? {
67 let node = find_covering_node(source_file.syntax(), text_range);
68 let ptr = LocalSyntaxPtr::new(node);
69 symbols.push(FileSymbol { file_id, name, ptr })
70 }
71
72 Ok(Arc::new(SymbolIndex::new(symbols)))
73}
74
75pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Cancelable<Vec<FileSymbol>> {
76 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
77 struct Snap(salsa::Snapshot<RootDatabase>);
78 impl Clone for Snap {
79 fn clone(&self) -> Snap {
80 Snap(self.0.snapshot())
81 }
82 }
83
84 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
85 let snap = Snap(db.snapshot());
86 db.library_roots()
87 .par_iter()
88 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
89 .collect()
90 } else {
91 let mut files = Vec::new();
92 for &root in db.local_roots().iter() {
93 let sr = db.source_root(root);
94 files.extend(sr.files.values().map(|&it| it))
95 }
96
97 let snap = Snap(db.snapshot());
98 files
99 .par_iter()
100 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
101 .filter_map(|it| it.ok())
102 .collect()
103 };
104 Ok(query.search(&buf))
36} 105}
37 106
38#[derive(Default, Debug)] 107#[derive(Default, Debug)]
39pub(crate) struct SymbolIndex { 108pub(crate) struct SymbolIndex {
40 symbols: Vec<(FileId, FileSymbol)>, 109 symbols: Vec<FileSymbol>,
41 map: fst::Map, 110 map: fst::Map,
42} 111}
43 112
@@ -56,6 +125,17 @@ impl Hash for SymbolIndex {
56} 125}
57 126
58impl SymbolIndex { 127impl SymbolIndex {
128 fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
129 fn cmp(s1: &FileSymbol, s2: &FileSymbol) -> Ordering {
130 unicase::Ascii::new(s1.name.as_str()).cmp(&unicase::Ascii::new(s2.name.as_str()))
131 }
132 symbols.par_sort_by(cmp);
133 symbols.dedup_by(|s1, s2| cmp(s1, s2) == Ordering::Equal);
134 let names = symbols.iter().map(|it| it.name.as_str().to_lowercase());
135 let map = fst::Map::from_iter(names.into_iter().zip(0u64..)).unwrap();
136 SymbolIndex { symbols, map }
137 }
138
59 pub(crate) fn len(&self) -> usize { 139 pub(crate) fn len(&self) -> usize {
60 self.symbols.len() 140 self.symbols.len()
61 } 141 }
@@ -63,29 +143,21 @@ impl SymbolIndex {
63 pub(crate) fn for_files( 143 pub(crate) fn for_files(
64 files: impl ParallelIterator<Item = (FileId, SourceFileNode)>, 144 files: impl ParallelIterator<Item = (FileId, SourceFileNode)>,
65 ) -> SymbolIndex { 145 ) -> SymbolIndex {
66 let mut symbols = files 146 let symbols = files
67 .flat_map(|(file_id, file)| { 147 .flat_map(|(file_id, file)| {
68 ra_editor::file_symbols(&file) 148 file.syntax()
69 .into_iter() 149 .descendants()
70 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) 150 .filter_map(to_symbol)
151 .map(move |(name, ptr)| FileSymbol { name, ptr, file_id })
71 .collect::<Vec<_>>() 152 .collect::<Vec<_>>()
72 }) 153 })
73 .collect::<Vec<_>>(); 154 .collect::<Vec<_>>();
74 symbols.par_sort_by(|s1, s2| s1.0.cmp(&s2.0)); 155 SymbolIndex::new(symbols)
75 symbols.dedup_by(|s1, s2| s1.0 == s2.0);
76 let (names, symbols): (Vec<String>, Vec<(FileId, FileSymbol)>) =
77 symbols.into_iter().unzip();
78 let map = fst::Map::from_iter(names.into_iter().zip(0u64..)).unwrap();
79 SymbolIndex { symbols, map }
80 }
81
82 pub(crate) fn for_file(file_id: FileId, file: SourceFileNode) -> SymbolIndex {
83 SymbolIndex::for_files(rayon::iter::once((file_id, file)))
84 } 156 }
85} 157}
86 158
87impl Query { 159impl Query {
88 pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<(FileId, FileSymbol)> { 160 pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> {
89 let mut op = fst::map::OpBuilder::new(); 161 let mut op = fst::map::OpBuilder::new();
90 for file_symbols in indices.iter() { 162 for file_symbols in indices.iter() {
91 let automaton = fst::automaton::Subsequence::new(&self.lowercased); 163 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
@@ -101,14 +173,14 @@ impl Query {
101 let file_symbols = &indices[indexed_value.index]; 173 let file_symbols = &indices[indexed_value.index];
102 let idx = indexed_value.value as usize; 174 let idx = indexed_value.value as usize;
103 175
104 let (file_id, symbol) = &file_symbols.symbols[idx]; 176 let symbol = &file_symbols.symbols[idx];
105 if self.only_types && !is_type(symbol.kind) { 177 if self.only_types && !is_type(symbol.ptr.kind()) {
106 continue; 178 continue;
107 } 179 }
108 if self.exact && symbol.name != self.query { 180 if self.exact && symbol.name != self.query {
109 continue; 181 continue;
110 } 182 }
111 res.push((*file_id, symbol.clone())); 183 res.push(symbol.clone());
112 } 184 }
113 } 185 }
114 res 186 res
@@ -121,3 +193,30 @@ fn is_type(kind: SyntaxKind) -> bool {
121 _ => false, 193 _ => false,
122 } 194 }
123} 195}
196
197/// The actual data that is stored in the index. It should be as compact as
198/// possible.
199#[derive(Debug, Clone, PartialEq, Eq, Hash)]
200pub(crate) struct FileSymbol {
201 pub(crate) file_id: FileId,
202 pub(crate) name: SmolStr,
203 pub(crate) ptr: LocalSyntaxPtr,
204}
205
206fn to_symbol(node: SyntaxNodeRef) -> Option<(SmolStr, LocalSyntaxPtr)> {
207 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<(SmolStr, LocalSyntaxPtr)> {
208 let name = node.name()?.text();
209 let ptr = LocalSyntaxPtr::new(node.syntax());
210 Some((name, ptr))
211 }
212 visitor()
213 .visit(decl::<ast::FnDef>)
214 .visit(decl::<ast::StructDef>)
215 .visit(decl::<ast::EnumDef>)
216 .visit(decl::<ast::TraitDef>)
217 .visit(decl::<ast::Module>)
218 .visit(decl::<ast::TypeDef>)
219 .visit(decl::<ast::ConstDef>)
220 .visit(decl::<ast::StaticDef>)
221 .accept(node)?
222}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
index 38219da71..35e153ca0 100644
--- a/crates/ra_analysis/src/syntax_highlighting.rs
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -9,19 +9,19 @@ use crate::{
9 9
10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
11 let source_file = db.source_file(file_id); 11 let source_file = db.source_file(file_id);
12 let mut res = ra_editor::highlight(&source_file); 12 let mut res = ra_editor::highlight(source_file.syntax());
13 for macro_call in source_file 13 for macro_call in source_file
14 .syntax() 14 .syntax()
15 .descendants() 15 .descendants()
16 .filter_map(ast::MacroCall::cast) 16 .filter_map(ast::MacroCall::cast)
17 { 17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { 18 if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file()) 19 let mapped_ranges = ra_editor::highlight(exp.syntax().borrowed())
20 .into_iter() 20 .into_iter()
21 .filter_map(|r| { 21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?; 22 let mapped_range = exp.map_range_back(r.range)?;
23 let res = HighlightedRange { 23 let res = HighlightedRange {
24 range: mapped_range, 24 range: mapped_range + off,
25 tag: r.tag, 25 tag: r.tag,
26 }; 26 };
27 Some(res) 27 Some(res)
@@ -43,8 +43,9 @@ mod tests {
43 " 43 "
44 fn main() { 44 fn main() {
45 ctry!({ let x = 92; x}); 45 ctry!({ let x = 92; x});
46 vec![{ let x = 92; x}];
46 } 47 }
47 ", 48 ",
48 ); 49 );
49 let highlights = analysis.highlight(file_id).unwrap(); 50 let highlights = analysis.highlight(file_id).unwrap();
50 assert_eq_dbg( 51 assert_eq_dbg(
@@ -53,10 +54,39 @@ mod tests {
53 HighlightedRange { range: [41; 46), tag: "macro" }, 54 HighlightedRange { range: [41; 46), tag: "macro" },
54 HighlightedRange { range: [49; 52), tag: "keyword" }, 55 HighlightedRange { range: [49; 52), tag: "keyword" },
55 HighlightedRange { range: [57; 59), tag: "literal" }, 56 HighlightedRange { range: [57; 59), tag: "literal" },
57 HighlightedRange { range: [82; 86), tag: "macro" },
58 HighlightedRange { range: [89; 92), tag: "keyword" },
59 HighlightedRange { range: [97; 99), tag: "literal" },
56 HighlightedRange { range: [49; 52), tag: "keyword" }, 60 HighlightedRange { range: [49; 52), tag: "keyword" },
57 HighlightedRange { range: [53; 54), tag: "function" }, 61 HighlightedRange { range: [53; 54), tag: "function" },
58 HighlightedRange { range: [57; 59), tag: "literal" }, 62 HighlightedRange { range: [57; 59), tag: "literal" },
59 HighlightedRange { range: [61; 62), tag: "text" }]"#, 63 HighlightedRange { range: [61; 62), tag: "text" },
64 HighlightedRange { range: [89; 92), tag: "keyword" },
65 HighlightedRange { range: [93; 94), tag: "function" },
66 HighlightedRange { range: [97; 99), tag: "literal" },
67 HighlightedRange { range: [101; 102), tag: "text" }]"#,
68 &highlights,
69 )
70 }
71
72 // FIXME: this test is not really necessary: artifact of the inital hacky
73 // macros implementation.
74 #[test]
75 fn highlight_query_group_macro() {
76 let (analysis, file_id) = single_file(
77 "
78 salsa::query_group! {
79 pub trait HirDatabase: SyntaxDatabase {}
80 }
81 ",
82 );
83 let highlights = analysis.highlight(file_id).unwrap();
84 assert_eq_dbg(
85 r#"[HighlightedRange { range: [20; 32), tag: "macro" },
86 HighlightedRange { range: [13; 18), tag: "text" },
87 HighlightedRange { range: [51; 54), tag: "keyword" },
88 HighlightedRange { range: [55; 60), tag: "keyword" },
89 HighlightedRange { range: [61; 72), tag: "function" }]"#,
60 &highlights, 90 &highlights,
61 ) 91 )
62 } 92 }
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/test/main.rs
index a314fbc40..bfdf8aef2 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/test/main.rs
@@ -1,9 +1,12 @@
1mod runnables;
2mod type_of;
3
1use ra_syntax::TextRange; 4use ra_syntax::TextRange;
2use test_utils::{assert_eq_dbg, assert_eq_text}; 5use test_utils::{assert_eq_dbg, assert_eq_text};
3 6
4use ra_analysis::{ 7use ra_analysis::{
5 mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, 8 mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis},
6 AnalysisChange, CrateGraph, FileId, FnSignatureInfo, 9 AnalysisChange, CrateGraph, FileId, FnSignatureInfo, Query
7}; 10};
8 11
9fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) { 12fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) {
@@ -25,7 +28,7 @@ fn approximate_resolve_works_in_items() {
25 assert_eq_dbg( 28 assert_eq_dbg(
26 r#"ReferenceResolution { 29 r#"ReferenceResolution {
27 reference_range: [23; 26), 30 reference_range: [23; 26),
28 resolves_to: [(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })] 31 resolves_to: [NavigationTarget { file_id: FileId(1), name: "Foo", kind: STRUCT_DEF, range: [0; 11), ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]
29 }"#, 32 }"#,
30 &symbols, 33 &symbols,
31 ); 34 );
@@ -46,7 +49,7 @@ fn test_resolve_module() {
46 assert_eq_dbg( 49 assert_eq_dbg(
47 r#"ReferenceResolution { 50 r#"ReferenceResolution {
48 reference_range: [4; 7), 51 reference_range: [4; 7),
49 resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] 52 resolves_to: [NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]
50 }"#, 53 }"#,
51 &symbols, 54 &symbols,
52 ); 55 );
@@ -64,7 +67,7 @@ fn test_resolve_module() {
64 assert_eq_dbg( 67 assert_eq_dbg(
65 r#"ReferenceResolution { 68 r#"ReferenceResolution {
66 reference_range: [4; 7), 69 reference_range: [4; 7),
67 resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] 70 resolves_to: [NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]
68 }"#, 71 }"#,
69 &symbols, 72 &symbols,
70 ); 73 );
@@ -107,7 +110,7 @@ fn test_resolve_parent_module() {
107 ); 110 );
108 let symbols = analysis.parent_module(pos).unwrap(); 111 let symbols = analysis.parent_module(pos).unwrap();
109 assert_eq_dbg( 112 assert_eq_dbg(
110 r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, 113 r#"[NavigationTarget { file_id: FileId(1), name: "foo", kind: MODULE, range: [4; 7), ptr: None }]"#,
111 &symbols, 114 &symbols,
112 ); 115 );
113} 116}
@@ -126,7 +129,7 @@ fn test_resolve_parent_module_for_inline() {
126 ); 129 );
127 let symbols = analysis.parent_module(pos).unwrap(); 130 let symbols = analysis.parent_module(pos).unwrap();
128 assert_eq_dbg( 131 assert_eq_dbg(
129 r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, 132 r#"[NavigationTarget { file_id: FileId(1), name: "bar", kind: MODULE, range: [18; 21), ptr: None }]"#,
130 &symbols, 133 &symbols,
131 ); 134 );
132} 135}
@@ -528,10 +531,11 @@ fn test_rename_for_mut_param() {
528 }"#, 531 }"#,
529 ); 532 );
530} 533}
534
531fn test_rename(text: &str, new_name: &str, expected: &str) { 535fn test_rename(text: &str, new_name: &str, expected: &str) {
532 let (analysis, position) = single_file_with_position(text); 536 let (analysis, position) = single_file_with_position(text);
533 let edits = analysis.rename(position, new_name).unwrap(); 537 let edits = analysis.rename(position, new_name).unwrap();
534 let mut text_edit_bulder = ra_text_edit::TextEditBuilder::new(); 538 let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default();
535 let mut file_id: Option<FileId> = None; 539 let mut file_id: Option<FileId> = None;
536 for edit in edits { 540 for edit in edits {
537 file_id = Some(edit.file_id); 541 file_id = Some(edit.file_id);
@@ -544,3 +548,19 @@ fn test_rename(text: &str, new_name: &str, expected: &str) {
544 .apply(&*analysis.file_text(file_id.unwrap())); 548 .apply(&*analysis.file_text(file_id.unwrap()));
545 assert_eq_text!(expected, &*result); 549 assert_eq_text!(expected, &*result);
546} 550}
551
552#[test]
553fn world_symbols_include_stuff_from_macros() {
554 let (analysis, _) = single_file(
555 "
556salsa::query_group! {
557pub trait HirDatabase: SyntaxDatabase {}
558}
559 ",
560 );
561
562 let mut symbols = analysis.symbol_search(Query::new("Hir".into())).unwrap();
563 let s = symbols.pop().unwrap();
564 assert_eq!(s.name(), "HirDatabase");
565 assert_eq!(s.range(), TextRange::from_to(33.into(), 44.into()));
566}
diff --git a/crates/ra_analysis/tests/test/runnables.rs b/crates/ra_analysis/tests/test/runnables.rs
new file mode 100644
index 000000000..e6e0afbc3
--- /dev/null
+++ b/crates/ra_analysis/tests/test/runnables.rs
@@ -0,0 +1,109 @@
1use test_utils::assert_eq_dbg;
2
3use ra_analysis::mock_analysis::analysis_and_position;
4
5#[test]
6fn test_runnables() {
7 let (analysis, pos) = analysis_and_position(
8 r#"
9 //- /lib.rs
10 <|> //empty
11 fn main() {}
12
13 #[test]
14 fn test_foo() {}
15
16 #[test]
17 #[ignore]
18 fn test_foo() {}
19 "#,
20 );
21 let runnables = analysis.runnables(pos.file_id).unwrap();
22 assert_eq_dbg(
23 r#"[Runnable { range: [1; 21), kind: Bin },
24 Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
25 Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
26 &runnables,
27 )
28}
29
30#[test]
31fn test_runnables_module() {
32 let (analysis, pos) = analysis_and_position(
33 r#"
34 //- /lib.rs
35 <|> //empty
36 mod test_mod {
37 #[test]
38 fn test_foo1() {}
39 }
40 "#,
41 );
42 let runnables = analysis.runnables(pos.file_id).unwrap();
43 assert_eq_dbg(
44 r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod" } },
45 Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
46 &runnables,
47 )
48}
49
50#[test]
51fn test_runnables_one_depth_layer_module() {
52 let (analysis, pos) = analysis_and_position(
53 r#"
54 //- /lib.rs
55 <|> //empty
56 mod foo {
57 mod test_mod {
58 #[test]
59 fn test_foo1() {}
60 }
61 }
62 "#,
63 );
64 let runnables = analysis.runnables(pos.file_id).unwrap();
65 assert_eq_dbg(
66 r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
67 Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
68 &runnables,
69 )
70}
71
72#[test]
73fn test_runnables_multiple_depth_module() {
74 let (analysis, pos) = analysis_and_position(
75 r#"
76 //- /lib.rs
77 <|> //empty
78 mod foo {
79 mod bar {
80 mod test_mod {
81 #[test]
82 fn test_foo1() {}
83 }
84 }
85 }
86 "#,
87 );
88 let runnables = analysis.runnables(pos.file_id).unwrap();
89 assert_eq_dbg(
90 r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
91 Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
92 &runnables,
93 )
94}
95
96#[test]
97fn test_runnables_no_test_function_in_module() {
98 let (analysis, pos) = analysis_and_position(
99 r#"
100 //- /lib.rs
101 <|> //empty
102 mod test_mod {
103 fn foo1() {}
104 }
105 "#,
106 );
107 let runnables = analysis.runnables(pos.file_id).unwrap();
108 assert_eq_dbg(r#"[]"#, &runnables)
109}
diff --git a/crates/ra_analysis/tests/test/type_of.rs b/crates/ra_analysis/tests/test/type_of.rs
new file mode 100644
index 000000000..9d15b52a8
--- /dev/null
+++ b/crates/ra_analysis/tests/test/type_of.rs
@@ -0,0 +1,77 @@
1use ra_analysis::mock_analysis::single_file_with_range;
2
3#[test]
4fn test_type_of_for_function() {
5 let (analysis, range) = single_file_with_range(
6 "
7 pub fn foo() -> u32 { 1 };
8
9 fn main() {
10 let foo_test = <|>foo()<|>;
11 }
12 ",
13 );
14
15 let type_name = analysis.type_of(range).unwrap().unwrap();
16 assert_eq!("u32", &type_name);
17}
18
19// FIXME: improve type_of to make this work
20#[test]
21fn test_type_of_for_num() {
22 let (analysis, range) = single_file_with_range(
23 r#"
24 fn main() {
25 let foo_test = <|>"foo"<|>;
26 }
27 "#,
28 );
29
30 assert!(analysis.type_of(range).unwrap().is_none());
31}
32// FIXME: improve type_of to make this work
33#[test]
34fn test_type_of_for_binding() {
35 let (analysis, range) = single_file_with_range(
36 "
37 pub fn foo() -> u32 { 1 };
38
39 fn main() {
40 let <|>foo_test<|> = foo();
41 }
42 ",
43 );
44
45 assert!(analysis.type_of(range).unwrap().is_none());
46}
47
48// FIXME: improve type_of to make this work
49#[test]
50fn test_type_of_for_expr_1() {
51 let (analysis, range) = single_file_with_range(
52 "
53 fn main() {
54 let foo = <|>1 + foo_test<|>;
55 }
56 ",
57 );
58
59 let type_name = analysis.type_of(range).unwrap().unwrap();
60 assert_eq!("[unknown]", &type_name);
61}
62
63// FIXME: improve type_of to make this work
64#[test]
65fn test_type_of_for_expr_2() {
66 let (analysis, range) = single_file_with_range(
67 "
68 fn main() {
69 let foo: usize = 1;
70 let bar = <|>1 + foo_test<|>;
71 }
72 ",
73 );
74
75 let type_name = analysis.type_of(range).unwrap().unwrap();
76 assert_eq!("[unknown]", &type_name);
77}
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 939f7fe77..a3b856aa9 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -102,7 +102,7 @@ fn selections(file: &SourceFileNode, start: u32, end: u32) -> String {
102 let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into())); 102 let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into()));
103 while let Some(r) = cur { 103 while let Some(r) = cur {
104 ranges.push(r); 104 ranges.push(r);
105 cur = extend_selection(&file, r); 105 cur = extend_selection(file.syntax(), r);
106 } 106 }
107 let ranges = ranges 107 let ranges = ranges
108 .iter() 108 .iter()
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs
index dac94dd36..744cb2352 100644
--- a/crates/ra_db/src/syntax_ptr.rs
+++ b/crates/ra_db/src/syntax_ptr.rs
@@ -31,6 +31,10 @@ impl LocalSyntaxPtr {
31 pub fn range(self) -> TextRange { 31 pub fn range(self) -> TextRange {
32 self.range 32 self.range
33 } 33 }
34
35 pub fn kind(self) -> SyntaxKind {
36 self.kind
37 }
34} 38}
35 39
36#[test] 40#[test]
diff --git a/crates/ra_editor/src/assists.rs b/crates/ra_editor/src/assists.rs
new file mode 100644
index 000000000..cc40ee4c8
--- /dev/null
+++ b/crates/ra_editor/src/assists.rs
@@ -0,0 +1,185 @@
1//! This modules contains various "assits": suggestions for source code edits
2//! which are likely to occur at a given cursor positon. For example, if the
3//! cursor is on the `,`, a possible assist is swapping the elments around the
4//! comma.
5
6mod flip_comma;
7mod add_derive;
8mod add_impl;
9mod introduce_variable;
10mod change_visibility;
11
12use ra_text_edit::{TextEdit, TextEditBuilder};
13use ra_syntax::{
14 Direction, SyntaxNodeRef, TextUnit, TextRange,SourceFileNode, AstNode,
15 algo::{find_leaf_at_offset, find_covering_node, LeafAtOffset},
16};
17
18use crate::find_node_at_offset;
19
20pub use self::{
21 flip_comma::flip_comma,
22 add_derive::add_derive,
23 add_impl::add_impl,
24 introduce_variable::introduce_variable,
25 change_visibility::change_visibility,
26};
27
28/// Return all the assists applicable at the given position.
29pub fn assists(file: &SourceFileNode, range: TextRange) -> Vec<LocalEdit> {
30 let ctx = AssistCtx::new(file, range);
31 [
32 flip_comma,
33 add_derive,
34 add_impl,
35 introduce_variable,
36 change_visibility,
37 ]
38 .iter()
39 .filter_map(|&assist| ctx.clone().apply(assist))
40 .collect()
41}
42
43#[derive(Debug)]
44pub struct LocalEdit {
45 pub label: String,
46 pub edit: TextEdit,
47 pub cursor_position: Option<TextUnit>,
48}
49
50fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
51 node.siblings(direction)
52 .skip(1)
53 .find(|node| !node.kind().is_trivia())
54}
55
56/// `AssistCtx` allows to apply an assist or check if it could be applied.
57///
58/// Assists use a somewhat overengeneered approach, given the current needs. The
59/// assists workflow consists of two phases. In the first phase, a user asks for
60/// the list of available assists. In the second phase, the user picks a
61/// particular assist and it gets applied.
62///
63/// There are two peculiarities here:
64///
65/// * first, we ideally avoid computing more things then neccessary to answer
66/// "is assist applicable" in the first phase.
67/// * second, when we are appling assist, we don't have a gurantee that there
68/// weren't any changes between the point when user asked for assists and when
69/// they applied a particular assist. So, when applying assist, we need to do
70/// all the checks from scratch.
71///
72/// To avoid repeating the same code twice for both "check" and "apply"
73/// functions, we use an approach remeniscent of that of Django's function based
74/// views dealing with forms. Each assist receives a runtime parameter,
75/// `should_compute_edit`. It first check if an edit is applicable (potentially
76/// computing info required to compute the actual edit). If it is applicable,
77/// and `should_compute_edit` is `true`, it then computes the actual edit.
78///
79/// So, to implement the original assists workflow, we can first apply each edit
80/// with `should_compute_edit = false`, and then applying the selected edit
81/// again, with `should_compute_edit = true` this time.
82///
83/// Note, however, that we don't actually use such two-phase logic at the
84/// moment, because the LSP API is pretty awkward in this place, and it's much
85/// easier to just compute the edit eagarly :-)
86#[derive(Debug, Clone)]
87pub struct AssistCtx<'a> {
88 source_file: &'a SourceFileNode,
89 range: TextRange,
90 should_compute_edit: bool,
91}
92
93#[derive(Debug)]
94pub enum Assist {
95 Applicable,
96 Edit(LocalEdit),
97}
98
99#[derive(Default)]
100struct AssistBuilder {
101 edit: TextEditBuilder,
102 cursor_position: Option<TextUnit>,
103}
104
105impl<'a> AssistCtx<'a> {
106 pub fn new(source_file: &'a SourceFileNode, range: TextRange) -> AssistCtx {
107 AssistCtx {
108 source_file,
109 range,
110 should_compute_edit: false,
111 }
112 }
113
114 pub fn apply(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> Option<LocalEdit> {
115 self.should_compute_edit = true;
116 match assist(self) {
117 None => None,
118 Some(Assist::Edit(e)) => Some(e),
119 Some(Assist::Applicable) => unreachable!(),
120 }
121 }
122
123 pub fn check(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> bool {
124 self.should_compute_edit = false;
125 match assist(self) {
126 None => false,
127 Some(Assist::Edit(_)) => unreachable!(),
128 Some(Assist::Applicable) => true,
129 }
130 }
131
132 fn build(self, label: impl Into<String>, f: impl FnOnce(&mut AssistBuilder)) -> Option<Assist> {
133 if !self.should_compute_edit {
134 return Some(Assist::Applicable);
135 }
136 let mut edit = AssistBuilder::default();
137 f(&mut edit);
138 Some(Assist::Edit(LocalEdit {
139 label: label.into(),
140 edit: edit.edit.finish(),
141 cursor_position: edit.cursor_position,
142 }))
143 }
144
145 pub(crate) fn leaf_at_offset(&self) -> LeafAtOffset<SyntaxNodeRef<'a>> {
146 find_leaf_at_offset(self.source_file.syntax(), self.range.start())
147 }
148 pub(crate) fn node_at_offset<N: AstNode<'a>>(&self) -> Option<N> {
149 find_node_at_offset(self.source_file.syntax(), self.range.start())
150 }
151 pub(crate) fn covering_node(&self) -> SyntaxNodeRef<'a> {
152 find_covering_node(self.source_file.syntax(), self.range)
153 }
154}
155
156impl AssistBuilder {
157 fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
158 self.edit.replace(range, replace_with.into())
159 }
160 #[allow(unused)]
161 fn delete(&mut self, range: TextRange) {
162 self.edit.delete(range)
163 }
164 fn insert(&mut self, offset: TextUnit, text: impl Into<String>) {
165 self.edit.insert(offset, text.into())
166 }
167 fn set_cursor(&mut self, offset: TextUnit) {
168 self.cursor_position = Some(offset)
169 }
170}
171
172#[cfg(test)]
173fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) {
174 crate::test_utils::check_action(before, after, |file, off| {
175 let range = TextRange::offset_len(off, 0.into());
176 AssistCtx::new(file, range).apply(assist)
177 })
178}
179
180#[cfg(test)]
181fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) {
182 crate::test_utils::check_action_range(before, after, |file, range| {
183 AssistCtx::new(file, range).apply(assist)
184 })
185}
diff --git a/crates/ra_editor/src/assists/add_derive.rs b/crates/ra_editor/src/assists/add_derive.rs
new file mode 100644
index 000000000..1e2cd4f30
--- /dev/null
+++ b/crates/ra_editor/src/assists/add_derive.rs
@@ -0,0 +1,84 @@
1use ra_syntax::{
2 ast::{self, AstNode, AttrsOwner},
3 SyntaxKind::{WHITESPACE, COMMENT},
4 TextUnit,
5};
6
7use crate::assists::{AssistCtx, Assist};
8
9pub fn add_derive(ctx: AssistCtx) -> Option<Assist> {
10 let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
11 let node_start = derive_insertion_offset(nominal)?;
12 ctx.build("add `#[derive]`", |edit| {
13 let derive_attr = nominal
14 .attrs()
15 .filter_map(|x| x.as_call())
16 .filter(|(name, _arg)| name == "derive")
17 .map(|(_name, arg)| arg)
18 .next();
19 let offset = match derive_attr {
20 None => {
21 edit.insert(node_start, "#[derive()]\n");
22 node_start + TextUnit::of_str("#[derive(")
23 }
24 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
25 };
26 edit.set_cursor(offset)
27 })
28}
29
30// Insert `derive` after doc comments.
31fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> {
32 let non_ws_child = nominal
33 .syntax()
34 .children()
35 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
36 Some(non_ws_child.range().start())
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use crate::assists::check_assist;
43
44 #[test]
45 fn add_derive_new() {
46 check_assist(
47 add_derive,
48 "struct Foo { a: i32, <|>}",
49 "#[derive(<|>)]\nstruct Foo { a: i32, }",
50 );
51 check_assist(
52 add_derive,
53 "struct Foo { <|> a: i32, }",
54 "#[derive(<|>)]\nstruct Foo { a: i32, }",
55 );
56 }
57
58 #[test]
59 fn add_derive_existing() {
60 check_assist(
61 add_derive,
62 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
63 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
64 );
65 }
66
67 #[test]
68 fn add_derive_new_with_doc_comment() {
69 check_assist(
70 add_derive,
71 "
72/// `Foo` is a pretty important struct.
73/// It does stuff.
74struct Foo { a: i32<|>, }
75 ",
76 "
77/// `Foo` is a pretty important struct.
78/// It does stuff.
79#[derive(<|>)]
80struct Foo { a: i32, }
81 ",
82 );
83 }
84}
diff --git a/crates/ra_editor/src/assists/add_impl.rs b/crates/ra_editor/src/assists/add_impl.rs
new file mode 100644
index 000000000..9353e2717
--- /dev/null
+++ b/crates/ra_editor/src/assists/add_impl.rs
@@ -0,0 +1,66 @@
1use join_to_string::join;
2use ra_syntax::{
3 ast::{self, AstNode, NameOwner, TypeParamsOwner},
4 TextUnit,
5};
6
7use crate::assists::{AssistCtx, Assist};
8
9pub fn add_impl(ctx: AssistCtx) -> Option<Assist> {
10 let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
11 let name = nominal.name()?;
12 ctx.build("add impl", |edit| {
13 let type_params = nominal.type_param_list();
14 let start_offset = nominal.syntax().range().end();
15 let mut buf = String::new();
16 buf.push_str("\n\nimpl");
17 if let Some(type_params) = type_params {
18 type_params.syntax().text().push_to(&mut buf);
19 }
20 buf.push_str(" ");
21 buf.push_str(name.text().as_str());
22 if let Some(type_params) = type_params {
23 let lifetime_params = type_params
24 .lifetime_params()
25 .filter_map(|it| it.lifetime())
26 .map(|it| it.text());
27 let type_params = type_params
28 .type_params()
29 .filter_map(|it| it.name())
30 .map(|it| it.text());
31 join(lifetime_params.chain(type_params))
32 .surround_with("<", ">")
33 .to_buf(&mut buf);
34 }
35 buf.push_str(" {\n");
36 edit.set_cursor(start_offset + TextUnit::of_str(&buf));
37 buf.push_str("\n}");
38 edit.insert(start_offset, buf);
39 })
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::assists::check_assist;
46
47 #[test]
48 fn test_add_impl() {
49 check_assist(
50 add_impl,
51 "struct Foo {<|>}\n",
52 "struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
53 );
54 check_assist(
55 add_impl,
56 "struct Foo<T: Clone> {<|>}",
57 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
58 );
59 check_assist(
60 add_impl,
61 "struct Foo<'a, T: Foo<'a>> {<|>}",
62 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
63 );
64 }
65
66}
diff --git a/crates/ra_editor/src/assists/change_visibility.rs b/crates/ra_editor/src/assists/change_visibility.rs
new file mode 100644
index 000000000..ac75f635e
--- /dev/null
+++ b/crates/ra_editor/src/assists/change_visibility.rs
@@ -0,0 +1,88 @@
1use ra_syntax::{
2 AstNode,
3 ast::{self, VisibilityOwner, NameOwner},
4 SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF, IDENT},
5};
6
7use crate::assists::{AssistCtx, Assist};
8
9pub fn change_visibility(ctx: AssistCtx) -> Option<Assist> {
10 let offset = if let Some(keyword) = ctx.leaf_at_offset().find(|leaf| match leaf.kind() {
11 FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true,
12 _ => false,
13 }) {
14 let parent = keyword.parent()?;
15 let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
16 // Parent is not a definition, can't add visibility
17 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
18 return None;
19 }
20 // Already have visibility, do nothing
21 if parent.children().any(|child| child.kind() == VISIBILITY) {
22 return None;
23 }
24 parent.range().start()
25 } else {
26 let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
27 let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
28 if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
29 return None;
30 }
31 field.syntax().range().start()
32 };
33
34 ctx.build("make pub(crate)", |edit| {
35 edit.insert(offset, "pub(crate) ");
36 edit.set_cursor(offset);
37 })
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use crate::assists::check_assist;
44
45 #[test]
46 fn change_visibility_adds_pub_crate_to_items() {
47 check_assist(
48 change_visibility,
49 "<|>fn foo() {}",
50 "<|>pub(crate) fn foo() {}",
51 );
52 check_assist(
53 change_visibility,
54 "f<|>n foo() {}",
55 "<|>pub(crate) fn foo() {}",
56 );
57 check_assist(
58 change_visibility,
59 "<|>struct Foo {}",
60 "<|>pub(crate) struct Foo {}",
61 );
62 check_assist(
63 change_visibility,
64 "<|>mod foo {}",
65 "<|>pub(crate) mod foo {}",
66 );
67 check_assist(
68 change_visibility,
69 "<|>trait Foo {}",
70 "<|>pub(crate) trait Foo {}",
71 );
72 check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}");
73 check_assist(
74 change_visibility,
75 "unsafe f<|>n foo() {}",
76 "<|>pub(crate) unsafe fn foo() {}",
77 );
78 }
79
80 #[test]
81 fn change_visibility_works_with_struct_fields() {
82 check_assist(
83 change_visibility,
84 "struct S { <|>field: u32 }",
85 "struct S { <|>pub(crate) field: u32 }",
86 )
87 }
88}
diff --git a/crates/ra_editor/src/assists/flip_comma.rs b/crates/ra_editor/src/assists/flip_comma.rs
new file mode 100644
index 000000000..a343413cc
--- /dev/null
+++ b/crates/ra_editor/src/assists/flip_comma.rs
@@ -0,0 +1,31 @@
1use ra_syntax::{
2 Direction,
3 SyntaxKind::COMMA,
4};
5
6use crate::assists::{non_trivia_sibling, AssistCtx, Assist};
7
8pub fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
9 let comma = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COMMA)?;
10 let prev = non_trivia_sibling(comma, Direction::Prev)?;
11 let next = non_trivia_sibling(comma, Direction::Next)?;
12 ctx.build("flip comma", |edit| {
13 edit.replace(prev.range(), next.text());
14 edit.replace(next.range(), prev.text());
15 })
16}
17
18#[cfg(test)]
19mod tests {
20 use super::*;
21 use crate::assists::check_assist;
22
23 #[test]
24 fn flip_comma_works_for_function_parameters() {
25 check_assist(
26 flip_comma,
27 "fn foo(x: i32,<|> y: Result<(), ()>) {}",
28 "fn foo(y: Result<(), ()>,<|> x: i32) {}",
29 )
30 }
31}
diff --git a/crates/ra_editor/src/assists/introduce_variable.rs b/crates/ra_editor/src/assists/introduce_variable.rs
new file mode 100644
index 000000000..782861023
--- /dev/null
+++ b/crates/ra_editor/src/assists/introduce_variable.rs
@@ -0,0 +1,144 @@
1use ra_syntax::{
2 ast::{self, AstNode},
3 SyntaxKind::WHITESPACE,
4 SyntaxNodeRef, TextUnit,
5};
6
7use crate::assists::{AssistCtx, Assist};
8
9pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
10 let node = ctx.covering_node();
11 let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;
12
13 let anchor_stmt = anchor_stmt(expr)?;
14 let indent = anchor_stmt.prev_sibling()?;
15 if indent.kind() != WHITESPACE {
16 return None;
17 }
18 ctx.build("introduce variable", move |edit| {
19 let mut buf = String::new();
20
21 buf.push_str("let var_name = ");
22 expr.syntax().text().push_to(&mut buf);
23 let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) {
24 Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
25 } else {
26 false
27 };
28 if is_full_stmt {
29 edit.replace(expr.syntax().range(), buf);
30 } else {
31 buf.push_str(";");
32 indent.text().push_to(&mut buf);
33 edit.replace(expr.syntax().range(), "var_name".to_string());
34 edit.insert(anchor_stmt.range().start(), buf);
35 }
36 edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
37 })
38}
39
40/// Statement or last in the block expression, which will follow
41/// the freshly introduced var.
42fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> {
43 expr.syntax().ancestors().find(|&node| {
44 if ast::Stmt::cast(node).is_some() {
45 return true;
46 }
47 if let Some(expr) = node
48 .parent()
49 .and_then(ast::Block::cast)
50 .and_then(|it| it.expr())
51 {
52 if expr.syntax() == node {
53 return true;
54 }
55 }
56 false
57 })
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::assists::check_assist_range;
64
65 #[test]
66 fn test_introduce_var_simple() {
67 check_assist_range(
68 introduce_variable,
69 "
70fn foo() {
71 foo(<|>1 + 1<|>);
72}",
73 "
74fn foo() {
75 let <|>var_name = 1 + 1;
76 foo(var_name);
77}",
78 );
79 }
80
81 #[test]
82 fn test_introduce_var_expr_stmt() {
83 check_assist_range(
84 introduce_variable,
85 "
86fn foo() {
87 <|>1 + 1<|>;
88}",
89 "
90fn foo() {
91 let <|>var_name = 1 + 1;
92}",
93 );
94 }
95
96 #[test]
97 fn test_introduce_var_part_of_expr_stmt() {
98 check_assist_range(
99 introduce_variable,
100 "
101fn foo() {
102 <|>1<|> + 1;
103}",
104 "
105fn foo() {
106 let <|>var_name = 1;
107 var_name + 1;
108}",
109 );
110 }
111
112 #[test]
113 fn test_introduce_var_last_expr() {
114 check_assist_range(
115 introduce_variable,
116 "
117fn foo() {
118 bar(<|>1 + 1<|>)
119}",
120 "
121fn foo() {
122 let <|>var_name = 1 + 1;
123 bar(var_name)
124}",
125 );
126 }
127
128 #[test]
129 fn test_introduce_var_last_full_expr() {
130 check_assist_range(
131 introduce_variable,
132 "
133fn foo() {
134 <|>bar(1 + 1)<|>
135}",
136 "
137fn foo() {
138 let <|>var_name = bar(1 + 1);
139 var_name
140}",
141 );
142 }
143
144}
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs
deleted file mode 100644
index 7615f37a6..000000000
--- a/crates/ra_editor/src/code_actions.rs
+++ /dev/null
@@ -1,415 +0,0 @@
1use join_to_string::join;
2
3use ra_syntax::{
4 algo::{find_covering_node, find_leaf_at_offset},
5 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
6 Direction, SourceFileNode,
7 SyntaxKind::{COMMA, WHITESPACE, COMMENT, VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF},
8 SyntaxNodeRef, TextRange, TextUnit,
9};
10
11use crate::{find_node_at_offset, TextEdit, TextEditBuilder};
12
13#[derive(Debug)]
14pub struct LocalEdit {
15 pub label: String,
16 pub edit: TextEdit,
17 pub cursor_position: Option<TextUnit>,
18}
19
20pub fn flip_comma<'a>(
21 file: &'a SourceFileNode,
22 offset: TextUnit,
23) -> Option<impl FnOnce() -> LocalEdit + 'a> {
24 let syntax = file.syntax();
25
26 let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?;
27 let prev = non_trivia_sibling(comma, Direction::Prev)?;
28 let next = non_trivia_sibling(comma, Direction::Next)?;
29 Some(move || {
30 let mut edit = TextEditBuilder::new();
31 edit.replace(prev.range(), next.text().to_string());
32 edit.replace(next.range(), prev.text().to_string());
33 LocalEdit {
34 label: "flip comma".to_string(),
35 edit: edit.finish(),
36 cursor_position: None,
37 }
38 })
39}
40
41pub fn add_derive<'a>(
42 file: &'a SourceFileNode,
43 offset: TextUnit,
44) -> Option<impl FnOnce() -> LocalEdit + 'a> {
45 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
46 let node_start = derive_insertion_offset(nominal)?;
47 return Some(move || {
48 let derive_attr = nominal
49 .attrs()
50 .filter_map(|x| x.as_call())
51 .filter(|(name, _arg)| name == "derive")
52 .map(|(_name, arg)| arg)
53 .next();
54 let mut edit = TextEditBuilder::new();
55 let offset = match derive_attr {
56 None => {
57 edit.insert(node_start, "#[derive()]\n".to_string());
58 node_start + TextUnit::of_str("#[derive(")
59 }
60 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
61 };
62 LocalEdit {
63 label: "add `#[derive]`".to_string(),
64 edit: edit.finish(),
65 cursor_position: Some(offset),
66 }
67 });
68
69 // Insert `derive` after doc comments.
70 fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> {
71 let non_ws_child = nominal
72 .syntax()
73 .children()
74 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
75 Some(non_ws_child.range().start())
76 }
77}
78
79pub fn add_impl<'a>(
80 file: &'a SourceFileNode,
81 offset: TextUnit,
82) -> Option<impl FnOnce() -> LocalEdit + 'a> {
83 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
84 let name = nominal.name()?;
85
86 Some(move || {
87 let type_params = nominal.type_param_list();
88 let mut edit = TextEditBuilder::new();
89 let start_offset = nominal.syntax().range().end();
90 let mut buf = String::new();
91 buf.push_str("\n\nimpl");
92 if let Some(type_params) = type_params {
93 type_params.syntax().text().push_to(&mut buf);
94 }
95 buf.push_str(" ");
96 buf.push_str(name.text().as_str());
97 if let Some(type_params) = type_params {
98 let lifetime_params = type_params
99 .lifetime_params()
100 .filter_map(|it| it.lifetime())
101 .map(|it| it.text());
102 let type_params = type_params
103 .type_params()
104 .filter_map(|it| it.name())
105 .map(|it| it.text());
106 join(lifetime_params.chain(type_params))
107 .surround_with("<", ">")
108 .to_buf(&mut buf);
109 }
110 buf.push_str(" {\n");
111 let offset = start_offset + TextUnit::of_str(&buf);
112 buf.push_str("\n}");
113 edit.insert(start_offset, buf);
114 LocalEdit {
115 label: "add impl".to_string(),
116 edit: edit.finish(),
117 cursor_position: Some(offset),
118 }
119 })
120}
121
122pub fn introduce_variable<'a>(
123 file: &'a SourceFileNode,
124 range: TextRange,
125) -> Option<impl FnOnce() -> LocalEdit + 'a> {
126 let node = find_covering_node(file.syntax(), range);
127 let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;
128
129 let anchor_stmt = anchor_stmt(expr)?;
130 let indent = anchor_stmt.prev_sibling()?;
131 if indent.kind() != WHITESPACE {
132 return None;
133 }
134 return Some(move || {
135 let mut buf = String::new();
136 let mut edit = TextEditBuilder::new();
137
138 buf.push_str("let var_name = ");
139 expr.syntax().text().push_to(&mut buf);
140 let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) {
141 Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
142 } else {
143 false
144 };
145 if is_full_stmt {
146 edit.replace(expr.syntax().range(), buf);
147 } else {
148 buf.push_str(";");
149 indent.text().push_to(&mut buf);
150 edit.replace(expr.syntax().range(), "var_name".to_string());
151 edit.insert(anchor_stmt.range().start(), buf);
152 }
153 let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let ");
154 LocalEdit {
155 label: "introduce variable".to_string(),
156 edit: edit.finish(),
157 cursor_position: Some(cursor_position),
158 }
159 });
160
161 /// Statement or last in the block expression, which will follow
162 /// the freshly introduced var.
163 fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> {
164 expr.syntax().ancestors().find(|&node| {
165 if ast::Stmt::cast(node).is_some() {
166 return true;
167 }
168 if let Some(expr) = node
169 .parent()
170 .and_then(ast::Block::cast)
171 .and_then(|it| it.expr())
172 {
173 if expr.syntax() == node {
174 return true;
175 }
176 }
177 false
178 })
179 }
180}
181
182pub fn make_pub_crate<'a>(
183 file: &'a SourceFileNode,
184 offset: TextUnit,
185) -> Option<impl FnOnce() -> LocalEdit + 'a> {
186 let syntax = file.syntax();
187
188 let keyword = find_leaf_at_offset(syntax, offset).find(|leaf| match leaf.kind() {
189 FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true,
190 _ => false,
191 })?;
192 let parent = keyword.parent()?;
193 let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
194 let node_start = parent.range().start();
195 Some(move || {
196 let mut edit = TextEditBuilder::new();
197
198 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind())
199 || parent.children().any(|child| child.kind() == VISIBILITY)
200 {
201 return LocalEdit {
202 label: "make pub crate".to_string(),
203 edit: edit.finish(),
204 cursor_position: Some(offset),
205 };
206 }
207
208 edit.insert(node_start, "pub(crate) ".to_string());
209 LocalEdit {
210 label: "make pub crate".to_string(),
211 edit: edit.finish(),
212 cursor_position: Some(node_start),
213 }
214 })
215}
216
217fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
218 node.siblings(direction)
219 .skip(1)
220 .find(|node| !node.kind().is_trivia())
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::test_utils::{check_action, check_action_range};
227
228 #[test]
229 fn test_swap_comma() {
230 check_action(
231 "fn foo(x: i32,<|> y: Result<(), ()>) {}",
232 "fn foo(y: Result<(), ()>,<|> x: i32) {}",
233 |file, off| flip_comma(file, off).map(|f| f()),
234 )
235 }
236
237 #[test]
238 fn add_derive_new() {
239 check_action(
240 "struct Foo { a: i32, <|>}",
241 "#[derive(<|>)]\nstruct Foo { a: i32, }",
242 |file, off| add_derive(file, off).map(|f| f()),
243 );
244 check_action(
245 "struct Foo { <|> a: i32, }",
246 "#[derive(<|>)]\nstruct Foo { a: i32, }",
247 |file, off| add_derive(file, off).map(|f| f()),
248 );
249 }
250
251 #[test]
252 fn add_derive_existing() {
253 check_action(
254 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
255 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
256 |file, off| add_derive(file, off).map(|f| f()),
257 );
258 }
259
260 #[test]
261 fn add_derive_new_with_doc_comment() {
262 check_action(
263 "
264/// `Foo` is a pretty important struct.
265/// It does stuff.
266struct Foo { a: i32<|>, }
267 ",
268 "
269/// `Foo` is a pretty important struct.
270/// It does stuff.
271#[derive(<|>)]
272struct Foo { a: i32, }
273 ",
274 |file, off| add_derive(file, off).map(|f| f()),
275 );
276 }
277
278 #[test]
279 fn test_add_impl() {
280 check_action(
281 "struct Foo {<|>}\n",
282 "struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
283 |file, off| add_impl(file, off).map(|f| f()),
284 );
285 check_action(
286 "struct Foo<T: Clone> {<|>}",
287 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
288 |file, off| add_impl(file, off).map(|f| f()),
289 );
290 check_action(
291 "struct Foo<'a, T: Foo<'a>> {<|>}",
292 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
293 |file, off| add_impl(file, off).map(|f| f()),
294 );
295 }
296
297 #[test]
298 fn test_introduce_var_simple() {
299 check_action_range(
300 "
301fn foo() {
302 foo(<|>1 + 1<|>);
303}",
304 "
305fn foo() {
306 let <|>var_name = 1 + 1;
307 foo(var_name);
308}",
309 |file, range| introduce_variable(file, range).map(|f| f()),
310 );
311 }
312
313 #[test]
314 fn test_introduce_var_expr_stmt() {
315 check_action_range(
316 "
317fn foo() {
318 <|>1 + 1<|>;
319}",
320 "
321fn foo() {
322 let <|>var_name = 1 + 1;
323}",
324 |file, range| introduce_variable(file, range).map(|f| f()),
325 );
326 }
327
328 #[test]
329 fn test_introduce_var_part_of_expr_stmt() {
330 check_action_range(
331 "
332fn foo() {
333 <|>1<|> + 1;
334}",
335 "
336fn foo() {
337 let <|>var_name = 1;
338 var_name + 1;
339}",
340 |file, range| introduce_variable(file, range).map(|f| f()),
341 );
342 }
343
344 #[test]
345 fn test_introduce_var_last_expr() {
346 check_action_range(
347 "
348fn foo() {
349 bar(<|>1 + 1<|>)
350}",
351 "
352fn foo() {
353 let <|>var_name = 1 + 1;
354 bar(var_name)
355}",
356 |file, range| introduce_variable(file, range).map(|f| f()),
357 );
358 }
359
360 #[test]
361 fn test_introduce_var_last_full_expr() {
362 check_action_range(
363 "
364fn foo() {
365 <|>bar(1 + 1)<|>
366}",
367 "
368fn foo() {
369 let <|>var_name = bar(1 + 1);
370 var_name
371}",
372 |file, range| introduce_variable(file, range).map(|f| f()),
373 );
374 }
375
376 #[test]
377 fn test_make_pub_crate() {
378 check_action(
379 "<|>fn foo() {}",
380 "<|>pub(crate) fn foo() {}",
381 |file, off| make_pub_crate(file, off).map(|f| f()),
382 );
383 check_action(
384 "f<|>n foo() {}",
385 "<|>pub(crate) fn foo() {}",
386 |file, off| make_pub_crate(file, off).map(|f| f()),
387 );
388 check_action(
389 "<|>struct Foo {}",
390 "<|>pub(crate) struct Foo {}",
391 |file, off| make_pub_crate(file, off).map(|f| f()),
392 );
393 check_action("<|>mod foo {}", "<|>pub(crate) mod foo {}", |file, off| {
394 make_pub_crate(file, off).map(|f| f())
395 });
396 check_action(
397 "<|>trait Foo {}",
398 "<|>pub(crate) trait Foo {}",
399 |file, off| make_pub_crate(file, off).map(|f| f()),
400 );
401 check_action("m<|>od {}", "<|>pub(crate) mod {}", |file, off| {
402 make_pub_crate(file, off).map(|f| f())
403 });
404 check_action(
405 "pub(crate) f<|>n foo() {}",
406 "pub(crate) f<|>n foo() {}",
407 |file, off| make_pub_crate(file, off).map(|f| f()),
408 );
409 check_action(
410 "unsafe f<|>n foo() {}",
411 "<|>pub(crate) unsafe fn foo() {}",
412 |file, off| make_pub_crate(file, off).map(|f| f()),
413 );
414 }
415}
diff --git a/crates/ra_editor/src/diagnostics.rs b/crates/ra_editor/src/diagnostics.rs
index 1b336cfe2..199b0e502 100644
--- a/crates/ra_editor/src/diagnostics.rs
+++ b/crates/ra_editor/src/diagnostics.rs
@@ -57,7 +57,7 @@ fn check_unnecessary_braces_in_use_statement(
57 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree) 57 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
58 .unwrap_or_else(|| { 58 .unwrap_or_else(|| {
59 let to_replace = single_use_tree.syntax().text().to_string(); 59 let to_replace = single_use_tree.syntax().text().to_string();
60 let mut edit_builder = TextEditBuilder::new(); 60 let mut edit_builder = TextEditBuilder::default();
61 edit_builder.delete(range); 61 edit_builder.delete(range);
62 edit_builder.insert(range.start(), to_replace); 62 edit_builder.insert(range.start(), to_replace);
63 edit_builder.finish() 63 edit_builder.finish()
@@ -93,7 +93,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
93 let start = use_tree_list_node.prev_sibling()?.range().start(); 93 let start = use_tree_list_node.prev_sibling()?.range().start();
94 let end = use_tree_list_node.range().end(); 94 let end = use_tree_list_node.range().end();
95 let range = TextRange::from_to(start, end); 95 let range = TextRange::from_to(start, end);
96 let mut edit_builder = TextEditBuilder::new(); 96 let mut edit_builder = TextEditBuilder::default();
97 edit_builder.delete(range); 97 edit_builder.delete(range);
98 return Some(edit_builder.finish()); 98 return Some(edit_builder.finish());
99 } 99 }
@@ -111,7 +111,7 @@ fn check_struct_shorthand_initialization(
111 let field_name = name_ref.syntax().text().to_string(); 111 let field_name = name_ref.syntax().text().to_string();
112 let field_expr = expr.syntax().text().to_string(); 112 let field_expr = expr.syntax().text().to_string();
113 if field_name == field_expr { 113 if field_name == field_expr {
114 let mut edit_builder = TextEditBuilder::new(); 114 let mut edit_builder = TextEditBuilder::default();
115 edit_builder.delete(named_field.syntax().range()); 115 edit_builder.delete(named_field.syntax().range());
116 edit_builder.insert(named_field.syntax().range().start(), field_name); 116 edit_builder.insert(named_field.syntax().range().start(), field_name);
117 let edit = edit_builder.finish(); 117 let edit = edit_builder.finish();
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs
index 4665a336a..7a423852b 100644
--- a/crates/ra_editor/src/extend_selection.rs
+++ b/crates/ra_editor/src/extend_selection.rs
@@ -1,16 +1,12 @@
1use ra_syntax::{ 1use ra_syntax::{
2 algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, 2 algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset},
3 Direction, SourceFileNode, 3 Direction,
4 SyntaxKind::*, 4 SyntaxKind::*,
5 SyntaxNodeRef, TextRange, TextUnit, 5 SyntaxNodeRef, TextRange, TextUnit,
6}; 6};
7 7
8pub fn extend_selection(file: &SourceFileNode, range: TextRange) -> Option<TextRange> { 8pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
9 let syntax = file.syntax(); 9 let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING];
10 extend(syntax.borrowed(), range)
11}
12
13pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
14 if range.is_empty() { 10 if range.is_empty() {
15 let offset = range.start(); 11 let offset = range.start();
16 let mut leaves = find_leaf_at_offset(root, offset); 12 let mut leaves = find_leaf_at_offset(root, offset);
@@ -20,8 +16,8 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange>
20 let leaf_range = match leaves { 16 let leaf_range = match leaves {
21 LeafAtOffset::None => return None, 17 LeafAtOffset::None => return None,
22 LeafAtOffset::Single(l) => { 18 LeafAtOffset::Single(l) => {
23 if l.kind() == COMMENT { 19 if string_kinds.contains(&l.kind()) {
24 extend_single_word_in_comment(l, offset).unwrap_or_else(|| l.range()) 20 extend_single_word_in_comment_or_string(l, offset).unwrap_or_else(|| l.range())
25 } else { 21 } else {
26 l.range() 22 l.range()
27 } 23 }
@@ -31,7 +27,7 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange>
31 return Some(leaf_range); 27 return Some(leaf_range);
32 }; 28 };
33 let node = find_covering_node(root, range); 29 let node = find_covering_node(root, range);
34 if node.kind() == COMMENT && range == node.range() { 30 if string_kinds.contains(&node.kind()) && range == node.range() {
35 if let Some(range) = extend_comments(node) { 31 if let Some(range) = extend_comments(node) {
36 return Some(range); 32 return Some(range);
37 } 33 }
@@ -43,7 +39,10 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange>
43 } 39 }
44} 40}
45 41
46fn extend_single_word_in_comment(leaf: SyntaxNodeRef, offset: TextUnit) -> Option<TextRange> { 42fn extend_single_word_in_comment_or_string(
43 leaf: SyntaxNodeRef,
44 offset: TextUnit,
45) -> Option<TextRange> {
47 let text: &str = leaf.leaf_text()?; 46 let text: &str = leaf.leaf_text()?;
48 let cursor_position: u32 = (offset - leaf.range().start()).into(); 47 let cursor_position: u32 = (offset - leaf.range().start()).into();
49 48
@@ -126,6 +125,7 @@ fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
126#[cfg(test)] 125#[cfg(test)]
127mod tests { 126mod tests {
128 use super::*; 127 use super::*;
128 use ra_syntax::SourceFileNode;
129 use test_utils::extract_offset; 129 use test_utils::extract_offset;
130 130
131 fn do_check(before: &str, afters: &[&str]) { 131 fn do_check(before: &str, afters: &[&str]) {
@@ -133,7 +133,7 @@ mod tests {
133 let file = SourceFileNode::parse(&before); 133 let file = SourceFileNode::parse(&before);
134 let mut range = TextRange::offset_len(cursor, 0.into()); 134 let mut range = TextRange::offset_len(cursor, 0.into());
135 for &after in afters { 135 for &after in afters {
136 range = extend_selection(&file, range).unwrap(); 136 range = extend_selection(file.syntax(), range).unwrap();
137 let actual = &before[range]; 137 let actual = &before[range];
138 assert_eq!(after, actual); 138 assert_eq!(after, actual);
139 } 139 }
@@ -266,4 +266,16 @@ impl S {
266 &["hello", "// hello world"], 266 &["hello", "// hello world"],
267 ); 267 );
268 } 268 }
269
270 #[test]
271 fn test_extend_selection_string() {
272 do_check(
273 r#"
274fn bar(){}
275
276" fn f<|>oo() {"
277 "#,
278 &["foo", "\" fn foo() {\""],
279 );
280 }
269} 281}
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index a65637d52..ac283e2e0 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -1,28 +1,28 @@
1mod code_actions; 1pub mod assists;
2mod extend_selection; 2mod extend_selection;
3mod folding_ranges; 3mod folding_ranges;
4mod line_index; 4mod line_index;
5mod line_index_utils; 5mod line_index_utils;
6mod symbols; 6mod structure;
7#[cfg(test)] 7#[cfg(test)]
8mod test_utils; 8mod test_utils;
9mod typing; 9mod typing;
10mod diagnostics; 10mod diagnostics;
11 11
12pub use self::{ 12pub use self::{
13 code_actions::{add_derive, add_impl, flip_comma, introduce_variable, make_pub_crate, LocalEdit}, 13 assists::LocalEdit,
14 extend_selection::extend_selection, 14 extend_selection::extend_selection,
15 folding_ranges::{folding_ranges, Fold, FoldKind}, 15 folding_ranges::{folding_ranges, Fold, FoldKind},
16 line_index::{LineCol, LineIndex}, 16 line_index::{LineCol, LineIndex},
17 line_index_utils::translate_offset_with_edit, 17 line_index_utils::translate_offset_with_edit,
18 symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, 18 structure::{file_structure, StructureNode},
19 typing::{join_lines, on_enter, on_eq_typed}, 19 typing::{join_lines, on_enter, on_eq_typed},
20 diagnostics::diagnostics 20 diagnostics::diagnostics
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::TextEditBuilder;
23use ra_syntax::{ 23use ra_syntax::{
24 algo::find_leaf_at_offset, 24 algo::find_leaf_at_offset,
25 ast::{self, AstNode, NameOwner}, 25 ast::{self, AstNode},
26 SourceFileNode, 26 SourceFileNode,
27 SyntaxKind::{self, *}, 27 SyntaxKind::{self, *},
28 SyntaxNodeRef, TextRange, TextUnit, Direction, 28 SyntaxNodeRef, TextRange, TextUnit, Direction,
@@ -49,18 +49,6 @@ pub struct Diagnostic {
49 pub fix: Option<LocalEdit>, 49 pub fix: Option<LocalEdit>,
50} 50}
51 51
52#[derive(Debug)]
53pub struct Runnable {
54 pub range: TextRange,
55 pub kind: RunnableKind,
56}
57
58#[derive(Debug)]
59pub enum RunnableKind {
60 Test { name: String },
61 Bin,
62}
63
64pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 52pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
65 const BRACES: &[SyntaxKind] = &[ 53 const BRACES: &[SyntaxKind] = &[
66 L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE, 54 L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE,
@@ -79,11 +67,11 @@ pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUni
79 Some(matching_node.range().start()) 67 Some(matching_node.range().start())
80} 68}
81 69
82pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> { 70pub fn highlight(root: SyntaxNodeRef) -> Vec<HighlightedRange> {
83 // Visited nodes to handle highlighting priorities 71 // Visited nodes to handle highlighting priorities
84 let mut highlighted = FxHashSet::default(); 72 let mut highlighted = FxHashSet::default();
85 let mut res = Vec::new(); 73 let mut res = Vec::new();
86 for node in file.syntax().descendants() { 74 for node in root.descendants() {
87 if highlighted.contains(&node) { 75 if highlighted.contains(&node) {
88 continue; 76 continue;
89 } 77 }
@@ -133,29 +121,6 @@ pub fn syntax_tree(file: &SourceFileNode) -> String {
133 ::ra_syntax::utils::dump_tree(file.syntax()) 121 ::ra_syntax::utils::dump_tree(file.syntax())
134} 122}
135 123
136pub fn runnables(file: &SourceFileNode) -> Vec<Runnable> {
137 file.syntax()
138 .descendants()
139 .filter_map(ast::FnDef::cast)
140 .filter_map(|f| {
141 let name = f.name()?.text();
142 let kind = if name == "main" {
143 RunnableKind::Bin
144 } else if f.has_atom_attr("test") {
145 RunnableKind::Test {
146 name: name.to_string(),
147 }
148 } else {
149 return None;
150 };
151 Some(Runnable {
152 range: f.syntax().range(),
153 kind,
154 })
155 })
156 .collect()
157}
158
159pub fn find_node_at_offset<'a, N: AstNode<'a>>( 124pub fn find_node_at_offset<'a, N: AstNode<'a>>(
160 syntax: SyntaxNodeRef<'a>, 125 syntax: SyntaxNodeRef<'a>,
161 offset: TextUnit, 126 offset: TextUnit,
@@ -178,7 +143,7 @@ fn main() {}
178 println!("Hello, {}!", 92); 143 println!("Hello, {}!", 92);
179"#, 144"#,
180 ); 145 );
181 let hls = highlight(&file); 146 let hls = highlight(file.syntax());
182 assert_eq_dbg( 147 assert_eq_dbg(
183 r#"[HighlightedRange { range: [1; 11), tag: "comment" }, 148 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
184 HighlightedRange { range: [12; 14), tag: "keyword" }, 149 HighlightedRange { range: [12; 14), tag: "keyword" },
@@ -191,29 +156,6 @@ fn main() {}
191 } 156 }
192 157
193 #[test] 158 #[test]
194 fn test_runnables() {
195 let file = SourceFileNode::parse(
196 r#"
197fn main() {}
198
199#[test]
200fn test_foo() {}
201
202#[test]
203#[ignore]
204fn test_foo() {}
205"#,
206 );
207 let runnables = runnables(&file);
208 assert_eq_dbg(
209 r#"[Runnable { range: [1; 13), kind: Bin },
210 Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
211 Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
212 &runnables,
213 )
214 }
215
216 #[test]
217 fn test_matching_brace() { 159 fn test_matching_brace() {
218 fn do_check(before: &str, after: &str) { 160 fn do_check(before: &str, after: &str) {
219 let (pos, before) = extract_offset(before); 161 let (pos, before) = extract_offset(before);
diff --git a/crates/ra_editor/src/structure.rs b/crates/ra_editor/src/structure.rs
new file mode 100644
index 000000000..2292b1ddf
--- /dev/null
+++ b/crates/ra_editor/src/structure.rs
@@ -0,0 +1,129 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, NameOwner},
6 AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
19 let mut res = Vec::new();
20 let mut stack = Vec::new();
21
22 for event in file.syntax().preorder() {
23 match event {
24 WalkEvent::Enter(node) => {
25 if let Some(mut symbol) = structure_node(node) {
26 symbol.parent = stack.last().map(|&n| n);
27 stack.push(res.len());
28 res.push(symbol);
29 }
30 }
31 WalkEvent::Leave(node) => {
32 if structure_node(node).is_some() {
33 stack.pop().unwrap();
34 }
35 }
36 }
37 }
38 res
39}
40
41fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
42 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
43 let name = node.name()?;
44 Some(StructureNode {
45 parent: None,
46 label: name.text().to_string(),
47 navigation_range: name.syntax().range(),
48 node_range: node.syntax().range(),
49 kind: node.syntax().kind(),
50 })
51 }
52
53 visitor()
54 .visit(decl::<ast::FnDef>)
55 .visit(decl::<ast::StructDef>)
56 .visit(decl::<ast::NamedFieldDef>)
57 .visit(decl::<ast::EnumDef>)
58 .visit(decl::<ast::TraitDef>)
59 .visit(decl::<ast::Module>)
60 .visit(decl::<ast::TypeDef>)
61 .visit(decl::<ast::ConstDef>)
62 .visit(decl::<ast::StaticDef>)
63 .visit(|im: ast::ImplItem| {
64 let target_type = im.target_type()?;
65 let target_trait = im.target_trait();
66 let label = match target_trait {
67 None => format!("impl {}", target_type.syntax().text()),
68 Some(t) => format!(
69 "impl {} for {}",
70 t.syntax().text(),
71 target_type.syntax().text(),
72 ),
73 };
74
75 let node = StructureNode {
76 parent: None,
77 label,
78 navigation_range: target_type.syntax().range(),
79 node_range: im.syntax().range(),
80 kind: im.syntax().kind(),
81 };
82 Some(node)
83 })
84 .accept(node)?
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use test_utils::assert_eq_dbg;
91
92 #[test]
93 fn test_file_structure() {
94 let file = SourceFileNode::parse(
95 r#"
96struct Foo {
97 x: i32
98}
99
100mod m {
101 fn bar() {}
102}
103
104enum E { X, Y(i32) }
105type T = ();
106static S: i32 = 92;
107const C: i32 = 92;
108
109impl E {}
110
111impl fmt::Debug for E {}
112"#,
113 );
114 let structure = file_structure(&file);
115 assert_eq_dbg(
116 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
117 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
118 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
119 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
120 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
121 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
122 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
123 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
124 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
125 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
126 &structure,
127 )
128 }
129}
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs
deleted file mode 100644
index 9e25decfb..000000000
--- a/crates/ra_editor/src/symbols.rs
+++ /dev/null
@@ -1,246 +0,0 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, DocCommentsOwner, NameOwner},
6 AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct FileSymbol {
20 pub name: SmolStr,
21 pub node_range: TextRange,
22 pub kind: SyntaxKind,
23}
24
25impl FileSymbol {
26 pub fn docs(&self, file: &SourceFileNode) -> Option<String> {
27 file.syntax()
28 .descendants()
29 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
30 .filter_map(|node: SyntaxNodeRef| {
31 fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
32 let comments = node.doc_comment_text();
33 if comments.is_empty() {
34 None
35 } else {
36 Some(comments)
37 }
38 }
39
40 visitor()
41 .visit(doc_comments::<ast::FnDef>)
42 .visit(doc_comments::<ast::StructDef>)
43 .visit(doc_comments::<ast::EnumDef>)
44 .visit(doc_comments::<ast::TraitDef>)
45 .visit(doc_comments::<ast::Module>)
46 .visit(doc_comments::<ast::TypeDef>)
47 .visit(doc_comments::<ast::ConstDef>)
48 .visit(doc_comments::<ast::StaticDef>)
49 .accept(node)?
50 })
51 .nth(0)
52 }
53 /// Get a description of this node.
54 ///
55 /// e.g. `struct Name`, `enum Name`, `fn Name`
56 pub fn description(&self, file: &SourceFileNode) -> Option<String> {
57 // TODO: After type inference is done, add type information to improve the output
58 file.syntax()
59 .descendants()
60 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
61 .filter_map(|node: SyntaxNodeRef| {
62 // TODO: Refactor to be have less repetition
63 visitor()
64 .visit(|node: ast::FnDef| {
65 let mut string = "fn ".to_string();
66 node.name()?.syntax().text().push_to(&mut string);
67 Some(string)
68 })
69 .visit(|node: ast::StructDef| {
70 let mut string = "struct ".to_string();
71 node.name()?.syntax().text().push_to(&mut string);
72 Some(string)
73 })
74 .visit(|node: ast::EnumDef| {
75 let mut string = "enum ".to_string();
76 node.name()?.syntax().text().push_to(&mut string);
77 Some(string)
78 })
79 .visit(|node: ast::TraitDef| {
80 let mut string = "trait ".to_string();
81 node.name()?.syntax().text().push_to(&mut string);
82 Some(string)
83 })
84 .visit(|node: ast::Module| {
85 let mut string = "mod ".to_string();
86 node.name()?.syntax().text().push_to(&mut string);
87 Some(string)
88 })
89 .visit(|node: ast::TypeDef| {
90 let mut string = "type ".to_string();
91 node.name()?.syntax().text().push_to(&mut string);
92 Some(string)
93 })
94 .visit(|node: ast::ConstDef| {
95 let mut string = "const ".to_string();
96 node.name()?.syntax().text().push_to(&mut string);
97 Some(string)
98 })
99 .visit(|node: ast::StaticDef| {
100 let mut string = "static ".to_string();
101 node.name()?.syntax().text().push_to(&mut string);
102 Some(string)
103 })
104 .accept(node)?
105 })
106 .nth(0)
107 }
108}
109
110pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> {
111 file.syntax().descendants().filter_map(to_symbol).collect()
112}
113
114fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
115 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
116 let name = node.name()?;
117 Some(FileSymbol {
118 name: name.text(),
119 node_range: node.syntax().range(),
120 kind: node.syntax().kind(),
121 })
122 }
123 visitor()
124 .visit(decl::<ast::FnDef>)
125 .visit(decl::<ast::StructDef>)
126 .visit(decl::<ast::EnumDef>)
127 .visit(decl::<ast::TraitDef>)
128 .visit(decl::<ast::Module>)
129 .visit(decl::<ast::TypeDef>)
130 .visit(decl::<ast::ConstDef>)
131 .visit(decl::<ast::StaticDef>)
132 .accept(node)?
133}
134
135pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
136 let mut res = Vec::new();
137 let mut stack = Vec::new();
138
139 for event in file.syntax().preorder() {
140 match event {
141 WalkEvent::Enter(node) => {
142 if let Some(mut symbol) = structure_node(node) {
143 symbol.parent = stack.last().map(|&n| n);
144 stack.push(res.len());
145 res.push(symbol);
146 }
147 }
148 WalkEvent::Leave(node) => {
149 if structure_node(node).is_some() {
150 stack.pop().unwrap();
151 }
152 }
153 }
154 }
155 res
156}
157
158fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
159 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
160 let name = node.name()?;
161 Some(StructureNode {
162 parent: None,
163 label: name.text().to_string(),
164 navigation_range: name.syntax().range(),
165 node_range: node.syntax().range(),
166 kind: node.syntax().kind(),
167 })
168 }
169
170 visitor()
171 .visit(decl::<ast::FnDef>)
172 .visit(decl::<ast::StructDef>)
173 .visit(decl::<ast::NamedFieldDef>)
174 .visit(decl::<ast::EnumDef>)
175 .visit(decl::<ast::TraitDef>)
176 .visit(decl::<ast::Module>)
177 .visit(decl::<ast::TypeDef>)
178 .visit(decl::<ast::ConstDef>)
179 .visit(decl::<ast::StaticDef>)
180 .visit(|im: ast::ImplItem| {
181 let target_type = im.target_type()?;
182 let target_trait = im.target_trait();
183 let label = match target_trait {
184 None => format!("impl {}", target_type.syntax().text()),
185 Some(t) => format!(
186 "impl {} for {}",
187 t.syntax().text(),
188 target_type.syntax().text(),
189 ),
190 };
191
192 let node = StructureNode {
193 parent: None,
194 label,
195 navigation_range: target_type.syntax().range(),
196 node_range: im.syntax().range(),
197 kind: im.syntax().kind(),
198 };
199 Some(node)
200 })
201 .accept(node)?
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use test_utils::assert_eq_dbg;
208
209 #[test]
210 fn test_file_structure() {
211 let file = SourceFileNode::parse(
212 r#"
213struct Foo {
214 x: i32
215}
216
217mod m {
218 fn bar() {}
219}
220
221enum E { X, Y(i32) }
222type T = ();
223static S: i32 = 92;
224const C: i32 = 92;
225
226impl E {}
227
228impl fmt::Debug for E {}
229"#,
230 );
231 let symbols = file_structure(&file);
232 assert_eq_dbg(
233 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
234 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
235 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
236 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
237 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
238 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
239 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
240 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
241 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
242 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
243 &symbols,
244 )
245 }
246}
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs
index 21d068a7b..dd3d0f260 100644
--- a/crates/ra_editor/src/typing.rs
+++ b/crates/ra_editor/src/typing.rs
@@ -21,7 +21,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
21 None => { 21 None => {
22 return LocalEdit { 22 return LocalEdit {
23 label: "join lines".to_string(), 23 label: "join lines".to_string(),
24 edit: TextEditBuilder::new().finish(), 24 edit: TextEditBuilder::default().finish(),
25 cursor_position: None, 25 cursor_position: None,
26 }; 26 };
27 } 27 }
@@ -33,7 +33,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
33 }; 33 };
34 34
35 let node = find_covering_node(file.syntax(), range); 35 let node = find_covering_node(file.syntax(), range);
36 let mut edit = TextEditBuilder::new(); 36 let mut edit = TextEditBuilder::default();
37 for node in node.descendants() { 37 for node in node.descendants() {
38 let text = match node.leaf_text() { 38 let text = match node.leaf_text() {
39 Some(text) => text, 39 Some(text) => text,
@@ -76,7 +76,7 @@ pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
76 let indent = node_indent(file, comment.syntax())?; 76 let indent = node_indent(file, comment.syntax())?;
77 let inserted = format!("\n{}{} ", indent, prefix); 77 let inserted = format!("\n{}{} ", indent, prefix);
78 let cursor_position = offset + TextUnit::of_str(&inserted); 78 let cursor_position = offset + TextUnit::of_str(&inserted);
79 let mut edit = TextEditBuilder::new(); 79 let mut edit = TextEditBuilder::default();
80 edit.insert(offset, inserted); 80 edit.insert(offset, inserted);
81 Some(LocalEdit { 81 Some(LocalEdit {
82 label: "on enter".to_string(), 82 label: "on enter".to_string(),
@@ -127,7 +127,7 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit>
127 return None; 127 return None;
128 } 128 }
129 let offset = let_stmt.syntax().range().end(); 129 let offset = let_stmt.syntax().range().end();
130 let mut edit = TextEditBuilder::new(); 130 let mut edit = TextEditBuilder::default();
131 edit.insert(offset, ";".to_string()); 131 edit.insert(offset, ";".to_string());
132 Some(LocalEdit { 132 Some(LocalEdit {
133 label: "add semicolon".to_string(), 133 label: "add semicolon".to_string(),
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index e839a5a90..c6463235c 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -1,9 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_db::Cancelable;
3use ra_syntax::ast::{self, NameOwner, StructFlavor}; 4use ra_syntax::ast::{self, NameOwner, StructFlavor};
4 5
5use crate::{ 6use crate::{
6 DefId, Cancelable, Name, AsName, 7 DefId, Name, AsName,
7 db::HirDatabase, 8 db::HirDatabase,
8 type_ref::TypeRef, 9 type_ref::TypeRef,
9}; 10};
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 5a8ca3b47..73a4cdc5c 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,13 +1,14 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::SyntaxNode; 3use ra_syntax::{SyntaxNode, SourceFileNode};
4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; 4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable};
5 5
6use crate::{ 6use crate::{
7 DefLoc, DefId, Name, 7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
8 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
9 query_definitions, 9 query_definitions,
10 FnScopes, 10 FnScopes,
11 macros::MacroExpansion,
11 module::{ModuleId, ModuleTree, ModuleSource, 12 module::{ModuleId, ModuleTree, ModuleSource,
12 nameres::{ItemMap, InputModuleItems}}, 13 nameres::{ItemMap, InputModuleItems}},
13 ty::{InferenceResult, Ty}, 14 ty::{InferenceResult, Ty},
@@ -18,7 +19,17 @@ salsa::query_group! {
18 19
19pub trait HirDatabase: SyntaxDatabase 20pub trait HirDatabase: SyntaxDatabase
20 + AsRef<LocationIntener<DefLoc, DefId>> 21 + AsRef<LocationIntener<DefLoc, DefId>>
22 + AsRef<LocationIntener<MacroCallLoc, MacroCallId>>
21{ 23{
24 fn hir_source_file(file_id: HirFileId) -> SourceFileNode {
25 type HirSourceFileQuery;
26 use fn HirFileId::hir_source_file;
27 }
28 fn expand_macro_invocation(invoc: MacroCallId) -> Option<Arc<MacroExpansion>> {
29 type