Repository: rust-analyzer/rust-analyzer
Pull request #6989 (2020-12-21 19:07:46)

Build aarch64-apple-darwin binaries on CI
This splits the `dist` matrix job into four and tries to make `xtask dist` more principled about target and artifact naming.

lnicola: I have no idea if this will work, but I couldn't figure how to trigger the action on my fork.
richiksc: > I have no idea if this will work, but I couldn't figure how to trigger the action on my fork.

I guess you could get the action to trigger by merging this change into `trigger-nightly` on your fork?
lnicola: I think you can also trigger them manually, but actions don't usually run on forks.
lnicola: Oh, I had to push it to the `master` branch, then wait for a while.
richiksc: @lnicola Looks like it's building on your fork after pushing to `trigger-nightly`: https://github.com/lnicola/rust-analyzer/runs/1591171874
richiksc: Hmm, looks like the build failed during linking. I think it's because compilation for `aarch64-apple-darwin` needs to use the Xcode 12 `macosx11.0` SDK, but right now, it's using the `macosx10.15` SDK by default.

https://github.com/lnicola/rust-analyzer/runs/1591171874#step:9:477

```
= note: ld: warning: ignoring file /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/lib/libresolv.tbd, missing required architecture arm64 in file /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/lib/libresolv.tbd
```

