Merge branch 'main' into reconnections-2

This commit is contained in:
Antonio Scandurra 2022-12-02 19:09:33 +01:00
commit 969c314315
78 changed files with 2759 additions and 1281 deletions

View file

@ -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(),
}