copilot: Add the option to disable Next Edit Suggestions (#47438)

Adds a new setting to GitHub Copilot to toggle the Next Edit Suggestions
feature, it is enabled by default.

## Motivations
Due to some current usability issues with this feature, see #46880, and
some personal anecdotes of using it, it is currently rough to utilize,
so this gives the option to disable it.

## Related
- #47071 
- #30124
- #44486
## Release Notes
- Adds the ability to disable GitHub Copilot's Next Edit Suggestions
feature.
## User Interface

![image](https://github.com/user-attachments/assets/5a3d7166-68dd-4f5b-a220-0a9bd9282cd5)
## Text Example
The text example will be adding a `z` variable to a `Point3D` class in
TypeScript.
### With Next Edit Suggestions
In this example I am able to just press auto-complete (press TAB) 3x.
```ts
class Point3D {
    x: number;
    y: number;
    z: number; // <-- Cursor before z: suggested

    constructor(x: number, 
                y: number
                , z: number // <-- Next Suggestion
                ) { 
        this.x = x;
        this.y = y;
        this.z = z; // <-- Last Suggestion
    }
}
```
### Without Next Edit Suggestions
```ts
class Point3D {
    x: number;
    y: number;
    z: number; // <-- Cursor before z: the only suggestion

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}
```
This commit is contained in:
André Eriksson authored and GitHub committed 2026-01-23 15:57:38 +01:00
1 parent 30ca3ac44e
commit 9db716123d
5 files changed
+122 -62

No files matched your search

+2
View File
@@ -1528,11 +1528,13 @@
// "enterprise_uri": "",
// "proxy": "",
// "proxy_no_verify": false
// "enabled_next_edit_suggestions": true
// },
"copilot": {
"enterprise_uri": null,
"proxy": null,
"proxy_no_verify": null,
"enabled_next_edit_suggestions": true,
},
"codestral": {
"api_url": "https://codestral.mistral.ai",
+87 -62
View File
@@ -9,12 +9,13 @@ use ::fs::Fs;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashMap, HashSet};
use command_palette_hooks::CommandPaletteFilter;
use futures::future;
use futures::{Future, FutureExt, TryFutureExt, channel::oneshot, future::Shared, select_biased};
use gpui::{
App, AppContext as _, AsyncApp, Context, Entity, EntityId, EventEmitter, Global, Task,
WeakEntity, actions,
};
use language::language_settings::CopilotSettings;
use language::language_settings::{AllLanguageSettings, CopilotSettings};
use language::{
Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16, ToPointUtf16,
language_settings::{EditPredictionProvider, all_language_settings},
@@ -928,21 +929,63 @@ impl Copilot {
let hard_tabs = settings.hard_tabs;
drop(settings);
let nes_enabled = AllLanguageSettings::get_global(cx)
.edit_predictions
.copilot
.enabled_next_edit_suggestions
.unwrap_or(true);
cx.background_spawn(async move {
let (version, snapshot) = pending_snapshot.await?;
let lsp_position = point_to_lsp(position);
let nes_request = lsp
.request::<NextEditSuggestions>(request::NextEditSuggestionsParams {
let nes_fut = if nes_enabled {
lsp.request::<NextEditSuggestions>(request::NextEditSuggestionsParams {
text_document: lsp::VersionedTextDocumentIdentifier {
uri: uri.clone(),
version,
},
position: lsp_position,
})
.fuse();
.map(|resp| {
resp.into_response()
.ok()
.map(|result| {
result
.edits
.into_iter()
.map(|completion| {
let start = snapshot.clip_point_utf16(
point_from_lsp(completion.range.start),
Bias::Left,
);
let end = snapshot.clip_point_utf16(
point_from_lsp(completion.range.end),
Bias::Left,
);
CopilotEditPrediction {
buffer: buffer_entity.clone(),
range: snapshot.anchor_before(start)
..snapshot.anchor_after(end),
text: completion.text,
command: completion.command,
snapshot: snapshot.clone(),
source: CompletionSource::NextEditSuggestion,
}
})
.collect::<Vec<_>>()
})
.unwrap_or_default()
})
.left_future()
.fuse()
} else {
future::ready(Vec::<CopilotEditPrediction>::new())
.right_future()
.fuse()
};
let inline_request = lsp
let inline_fut = lsp
.request::<InlineCompletions>(request::InlineCompletionsParams {
text_document: lsp::VersionedTextDocumentIdentifier {
uri: uri.clone(),
@@ -957,74 +1000,56 @@ impl Copilot {
insert_spaces: !hard_tabs,
}),
})
.map(|resp| {
resp.into_response()
.ok()
.map(|result| {
result
.items
.into_iter()
.map(|item| {
let start = snapshot.clip_point_utf16(
point_from_lsp(item.range.start),
Bias::Left,
);
let end = snapshot.clip_point_utf16(
point_from_lsp(item.range.end),
Bias::Left,
);
CopilotEditPrediction {
buffer: buffer_entity.clone(),
range: snapshot.anchor_before(start)
..snapshot.anchor_after(end),
text: item.insert_text,
command: item.command,
snapshot: snapshot.clone(),
source: CompletionSource::InlineCompletion,
}
})
.collect::<Vec<_>>()
})
.unwrap_or_default()
})
.fuse();
futures::pin_mut!(nes_request, inline_request);
let convert_nes =
|result: request::NextEditSuggestionsResult| -> Vec<CopilotEditPrediction> {
result
.edits
.into_iter()
.map(|completion| {
let start = snapshot.clip_point_utf16(
point_from_lsp(completion.range.start),
Bias::Left,
);
let end = snapshot
.clip_point_utf16(point_from_lsp(completion.range.end), Bias::Left);
CopilotEditPrediction {
buffer: buffer_entity.clone(),
range: snapshot.anchor_before(start)..snapshot.anchor_after(end),
text: completion.text,
command: completion.command,
snapshot: snapshot.clone(),
source: CompletionSource::NextEditSuggestion,
}
})
.collect()
};
let convert_inline =
|result: request::InlineCompletionsResult| -> Vec<CopilotEditPrediction> {
result
.items
.into_iter()
.map(|item| {
let start = snapshot
.clip_point_utf16(point_from_lsp(item.range.start), Bias::Left);
let end = snapshot
.clip_point_utf16(point_from_lsp(item.range.end), Bias::Left);
CopilotEditPrediction {
buffer: buffer_entity.clone(),
range: snapshot.anchor_before(start)..snapshot.anchor_after(end),
text: item.insert_text,
command: item.command,
snapshot: snapshot.clone(),
source: CompletionSource::InlineCompletion,
}
})
.collect()
};
futures::pin_mut!(nes_fut, inline_fut);
let mut nes_result: Option<Vec<CopilotEditPrediction>> = None;
let mut inline_result: Option<Vec<CopilotEditPrediction>> = None;
loop {
select_biased! {
nes = nes_request => {
let completions = nes.into_response().ok().map(convert_nes).unwrap_or_default();
if !completions.is_empty() {
return Ok(completions);
nes = nes_fut => {
if !nes.is_empty() {
return Ok(nes);
}
nes_result = Some(completions);
nes_result = Some(nes);
}
inline = inline_request => {
let completions = inline.into_response().ok().map(convert_inline).unwrap_or_default();
if !completions.is_empty() {
return Ok(completions);
inline = inline_fut => {
if !inline.is_empty() {
return Ok(inline);
}
inline_result = Some(completions);
inline_result = Some(inline);
}
complete => break,
}
@@ -942,6 +942,11 @@ impl EditPredictionButton {
cx: &mut Context<Self>,
) -> Entity<ContextMenu> {
let all_language_settings = all_language_settings(None, cx);
let next_edit_suggestions = all_language_settings
.edit_predictions
.copilot
.enabled_next_edit_suggestions
.unwrap_or(true);
let copilot_config = copilot_chat::CopilotChatConfiguration {
enterprise_uri: all_language_settings
.edit_predictions
@@ -957,6 +962,27 @@ impl EditPredictionButton {
self.add_provider_switching_section(menu, EditPredictionProvider::Copilot, cx);
menu.separator()
.item(
ContextMenuEntry::new("Copilot: Next Edit Suggestions")
.toggleable(IconPosition::Start, next_edit_suggestions)
.handler({
let fs = self.fs.clone();
move |_, cx| {
update_settings_file(fs.clone(), cx, move |settings, _| {
settings
.project
.all_languages
.edit_predictions
.get_or_insert_default()
.copilot
.get_or_insert_default()
.enabled_next_edit_suggestions =
Some(!next_edit_suggestions);
});
}
}),
)
.separator()
.link(
"Go to Copilot Settings",
OpenBrowser { url: settings_url }.boxed_clone(),
+3
View File
@@ -483,6 +483,8 @@ pub struct CopilotSettings {
pub proxy_no_verify: Option<bool>,
/// Enterprise URI for Copilot.
pub enterprise_uri: Option<String>,
/// Whether the Copilot Next Edit Suggestions feature is enabled.
pub enabled_next_edit_suggestions: Option<bool>,
}
#[derive(Clone, Debug, Default)]
@@ -741,6 +743,7 @@ impl settings::Settings for AllLanguageSettings {
proxy: copilot.proxy,
proxy_no_verify: copilot.proxy_no_verify,
enterprise_uri: copilot.enterprise_uri,
enabled_next_edit_suggestions: copilot.enabled_next_edit_suggestions,
};
let codestral = edit_predictions.codestral.unwrap();
+4
View File
@@ -210,6 +210,10 @@ pub struct CopilotSettingsContent {
///
/// Default: none
pub enterprise_uri: Option<String>,
/// Whether the Copilot Next Edit Suggestions feature is enabled.
///
/// Default: true
pub enabled_next_edit_suggestions: Option<bool>,
}
#[with_fallible_options]