WIP and merge

This commit is contained in:
Anthony 2025-06-27 18:38:25 -04:00
parent 97f4406ef6
commit 1bdde8b2e4
584 changed files with 33536 additions and 17400 deletions

View file

@ -20,6 +20,7 @@ test-support = [
"text/test-support",
"tree-sitter-rust",
"tree-sitter-python",
"tree-sitter-rust",
"tree-sitter-typescript",
"settings/test-support",
"util/test-support",

View file

@ -1,12 +1,6 @@
pub use crate::{
Grammar, Language, LanguageRegistry,
diagnostic_set::DiagnosticSet,
highlight_map::{HighlightId, HighlightMap},
proto,
};
use crate::{
LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, TextObject,
TreeSitterOptions,
DebuggerTextObject, LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag,
TextObject, TreeSitterOptions,
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
language_settings::{LanguageSettings, language_settings},
outline::OutlineItem,
@ -17,6 +11,12 @@ use crate::{
task_context::RunnableRange,
text_diff::text_diff,
};
pub use crate::{
Grammar, Language, LanguageRegistry,
diagnostic_set::DiagnosticSet,
highlight_map::{HighlightId, HighlightMap},
proto,
};
use anyhow::{Context as _, Result};
pub use clock::ReplicaId;
use clock::{AGENT_REPLICA_ID, Lamport};
@ -107,6 +107,7 @@ pub struct Buffer {
reload_task: Option<Task<Result<()>>>,
language: Option<Arc<Language>>,
autoindent_requests: Vec<Arc<AutoindentRequest>>,
wait_for_autoindent_txs: Vec<oneshot::Sender<()>>,
pending_autoindent: Option<Task<()>>,
sync_parse_timeout: Duration,
syntax_map: Mutex<SyntaxMap>,
@ -949,6 +950,7 @@ impl Buffer {
sync_parse_timeout: Duration::from_millis(1),
parse_status: watch::channel(ParseStatus::Idle),
autoindent_requests: Default::default(),
wait_for_autoindent_txs: Default::default(),
pending_autoindent: Default::default(),
language: None,
remote_selections: Default::default(),
@ -1454,7 +1456,7 @@ impl Buffer {
self.syntax_map.lock().contains_unknown_injections()
}
#[cfg(test)]
#[cfg(any(test, feature = "test-support"))]
pub fn set_sync_parse_timeout(&mut self, timeout: Duration) {
self.sync_parse_timeout = timeout;
}
@ -1605,6 +1607,9 @@ impl Buffer {
}
} else {
self.autoindent_requests.clear();
for tx in self.wait_for_autoindent_txs.drain(..) {
tx.send(()).ok();
}
}
}
@ -1786,6 +1791,9 @@ impl Buffer {
cx: &mut Context<Self>,
) {
self.autoindent_requests.clear();
for tx in self.wait_for_autoindent_txs.drain(..) {
tx.send(()).ok();
}
let edits: Vec<_> = indent_sizes
.into_iter()
@ -2125,6 +2133,16 @@ impl Buffer {
self.text.give_up_waiting();
}
pub fn wait_for_autoindent_applied(&mut self) -> Option<oneshot::Receiver<()>> {
let mut rx = None;
if !self.autoindent_requests.is_empty() {
let channel = oneshot::channel();
self.wait_for_autoindent_txs.push(channel.0);
rx = Some(channel.1);
}
rx
}
/// Stores a set of selections that should be broadcasted to all of the buffer's replicas.
pub fn set_active_selections(
&mut self,
@ -3835,6 +3853,74 @@ impl BufferSnapshot {
.filter(|pair| !pair.newline_only)
}
pub fn debug_variables_query<T: ToOffset>(
&self,
range: Range<T>,
) -> impl Iterator<Item = (Range<usize>, DebuggerTextObject)> + '_ {
let range = range.start.to_offset(self).saturating_sub(1)
..self.len().min(range.end.to_offset(self) + 1);
let mut matches = self.syntax.matches_with_options(
range.clone(),
&self.text,
TreeSitterOptions::default(),
|grammar| grammar.debug_variables_config.as_ref().map(|c| &c.query),
);
let configs = matches
.grammars()
.iter()
.map(|grammar| grammar.debug_variables_config.as_ref())
.collect::<Vec<_>>();
let mut captures = Vec::<(Range<usize>, DebuggerTextObject)>::new();
iter::from_fn(move || {
loop {
while let Some(capture) = captures.pop() {
if capture.0.overlaps(&range) {
return Some(capture);
}
}
let mat = matches.peek()?;
let Some(config) = configs[mat.grammar_index].as_ref() else {
matches.advance();
continue;
};
for capture in mat.captures {
let Some(ix) = config
.objects_by_capture_ix
.binary_search_by_key(&capture.index, |e| e.0)
.ok()
else {
continue;
};
let text_object = config.objects_by_capture_ix[ix].1;
let byte_range = capture.node.byte_range();
let mut found = false;
for (range, existing) in captures.iter_mut() {
if existing == &text_object {
range.start = range.start.min(byte_range.start);
range.end = range.end.max(byte_range.end);
found = true;
break;
}
}
if !found {
captures.push((byte_range, text_object));
}
}
matches.advance();
}
})
}
pub fn text_object_ranges<T: ToOffset>(
&self,
range: Range<T>,
@ -4273,6 +4359,11 @@ impl BufferSnapshot {
self.non_text_state_update_count
}
/// An integer version that changes when the buffer's syntax changes.
pub fn syntax_update_count(&self) -> usize {
self.syntax.update_count()
}
/// Returns a snapshot of underlying file.
pub fn file(&self) -> Option<&Arc<dyn File>> {
self.file.as_ref()

View file

@ -32,7 +32,9 @@ use futures::Future;
use gpui::{App, AsyncApp, Entity, SharedString, Task};
pub use highlight_map::HighlightMap;
use http_client::HttpClient;
pub use language_registry::{LanguageName, LoadedLanguage};
pub use language_registry::{
LanguageName, LanguageServerStatusUpdate, LoadedLanguage, ServerHealth,
};
use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions};
pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};
use parking_lot::Mutex;
@ -1080,6 +1082,7 @@ pub struct Grammar {
pub embedding_config: Option<EmbeddingConfig>,
pub(crate) injection_config: Option<InjectionConfig>,
pub(crate) override_config: Option<OverrideConfig>,
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
pub(crate) highlight_map: Mutex<HighlightMap>,
}
@ -1102,6 +1105,22 @@ pub struct OutlineConfig {
pub annotation_capture_ix: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DebuggerTextObject {
Variable,
Scope,
}
impl DebuggerTextObject {
pub fn from_capture_name(name: &str) -> Option<DebuggerTextObject> {
match name {
"debug-variable" => Some(DebuggerTextObject::Variable),
"debug-scope" => Some(DebuggerTextObject::Scope),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TextObject {
InsideFunction,
@ -1204,6 +1223,11 @@ struct BracketsPatternConfig {
newline_only: bool,
}
pub struct DebugVariablesConfig {
pub query: Query,
pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
}
impl Language {
pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
Self::new_with_id(LanguageId::new(), config, ts_language)
@ -1235,6 +1259,7 @@ impl Language {
redactions_config: None,
runnable_config: None,
error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
debug_variables_config: None,
ts_language,
highlight_map: Default::default(),
})
@ -1305,6 +1330,11 @@ impl Language {
.with_text_object_query(query.as_ref())
.context("Error loading textobject query")?;
}
if let Some(query) = queries.debugger {
self = self
.with_debug_variables_query(query.as_ref())
.context("Error loading debug variables query")?;
}
Ok(self)
}
@ -1423,6 +1453,24 @@ impl Language {
Ok(self)
}
pub fn with_debug_variables_query(mut self, source: &str) -> Result<Self> {
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
let query = Query::new(&grammar.ts_language, source)?;
let mut objects_by_capture_ix = Vec::new();
for (ix, name) in query.capture_names().iter().enumerate() {
if let Some(text_object) = DebuggerTextObject::from_capture_name(name) {
objects_by_capture_ix.push((ix as u32, text_object));
}
}
grammar.debug_variables_config = Some(DebugVariablesConfig {
query,
objects_by_capture_ix,
});
Ok(self)
}
pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
let query = Query::new(&grammar.ts_language, source)?;
@ -1928,6 +1976,10 @@ impl Grammar {
.capture_index_for_name(name)?;
Some(self.highlight_map.lock().get(capture_id))
}
pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {
self.debug_variables_config.as_ref()
}
}
impl CodeLabel {
@ -1980,25 +2032,27 @@ impl CodeLabel {
} else {
label.clone()
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..label_length);
Self {
text,
runs,
filter_range: 0..label_length,
filter_range,
}
}
pub fn plain(text: String, filter_text: Option<&str>) -> Self {
let mut result = Self {
let filter_range = filter_text
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..text.len());
Self {
runs: Vec::new(),
filter_range: 0..text.len(),
filter_range,
text,
};
if let Some(filter_text) = filter_text {
if let Some(ix) = result.text.find(filter_text) {
result.filter_range = ix..ix + filter_text.len();
}
}
result
}
pub fn push_str(&mut self, text: &str, highlight: Option<HighlightId>) {

View file

@ -107,7 +107,7 @@ pub struct LanguageRegistry {
state: RwLock<LanguageRegistryState>,
language_server_download_dir: Option<Arc<Path>>,
executor: BackgroundExecutor,
lsp_binary_status_tx: BinaryStatusSender,
lsp_binary_status_tx: ServerStatusSender,
}
struct LanguageRegistryState {
@ -138,11 +138,28 @@ pub struct FakeLanguageServerEntry {
pub _server: Option<lsp::FakeLanguageServer>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LanguageServerStatusUpdate {
Binary(BinaryStatus),
Health(ServerHealth, Option<SharedString>),
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub enum ServerHealth {
Ok,
Warning,
Error,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinaryStatus {
None,
CheckingForUpdate,
Downloading,
Starting,
Stopping,
Stopped,
Failed { error: String },
}
@ -212,7 +229,7 @@ pub const QUERY_FILENAME_PREFIXES: &[(
("overrides", |q| &mut q.overrides),
("redactions", |q| &mut q.redactions),
("runnables", |q| &mut q.runnables),
("debug_variables", |q| &mut q.debug_variables),
("debugger", |q| &mut q.debugger),
("textobjects", |q| &mut q.text_objects),
];
@ -229,12 +246,12 @@ pub struct LanguageQueries {
pub redactions: Option<Cow<'static, str>>,
pub runnables: Option<Cow<'static, str>>,
pub text_objects: Option<Cow<'static, str>>,
pub debug_variables: Option<Cow<'static, str>>,
pub debugger: Option<Cow<'static, str>>,
}
#[derive(Clone, Default)]
struct BinaryStatusSender {
txs: Arc<Mutex<Vec<mpsc::UnboundedSender<(SharedString, BinaryStatus)>>>>,
struct ServerStatusSender {
txs: Arc<Mutex<Vec<mpsc::UnboundedSender<(LanguageServerName, BinaryStatus)>>>>,
}
pub struct LoadedLanguage {
@ -1071,8 +1088,8 @@ impl LanguageRegistry {
self.state.read().all_lsp_adapters.get(name).cloned()
}
pub fn update_lsp_status(&self, server_name: LanguageServerName, status: BinaryStatus) {
self.lsp_binary_status_tx.send(server_name.0, status);
pub fn update_lsp_binary_status(&self, server_name: LanguageServerName, status: BinaryStatus) {
self.lsp_binary_status_tx.send(server_name, status);
}
pub fn next_language_server_id(&self) -> LanguageServerId {
@ -1127,7 +1144,7 @@ impl LanguageRegistry {
pub fn language_server_binary_statuses(
&self,
) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> {
) -> mpsc::UnboundedReceiver<(LanguageServerName, BinaryStatus)> {
self.lsp_binary_status_tx.subscribe()
}
@ -1241,14 +1258,14 @@ impl LanguageRegistryState {
}
}
impl BinaryStatusSender {
fn subscribe(&self) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> {
impl ServerStatusSender {
fn subscribe(&self) -> mpsc::UnboundedReceiver<(LanguageServerName, BinaryStatus)> {
let (tx, rx) = mpsc::unbounded();
self.txs.lock().push(tx);
rx
}
fn send(&self, name: SharedString, status: BinaryStatus) {
fn send(&self, name: LanguageServerName, status: BinaryStatus) {
let mut txs = self.txs.lock();
txs.retain(|tx| tx.unbounded_send((name.clone(), status.clone())).is_ok());
}

View file

@ -288,6 +288,8 @@ pub struct CopilotSettings {
pub proxy: Option<String>,
/// Disable certificate verification for proxy (not recommended).
pub proxy_no_verify: Option<bool>,
/// Enterprise URI for Copilot.
pub enterprise_uri: Option<String>,
}
/// The settings for all languages.
@ -607,6 +609,11 @@ pub struct CopilotSettingsContent {
/// Default: false
#[serde(default)]
pub proxy_no_verify: Option<bool>,
/// Enterprise URI for Copilot.
///
/// Default: none
#[serde(default)]
pub enterprise_uri: Option<String>,
}
/// The settings for enabling/disabling features.
@ -1228,10 +1235,10 @@ impl settings::Settings for AllLanguageSettings {
let mut copilot_settings = default_value
.edit_predictions
.as_ref()
.map(|settings| settings.copilot.clone())
.map(|copilot| CopilotSettings {
proxy: copilot.proxy,
proxy_no_verify: copilot.proxy_no_verify,
.map(|settings| CopilotSettings {
proxy: settings.copilot.proxy.clone(),
proxy_no_verify: settings.copilot.proxy_no_verify,
enterprise_uri: settings.copilot.enterprise_uri.clone(),
})
.unwrap_or_default();
@ -1287,6 +1294,14 @@ impl settings::Settings for AllLanguageSettings {
copilot_settings.proxy_no_verify = Some(proxy_no_verify);
}
if let Some(enterprise_uri) = user_settings
.edit_predictions
.as_ref()
.and_then(|settings| settings.copilot.enterprise_uri.clone())
{
copilot_settings.enterprise_uri = Some(enterprise_uri);
}
// A user's global settings override the default global settings and
// all default language-specific settings.
merge_settings(&mut defaults, &user_settings.defaults);

View file

@ -122,6 +122,7 @@ impl<T> Outline<T> {
},
query,
smart_case,
true,
100,
&Default::default(),
executor.clone(),

View file

@ -11,6 +11,8 @@ use text::*;
pub use proto::{BufferState, File, Operation};
use super::{point_from_lsp, point_to_lsp};
/// Deserializes a `[text::LineEnding]` from the RPC representation.
pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
match message {
@ -582,3 +584,33 @@ pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry
})
.collect()
}
pub fn serialize_lsp_edit(edit: lsp::TextEdit) -> proto::TextEdit {
let start = point_from_lsp(edit.range.start).0;
let end = point_from_lsp(edit.range.end).0;
proto::TextEdit {
new_text: edit.new_text,
lsp_range_start: Some(proto::PointUtf16 {
row: start.row,
column: start.column,
}),
lsp_range_end: Some(proto::PointUtf16 {
row: end.row,
column: end.column,
}),
}
}
pub fn deserialize_lsp_edit(edit: proto::TextEdit) -> Option<lsp::TextEdit> {
let start = edit.lsp_range_start?;
let start = PointUtf16::new(start.row, start.column);
let end = edit.lsp_range_end?;
let end = PointUtf16::new(end.row, end.column);
Some(lsp::TextEdit {
range: lsp::Range {
start: point_to_lsp(start),
end: point_to_lsp(end),
},
new_text: edit.new_text,
})
}

View file

@ -32,6 +32,7 @@ pub struct SyntaxSnapshot {
parsed_version: clock::Global,
interpolated_version: clock::Global,
language_registry_version: usize,
update_count: usize,
}
#[derive(Default)]
@ -257,7 +258,9 @@ impl SyntaxMap {
}
pub fn clear(&mut self, text: &BufferSnapshot) {
let update_count = self.snapshot.update_count + 1;
self.snapshot = SyntaxSnapshot::new(text);
self.snapshot.update_count = update_count;
}
}
@ -268,6 +271,7 @@ impl SyntaxSnapshot {
parsed_version: clock::Global::default(),
interpolated_version: clock::Global::default(),
language_registry_version: 0,
update_count: 0,
}
}
@ -275,6 +279,10 @@ impl SyntaxSnapshot {
self.layers.is_empty()
}
pub fn update_count(&self) -> usize {
self.update_count
}
pub fn interpolate(&mut self, text: &BufferSnapshot) {
let edits = text
.anchored_edits_since::<(usize, Point)>(&self.interpolated_version)
@ -443,6 +451,8 @@ impl SyntaxSnapshot {
self.language_registry_version = registry.version();
}
}
self.update_count += 1;
}
fn reparse_with_ranges(