There are two ways around this: you can manually select the macOS 11 SDK, according to [shepmaster's instructions](https://github.com/shepmaster/rust/tree/silicon/silicon):

```
SDKROOT=$(xcrun -sdk macosx11.0 --show-sdk-path) \
MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx11.0 --show-sdk-platform-version) \
cargo build --target=aarch64-apple-darwin
```

Or you can set GitHub Actions to run on the `macos-11.0` runner, which is what I did on a simple Rust command-line tool project I contributed to. That should require no extra configuration to cross-compile for `aarch64-apple-darwin`.

```diff
jobs:
  dist:
    name: dist
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
-        os: [ubuntu-16.04, windows-latest, macos-latest]
+        os: [ubuntu-16.04, windows-latest, macos-11.0]
```
richiksc: Also, will the necessary code change to `code/src/main.ts` be in this PR or another PR if this gets merged? `platform` would need to be set to `"mac-arm"`, I'm guessing.
lnicola: Thanks for looking it up -- I had to step out for a bit. I do wonder if the `macos-11.0` binaries will be compatible with whatever `macos-latest` works on.

> Also, will the necessary code change to code/src/main.ts be in this PR or another PR if this gets merged? platform would need to be set to "mac-arm", I'm guessing.

Yeah, I planned to do it in a different PR, since I'm not sure if this is going to work.
richiksc: @lnicola Looks like the binaries built on CI: https://github.com/lnicola/rust-analyzer/releases/tag/nightly
lnicola: I don't think they did, there's no ARM binary. Maybe it used the code from the wrong branch?

Anyway, I suppose it would be better to restructure the workflow to build them in parallel.
richiksc: > I don't think they did, there's no ARM binary. Maybe it used the code from the wrong branch? EDIT: ah, the real nightly kicked in and overwritten the release.
> 
> Anyway, I suppose it would be better to restructure the workflow to build them in parallel.

Yep, the real nightly kicked in. I wish there was a way for you to tag it to build as separate. Potentially add an `on: push: tags: build-*` or something and push a tag, and then have the release action push it that tag.

I don't know if there's a way to build them in parallel in the same job. Wouldn't it take more time to set up another whole macOS runner just to build the additional binary?
lnicola: @richiksc if you don't mind, can you download a `rust-analyzer-mac-arm` file from https://github.com/lnicola/rust-analyzer/releases/tag/nightly and give it a spin? If you're having issues with Code, you can run it from the command line, e.g. `rust-analyzer-mac-arm analysis-stats .` in the RA repo (testing both of the `x86-64` and `aarch64` versions might be a nice benchmark).

> I don't know if there's a way to build them in parallel in the same job. Wouldn't it take more time to set up another whole macOS runner just to build the additional binary?

In separate jobs, I meant. Not sure if it's better to use a single job or two, but it might make sense to have two.
matklad: r=me
lnicola: > As a super-minor stylistic changes, I am not a fan of [...]

No problem, I shortened those, maybe even too much. I left `get_target` unchanged since it was different. Arguably, it could be called `pick_target` or something else, but I'd rather we passed `RA_TARGET` explicitly.. actually let me to just that.
lnicola: I triggered another test run (sorry), to make sure the workflow changes still work.

Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 919d58925e..09752b817f 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -15,12 +15,9 @@ env:
   RUSTUP_MAX_RETRIES: 10
 
 jobs:
-  dist:
-    name: dist
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [ubuntu-16.04, windows-latest, macos-latest]
+  dist-windows:
+    name: dist (Windows)
+    runs-on: windows-latest
 
     steps:
     - name: Checkout repository
@@ -30,8 +27,7 @@ jobs:
     # which takes a long time. The fastest way to do this is to rename the
     # existing folder, as deleting it takes about as much time as not doing
     # anything and just updating rust-docs.
-    - name: Rename existing rust toolchain (Windows)
-      if: matrix.os == 'windows-latest'
+    - name: Rename existing rust toolchain
       run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old
 
     - name: Install Rust toolchain
@@ -41,38 +37,116 @@ jobs:
         profile: minimal
         override: true
 
+    - name: Dist
+      run: cargo xtask dist
+      env:
+        RA_TARGET: x86_64-pc-windows-msvc
+
+    - name: Upload artifacts
+      uses: actions/upload-artifact@v1
+      with:
+        name: dist-windows-latest
+        path: ./dist
+
+  dist-ubuntu:
+    name: dist (Ubuntu 16.04)
+    runs-on: ubuntu-16.04
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    - name: Install Rust toolchain
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: true
+
     - name: Install Nodejs
-      if: matrix.os == 'ubuntu-16.04'
       uses: actions/setup-node@v1
       with:
         node-version: 12.x
 
     - name: Dist
-      if: matrix.os == 'ubuntu-16.04' && github.ref == 'refs/heads/release'
+      if: github.ref == 'refs/heads/release'
       run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER
+      env:
+        RA_TARGET: x86_64-unknown-linux-gnu
 
     - name: Dist
-      if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release'
+      if: github.ref != 'refs/heads/release'
       run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly
+      env:
+        RA_TARGET: x86_64-unknown-linux-gnu
+
+    - name: Nightly analysis-stats check
+      if: github.ref != 'refs/heads/release'
+      run: ./dist/rust-analyzer-x86_64-unknown-linux-gnu analysis-stats .
+
+    - name: Upload artifacts
+      uses: actions/upload-artifact@v1
+      with:
+        name: dist-ubuntu-16.04
+        path: ./dist
+
+  dist-macos-latest:
+    name: dist (MacOS latest)
+    runs-on: macos-latest
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    - name: Install Rust toolchain
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: true
 
     - name: Dist
-      if: matrix.os != 'ubuntu-16.04'
       run: cargo xtask dist
+      env:
+        RA_TARGET: x86_64-apple-darwin
 
-    - name: Nightly analysis-stats check
-      if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release'
-      run: ./dist/rust-analyzer-linux analysis-stats .
+    - name: Upload artifacts
+      uses: actions/upload-artifact@v1
+      with:
+        name: dist-macos-latest
+        path: ./dist
+
+  dist-macos-11:
+    name: dist (MacOS 11.0)
+    runs-on: macos-11.0
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    - name: Install Rust toolchain (beta)
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: beta
+        target: aarch64-apple-darwin
+        profile: minimal
+        override: true
+
+    - name: Dist
+      run: cargo xtask dist
+      env:
+        RA_TARGET: aarch64-apple-darwin
 
     - name: Upload artifacts
       uses: actions/upload-artifact@v1
       with:
-        name: dist-${{ matrix.os }}
+        name: dist-macos-11.0
         path: ./dist
 
   publish:
     name: publish
     runs-on: ubuntu-16.04
-    needs: ['dist']
+    needs: ['dist-windows', 'dist-ubuntu', 'dist-macos-latest', 'dist-macos-11']
     steps:
     - name: Install Nodejs
       uses: actions/setup-node@v1
@@ -91,6 +165,10 @@ jobs:
     - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
     - run: 'echo "HEAD_SHA: $HEAD_SHA"'
 
+    - uses: actions/download-artifact@v1
+      with:
+        name: dist-macos-11.0
+        path: dist
     - uses: actions/download-artifact@v1
       with:
         name: dist-macos-latest
@@ -103,7 +181,7 @@ jobs:
       with:
         name: dist-windows-latest
         path: dist
-    - run: ls -all ./dist
+    - run: ls -al ./dist
 
     - name: Publish Release
       uses: ./.github/actions/github-release
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 80cf9aba11..69499ea326 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -4,10 +4,10 @@ use std::mem;
 
 use algo::find_covering_element;
 use hir::Semantics;
-use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange};
+use ide_db::base_db::{FileId, FileRange};
 use ide_db::{
     label::Label,
-    source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
+    source_change::{SourceChange, SourceFileEdit},
     RootDatabase,
 };
 use syntax::{
@@ -209,7 +209,6 @@ pub(crate) struct AssistBuilder {
     file_id: FileId,
     is_snippet: bool,
     source_file_edits: Vec<SourceFileEdit>,
-    file_system_edits: Vec<FileSystemEdit>,
 }
 
 impl AssistBuilder {
@@ -219,7 +218,6 @@ impl AssistBuilder {
             file_id,
             is_snippet: false,
             source_file_edits: Vec::default(),
-            file_system_edits: Vec::default(),
         }
     }
 
@@ -284,17 +282,12 @@ impl AssistBuilder {
             algo::diff(&node, &new).into_text_edit(&mut self.edit);
         }
     }
-    pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
-        let file_system_edit =
-            FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() };
-        self.file_system_edits.push(file_system_edit);
-    }
 
     fn finish(mut self) -> SourceChange {
         self.commit();
         SourceChange {
             source_file_edits: mem::take(&mut self.source_file_edits),
-            file_system_edits: mem::take(&mut self.file_system_edits),
+            file_system_edits: Default::default(),
             is_snippet: self.is_snippet,
         }
     }
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs
deleted file mode 100644
index 5fc190fa67..0000000000
--- a/crates/assists/src/handlers/extract_module_to_file.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-use ast::edit::IndentLevel;
-use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt};
-use syntax::{
-    ast::{self, edit::AstNodeEdit, NameOwner},
-    AstNode,
-};
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: extract_module_to_file
-//
-// This assist extract module to file.
-//
-// ```
-// mod foo {<|>
-//     fn t() {}
-// }
-// ```
-// ->
-// ```
-// mod foo;
-// ```
-pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let assist_id = AssistId("extract_module_to_file", AssistKind::RefactorExtract);
-    let assist_label = "Extract module to file";
-    let db = ctx.db();
-    let module_ast = ctx.find_node_at_offset::<ast::Module>()?;
-    let module_items = module_ast.item_list()?;
-    let dedent_module_items_text = module_items.dedent(IndentLevel(1)).to_string();
-    let module_name = module_ast.name()?;
-    let target = module_ast.syntax().text_range();
-    let anchor_file_id = ctx.frange.file_id;
-    let sr = db.file_source_root(anchor_file_id);
-    let sr = db.source_root(sr);
-    let file_path = sr.path_for_file(&anchor_file_id)?;
-    let (file_name, file_ext) = file_path.name_and_extension()?;
-    acc.add(assist_id, assist_label, target, |builder| {
-        builder.replace(target, format!("mod {};", module_name));
-        let path = if is_main_or_lib(file_name) {
-            format!("./{}.{}", module_name, file_ext.unwrap())
-        } else {
-            format!("./{}/{}.{}", file_name, module_name, file_ext.unwrap())
-        };
-        let dst = AnchoredPathBuf { anchor: anchor_file_id, path };
-        let contents = update_module_items_string(dedent_module_items_text);
-        builder.create_file(dst, contents);
-    })
-}
-fn is_main_or_lib(file_name: &str) -> bool {
-    file_name == "main".to_string() || file_name == "lib".to_string()
-}
-fn update_module_items_string(items_str: String) -> String {
-    let mut items_string_lines: Vec<&str> = items_str.lines().collect();
-    items_string_lines.pop(); // Delete last line
-    items_string_lines.reverse();
-    items_string_lines.pop(); // Delete first line
-    items_string_lines.reverse();
-
-    let string = items_string_lines.join("\n");
-    format!("{}", string)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::check_assist;
-
-    use super::*;
-
-    #[test]
-    fn extract_module_to_file_with_basic_module() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /foo.rs crate:foo
-mod tests {<|>
-    #[test] fn t() {}
-}
-"#,
-            r#"
-//- /foo.rs
-mod tests;
-//- /foo/tests.rs
-#[test] fn t() {}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_file_path() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /src/foo.rs crate:foo
-mod bar {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /src/foo.rs
-mod bar;
-fn main() {
-    println!("Hello, world!");
-}
-//- /src/foo/bar.rs
-fn f() {
-
-}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_main_filw() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /main.rs
-mod foo {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /main.rs
-mod foo;
-fn main() {
-    println!("Hello, world!");
-}
-//- /foo.rs
-fn f() {
-
-}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_lib_file() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /lib.rs
-mod foo {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /lib.rs
-mod foo;
-fn main() {
-    println!("Hello, world!");
-}
-//- /foo.rs
-fn f() {
-
-}"#,
-        )
-    }
-}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 6b89b2d044..6e736ccb38 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -129,7 +129,6 @@ mod handlers {
     mod convert_integer_literal;
     mod early_return;
     mod expand_glob_import;
-    mod extract_module_to_file;
     mod extract_struct_from_enum_variant;
     mod extract_variable;
     mod fill_match_arms;
@@ -180,7 +179,6 @@ mod handlers {
             convert_integer_literal::convert_integer_literal,
             early_return::convert_to_guarded_return,
             expand_glob_import::expand_glob_import,
-            extract_module_to_file::extract_module_to_file,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_variable::extract_variable,
             fill_match_arms::fill_match_arms,
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3ad30f0c9e..049f808dc6 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -619,7 +619,6 @@ fn test_fn() {
                                                 ),
                                                 path: "foo.rs",
                                             },
-                                            initial_contents: "",
                                         },
                                     ],
                                     is_snippet: false,
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d79f5c1700..e8b8966238 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -40,7 +40,6 @@ impl DiagnosticWithFix for UnresolvedModule {
                     anchor: self.file.original_file(sema.db),
                     path: self.candidate.clone(),
                 },
-                initial_contents: "".to_string(),
             }
             .into(),
             unresolved_module.syntax().text_range(),
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index 10c0abdaca..e87d98dadc 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -44,7 +44,7 @@ impl From<Vec<SourceFileEdit>> for SourceChange {
 
 #[derive(Debug, Clone)]
 pub enum FileSystemEdit {
-    CreateFile { dst: AnchoredPathBuf, initial_contents: String },
+    CreateFile { dst: AnchoredPathBuf },
     MoveFile { src: FileId, dst: AnchoredPathBuf },
 }
 
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 5a1ae96aa0..e0561b5a7a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -634,47 +634,30 @@ pub(crate) fn snippet_text_document_edit(
     Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
 }
 
-pub(crate) fn snippet_text_document_ops(
+pub(crate) fn resource_op(
     snap: &GlobalStateSnapshot,
     file_system_edit: FileSystemEdit,
-) -> Vec<lsp_ext::SnippetDocumentChangeOperation> {
-    let mut ops = Vec::new();
+) -> lsp_types::ResourceOp {
     match file_system_edit {
-        FileSystemEdit::CreateFile { dst, initial_contents } => {
+        FileSystemEdit::CreateFile { dst } => {
             let uri = snap.anchored_path(&dst);
-            let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile {
-                uri: uri.clone(),
+            lsp_types::ResourceOp::Create(lsp_types::CreateFile {
+                uri,
                 options: None,
                 annotation_id: None,
-            });
-            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file));
-            if !initial_contents.is_empty() {
-                let text_document =
-                    lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None };
-                let range = range(&LineIndex::new(""), TextRange::empty(TextSize::from(0)));
-                let text_edit = lsp_ext::SnippetTextEdit {
-                    range,
-                    new_text: initial_contents,
-                    insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
-                };
-                let edit_file =
-                    lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
-                ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file));
-            }
+            })
         }
         FileSystemEdit::MoveFile { src, dst } => {
             let old_uri = snap.file_id_to_url(src);
             let new_uri = snap.anchored_path(&dst);
-            let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
+            lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
                 old_uri,
                 new_uri,
                 options: None,
                 annotation_id: None,
-            });
-            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file))
+            })
         }
     }
-    ops
 }
 
 pub(crate) fn snippet_workspace_edit(
@@ -683,8 +666,8 @@ pub(crate) fn snippet_workspace_edit(
 ) -> Result<lsp_ext::SnippetWorkspaceEdit> {
     let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
     for op in source_change.file_system_edits {
-        let ops = snippet_text_document_ops(snap, op);
-        document_changes.extend_from_slice(&ops);
+        let op = resource_op(&snap, op);
+        document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
     }
     for edit in source_change.source_file_edits {
         let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index c1cf00fa01..5096ce4dab 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -4,5 +4,6 @@
 !package.json
 !package-lock.json
 !ra_syntax_tree.tmGrammar.json
+!rust.tmGrammar.json
 !icon.png
 !README.md
diff --git a/editors/code/package.json b/editors/code/package.json
index abcc84edac..ad01fea7b6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -748,6 +748,11 @@
             }
         ],
         "grammars": [
+            {
+                "language": "rust",
+                "scopeName": "source.rust",
+                "path": "rust.tmGrammar.json"
+            },
             {
                 "language": "ra_syntax_tree",
                 "scopeName": "source.ra_syntax_tree",
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
new file mode 100644
index 0000000000..4759bb1162
--- /dev/null
+++ b/editors/code/rust.tmGrammar.json
@@ -0,0 +1,1140 @@
+{
+    "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+    "name": "Rust",
+    "fileTypes": [
+        "rs"
+    ],
+    "scopeName": "source.rust",
+    "patterns": [
+        {
+            "comment": "boxed slice literal",
+            "begin": "(<)(\\[)",
+            "beginCaptures": {
+                "1": {
+                    "name": "punctuation.brackets.angle.rust"
+                },
+                "2": {
+                    "name": "punctuation.brackets.square.rust"
+                }
+            },
+            "end": ">",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.brackets.angle.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#gtypes"
+                },
+                {
+                    "include": "#lvariables"
+                },
+                {
+                    "include": "#lifetimes"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#types"
+                }
+            ]
+        },
+        {
+            "comment": "macro type metavariables",
+            "name": "meta.macro.metavariable.type.rust",
+            "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "captures": {
+                "1": {
+                    "name": "keyword.operator.macro.dollar.rust"
+                },
+                "3": {
+                    "name": "keyword.other.crate.rust"
+                },
+                "4": {
+                    "name": "entity.name.type.metavariable.rust"
+                },
+                "6": {
+                    "name": "keyword.operator.key-value.rust"
+                },
+                "7": {
+                    "name": "variable.other.metavariable.specifier.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#keywords"
+                }
+            ]
+        },
+        {
+            "comment": "macro metavariables",
+            "name": "meta.macro.metavariable.rust",
+            "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "captures": {
+                "1": {
+                    "name": "keyword.operator.macro.dollar.rust"
+                },
+                "2": {
+                    "name": "variable.other.metavariable.name.rust"
+                },
+                "4": {
+                    "name": "keyword.operator.key-value.rust"
+                },
+                "5": {
+                    "name": "variable.other.metavariable.specifier.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#keywords"
+                }
+            ]
+        },
+        {
+            "comment": "macro rules",
+            "name": "meta.macro.rules.rust",
+            "match": "\\b(macro_rules!)\\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\\s+(\\{)",
+            "captures": {
+                "1": {
+                    "name": "entity.name.function.macro.rules.rust"
+                },
+                "3": {
+                    "name": "entity.name.function.macro.rust"
+                },
+                "4": {
+                    "name": "entity.name.type.macro.rust"
+                },
+                "5": {
+                    "name": "punctuation.brackets.curly.rust"
+                }
+            }
+        },
+        {
+            "comment": "attributes",
+            "name": "meta.attribute.rust",
+            "begin": "(#)(\\!?)(\\[)",
+            "beginCaptures": {
+                "1": {
+                    "name": "punctuation.definition.attribute.rust"
+                },
+                "2": {
+                    "name": "keyword.operator.attribute.inner.rust"
+                },
+                "3": {
+                    "name": "punctuation.brackets.attribute.rust"
+                }
+            },
+            "end": "\\]",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.brackets.attribute.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#strings"
+                },
+                {
+                    "include": "#gtypes"
+                },
+                {
+                    "include": "#types"
+                }
+            ]
+        },
+        {
+            "comment": "modules",
+            "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)",
+            "captures": {
+                "1": {
+                    "name": "storage.type.rust"
+                },
+                "2": {
+                    "name": "entity.name.module.rust"
+                }
+            }
+        },
+        {
+            "comment": "external crate imports",
+            "name": "meta.import.rust",
+            "begin": "\\b(extern)\\s+(crate)",
+            "beginCaptures": {
+                "1": {
+                    "name": "storage.type.rust"
+                },
+                "2": {
+                    "name": "keyword.other.crate.rust"
+                }
+            },
+            "end": ";",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.semi.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#punctuation"
+                }
+            ]
+        },
+        {
+            "comment": "use statements",
+            "name": "meta.use.rust",
+            "begin": "\\b(use)\\s",
+            "beginCaptures": {
+                "1": {
+                    "name": "keyword.other.rust"
+                }
+            },
+            "end": ";",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.semi.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#namespaces"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#types"
+                },
+                {
+                    "include": "#lvariables"
+                }
+            ]
+        },
+        {
+            "include": "#block-comments"
+        },
+        {
+            "include": "#comments"
+        },
+        {
+            "include": "#lvariables"
+        },
+        {
+            "include": "#constants"
+        },
+        {
+            "include": "#gtypes"
+        },
+        {
+            "include": "#functions"
+        },
+        {
+            "include": "#types"
+        },
+        {
+            "include": "#keywords"
+        },
+        {
+            "include": "#lifetimes"
+        },
+        {
+            "include": "#macros"
+        },
+        {
+            "include": "#namespaces"
+        },
+        {
+            "include": "#punctuation"
+        },
+        {
+            "include": "#strings"
+        },
+        {
+            "include": "#variables"
+        }
+    ],
+    "repository": {
+        "comments": {
+            "patterns": [
+                {
+                    "comment": "documentation comments",
+                    "name": "comment.line.documentation.rust",
+                    "match": "^\\s*///.*"
+                },
+                {
+                    "comment": "line comments",
+                    "name": "comment.line.double-slash.rust",
+                    "match": "\\s*//.*"
+                }
+            ]
+        },
+        "block-comments": {
+            "patterns": [
+                {
+                    "comment": "empty block comments",
+                    "name": "comment.block.rust",
+                    "match": "/\\*\\*/"
+                },
+                {
+                    "comment": "block documentation comments",
+                    "name": "comment.block.documentation.rust",
+                    "begin": "/\\*\\*",
+                    "end": "\\*/",
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        }
+                    ]
+                },
+                {
+                    "comment": "block comments",
+                    "name": "comment.block.rust",
+                    "begin": "/\\*(?!\\*)",
+                    "end": "\\*/",
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        }
+                    ]
+                }
+            ]
+        },
+        "constants": {
+            "patterns": [
+                {
+                    "comment": "ALL CAPS constants",
+                    "name": "constant.other.caps.rust",
+                    "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b"
+                },
+                {
+                    "comment": "constant declarations",
+                    "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "constant.other.caps.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "decimal integers and floats",
+                    "name": "constant.numeric.decimal.rust",
+                    "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.separator.dot.decimal.rust"
+                        },
+                        "2": {
+                            "name": "keyword.operator.exponent.rust"
+                        },
+                        "3": {
+                            "name": "keyword.operator.exponent.sign.rust"
+                        },
+                        "4": {
+                            "name": "constant.numeric.decimal.exponent.mantissa.rust"
+                        },
+                        "5": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "hexadecimal integers",
+                    "name": "constant.numeric.hex.rust",
+                    "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "octal integers",
+                    "name": "constant.numeric.oct.rust",
+                    "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "binary integers",
+                    "name": "constant.numeric.bin.rust",
+                    "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "booleans",
+                    "name": "constant.language.bool.rust",
+                    "match": "\\b(true|false)\\b"
+                }
+            ]
+        },
+        "escapes": {
+            "comment": "escapes: ASCII, byte, Unicode, quote, regex",
+            "name": "constant.character.escape.rust",
+            "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))",
+            "captures": {
+                "1": {
+                    "name": "constant.character.escape.backslash.rust"
+                },
+                "2": {
+                    "name": "constant.character.escape.bit.rust"
+                },
+                "3": {
+                    "name": "constant.character.escape.unicode.rust"
+                },
+                "4": {
+                    "name": "constant.character.escape.unicode.punctuation.rust"
+                },
+                "5": {
+                    "name": "constant.character.escape.unicode.punctuation.rust"
+                }
+            }
+        },
+        "functions": {
+            "patterns": [
+                {
+                    "comment": "pub as a function",
+                    "match": "\\b(pub)(\\()",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.other.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "function definition",
+                    "name": "meta.function.definition.rust",
+                    "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "keyword.other.fn.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.function.rust"
+                        },
+                        "4": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "5": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "end": "\\{|;",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.curly.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "function/method calls, chaining",
+                    "name": "meta.function.call.rust",
+                    "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.function.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "end": "\\)",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "function/method calls with turbofish",
+                    "name": "meta.function.call.rust",
+                    "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\\()",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.function.rust"
+                        }
+                    },
+                    "end": "\\)",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                }
+            ]
+        },
+        "keywords": {
+            "patterns": [
+                {
+                    "comment": "control flow keywords",
+                    "name": "keyword.control.rust",
+                    "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b"
+                },
+                {
+                    "comment": "storage keywords",
+                    "name": "storage.type.rust",
+                    "match": "\\b(const|enum|extern|let|macro|mod|struct|trait|type)\\b"
+                },
+                {
+                    "comment": "storage modifiers",
+                    "name": "storage.modifier.rust",
+                    "match": "\\b(abstract|static)\\b"
+                },
+                {
+                    "comment": "other keywords",
+                    "name": "keyword.other.rust",
+                    "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b"
+                },
+                {
+                    "comment": "fn",
+                    "name": "keyword.other.fn.rust",
+                    "match": "\\bfn\\b"
+                },
+                {
+                    "comment": "crate",
+                    "name": "keyword.other.crate.rust",
+                    "match": "\\bcrate\\b"
+                },
+                {
+                    "comment": "mut",
+                    "name": "storage.modifier.mut.rust",
+                    "match": "\\bmut\\b"
+                },
+                {
+                    "comment": "logical operators",
+                    "name": "keyword.operator.logical.rust",
+                    "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)"
+                },
+                {
+                    "comment": "logical AND, borrow references",
+                    "name": "keyword.operator.borrow.and.rust",
+                    "match": "&(?![&=])"
+                },
+                {
+                    "comment": "assignment operators",
+                    "name": "keyword.operator.assignment.rust",
+                    "match": "(\\+=|-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)"
+                },
+                {
+                    "comment": "single equal",
+                    "name": "keyword.operator.assignment.equal.rust",
+                    "match": "(?<![<>])=(?!=|>)"
+                },
+                {
+                    "comment": "comparison operators",
+                    "name": "keyword.operator.comparison.rust",
+                    "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)"
+                },
+                {
+                    "comment": "math operators",
+                    "name": "keyword.operator.math.rust",
+                    "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
+                },
+                {
+                    "comment": "less than, greater than (special case)",
+                    "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.square.rust"
+                        },
+                        "3": {
+                            "name": "punctuation.brackets.curly.rust"
+                        },
+                        "4": {
+                            "name": "keyword.operator.comparison.rust"
+                        },
+                        "5": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "6": {
+                            "name": "punctuation.brackets.square.rust"
+                        },
+                        "7": {
+                            "name": "punctuation.brackets.curly.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "namespace operator",
+                    "name": "keyword.operator.namespace.rust",
+                    "match": "::"
+                },
+                {
+                    "comment": "dereference asterisk",
+                    "match": "(\\*)(?=\\w+)",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.operator.dereference.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "subpattern binding",
+                    "name": "keyword.operator.subpattern.rust",
+                    "match": "@"
+                },
+                {
+                    "comment": "dot access",
+                    "name": "keyword.operator.access.dot.rust",
+                    "match": "\\.(?!\\.)"
+                },
+                {
+                    "comment": "ranges, range patterns",
+                    "name": "keyword.operator.range.rust",
+                    "match": "\\.{2}(=|\\.)?"
+                },
+                {
+                    "comment": "colon",
+                    "name": "keyword.operator.key-value.rust",
+                    "match": ":(?!:)"
+                },
+                {
+                    "comment": "dashrocket, skinny arrow",
+                    "name": "keyword.operator.arrow.skinny.rust",
+                    "match": "->"
+                },
+                {
+                    "comment": "hashrocket, fat arrow",
+                    "name": "keyword.operator.arrow.fat.rust",
+                    "match": "=>"
+                },
+                {
+                    "comment": "dollar macros",
+                    "name": "keyword.operator.macro.dollar.rust",
+                    "match": "\\$"
+                },
+                {
+                    "comment": "question mark operator, questionably sized, macro kleene matcher",
+                    "name": "keyword.operator.question.rust",
+                    "match": "\\?"
+                }
+            ]
+        },
+        "interpolations": {
+            "comment": "curly brace interpolations",
+            "name": "meta.interpolation.rust",
+            "match": "({)[^\"{}]*(})",
+            "captures": {
+                "1": {
+                    "name": "punctuation.definition.interpolation.rust"
+                },
+                "2": {
+                    "name": "punctuation.definition.interpolation.rust"
+                }
+            }
+        },
+        "lifetimes": {
+            "patterns": [
+                {
+                    "comment": "named lifetime parameters",
+                    "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.definition.lifetime.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.lifetime.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "borrowing references to named lifetimes",
+                    "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.operator.borrow.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.lifetime.rust"
+                        },
+                        "3": {
+                            "name": "entity.name.type.lifetime.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "macros": {
+            "patterns": [
+                {
+                    "comment": "macros",
+                    "name": "meta.macro.rust",
+                    "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))",
+                    "captures": {
+                        "2": {
+                            "name": "entity.name.function.macro.rust"
+                        },
+                        "3": {
+                            "name": "entity.name.type.macro.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "namespaces": {
+            "patterns": [
+                {
+                    "comment": "namespace (non-type, non-function path segment)",
+                    "match": "(?<![A-Za-z0-9_])([a-z0-9_]+)((?<!super|self)::)",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.namespace.rust"
+                        },
+                        "2": {
+                            "name": "keyword.operator.namespace.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "types": {
+            "patterns": [
+                {
+                    "comment": "numeric types",
+                    "match": "(?<![A-Za-z])(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "parameterized types",
+                    "begin": "\\b([A-Z][A-Za-z0-9]*)(<)",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.type.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "end": ">",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "primitive types",
+                    "name": "entity.name.type.primitive.rust",
+                    "match": "\\b(bool|char|str)\\b"
+                },
+                {
+                    "comment": "trait declarations",
+                    "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.trait.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "struct declarations",
+                    "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.struct.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "enum declarations",
+                    "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.enum.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "type declarations",
+                    "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.declaration.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "types",
+                    "name": "entity.name.type.rust",
+                    "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)"
+                }
+            ]
+        },
+        "gtypes": {
+            "patterns": [
+                {
+                    "comment": "option types",
+                    "name": "entity.name.type.option.rust",
+                    "match": "\\b(Some|None)\\b"
+                },
+                {
+                    "comment": "result types",
+                    "name": "entity.name.type.result.rust",
+                    "match": "\\b(Ok|Err)\\b"
+                }
+            ]
+        },
+        "punctuation": {
+            "patterns": [
+                {
+                    "comment": "comma",
+                    "name": "punctuation.comma.rust",
+                    "match": ","
+                },
+                {
+                    "comment": "curly braces",
+                    "name": "punctuation.brackets.curly.rust",
+                    "match": "[{}]"
+                },
+                {
+                    "comment": "parentheses, round brackets",
+                    "name": "punctuation.brackets.round.rust",
+                    "match": "[()]"
+                },
+                {
+                    "comment": "semicolon",
+                    "name": "punctuation.semi.rust",
+                    "match": ";"
+                },
+                {
+                    "comment": "square brackets",
+                    "name": "punctuation.brackets.square.rust",
+                    "match": "[\\[\\]]"
+                },
+                {
+                    "comment": "angle brackets",
+                    "name": "punctuation.brackets.angle.rust",
+                    "match": "(?<!=)[<>]"
+                }
+            ]
+        },
+        "strings": {
+            "patterns": [
+                {
+                    "comment": "double-quoted strings and byte strings",
+                    "name": "string.quoted.double.rust",
+                    "begin": "(b?)(\")",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "end": "\"",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#escapes"
+                        },
+                        {
+                            "include": "#interpolations"
+                        }
+                    ]
+                },
+                {
+                    "comment": "double-quoted raw strings and raw byte strings",
+                    "name": "string.quoted.double.rust",
+                    "begin": "(b?r)(#*)(\")",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.raw.rust"
+                        },
+                        "3": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "end": "(\")(\\2)",
+                    "endCaptures": {
+                        "1": {
+                            "name": "punctuation.definition.string.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.raw.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "characters and bytes",
+                    "name": "string.quoted.single.char.rust",
+                    "begin": "(b)?(')",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.char.rust"
+                        }
+                    },
+                    "end": "'",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.definition.char.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#escapes"
+                        }
+                    ]
+                }
+            ]
+        },
+        "lvariables": {
+            "patterns": [
+                {
+                    "comment": "self",
+                    "name": "variable.language.self.rust",
+                    "match": "\\b[Ss]elf\\b"
+                },
+                {
+                    "comment": "super",
+                    "name": "variable.language.super.rust",
+                    "match": "\\bsuper\\b"
+                }
+            ]
+        },
+        "variables": {
+            "patterns": [
+                {
+                    "comment": "variables",
+                    "name": "variable.other.rust",
+                    "match": "\\b(?<!(?<!\\.)\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b"
+                }
+            ]
+        }
+    }
+}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 9d4823a34d..92bc4d7f72 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -470,7 +470,7 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
             return;
         }
         const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit);
-        await vscode.workspace.applyEdit(edit);
+        await applySnippetWorkspaceEdit(edit);
     };
 }
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 4b2d3c8a55..4eaaed62bd 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -19,8 +19,9 @@ let ctx: Ctx | undefined;
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
 export async function activate(context: vscode.ExtensionContext) {
-    // VS Code doesn't show a notification when an extension fails to activate
-    // so we do it ourselves.
+    // For some reason vscode not always shows pop-up error notifications
+    // when an extension fails to activate, so we do it explicitly by ourselves.
+    // FIXME: remove this bit of code once vscode fixes this issue: https://github.com/microsoft/vscode/issues/101242
     await tryActivate(context).catch(err => {
         void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
         throw err;
@@ -291,8 +292,6 @@ async function getServer(config: Config, state: PersistentState): Promise<string
         if (process.platform === "linux") platform = "linux";
         if (process.platform === "darwin") platform = "mac";
         if (process.platform === "win32") platform = "windows";
-    } else if (process.arch === "arm64" && process.platform === "darwin") {
-        platform = "mac";
     }
     if (platform === undefined) {
         vscode.window.showErrorMessage(
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index 9e15a5a4c9..d07ad94204 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -58,30 +58,74 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
 }
 
 fn dist_server() -> Result<()> {
-    if cfg!(target_os = "linux") {
+    let target = get_target();
+    if target.contains("-linux-gnu") {
         env::set_var("CC", "clang");
     }
-    cmd!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release").run()?;
-
-    let (src, dst) = if cfg!(target_os = "linux") {
-        ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux")
-    } else if cfg!(target_os = "windows") {
-        ("./target/release/rust-analyzer.exe", "./dist/rust-analyzer-windows.exe")
-    } else if cfg!(target_os = "macos") {
-        ("./target/release/rust-analyzer", "./dist/rust-analyzer-mac")
-    } else {
-        panic!("Unsupported OS")
-    };
 
-    let src = Path::new(src);
-    let dst = Path::new(dst);
+    let toolchain = toolchain(&target);
+    cmd!("cargo +{toolchain} build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --release").run()?;
 
+    let suffix = exe_suffix(&target);
+    let src =
+        Path::new("target").join(&target).join("release").join(format!("rust-analyzer{}", suffix));
+    let dst = Path::new("dist").join(format!("rust-analyzer-{}{}", target, suffix));
     cp(&src, &dst)?;
     gzip(&src, &dst.with_extension("gz"))?;
 
+    // FIXME: the old names are temporarily kept for client compatibility, but they should be removed
+    // Remove this block after a couple of releases
+    match target.as_ref() {
+        "x86_64-unknown-linux-gnu" => {
+            cp(&src, "dist/rust-analyzer-linux")?;
+            gzip(&src, Path::new("dist/rust-analyzer-linux.gz"))?;
+        }
+        "x86_64-pc-windows-msvc" => {
+            cp(&src, "dist/rust-analyzer-windows.exe")?;
+            gzip(&src, Path::new("dist/rust-analyzer-windows.gz"))?;
+        }
+        "x86_64-apple-darwin" => {
+            cp(&src, "dist/rust-analyzer-mac")?;
+            gzip(&src, Path::new("dist/rust-analyzer-mac.gz"))?;
+        }
+        _ => {}
+    }
+
     Ok(())
 }
 
+fn get_target() -> String {
+    match env::var("RA_TARGET") {
+        Ok(target) => target,
+        _ => {
+            if cfg!(target_os = "linux") {
+                "x86_64-unknown-linux-gnu".to_string()
+            } else if cfg!(target_os = "windows") {
+                "x86_64-pc-windows-msvc".to_string()
+            } else if cfg!(target_os = "macos") {
+                "x86_64-apple-darwin".to_string()
+            } else {
+                panic!("Unsupported OS, maybe try setting RA_TARGET")
+            }
+        }
+    }
+}
+
+fn exe_suffix(target: &str) -> String {
+    if target.contains("-windows-") {
+        ".exe".into()
+    } else {
+        "".into()
+    }
+}
+
+fn toolchain(target: &str) -> String {
+    match target {
+        "aarch64-apple-darwin" => "beta".to_string(),
+        _ => "stable".to_string(),
+    }
+}
+
 fn gzip(src_path: &Path, dest_path: &Path) -> Result<()> {
     let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
     let mut input = io::BufReader::new(File::open(src_path)?);


Test patch:
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index b41f4874a5..709a34d03a 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -2,7 +2,6 @@ mod generated;
 
 use hir::Semantics;
 use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
-use ide_db::source_change::FileSystemEdit;
 use ide_db::RootDatabase;
 use syntax::TextRange;
 use test_utils::{assert_eq_text, extract_offset, extract_range};
@@ -48,7 +47,7 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
     let before = db.file_text(file_id).to_string();
     let frange = FileRange { file_id, range: selection.into() };
 
-    let assist = Assist::resolved(&db, &AssistConfig::default(), frange)
+    let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
         .into_iter()
         .find(|assist| assist.assist.id.0 == assist_id)
         .unwrap_or_else(|| {
@@ -64,12 +63,9 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
         });
 
     let actual = {
+        let change = assist.source_change.source_file_edits.pop().unwrap();
         let mut actual = before;
-        for source_file_edit in assist.source_change.source_file_edits {
-            if source_file_edit.file_id == file_id {
-                source_file_edit.edit.apply(&mut actual)
-            }
-        }
+        change.edit.apply(&mut actual);
         actual
     };
     assert_eq_text!(&after, &actual);
@@ -103,8 +99,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
         (Some(assist), ExpectedResult::After(after)) => {
             let mut source_change = assist.source_change;
             assert!(!source_change.source_file_edits.is_empty());
-            let skip_header = source_change.source_file_edits.len() == 1
-                && source_change.file_system_edits.len() == 0;
+            let skip_header = source_change.source_file_edits.len() == 1;
             source_change.source_file_edits.sort_by_key(|it| it.file_id);
 
             let mut buf = String::new();
@@ -120,21 +115,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
                 buf.push_str(&text);
             }
 
-            for file_system_edit in source_change.file_system_edits.clone() {
-                match file_system_edit {
-                    FileSystemEdit::CreateFile { dst, initial_contents } => {
-                        let sr = db.file_source_root(dst.anchor);
-                        let sr = db.source_root(sr);
-                        let mut base = sr.path_for_file(&dst.anchor).unwrap().clone();
-                        base.pop();
-                        let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]);
-                        format_to!(buf, "//- {}\n", created_file_path);
-                        buf.push_str(&initial_contents);
-                    }
-                    _ => (),
-                }
-            }
-
             assert_eq_text!(after, &buf);
         }
         (Some(assist), ExpectedResult::Target(target)) => {
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e9093ec536..cc7c4a3433 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -235,21 +235,6 @@ fn qux(bar: Bar, baz: Baz) {}
     )
 }
 
-#[test]
-fn doctest_extract_module_to_file() {
-    check_doc_test(
-        "extract_module_to_file",
-        r#####"
-mod foo {<|>
-    fn t() {}
-}
-"#####,
-        r#####"
-mod foo;
-"#####,
-    )
-}
-
 #[test]
 fn doctest_extract_struct_from_enum_variant() {
     check_doc_test(