Merge branch 'main' into in-app-feedback
This commit is contained in:
commit
5517e743e1
6 changed files with 146 additions and 18 deletions
|
@ -14,8 +14,11 @@ use language::{range_to_lsp, FakeLspAdapter, Language, LanguageConfig, PointUtf1
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{search::SearchQuery, Project};
|
use project::{search::SearchQuery, Project};
|
||||||
use rand::prelude::*;
|
use rand::{
|
||||||
use std::{env, path::PathBuf, sync::Arc};
|
distributions::{Alphanumeric, DistString},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use std::{env, ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
async fn test_random_collaboration(
|
async fn test_random_collaboration(
|
||||||
|
@ -382,9 +385,15 @@ async fn test_random_collaboration(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(None, None) => {}
|
(None, None) => {}
|
||||||
(None, _) => panic!("host's file is None, guest's isn't "),
|
(None, _) => panic!("host's file is None, guest's isn't"),
|
||||||
(_, None) => panic!("guest's file is None, hosts's isn't "),
|
(_, None) => panic!("guest's file is None, hosts's isn't"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let host_diff_base =
|
||||||
|
host_buffer.read_with(host_cx, |b, _| b.diff_base().map(ToString::to_string));
|
||||||
|
let guest_diff_base = guest_buffer
|
||||||
|
.read_with(client_cx, |b, _| b.diff_base().map(ToString::to_string));
|
||||||
|
assert_eq!(guest_diff_base, host_diff_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -543,9 +552,10 @@ async fn randomly_mutate_client(
|
||||||
50..=59 if !client.local_projects.is_empty() || !client.remote_projects.is_empty() => {
|
50..=59 if !client.local_projects.is_empty() || !client.remote_projects.is_empty() => {
|
||||||
randomly_mutate_worktrees(client, &rng, cx).await?;
|
randomly_mutate_worktrees(client, &rng, cx).await?;
|
||||||
}
|
}
|
||||||
60..=84 if !client.local_projects.is_empty() || !client.remote_projects.is_empty() => {
|
60..=74 if !client.local_projects.is_empty() || !client.remote_projects.is_empty() => {
|
||||||
randomly_query_and_mutate_buffers(client, &rng, cx).await?;
|
randomly_query_and_mutate_buffers(client, &rng, cx).await?;
|
||||||
}
|
}
|
||||||
|
75..=84 => randomly_mutate_git(client, &rng).await,
|
||||||
_ => randomly_mutate_fs(client, &rng).await,
|
_ => randomly_mutate_fs(client, &rng).await,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,6 +615,54 @@ async fn randomly_mutate_active_call(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn randomly_mutate_git(client: &mut TestClient, rng: &Mutex<StdRng>) {
|
||||||
|
let directories = client.fs.directories().await;
|
||||||
|
let mut dir_path = directories.choose(&mut *rng.lock()).unwrap().clone();
|
||||||
|
if dir_path.file_name() == Some(OsStr::new(".git")) {
|
||||||
|
dir_path.pop();
|
||||||
|
}
|
||||||
|
let mut git_dir_path = dir_path.clone();
|
||||||
|
git_dir_path.push(".git");
|
||||||
|
|
||||||
|
if !directories.contains(&git_dir_path) {
|
||||||
|
log::info!(
|
||||||
|
"{}: creating git directory at {:?}",
|
||||||
|
client.username,
|
||||||
|
git_dir_path
|
||||||
|
);
|
||||||
|
client.fs.create_dir(&git_dir_path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child_paths = client.fs.read_dir(&dir_path).await.unwrap();
|
||||||
|
let mut child_file_paths = Vec::new();
|
||||||
|
while let Some(child_path) = child_paths.next().await {
|
||||||
|
let child_path = child_path.unwrap();
|
||||||
|
if client.fs.is_file(&child_path).await {
|
||||||
|
child_file_paths.push(child_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let count = rng.lock().gen_range(0..=child_file_paths.len());
|
||||||
|
child_file_paths.shuffle(&mut *rng.lock());
|
||||||
|
child_file_paths.truncate(count);
|
||||||
|
|
||||||
|
let mut new_index = Vec::new();
|
||||||
|
for abs_child_file_path in &child_file_paths {
|
||||||
|
let child_file_path = abs_child_file_path.strip_prefix(&dir_path).unwrap();
|
||||||
|
let new_base = Alphanumeric.sample_string(&mut *rng.lock(), 16);
|
||||||
|
new_index.push((child_file_path, new_base));
|
||||||
|
}
|
||||||
|
log::info!(
|
||||||
|
"{}: updating Git index at {:?}: {:#?}",
|
||||||
|
client.username,
|
||||||
|
git_dir_path,
|
||||||
|
new_index
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.fs
|
||||||
|
.set_index_for_repo(&git_dir_path, &new_index)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn randomly_mutate_fs(client: &mut TestClient, rng: &Mutex<StdRng>) {
|
async fn randomly_mutate_fs(client: &mut TestClient, rng: &Mutex<StdRng>) {
|
||||||
let is_dir = rng.lock().gen::<bool>();
|
let is_dir = rng.lock().gen::<bool>();
|
||||||
let mut new_path = client
|
let mut new_path = client
|
||||||
|
|
|
@ -828,6 +828,23 @@ impl CompletionsMenu {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Remove all candidates where the query's start does not match the start of any word in the candidate
|
||||||
|
if let Some(query) = query {
|
||||||
|
if let Some(query_start) = query.chars().next() {
|
||||||
|
matches.retain(|string_match| {
|
||||||
|
split_words(&string_match.string).any(|word| {
|
||||||
|
//Check that the first codepoint of the word as lowercase matches the first
|
||||||
|
//codepoint of the query as lowercase
|
||||||
|
word.chars()
|
||||||
|
.flat_map(|codepoint| codepoint.to_lowercase())
|
||||||
|
.zip(query_start.to_lowercase())
|
||||||
|
.all(|(word_cp, query_cp)| word_cp == query_cp)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
matches.sort_unstable_by_key(|mat| {
|
matches.sort_unstable_by_key(|mat| {
|
||||||
let completion = &self.completions[mat.candidate_id];
|
let completion = &self.completions[mat.candidate_id];
|
||||||
(
|
(
|
||||||
|
@ -6811,6 +6828,34 @@ pub fn styled_runs_for_code_label<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
|
||||||
|
let mut index = 0;
|
||||||
|
let mut codepoints = text.char_indices().peekable();
|
||||||
|
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
let start_index = index;
|
||||||
|
while let Some((new_index, codepoint)) = codepoints.next() {
|
||||||
|
index = new_index + codepoint.len_utf8();
|
||||||
|
let current_upper = codepoint.is_uppercase();
|
||||||
|
let next_upper = codepoints
|
||||||
|
.peek()
|
||||||
|
.map(|(_, c)| c.is_uppercase())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !current_upper && next_upper {
|
||||||
|
return Some(&text[start_index..index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = text.len();
|
||||||
|
if start_index < text.len() {
|
||||||
|
return Some(&text[start_index..]);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.flat_map(|word| word.split_inclusive('_'))
|
||||||
|
}
|
||||||
|
|
||||||
trait RangeExt<T> {
|
trait RangeExt<T> {
|
||||||
fn sorted(&self) -> Range<T>;
|
fn sorted(&self) -> Range<T>;
|
||||||
fn to_inclusive(&self) -> RangeInclusive<T>;
|
fn to_inclusive(&self) -> RangeInclusive<T>;
|
||||||
|
|
|
@ -5445,6 +5445,20 @@ async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppCon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_words() {
|
||||||
|
fn split<'a>(text: &'a str) -> Vec<&'a str> {
|
||||||
|
split_words(text).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(split("HelloWorld"), &["Hello", "World"]);
|
||||||
|
assert_eq!(split("hello_world"), &["hello_", "world"]);
|
||||||
|
assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
|
||||||
|
assert_eq!(split("Hello_World"), &["Hello_", "World"]);
|
||||||
|
assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
|
||||||
|
assert_eq!(split("helloworld"), &["helloworld"]);
|
||||||
|
}
|
||||||
|
|
||||||
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
||||||
let point = DisplayPoint::new(row as u32, column as u32);
|
let point = DisplayPoint::new(row as u32, column as u32);
|
||||||
point..point
|
point..point
|
||||||
|
|
|
@ -682,7 +682,6 @@ impl Buffer {
|
||||||
task
|
task
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
|
||||||
pub fn diff_base(&self) -> Option<&str> {
|
pub fn diff_base(&self) -> Option<&str> {
|
||||||
self.diff_base.as_deref()
|
self.diff_base.as_deref()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5189,11 +5189,7 @@ impl Project {
|
||||||
|
|
||||||
let operations = buffer.serialize_ops(Some(remote_version), cx);
|
let operations = buffer.serialize_ops(Some(remote_version), cx);
|
||||||
let client = this.client.clone();
|
let client = this.client.clone();
|
||||||
let file = buffer.file().cloned();
|
if let Some(file) = buffer.file() {
|
||||||
cx.background()
|
|
||||||
.spawn(
|
|
||||||
async move {
|
|
||||||
if let Some(file) = file {
|
|
||||||
client
|
client
|
||||||
.send(proto::UpdateBufferFile {
|
.send(proto::UpdateBufferFile {
|
||||||
project_id,
|
project_id,
|
||||||
|
@ -5203,6 +5199,17 @@ impl Project {
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client
|
||||||
|
.send(proto::UpdateDiffBase {
|
||||||
|
project_id,
|
||||||
|
buffer_id: buffer_id as u64,
|
||||||
|
diff_base: buffer.diff_base().map(Into::into),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
|
||||||
|
cx.background()
|
||||||
|
.spawn(
|
||||||
|
async move {
|
||||||
let operations = operations.await;
|
let operations = operations.await;
|
||||||
for chunk in split_operations(operations) {
|
for chunk in split_operations(operations) {
|
||||||
client
|
client
|
||||||
|
|
|
@ -2143,7 +2143,6 @@ impl Workspace {
|
||||||
let call = self.active_call()?;
|
let call = self.active_call()?;
|
||||||
let room = call.read(cx).room()?.read(cx);
|
let room = call.read(cx).room()?.read(cx);
|
||||||
let participant = room.remote_participant_for_peer_id(leader_id)?;
|
let participant = room.remote_participant_for_peer_id(leader_id)?;
|
||||||
|
|
||||||
let mut items_to_add = Vec::new();
|
let mut items_to_add = Vec::new();
|
||||||
match participant.location {
|
match participant.location {
|
||||||
call::ParticipantLocation::SharedProject { project_id } => {
|
call::ParticipantLocation::SharedProject { project_id } => {
|
||||||
|
@ -2154,6 +2153,12 @@ impl Workspace {
|
||||||
.and_then(|id| state.items_by_leader_view_id.get(&id))
|
.and_then(|id| state.items_by_leader_view_id.get(&id))
|
||||||
{
|
{
|
||||||
items_to_add.push((pane.clone(), item.boxed_clone()));
|
items_to_add.push((pane.clone(), item.boxed_clone()));
|
||||||
|
} else {
|
||||||
|
if let Some(shared_screen) =
|
||||||
|
self.shared_screen_for_peer(leader_id, pane, cx)
|
||||||
|
{
|
||||||
|
items_to_add.push((pane.clone(), Box::new(shared_screen)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue