Repository: rust-analyzer/rust-analyzer
Pull request #7901 (2021-03-07 10:14:19)

Make extension respect http proxy settings
This patch makes vscode extension respect proxy settings when fetching release metadata and rust-analyzer binary.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index c392a89070..39f27098a8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1919,18 +1919,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
 
 [[package]]
 name = "xflags"
-version = "0.2.1"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ad6ce6a0b7224130015b4ebac796478ac04e0079f5d222a690efea06a9208a"
+checksum = "222e914b43cec5d7305ac5116d10a14b3a52c50e9062d642c92631f3beabc729"
 dependencies = [
  "xflags-macros",
 ]
 
 [[package]]
 name = "xflags-macros"
-version = "0.2.1"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8037d3ca14996158b03c0fa905d0834906ef0fc7044df72c1f5ff690e5e62c9"
+checksum = "52f18f5b4aa7f95e209d5b9274f6164c3938920b4d5c75f97f0dd16daee25ddd"
 dependencies = [
  "proc-macro2",
 ]
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 1422224ac0..dc38f90e93 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -99,7 +99,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
             format!("Import `{}`", &import),
             range,
             |builder| {
-                let rewriter = insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use);
+                let rewriter =
+                    insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge);
                 builder.rewrite(rewriter);
             },
         );
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 4f0422e960..5c7678b539 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -154,7 +154,7 @@ fn insert_import(
         mod_path.pop_segment();
         mod_path.push_segment(variant_hir_name.clone());
         let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
-        *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use);
+        *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
     }
     Some(())
 }
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index 55481af347..f3bc6cf398 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -44,7 +44,7 @@ pub(crate) fn replace_qualified_name_with_use(
             let mut rewriter = SyntaxRewriter::default();
             shorten_paths(&mut rewriter, syntax.clone(), &path);
             if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
-                rewriter += insert_use(import_scope, path, ctx.config.insert_use);
+                rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge);
                 builder.rewrite(rewriter);
             }
         },
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 9b2435c4b6..884711f11e 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -5,7 +5,7 @@ use std::fmt;
 use hir::{Documentation, ModPath, Mutability};
 use ide_db::{
     helpers::{
-        insert_use::{self, ImportScope, InsertUseConfig},
+        insert_use::{self, ImportScope, MergeBehavior},
         mod_path_to_ast, SnippetCap,
     },
     SymbolKind,
@@ -280,11 +280,14 @@ pub struct ImportEdit {
 impl ImportEdit {
     /// Attempts to insert the import to the given scope, producing a text edit.
     /// May return no edit in edge cases, such as scope already containing the import.
-    pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
+    pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> {
         let _p = profile::span("ImportEdit::to_text_edit");
 
-        let rewriter =
-            insert_use::insert_use(&self.import_scope, mod_path_to_ast(&self.import_path), cfg);
+        let rewriter = insert_use::insert_use(
+            &self.import_scope,
+            mod_path_to_ast(&self.import_path),
+            merge_behavior,
+        );
         let old_ast = rewriter.rewrite_root()?;
         let mut import_insert = TextEdit::builder();
         algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index b0b809791e..76f31de9e3 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -156,7 +156,7 @@ pub fn resolve_completion_edits(
         .find(|mod_path| mod_path.to_string() == full_import_path)?;
 
     ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
-        .to_text_edit(config.insert_use)
+        .to_text_edit(config.insert_use.merge)
         .map(|edit| vec![edit])
 }
 
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index f52aee3445..fd40351987 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -19,7 +19,6 @@ use test_utils::mark;
 pub struct InsertUseConfig {
     pub merge: Option<MergeBehavior>,
     pub prefix_kind: hir::PrefixKind,
-    pub group: bool,
 }
 
 #[derive(Debug, Clone)]
@@ -100,13 +99,13 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
 pub fn insert_use<'a>(
     scope: &ImportScope,
     path: ast::Path,
-    cfg: InsertUseConfig,
+    merge: Option<MergeBehavior>,
 ) -> SyntaxRewriter<'a> {
     let _p = profile::span("insert_use");
     let mut rewriter = SyntaxRewriter::default();
     let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
     // merge into existing imports if possible
-    if let Some(mb) = cfg.merge {
+    if let Some(mb) = merge {
         for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
             if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
                 rewriter.replace(existing_use.syntax(), merged.syntax());
@@ -117,7 +116,7 @@ pub fn insert_use<'a>(
 
     // either we weren't allowed to merge or there is no import that fits the merge conditions
     // so look for the place we have to insert to
-    let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group);
+    let (insert_position, add_blank) = find_insert_position(scope, path);
 
     let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
         Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into())
@@ -539,7 +538,6 @@ impl AddBlankLine {
 fn find_insert_position(
     scope: &ImportScope,
     insert_path: ast::Path,
-    group_imports: bool,
 ) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
     let group = ImportGroup::new(&insert_path);
     let path_node_iter = scope
@@ -552,14 +550,6 @@ fn find_insert_position(
             let has_tl = tree.use_tree_list().is_some();
             Some((path, has_tl, node))
         });
-
-    if !group_imports {
-        if let Some((_, _, node)) = path_node_iter.last() {
-            return (InsertPosition::After(node.into()), AddBlankLine::Before);
-        }
-        return (InsertPosition::First, AddBlankLine::AfterTwice);
-    }
-
     // Iterator that discards anything thats not in the required grouping
     // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
     let group_iter = path_node_iter
@@ -575,7 +565,6 @@ fn find_insert_position(
             use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater
         },
     );
-
     match post_insert {
         // insert our import before that element
         Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After),
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 3130785ccc..8789f08521 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ jod-thread = "0.1.0"
 log = "0.4.8"
 lsp-types = { version = "0.88.0", features = ["proposed"] }
 parking_lot = "0.11.0"
-xflags = "0.2.1"
+xflags = "0.1.2"
 oorandom = "11.1.2"
 rustc-hash = "1.1.0"
 serde = { version = "1.0.106", features = ["derive"] }
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index 3a7caaf3fc..244912d26e 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -6,9 +6,7 @@ use ide_ssr::{SsrPattern, SsrRule};
 use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
 use vfs::AbsPathBuf;
 
-xflags::xflags! {
-    src "./src/bin/flags.rs"
-
+xflags::args_parser! {
     /// LSP server for the Rust programming language.
     cmd rust-analyzer {
         /// Verbosity level, can be repeated multiple times.
@@ -122,7 +120,7 @@ xflags::xflags! {
 
 // generated start
 // The following code is generated by `xflags` macro.
-// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
+// Run `env XFLAGS_DUMP= cargo build` to regenerate.
 #[derive(Debug)]
 pub struct RustAnalyzer {
     pub verbose: u32,
@@ -160,7 +158,7 @@ pub struct Parse {
 }
 
 #[derive(Debug)]
-pub struct Symbols;
+pub struct Symbols {}
 
 #[derive(Debug)]
 pub struct Highlight {
@@ -213,13 +211,14 @@ pub struct Search {
 }
 
 #[derive(Debug)]
-pub struct ProcMacro;
+pub struct ProcMacro {}
 
 impl RustAnalyzer {
-    pub const HELP: &'static str = Self::HELP_;
+    pub const HELP: &'static str = Self::_HELP;
 
     pub fn from_env() -> xflags::Result<Self> {
-        Self::from_env_()
+        let mut p = xflags::rt::Parser::new_from_env();
+        Self::_parse(&mut p)
     }
 }
 // generated end
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 49994824f5..3bd7e678d3 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -108,11 +108,7 @@ impl BenchCmd {
                         add_call_parenthesis: true,
                         add_call_argument_snippets: true,
                         snippet_cap: SnippetCap::new(true),
-                        insert_use: InsertUseConfig {
-                            merge: None,
-                            prefix_kind: PrefixKind::Plain,
-                            group: true,
-                        },
+                        insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
                     };
                     let res = do_work(&mut host, file_id, |analysis| {
                         analysis.completions(&options, file_position)
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cac48e9117..3671367025 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -35,8 +35,7 @@ config_data! {
         assist_importMergeBehaviour: MergeBehaviorDef  = "\"full\"",
         /// The path structure for newly inserted paths to use.
         assist_importPrefix: ImportPrefixDef           = "\"plain\"",
-        /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
-        assist_importGroup: bool                       = "true",
+
         /// Show function name and docs in parameter hints.
         callInfo_full: bool = "true",
 
@@ -575,7 +574,6 @@ impl Config {
                 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
                 ImportPrefixDef::BySelf => PrefixKind::BySelf,
             },
-            group: self.data.assist_importGroup,
         }
     }
     pub fn completion(&self) -> CompletionConfig {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 4235eb6ddf..c1ca88df67 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1087,11 +1087,7 @@ mod tests {
                     add_call_parenthesis: true,
                     add_call_argument_snippets: true,
                     snippet_cap: SnippetCap::new(true),
-                    insert_use: InsertUseConfig {
-                        merge: None,
-                        prefix_kind: PrefixKind::Plain,
-                        group: true,
-                    },
+                    insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
                 },
                 ide_db::base_db::FilePosition { file_id, offset },
             )
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e564c14279..1dbf2a611d 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -2,8 +2,6 @@
  The strategy to use when inserting new imports or merging imports.
 [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`)::
  The path structure for newly inserted paths to use.
-[[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`)::
- Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
 [[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`)::
  Show function name and docs in parameter hints.
 [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 9d0d1d4ec8..198c17556d 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -9,6 +9,7 @@
             "version": "0.4.0-dev",
             "license": "MIT OR Apache-2.0",
             "dependencies": {
+                "https-proxy-agent": "^5.0.0",
                 "node-fetch": "^2.6.1",
                 "vscode-languageclient": "^7.1.0-next.4"
             },
@@ -515,7 +516,6 @@
             "version": "6.0.2",
             "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
             "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
-            "dev": true,
             "dependencies": {
                 "debug": "4"
             },
@@ -830,6 +830,7 @@
             "dependencies": {
                 "anymatch": "~3.1.1",
                 "braces": "~3.0.2",
+                "fsevents": "~2.3.1",
                 "glob-parent": "~5.1.0",
                 "is-binary-path": "~2.1.0",
                 "is-glob": "~4.0.1",
@@ -959,7 +960,6 @@
             "version": "4.3.1",
             "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
             "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
-            "dev": true,
             "dependencies": {
                 "ms": "2.1.2"
             },
@@ -1759,7 +1759,6 @@
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
             "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
-            "dev": true,
             "dependencies": {
                 "agent-base": "6",
                 "debug": "4"
@@ -2236,8 +2235,7 @@
         "node_modules/ms": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-            "dev": true
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
         },
         "node_modules/mute-stream": {
             "version": "0.0.8",
@@ -2682,6 +2680,9 @@
             "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz",
             "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==",
             "dev": true,
+            "dependencies": {
+                "fsevents": "~2.3.1"
+            },
             "bin": {
                 "rollup": "dist/bin/rollup"
             },
@@ -3843,7 +3844,6 @@
             "version": "6.0.2",
             "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
             "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
-            "dev": true,
             "requires": {
                 "debug": "4"
             }
@@ -4190,7 +4190,6 @@
             "version": "4.3.1",
             "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
             "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
-            "dev": true,
             "requires": {
                 "ms": "2.1.2"
             }
@@ -4798,7 +4797,6 @@
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
             "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
-            "dev": true,
             "requires": {
                 "agent-base": "6",
                 "debug": "4"
@@ -5175,8 +5173,7 @@
         "ms": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-            "dev": true
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
         },
         "mute-stream": {
             "version": "0.0.8",
diff --git a/editors/code/package.json b/editors/code/package.json
index 5f41f7803a..68f58d3cad 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -35,6 +35,7 @@
         "test": "node ./out/tests/runTests.js"
     },
     "dependencies": {
+        "https-proxy-agent": "^5.0.0",
         "node-fetch": "^2.6.1",
         "vscode-languageclient": "^7.1.0-next.4"
     },
@@ -385,11 +386,6 @@
                         "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to."
                     ]
                 },
-                "rust-analyzer.assist.importGroup": {
-                    "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.",
-                    "default": true,
-                    "type": "boolean"
-                },
                 "rust-analyzer.callInfo.full": {
                     "markdownDescription": "Show function name and docs in parameter hints.",
                     "default": true,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index ddb5cfbd33..82f0a0566a 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -100,6 +100,14 @@ export class Config {
     get channel() { return this.get<UpdatesChannel>("updates.channel"); }
     get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
     get traceExtension() { return this.get<boolean>("trace.extension"); }
+    get httpProxy() {
+        const httpProxy = vscode
+            .workspace
+            .getConfiguration('http')
+            .get<null | string>("proxy")!;
+
+        return httpProxy || process.env["https_proxy"] || process.env["HTTPS_PROXY"];
+    }
 
     get inlayHints() {
         return {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 00393d6e8f..1be4f17587 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -183,7 +183,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
     }
 
     const release = await downloadWithRetryDialog(state, async () => {
-        return await fetchRelease("nightly", state.githubToken);
+        return await fetchRelease("nightly", state.githubToken, config.httpProxy);
     }).catch(async (e) => {
         log.error(e);
         if (state.releaseId === undefined) { // Show error only for the initial download
@@ -209,6 +209,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
             url: artifact.browser_download_url,
             dest,
             progressTitle: "Downloading rust-analyzer extension",
+            httpProxy: config.httpProxy,
         });
     });
 
@@ -331,7 +332,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
 
     const releaseTag = config.package.releaseTag;
     const release = await downloadWithRetryDialog(state, async () => {
-        return await fetchRelease(releaseTag, state.githubToken);
+        return await fetchRelease(releaseTag, state.githubToken, config.httpProxy);
     });
     const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
     assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
@@ -343,6 +344,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
             progressTitle: "Downloading rust-analyzer server",
             gunzip: true,
             mode: 0o755,
+            httpProxy: config.httpProxy,
         });
     });
 
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index d39dc1baf0..07ebc615c7 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,4 +1,6 @@
 import fetch from "node-fetch";
+var HttpsProxyAgent = require('https-proxy-agent');
+
 import * as vscode from "vscode";
 import * as stream from "stream";
 import * as crypto from "crypto";
@@ -17,6 +19,7 @@ const REPO = "rust-analyzer";
 export async function fetchRelease(
     releaseTag: string,
     githubToken: string | null | undefined,
+    httpProxy: string | null | undefined,
 ): Promise<GithubRelease> {
 
     const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
@@ -30,7 +33,14 @@ export async function fetchRelease(
         headers.Authorization = "token " + githubToken;
     }
 
-    const response = await fetch(requestUrl, { headers: headers });
+    const response = await (() => {
+        if (httpProxy) {
+            log.debug(`Fetching release metadata via proxy: ${httpProxy}`);
+            return fetch(requestUrl, { headers: headers, agent: new HttpsProxyAgent(httpProxy) });
+        }
+
+        return fetch(requestUrl, { headers: headers });
+    })();
 
     if (!response.ok) {
         log.error("Error fetching artifact release info", {
@@ -73,6 +83,7 @@ interface DownloadOpts {
     dest: string;
     mode?: number;
     gunzip?: boolean;
+    httpProxy?: string;
 }
 
 export async function download(opts: DownloadOpts) {
@@ -91,7 +102,7 @@ export async function download(opts: DownloadOpts) {
         },
         async (progress, _cancellationToken) => {
             let lastPercentage = 0;
-            await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => {
+            await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => {
                 const newPercentage = Math.round((readBytes / totalBytes) * 100);
                 if (newPercentage !== lastPercentage) {
                     progress.report({
@@ -113,9 +124,17 @@ async function downloadFile(
     destFilePath: fs.PathLike,
     mode: number | undefined,
     gunzip: boolean,
+    httpProxy: string | null | undefined,
     onProgress: (readBytes: number, totalBytes: number) => void
 ): Promise<void> {
-    const res = await fetch(url);
+    const res = await (() => {
+        if (httpProxy) {
+            log.debug(`Downloading ${url} via proxy: ${httpProxy}`);
+            return fetch(url, { agent: new HttpsProxyAgent(httpProxy) });
+        }
+
+        return fetch(url);
+    })();
 
     if (!res.ok) {
         log.error("Error", res.status, "while downloading file from", url);
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index e084f0df65..b17dde598b 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -15,5 +15,5 @@ ungrammar = "=1.11"
 walkdir = "2.3.1"
 write-json = "0.1.0"
 xshell = "0.1"
-xflags = "0.2.1"
+xflags = "0.1.2"
 # Avoid adding more dependencies to this crate
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index b39d937cac..56eda5b1e5 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -2,9 +2,7 @@
 
 use crate::install::{ClientOpt, Malloc, ServerOpt};
 
-xflags::xflags! {
-    src "./src/flags.rs"
-
+xflags::args_parser! {
     /// Run custom build command.
     cmd xtask {
         default cmd help {
@@ -57,7 +55,7 @@ xflags::xflags! {
 
 // generated start
 // The following code is generated by `xflags` macro.
-// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
+// Run `env XFLAGS_DUMP= cargo build` to regenerate.
 #[derive(Debug)]
 pub struct Xtask {
     pub subcommand: XtaskCmd,
@@ -98,13 +96,13 @@ pub struct Codegen {
 }
 
 #[derive(Debug)]
-pub struct Lint;
+pub struct Lint {}
 
 #[derive(Debug)]
-pub struct FuzzTests;
+pub struct FuzzTests {}
 
 #[derive(Debug)]
-pub struct PreCache;
+pub struct PreCache {}
 
 #[derive(Debug)]
 pub struct Release {
@@ -133,10 +131,11 @@ pub struct Bb {
 }
 
 impl Xtask {
-    pub const HELP: &'static str = Self::HELP_;
+    pub const HELP: &'static str = Self::_HELP;
 
     pub fn from_env() -> xflags::Result<Self> {
-        Self::from_env_()
+        let mut p = xflags::rt::Parser::new_from_env();
+        Self::_parse(&mut p)
     }
 }
 // generated end


Test patch:
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index a7a923beb4..b7f6167607 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -23,7 +23,6 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
     insert_use: InsertUseConfig {
         merge: Some(MergeBehavior::Full),
         prefix_kind: hir::PrefixKind::Plain,
-        group: true,
     },
 };
 
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 9da8440310..baff833051 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -25,7 +25,6 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
     insert_use: InsertUseConfig {
         merge: Some(MergeBehavior::Full),
         prefix_kind: PrefixKind::Plain,
-        group: true,
     },
 };
 
@@ -120,7 +119,7 @@ pub(crate) fn check_edit_with_config(
 
     let mut combined_edit = completion.text_edit().to_owned();
     if let Some(import_text_edit) =
-        completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use))
+        completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge))
     {
         combined_edit.union(import_text_edit).expect(
             "Failed to apply completion resolve changes: change ranges overlap, but should not",
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 67d0d6fb6d..4bbe66f1fe 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -1,31 +1,7 @@
 use super::*;
 
-use hir::PrefixKind;
 use test_utils::assert_eq_text;
 
-#[test]
-fn insert_not_group() {
-    check(
-        "use external_crate2::bar::A",
-        r"
-use std::bar::B;
-use external_crate::bar::A;
-use crate::bar::A;
-use self::bar::A;
-use super::bar::A;",
-        r"
-use std::bar::B;
-use external_crate::bar::A;
-use crate::bar::A;
-use self::bar::A;
-use super::bar::A;
-use external_crate2::bar::A;",
-        None,
-        false,
-        false,
-    );
-}
-
 #[test]
 fn insert_existing() {
     check_full("std::fs", "use std::fs;", "use std::fs;")
@@ -264,7 +240,6 @@ fn insert_empty_module() {
 }",
         None,
         true,
-        true,
     )
 }
 
@@ -609,7 +584,6 @@ fn check(
     ra_fixture_after: &str,
     mb: Option<MergeBehavior>,
     module: bool,
-    group: bool,
 ) {
     let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
     if module {
@@ -623,25 +597,21 @@ fn check(
         .find_map(ast::Path::cast)
         .unwrap();
 
-    let rewriter = insert_use(
-        &file,
-        path,
-        InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group },
-    );
+    let rewriter = insert_use(&file, path, mb);
     let result = rewriter.rewrite(file.as_syntax_node()).to_string();
     assert_eq_text!(ra_fixture_after, &result);
 }
 
 fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true)
+    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false)
 }
 
 fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true)
+    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false)
 }
 
 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, None, false, true)
+    check(path, ra_fixture_before, ra_fixture_after, None, false)
 }
 
 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {