Skip to content

Commit

Permalink
Support HTTP(s) proxy for Copilot completions language server
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-kaplan committed Feb 6, 2025
1 parent dfd11c3 commit 12c5452
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 9 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/copilot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ async-tar.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
ctor.workspace = true
command_palette_hooks.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
http_client.workspace = true
inline_completion.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true
menu.workspace = true
node_runtime.workspace = true
Expand Down Expand Up @@ -65,6 +67,7 @@ clock = { workspace = true, features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
fs = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }
Expand Down
80 changes: 71 additions & 9 deletions crates/copilot/src/copilot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ use gpui::{
use http_client::github::get_release_by_tag_name;
use http_client::HttpClient;
use language::{
language_settings::{all_language_settings, language_settings, InlineCompletionProvider},
language_settings::{
all_language_settings, language_settings, AllLanguageSettings, InlineCompletionProvider,
},
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
ToPointUtf16,
};
use log;
use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId, LanguageServerName};
use node_runtime::NodeRuntime;
use parking_lot::Mutex;
use request::StatusNotification;
use settings::SettingsStore;
use settings::{Settings, SettingsStore};
use smol::{fs, io::BufReader, stream::StreamExt};
use std::{
any::TypeId,
Expand Down Expand Up @@ -368,13 +371,20 @@ impl Copilot {
let server_id = self.server_id;
let http = self.http.clone();
let node_runtime = self.node_runtime.clone();
if all_language_settings(None, cx).inline_completions.provider
== InlineCompletionProvider::Copilot
{
let lang_settings = all_language_settings(None, cx);
if lang_settings.inline_completions.provider == InlineCompletionProvider::Copilot {
if matches!(self.server, CopilotServer::Disabled) {
let server_env = self.calculate_language_server_env(lang_settings);
let start_task = cx
.spawn(move |this, cx| {
Self::start_language_server(server_id, http, node_runtime, this, cx)
Self::start_language_server(
server_id,
http,
node_runtime,
server_env,
this,
cx,
)
})
.shared();
self.server = CopilotServer::Starting { task: start_task };
Expand All @@ -386,6 +396,46 @@ impl Copilot {
}
}

fn calculate_language_server_env(
&mut self,
lang_settings: &AllLanguageSettings,
) -> Option<HashMap<String, String>> {
let proxy_url = lang_settings.inline_completions.proxy.to_owned();
let no_verify = lang_settings.inline_completions.proxy_no_verify.to_owned();
match proxy_url {
Some(url) => {
let env_name;
if url.starts_with("http:") {
env_name = String::from("HTTP_PROXY");
} else if url.starts_with("https:") {
env_name = String::from("HTTPS_proxy");
} else {
// Only HTTP and HTTPS proxies are currently supported for the language server
log::error!("Unsupported protocol scheme for language server proxy (must be http or https)");
return None;
}

let mut server_env: HashMap<String, String> = HashMap::default();
server_env.insert(env_name, url);

match no_verify {
Some(no_verify_val) => {
if no_verify_val {
server_env.insert(
String::from("NODE_TLS_REJECT_UNAUTHORIZED"),
String::from("0"),
);
}
}
None => {}
};

Some(server_env)
}
None => None,
}
}

#[cfg(any(test, feature = "test-support"))]
pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
use lsp::FakeLanguageServer;
Expand Down Expand Up @@ -423,18 +473,19 @@ impl Copilot {
new_server_id: LanguageServerId,
http: Arc<dyn HttpClient>,
node_runtime: NodeRuntime,
server_env: Option<HashMap<String, String>>,
this: WeakEntity<Self>,
mut cx: AsyncApp,
) {
let start_language_server = async {
let server_path = get_copilot_lsp(http).await?;
let node_path = node_runtime.binary_path().await?;
let arguments: Vec<OsString> = vec![server_path.into(), "--stdio".into()];

let binary = LanguageServerBinary {
path: node_path,
arguments,
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
env: None,
env: server_env,
};

let root_path = if cfg!(target_os = "windows") {
Expand Down Expand Up @@ -611,14 +662,17 @@ impl Copilot {
}

pub fn reinstall(&mut self, cx: &mut Context<Self>) -> Task<()> {
let lang_settings = all_language_settings(None, cx);
let server_env = self.calculate_language_server_env(lang_settings);
let start_task = cx
.spawn({
let http = self.http.clone();
let node_runtime = self.node_runtime.clone();
let server_id = self.server_id;
move |this, cx| async move {
clear_copilot_dir().await;
Self::start_language_server(server_id, http, node_runtime, this, cx).await
Self::start_language_server(server_id, http, node_runtime, server_env, this, cx)
.await
}
})
.shared();
Expand Down Expand Up @@ -1278,3 +1332,11 @@ mod tests {
}
}
}

#[cfg(test)]
#[ctor::ctor]
fn init_logger() {
if std::env::var("RUST_LOG").is_ok() {
env_logger::init();
}
}
42 changes: 42 additions & 0 deletions crates/language/src/language_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ pub struct InlineCompletionSettings {
pub provider: InlineCompletionProvider,
/// A list of globs representing files that inline completions should be disabled for.
pub disabled_globs: Vec<GlobMatcher>,
/// HTTP/HTTPS proxy to use for completions (currently supported only for Copilot)
pub proxy: Option<String>,
/// Disable certificate verification for completions proxy (not recommended)
pub proxy_no_verify: Option<bool>,
}

/// The settings for all languages.
Expand Down Expand Up @@ -406,6 +410,16 @@ pub struct InlineCompletionSettingsContent {
/// A list of globs representing files that inline completions should be disabled for.
#[serde(default)]
pub disabled_globs: Option<Vec<String>>,
/// HTTP/HTTPS proxy to use for completions (currently supported only for Copilot)
///
/// Default: none
#[serde(default)]
pub proxy: Option<String>,
/// Disable certificate verification for completions proxy (not recommended)
///
/// Default: false
#[serde(default)]
pub proxy_no_verify: Option<bool>,
}

/// The settings for enabling/disabling features.
Expand Down Expand Up @@ -1005,6 +1019,16 @@ impl settings::Settings for AllLanguageSettings {
.map(|globs| globs.iter().collect())
.ok_or_else(Self::missing_default)?;

let mut completions_proxy = default_value
.inline_completions
.as_ref()
.and_then(|f| f.proxy.clone());

let mut completions_proxy_no_verify = default_value
.inline_completions
.as_ref()
.and_then(|f| f.proxy_no_verify);

let mut file_types: HashMap<Arc<str>, GlobSet> = HashMap::default();

for (language, suffixes) in &default_value.file_types {
Expand Down Expand Up @@ -1036,6 +1060,22 @@ impl settings::Settings for AllLanguageSettings {
completion_globs.extend(globs.iter());
}

if let Some(proxy) = user_settings
.inline_completions
.as_ref()
.and_then(|f| f.proxy.clone())
{
completions_proxy = Some(proxy);
}

if let Some(proxy_no_verify) = user_settings
.inline_completions
.as_ref()
.and_then(|f| f.proxy_no_verify)
{
completions_proxy_no_verify = Some(proxy_no_verify);
}

// A user's global settings override the default global settings and
// all default language-specific settings.
merge_settings(&mut defaults, &user_settings.defaults);
Expand Down Expand Up @@ -1086,6 +1126,8 @@ impl settings::Settings for AllLanguageSettings {
.iter()
.filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
.collect(),
proxy: completions_proxy,
proxy_no_verify: completions_proxy_no_verify,
},
defaults,
languages,
Expand Down

0 comments on commit 12c5452

Please sign in to comment.