Merge branch 'main' into reconnections-2
This commit is contained in:
commit
969c314315
78 changed files with 2759 additions and 1281 deletions
|
@ -30,6 +30,7 @@ use language::{
|
|||
CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent,
|
||||
File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt,
|
||||
Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction,
|
||||
Unclipped,
|
||||
};
|
||||
use lsp::{
|
||||
DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString,
|
||||
|
@ -251,7 +252,7 @@ pub struct Symbol {
|
|||
pub label: CodeLabel,
|
||||
pub name: String,
|
||||
pub kind: lsp::SymbolKind,
|
||||
pub range: Range<PointUtf16>,
|
||||
pub range: Range<Unclipped<PointUtf16>>,
|
||||
pub signature: [u8; 32],
|
||||
}
|
||||
|
||||
|
@ -2582,7 +2583,7 @@ impl Project {
|
|||
language_server_id: usize,
|
||||
abs_path: PathBuf,
|
||||
version: Option<i32>,
|
||||
diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
|
||||
diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
|
||||
cx: &mut ModelContext<Project>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let (worktree, relative_path) = self
|
||||
|
@ -2620,7 +2621,7 @@ impl Project {
|
|||
fn update_buffer_diagnostics(
|
||||
&mut self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
mut diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
|
||||
mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
|
||||
version: Option<i32>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<()> {
|
||||
|
@ -2644,7 +2645,7 @@ impl Project {
|
|||
let mut sanitized_diagnostics = Vec::new();
|
||||
let edits_since_save = Patch::new(
|
||||
snapshot
|
||||
.edits_since::<PointUtf16>(buffer.read(cx).saved_version())
|
||||
.edits_since::<Unclipped<PointUtf16>>(buffer.read(cx).saved_version())
|
||||
.collect(),
|
||||
);
|
||||
for entry in diagnostics {
|
||||
|
@ -2664,13 +2665,14 @@ impl Project {
|
|||
let mut range = snapshot.clip_point_utf16(start, Bias::Left)
|
||||
..snapshot.clip_point_utf16(end, Bias::Right);
|
||||
|
||||
// Expand empty ranges by one character
|
||||
// Expand empty ranges by one codepoint
|
||||
if range.start == range.end {
|
||||
// This will be go to the next boundary when being clipped
|
||||
range.end.column += 1;
|
||||
range.end = snapshot.clip_point_utf16(range.end, Bias::Right);
|
||||
range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right);
|
||||
if range.start == range.end && range.end.column > 0 {
|
||||
range.start.column -= 1;
|
||||
range.start = snapshot.clip_point_utf16(range.start, Bias::Left);
|
||||
range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Left);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3273,7 +3275,7 @@ impl Project {
|
|||
return Task::ready(Ok(Default::default()));
|
||||
};
|
||||
|
||||
let position = position.to_point_utf16(source_buffer);
|
||||
let position = Unclipped(position.to_point_utf16(source_buffer));
|
||||
let anchor = source_buffer.anchor_after(position);
|
||||
|
||||
if worktree.read(cx).as_local().is_some() {
|
||||
|
@ -3292,7 +3294,7 @@ impl Project {
|
|||
lsp::TextDocumentIdentifier::new(
|
||||
lsp::Url::from_file_path(buffer_abs_path).unwrap(),
|
||||
),
|
||||
point_to_lsp(position),
|
||||
point_to_lsp(position.0),
|
||||
),
|
||||
context: Default::default(),
|
||||
work_done_progress_params: Default::default(),
|
||||
|
@ -3314,88 +3316,91 @@ impl Project {
|
|||
let snapshot = this.snapshot();
|
||||
let clipped_position = this.clip_point_utf16(position, Bias::Left);
|
||||
let mut range_for_token = None;
|
||||
completions.into_iter().filter_map(move |lsp_completion| {
|
||||
// For now, we can only handle additional edits if they are returned
|
||||
// when resolving the completion, not if they are present initially.
|
||||
if lsp_completion
|
||||
.additional_text_edits
|
||||
.as_ref()
|
||||
.map_or(false, |edits| !edits.is_empty())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
|
||||
// If the language server provides a range to overwrite, then
|
||||
// check that the range is valid.
|
||||
Some(lsp::CompletionTextEdit::Edit(edit)) => {
|
||||
let range = range_from_lsp(edit.range);
|
||||
let start = snapshot.clip_point_utf16(range.start, Bias::Left);
|
||||
let end = snapshot.clip_point_utf16(range.end, Bias::Left);
|
||||
if start != range.start || end != range.end {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
edit.new_text.clone(),
|
||||
)
|
||||
}
|
||||
// If the language server does not provide a range, then infer
|
||||
// the range based on the syntax tree.
|
||||
None => {
|
||||
if position != clipped_position {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
let Range { start, end } = range_for_token
|
||||
.get_or_insert_with(|| {
|
||||
let offset = position.to_offset(&snapshot);
|
||||
let (range, kind) = snapshot.surrounding_word(offset);
|
||||
if kind == Some(CharKind::Word) {
|
||||
range
|
||||
} else {
|
||||
offset..offset
|
||||
}
|
||||
})
|
||||
.clone();
|
||||
let text = lsp_completion
|
||||
.insert_text
|
||||
.as_ref()
|
||||
.unwrap_or(&lsp_completion.label)
|
||||
.clone();
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
text,
|
||||
)
|
||||
}
|
||||
Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
|
||||
log::info!("unsupported insert/replace completion");
|
||||
completions
|
||||
.into_iter()
|
||||
.filter_map(move |mut lsp_completion| {
|
||||
// For now, we can only handle additional edits if they are returned
|
||||
// when resolving the completion, not if they are present initially.
|
||||
if lsp_completion
|
||||
.additional_text_edits
|
||||
.as_ref()
|
||||
.map_or(false, |edits| !edits.is_empty())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
LineEnding::normalize(&mut new_text);
|
||||
let language = language.clone();
|
||||
Some(async move {
|
||||
let label = if let Some(language) = language {
|
||||
language.label_for_completion(&lsp_completion).await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Completion {
|
||||
old_range,
|
||||
new_text,
|
||||
label: label.unwrap_or_else(|| {
|
||||
CodeLabel::plain(
|
||||
lsp_completion.label.clone(),
|
||||
lsp_completion.filter_text.as_deref(),
|
||||
let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref()
|
||||
{
|
||||
// If the language server provides a range to overwrite, then
|
||||
// check that the range is valid.
|
||||
Some(lsp::CompletionTextEdit::Edit(edit)) => {
|
||||
let range = range_from_lsp(edit.range);
|
||||
let start = snapshot.clip_point_utf16(range.start, Bias::Left);
|
||||
let end = snapshot.clip_point_utf16(range.end, Bias::Left);
|
||||
if start != range.start.0 || end != range.end.0 {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
edit.new_text.clone(),
|
||||
)
|
||||
}),
|
||||
lsp_completion,
|
||||
}
|
||||
}
|
||||
// If the language server does not provide a range, then infer
|
||||
// the range based on the syntax tree.
|
||||
None => {
|
||||
if position.0 != clipped_position {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
let Range { start, end } = range_for_token
|
||||
.get_or_insert_with(|| {
|
||||
let offset = position.to_offset(&snapshot);
|
||||
let (range, kind) = snapshot.surrounding_word(offset);
|
||||
if kind == Some(CharKind::Word) {
|
||||
range
|
||||
} else {
|
||||
offset..offset
|
||||
}
|
||||
})
|
||||
.clone();
|
||||
let text = lsp_completion
|
||||
.insert_text
|
||||
.as_ref()
|
||||
.unwrap_or(&lsp_completion.label)
|
||||
.clone();
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
text,
|
||||
)
|
||||
}
|
||||
Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
|
||||
log::info!("unsupported insert/replace completion");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
LineEnding::normalize(&mut new_text);
|
||||
let language = language.clone();
|
||||
Some(async move {
|
||||
let mut label = None;
|
||||
if let Some(language) = language {
|
||||
language.process_completion(&mut lsp_completion).await;
|
||||
label = language.label_for_completion(&lsp_completion).await;
|
||||
}
|
||||
Completion {
|
||||
old_range,
|
||||
new_text,
|
||||
label: label.unwrap_or_else(|| {
|
||||
CodeLabel::plain(
|
||||
lsp_completion.label.clone(),
|
||||
lsp_completion.filter_text.as_deref(),
|
||||
)
|
||||
}),
|
||||
lsp_completion,
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
Ok(futures::future::join_all(completions).await)
|
||||
|
@ -3448,29 +3453,41 @@ impl Project {
|
|||
let buffer_id = buffer.remote_id();
|
||||
|
||||
if self.is_local() {
|
||||
let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
|
||||
{
|
||||
server.clone()
|
||||
} else {
|
||||
return Task::ready(Ok(Default::default()));
|
||||
let lang_server = match self.language_server_for_buffer(buffer, cx) {
|
||||
Some((_, server)) => server.clone(),
|
||||
_ => return Task::ready(Ok(Default::default())),
|
||||
};
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let resolved_completion = lang_server
|
||||
.request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
|
||||
.await?;
|
||||
|
||||
if let Some(edits) = resolved_completion.additional_text_edits {
|
||||
let edits = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.edits_from_lsp(&buffer_handle, edits, None, cx)
|
||||
})
|
||||
.await?;
|
||||
|
||||
buffer_handle.update(&mut cx, |buffer, cx| {
|
||||
buffer.finalize_last_transaction();
|
||||
buffer.start_transaction();
|
||||
|
||||
for (range, text) in edits {
|
||||
buffer.edit([(range, text)], None, cx);
|
||||
let primary = &completion.old_range;
|
||||
let start_within = primary.start.cmp(&range.start, buffer).is_le()
|
||||
&& primary.end.cmp(&range.start, buffer).is_ge();
|
||||
let end_within = range.start.cmp(&primary.end, buffer).is_le()
|
||||
&& range.end.cmp(&primary.end, buffer).is_ge();
|
||||
|
||||
//Skip addtional edits which overlap with the primary completion edit
|
||||
//https://github.com/zed-industries/zed/pull/1871
|
||||
if !start_within && !end_within {
|
||||
buffer.edit([(range, text)], None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
let transaction = if buffer.end_transaction(cx).is_some() {
|
||||
let transaction = buffer.finalize_last_transaction().unwrap().clone();
|
||||
if !push_to_history {
|
||||
|
@ -3568,7 +3585,13 @@ impl Project {
|
|||
partial_result_params: Default::default(),
|
||||
context: lsp::CodeActionContext {
|
||||
diagnostics: relevant_diagnostics,
|
||||
only: None,
|
||||
only: Some(vec![
|
||||
lsp::CodeActionKind::EMPTY,
|
||||
lsp::CodeActionKind::QUICKFIX,
|
||||
lsp::CodeActionKind::REFACTOR,
|
||||
lsp::CodeActionKind::REFACTOR_EXTRACT,
|
||||
lsp::CodeActionKind::SOURCE,
|
||||
]),
|
||||
},
|
||||
})
|
||||
.await?
|
||||
|
@ -5111,22 +5134,30 @@ impl Project {
|
|||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::GetCompletionsResponse> {
|
||||
let position = envelope
|
||||
.payload
|
||||
.position
|
||||
.and_then(language::proto::deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
let version = deserialize_version(envelope.payload.version);
|
||||
let buffer = this.read_with(&cx, |this, cx| {
|
||||
this.opened_buffers
|
||||
.get(&envelope.payload.buffer_id)
|
||||
.and_then(|buffer| buffer.upgrade(cx))
|
||||
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
|
||||
})?;
|
||||
|
||||
let position = envelope
|
||||
.payload
|
||||
.position
|
||||
.and_then(language::proto::deserialize_anchor)
|
||||
.map(|p| {
|
||||
buffer.read_with(&cx, |buffer, _| {
|
||||
buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
|
||||
let version = deserialize_version(envelope.payload.version);
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_version(version))
|
||||
.await;
|
||||
let version = buffer.read_with(&cx, |buffer, _| buffer.version());
|
||||
|
||||
let completions = this
|
||||
.update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
|
||||
.await?;
|
||||
|
@ -5613,8 +5644,8 @@ impl Project {
|
|||
},
|
||||
|
||||
name: serialized_symbol.name,
|
||||
range: PointUtf16::new(start.row, start.column)
|
||||
..PointUtf16::new(end.row, end.column),
|
||||
range: Unclipped(PointUtf16::new(start.row, start.column))
|
||||
..Unclipped(PointUtf16::new(end.row, end.column)),
|
||||
kind,
|
||||
signature: serialized_symbol
|
||||
.signature
|
||||
|
@ -5700,10 +5731,10 @@ impl Project {
|
|||
|
||||
let mut lsp_edits = lsp_edits.into_iter().peekable();
|
||||
let mut edits = Vec::new();
|
||||
while let Some((mut range, mut new_text)) = lsp_edits.next() {
|
||||
while let Some((range, mut new_text)) = lsp_edits.next() {
|
||||
// Clip invalid ranges provided by the language server.
|
||||
range.start = snapshot.clip_point_utf16(range.start, Bias::Left);
|
||||
range.end = snapshot.clip_point_utf16(range.end, Bias::Left);
|
||||
let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
|
||||
..snapshot.clip_point_utf16(range.end, Bias::Left);
|
||||
|
||||
// Combine any LSP edits that are adjacent.
|
||||
//
|
||||
|
@ -5715,11 +5746,11 @@ impl Project {
|
|||
// In order for the diffing logic below to work properly, any edits that
|
||||
// cancel each other out must be combined into one.
|
||||
while let Some((next_range, next_text)) = lsp_edits.peek() {
|
||||
if next_range.start > range.end {
|
||||
if next_range.start.row > range.end.row + 1
|
||||
|| next_range.start.column > 0
|
||||
if next_range.start.0 > range.end {
|
||||
if next_range.start.0.row > range.end.row + 1
|
||||
|| next_range.start.0.column > 0
|
||||
|| snapshot.clip_point_utf16(
|
||||
PointUtf16::new(range.end.row, u32::MAX),
|
||||
Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
|
||||
Bias::Left,
|
||||
) > range.end
|
||||
{
|
||||
|
@ -5727,7 +5758,7 @@ impl Project {
|
|||
}
|
||||
new_text.push('\n');
|
||||
}
|
||||
range.end = next_range.end;
|
||||
range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
|
||||
new_text.push_str(next_text);
|
||||
lsp_edits.next();
|
||||
}
|
||||
|
@ -6000,13 +6031,13 @@ fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
|
|||
path: symbol.path.path.to_string_lossy().to_string(),
|
||||
name: symbol.name.clone(),
|
||||
kind: unsafe { mem::transmute(symbol.kind) },
|
||||
start: Some(proto::Point {
|
||||
row: symbol.range.start.row,
|
||||
column: symbol.range.start.column,
|
||||
start: Some(proto::PointUtf16 {
|
||||
row: symbol.range.start.0.row,
|
||||
column: symbol.range.start.0.column,
|
||||
}),
|
||||
end: Some(proto::Point {
|
||||
row: symbol.range.end.row,
|
||||
column: symbol.range.end.column,
|
||||
end: Some(proto::PointUtf16 {
|
||||
row: symbol.range.end.0.row,
|
||||
column: symbol.range.end.0.column,
|
||||
}),
|
||||
signature: symbol.signature.to_vec(),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue