Merge pull request #1155 from zed-industries/golang

Add Go support
This commit is contained in:
Max Brunsfeld 2022-06-09 14:18:37 -07:00 committed by GitHub
commit 87ba68e3ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1229 additions and 277 deletions

View file

@ -32,7 +32,8 @@ use gpui::{
pub use language::{char_kind, CharKind};
use language::{
BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity,
Language, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal,
TransactionId,
};
use multi_buffer::MultiBufferChunks;
pub use multi_buffer::{
@ -51,7 +52,7 @@ use std::{
any::TypeId,
borrow::Cow,
cmp::{self, Ordering, Reverse},
iter, mem,
mem,
ops::{Deref, DerefMut, Range, RangeInclusive},
sync::Arc,
time::{Duration, Instant},
@ -1932,9 +1933,8 @@ impl Editor {
.iter()
.map(|selection| {
let start_point = selection.start.to_point(&buffer);
let indent = buffer
.indent_column_for_line(start_point.row)
.min(start_point.column);
let mut indent = buffer.indent_size_for_line(start_point.row);
indent.len = cmp::min(indent.len, start_point.column);
let start = selection.start;
let end = selection.end;
@ -1967,9 +1967,9 @@ impl Editor {
});
}
let mut new_text = String::with_capacity(1 + indent as usize);
let mut new_text = String::with_capacity(1 + indent.len as usize);
new_text.push('\n');
new_text.extend(iter::repeat(' ').take(indent as usize));
new_text.extend(indent.chars());
if insert_extra_newline {
new_text = new_text.repeat(2);
}
@ -3070,14 +3070,21 @@ impl Editor {
.buffer_snapshot
.buffer_line_for_row(old_head.row)
{
let indent_column =
buffer.indent_column_for_line(line_buffer_range.start.row);
let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
let language_name = buffer.language().map(|language| language.name());
let indent = cx.global::<Settings>().tab_size(language_name.as_deref());
if old_head.column <= indent_column && old_head.column > 0 {
let indent_len = match indent_size.kind {
IndentKind::Space => {
cx.global::<Settings>().tab_size(language_name.as_deref())
}
IndentKind::Tab => 1,
};
if old_head.column <= indent_size.len && old_head.column > 0 {
new_head = cmp::min(
new_head,
Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
Point::new(
old_head.row,
((old_head.column - 1) / indent_len) * indent_len,
),
);
}
}
@ -3128,21 +3135,27 @@ impl Editor {
for selection in &mut selections {
let language_name =
buffer.language_at(selection.start, cx).map(|l| l.name());
let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
let char_column = buffer
.read(cx)
.text_for_range(Point::new(selection.start.row, 0)..selection.start)
.flat_map(str::chars)
.count();
let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
let settings = cx.global::<Settings>();
let tab_size = if settings.hard_tabs(language_name.as_deref()) {
IndentSize::tab()
} else {
let tab_size = settings.tab_size(language_name.as_deref());
let char_column = buffer
.read(cx)
.text_for_range(Point::new(selection.start.row, 0)..selection.start)
.flat_map(str::chars)
.count();
let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
IndentSize::spaces(chars_to_next_tab_stop)
};
buffer.edit(
[(
selection.start..selection.start,
" ".repeat(chars_to_next_tab_stop as usize),
tab_size.chars().collect::<String>(),
)],
cx,
);
selection.start.column += chars_to_next_tab_stop;
selection.start.column += tab_size.len;
selection.end = selection.start;
}
});
@ -3163,7 +3176,14 @@ impl Editor {
let snapshot = buffer.snapshot(cx);
for selection in &mut selections {
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
let settings = &cx.global::<Settings>();
let tab_size = settings.tab_size(language_name.as_deref());
let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
IndentKind::Tab
} else {
IndentKind::Space
};
let mut start_row = selection.start.row;
let mut end_row = selection.end.row + 1;
@ -3187,26 +3207,35 @@ impl Editor {
}
for row in start_row..end_row {
let indent_column = snapshot.indent_column_for_line(row);
let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
let current_indent = snapshot.indent_size_for_line(row);
let indent_delta = match (current_indent.kind, indent_kind) {
(IndentKind::Space, IndentKind::Space) => {
let columns_to_next_tab_stop =
tab_size - (current_indent.len % tab_size);
IndentSize::spaces(columns_to_next_tab_stop)
}
(IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
(_, IndentKind::Tab) => IndentSize::tab(),
};
let row_start = Point::new(row, 0);
buffer.edit(
[(
row_start..row_start,
" ".repeat(columns_to_next_tab_stop as usize),
indent_delta.chars().collect::<String>(),
)],
cx,
);
// Update this selection's endpoints to reflect the indentation.
if row == selection.start.row {
selection.start.column += columns_to_next_tab_stop as u32;
selection.start.column += indent_delta.len;
}
if row == selection.end.row {
selection.end.column += columns_to_next_tab_stop as u32;
selection.end.column += indent_delta.len as u32;
}
last_indent = Some((row, columns_to_next_tab_stop as u32));
last_indent = Some((row, indent_delta.len));
}
}
});
@ -3239,12 +3268,19 @@ impl Editor {
}
for row in rows {
let column = snapshot.indent_column_for_line(row);
if column > 0 {
let mut deletion_len = column % tab_size;
if deletion_len == 0 {
deletion_len = tab_size;
}
let indent_size = snapshot.indent_size_for_line(row);
if indent_size.len > 0 {
let deletion_len = match indent_size.kind {
IndentKind::Space => {
let columns_to_prev_tab_stop = indent_size.len % tab_size;
if columns_to_prev_tab_stop == 0 {
tab_size
} else {
columns_to_prev_tab_stop
}
}
IndentKind::Tab => 1,
};
deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
last_outdent = Some(row);
}
@ -4558,7 +4594,7 @@ impl Editor {
continue;
}
let start = Point::new(row, snapshot.indent_column_for_line(row));
let start = Point::new(row, snapshot.indent_size_for_line(row).len);
let mut line_bytes = snapshot
.bytes_in_range(start..snapshot.max_point())
.flatten()
@ -7712,6 +7748,88 @@ mod tests {
four"});
}
#[gpui::test]
async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await;
cx.update(|cx| {
cx.update_global::<Settings, _, _>(|settings, _| {
settings.hard_tabs = true;
});
});
// select two ranges on one line
cx.set_state(indoc! {"
[one} [two}
three
four"});
cx.update_editor(|e, cx| e.tab(&Tab, cx));
cx.assert_editor_state(indoc! {"
\t[one} [two}
three
four"});
cx.update_editor(|e, cx| e.tab(&Tab, cx));
cx.assert_editor_state(indoc! {"
\t\t[one} [two}
three
four"});
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
cx.assert_editor_state(indoc! {"
\t[one} [two}
three
four"});
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
cx.assert_editor_state(indoc! {"
[one} [two}
three
four"});
// select across a line ending
cx.set_state(indoc! {"
one two
t[hree
}four"});
cx.update_editor(|e, cx| e.tab(&Tab, cx));
cx.assert_editor_state(indoc! {"
one two
\tt[hree
}four"});
cx.update_editor(|e, cx| e.tab(&Tab, cx));
cx.assert_editor_state(indoc! {"
one two
\t\tt[hree
}four"});
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
cx.assert_editor_state(indoc! {"
one two
\tt[hree
}four"});
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
cx.assert_editor_state(indoc! {"
one two
t[hree
}four"});
// Ensure that indenting/outdenting works when the cursor is at column 0.
cx.set_state(indoc! {"
one two
|three
four"});
cx.assert_editor_state(indoc! {"
one two
|three
four"});
cx.update_editor(|e, cx| e.tab(&Tab, cx));
cx.assert_editor_state(indoc! {"
one two
\t|three
four"});
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
cx.assert_editor_state(indoc! {"
one two
|three
four"});
}
#[gpui::test]
fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
cx.set_global(

View file

@ -106,7 +106,7 @@ pub fn line_beginning(
let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
let indent_start = Point::new(
point.row,
map.buffer_snapshot.indent_column_for_line(point.row),
map.buffer_snapshot.indent_size_for_line(point.row).len,
)
.to_display_point(map);
let line_start = map.prev_line_boundary(point).1;

View file

@ -8,8 +8,8 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
pub use language::Completion;
use language::{
char_kind, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, DiagnosticEntry, Event, File,
Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _,
ToPointUtf16 as _, TransactionId,
IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _,
ToPoint as _, ToPointUtf16 as _, TransactionId,
};
use settings::Settings;
use smallvec::SmallVec;
@ -341,9 +341,14 @@ impl MultiBuffer {
if let Some(buffer) = self.as_singleton() {
return buffer.update(cx, |buffer, cx| {
let language_name = buffer.language().map(|language| language.name());
let indent_size = cx.global::<Settings>().tab_size(language_name.as_deref());
if autoindent {
let language_name = buffer.language().map(|language| language.name());
let settings = cx.global::<Settings>();
let indent_size = if settings.hard_tabs(language_name.as_deref()) {
IndentSize::tab()
} else {
IndentSize::spaces(settings.tab_size(language_name.as_deref()))
};
buffer.edit_with_autoindent(edits, indent_size, cx);
} else {
buffer.edit(edits, cx);
@ -462,9 +467,15 @@ impl MultiBuffer {
}
}
let language_name = buffer.language().map(|l| l.name());
let indent_size = cx.global::<Settings>().tab_size(language_name.as_deref());
if autoindent {
let settings = cx.global::<Settings>();
let indent_size = if settings.hard_tabs(language_name.as_deref()) {
IndentSize::tab()
} else {
IndentSize::spaces(settings.tab_size(language_name.as_deref()))
};
buffer.edit_with_autoindent(deletions, indent_size, cx);
buffer.edit_with_autoindent(insertions, indent_size, cx);
} else {
@ -1838,14 +1849,16 @@ impl MultiBufferSnapshot {
}
}
pub fn indent_column_for_line(&self, row: u32) -> u32 {
pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
if let Some((buffer, range)) = self.buffer_line_for_row(row) {
buffer
.indent_column_for_line(range.start.row)
let mut size = buffer.indent_size_for_line(range.start.row);
size.len = size
.len
.min(range.end.column)
.saturating_sub(range.start.column)
.saturating_sub(range.start.column);
size
} else {
0
IndentSize::spaces(0)
}
}

View file

@ -109,9 +109,10 @@ impl<'a> EditorTestContext<'a> {
self.editor.update(self.cx, update)
}
pub fn editor_text(&mut self) -> String {
self.editor
.update(self.cx, |editor, cx| editor.snapshot(cx).text())
pub fn buffer_text(&mut self) -> String {
self.editor.read_with(self.cx, |editor, cx| {
editor.buffer.read(cx).snapshot(cx).text()
})
}
pub fn simulate_keystroke(&mut self, keystroke_text: &str) {
@ -171,10 +172,10 @@ impl<'a> EditorTestContext<'a> {
&text,
vec!['|'.into(), ('[', '}').into(), ('{', ']').into()],
);
let editor_text = self.editor_text();
let buffer_text = self.buffer_text();
assert_eq!(
editor_text, unmarked_text,
"Unmarked text doesn't match editor text"
buffer_text, unmarked_text,
"Unmarked text doesn't match buffer text"
);
let expected_empty_selections = selection_ranges.remove(&'|'.into()).unwrap_or_default();
@ -254,7 +255,7 @@ impl<'a> EditorTestContext<'a> {
let actual_selections =
self.insert_markers(&empty_selections, &reverse_selections, &forward_selections);
let unmarked_text = self.editor_text();
let unmarked_text = self.buffer_text();
let all_eq: Result<(), SetEqError<String>> =
set_eq!(expected_empty_selections, empty_selections)
.map_err(|err| {
@ -322,7 +323,7 @@ impl<'a> EditorTestContext<'a> {
reverse_selections: &Vec<Range<usize>>,
forward_selections: &Vec<Range<usize>>,
) -> String {
let mut editor_text_with_selections = self.editor_text();
let mut editor_text_with_selections = self.buffer_text();
let mut selection_marks = BTreeMap::new();
for range in empty_selections {
selection_marks.insert(&range.start, '|');