Compare commits
19 Commits
fix-git-ht
...
v0.154.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49db2122c6 | ||
|
|
2bdb395267 | ||
|
|
b34d42aed3 | ||
|
|
e9a529bf34 | ||
|
|
85a44705e7 | ||
|
|
8b57259669 | ||
|
|
25e2e9c672 | ||
|
|
b2ee628360 | ||
|
|
3ea89a1f9c | ||
|
|
752f6a7c33 | ||
|
|
1e2fcfb386 | ||
|
|
3ef8491fcb | ||
|
|
ce3a6350e6 | ||
|
|
15931d1b77 | ||
|
|
2a5925c235 | ||
|
|
153fb9eab8 | ||
|
|
2330f0d2f9 | ||
|
|
76b7c1df43 | ||
|
|
a0a1b1c69b |
2
.github/workflows/bump_patch_version.yml
vendored
2
.github/workflows/bump_patch_version.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit --features vendored-openssl
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit
|
||||
output=$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')
|
||||
git commit -am "Bump to $output for @$GITHUB_ACTOR" --author "Zed Bot <hi@zed.dev>"
|
||||
git tag v${output}${tag_suffix}
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -271,7 +271,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
name: Create a Linux bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- buildjet-16vcpu-ubuntu-2004
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
needs: [linux_tests]
|
||||
env:
|
||||
@@ -284,7 +284,7 @@ jobs:
|
||||
clean: false
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
run: ./script/linux && ./script/install-mold 2.34.0
|
||||
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
|
||||
4
.github/workflows/release_nightly.yml
vendored
4
.github/workflows/release_nightly.yml
vendored
@@ -97,7 +97,7 @@ jobs:
|
||||
name: Create a Linux *.tar.gz bundle for x86
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- buildjet-16vcpu-ubuntu-2004
|
||||
needs: tests
|
||||
env:
|
||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
run: ./script/linux && ./script/install-mold 2.34.0
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2408,7 +2408,6 @@ dependencies = [
|
||||
"cocoa 0.26.0",
|
||||
"collections",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"futures 0.3.30",
|
||||
"gpui",
|
||||
"http_client",
|
||||
@@ -10994,6 +10993,7 @@ dependencies = [
|
||||
"text",
|
||||
"theme",
|
||||
"ui",
|
||||
"unicode-segmentation",
|
||||
"util",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
@@ -14375,7 +14375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.154.0"
|
||||
version = "0.154.4"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
||||
26
Dockerfile-distros
Normal file
26
Dockerfile-distros
Normal file
@@ -0,0 +1,26 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE}
|
||||
WORKDIR /app
|
||||
ARG TZ=Etc/UTC \
|
||||
LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8 \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
ENV CARGO_TERM_COLOR=always
|
||||
|
||||
COPY script/linux script/
|
||||
RUN ./script/linux
|
||||
COPY script/install-mold script/install-cmake script/
|
||||
RUN ./script/install-mold "2.34.0"
|
||||
RUN ./script/install-cmake "3.30.4"
|
||||
|
||||
COPY . .
|
||||
|
||||
# When debugging, make these into individual RUN statements.
|
||||
# Cleanup to avoid saving big layers we aren't going to use.
|
||||
RUN . "$HOME/.cargo/env" \
|
||||
&& cargo fetch \
|
||||
&& cargo build \
|
||||
&& cargo run -- --help \
|
||||
&& cargo clean --quiet
|
||||
2
Dockerfile-distros.dockerignore
Normal file
2
Dockerfile-distros.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
**/target
|
||||
**/node_modules
|
||||
@@ -3533,7 +3533,9 @@ impl ContextEditor {
|
||||
for chunk in context.buffer().read(cx).text_for_range(range) {
|
||||
text.push_str(chunk);
|
||||
}
|
||||
text.push('\n');
|
||||
if message.offset_range.end < selection.range().end {
|
||||
text.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
|
||||
|
||||
let client = client::Client::global(cx).http_client();
|
||||
let url = client.build_url(&format!(
|
||||
"/api/release_notes/{}/{}",
|
||||
"/api/release_notes/v2/{}/{}",
|
||||
release_channel.dev_name(),
|
||||
version
|
||||
));
|
||||
|
||||
@@ -23,7 +23,6 @@ chrono = { workspace = true, features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
|
||||
@@ -6743,9 +6743,31 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
let row = selection.head().row;
|
||||
let indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
|
||||
let indent_end = Point::new(row, indent_size.len);
|
||||
// Since not all lines in the selection may be at the same indent
|
||||
// level, choose the indent size that is the most common between all
|
||||
// of the lines.
|
||||
//
|
||||
// If there is a tie, we use the deepest indent.
|
||||
let (indent_size, indent_end) = {
|
||||
let mut indent_size_occurrences = HashMap::default();
|
||||
let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
|
||||
|
||||
for row in start_row..=end_row {
|
||||
let indent = buffer.indent_size_for_line(MultiBufferRow(row));
|
||||
rows_by_indent_size.entry(indent).or_default().push(row);
|
||||
*indent_size_occurrences.entry(indent).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
let indent_size = indent_size_occurrences
|
||||
.into_iter()
|
||||
.max_by_key(|(indent, count)| (*count, indent.len))
|
||||
.map(|(indent, _)| indent)
|
||||
.unwrap_or_default();
|
||||
let row = rows_by_indent_size[&indent_size][0];
|
||||
let indent_end = Point::new(row, indent_size.len);
|
||||
|
||||
(indent_size, indent_end)
|
||||
};
|
||||
|
||||
let mut line_prefix = indent_size.chars().collect::<String>();
|
||||
|
||||
@@ -6795,10 +6817,22 @@ impl Editor {
|
||||
let start = Point::new(start_row, 0);
|
||||
let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
|
||||
let selection_text = buffer.text_for_range(start..end).collect::<String>();
|
||||
let unwrapped_text = selection_text
|
||||
let Some(lines_without_prefixes) = selection_text
|
||||
.lines()
|
||||
.map(|line| line.strip_prefix(&line_prefix).unwrap())
|
||||
.join(" ");
|
||||
.map(|line| {
|
||||
line.strip_prefix(&line_prefix)
|
||||
.or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
|
||||
.ok_or_else(|| {
|
||||
anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.log_err()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let unwrapped_text = lines_without_prefixes.join(" ");
|
||||
let wrap_column = buffer
|
||||
.settings_at(Point::new(start_row, 0), cx)
|
||||
.preferred_line_length as usize;
|
||||
|
||||
@@ -4249,6 +4249,80 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
||||
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
|
||||
cx.assert_editor_state(wrapped_text);
|
||||
}
|
||||
|
||||
// Test rewrapping unaligned comments in a selection.
|
||||
{
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
line_comments: vec!["// ".into(), "/// ".into()],
|
||||
..LanguageConfig::default()
|
||||
},
|
||||
Some(tree_sitter_rust::LANGUAGE.into()),
|
||||
));
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let unwrapped_text = indoc! {"
|
||||
fn foo() {
|
||||
if true {
|
||||
« // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
|
||||
// Praesent semper egestas tellus id dignissim.ˇ»
|
||||
do_something();
|
||||
} else {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
"};
|
||||
|
||||
let wrapped_text = indoc! {"
|
||||
fn foo() {
|
||||
if true {
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
|
||||
// mollis elit purus, a ornare lacus gravida vitae. Praesent semper
|
||||
// egestas tellus id dignissim.ˇ
|
||||
do_something();
|
||||
} else {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
"};
|
||||
|
||||
cx.set_state(unwrapped_text);
|
||||
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
|
||||
cx.assert_editor_state(wrapped_text);
|
||||
|
||||
let unwrapped_text = indoc! {"
|
||||
fn foo() {
|
||||
if true {
|
||||
«ˇ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
|
||||
// Praesent semper egestas tellus id dignissim.»
|
||||
do_something();
|
||||
} else {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
"};
|
||||
|
||||
let wrapped_text = indoc! {"
|
||||
fn foo() {
|
||||
if true {
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
|
||||
// mollis elit purus, a ornare lacus gravida vitae. Praesent semper
|
||||
// egestas tellus id dignissim.ˇ
|
||||
do_something();
|
||||
} else {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
"};
|
||||
|
||||
cx.set_state(unwrapped_text);
|
||||
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
|
||||
cx.assert_editor_state(wrapped_text);
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
@@ -386,7 +386,7 @@ fn matching_history_items<'a>(
|
||||
.chars(),
|
||||
),
|
||||
};
|
||||
candidates_paths.insert(Arc::clone(&found_path.project.path), found_path);
|
||||
candidates_paths.insert(&found_path.project, found_path);
|
||||
Some((found_path.project.worktree_id, candidate))
|
||||
})
|
||||
.fold(
|
||||
@@ -411,17 +411,21 @@ fn matching_history_items<'a>(
|
||||
max_results,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|path_match| {
|
||||
let (_, found_path) = candidates_paths
|
||||
.remove_entry(&path_match.path)
|
||||
.expect("candidate info not found");
|
||||
(
|
||||
Arc::clone(&path_match.path),
|
||||
Match::History {
|
||||
path: found_path.clone(),
|
||||
panel_match: Some(ProjectPanelOrdMatch(path_match)),
|
||||
},
|
||||
)
|
||||
.filter_map(|path_match| {
|
||||
candidates_paths
|
||||
.remove_entry(&ProjectPath {
|
||||
worktree_id: WorktreeId::from_usize(path_match.worktree_id),
|
||||
path: Arc::clone(&path_match.path),
|
||||
})
|
||||
.map(|(_, found_path)| {
|
||||
(
|
||||
Arc::clone(&path_match.path),
|
||||
Match::History {
|
||||
path: found_path.clone(),
|
||||
panel_match: Some(ProjectPanelOrdMatch(path_match)),
|
||||
},
|
||||
)
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ pub struct BufferSnapshot {
|
||||
|
||||
/// The kind and amount of indentation in a particular line. For now,
|
||||
/// assumes that indentation is all the same character.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
||||
pub struct IndentSize {
|
||||
/// The number of bytes that comprise the indentation.
|
||||
pub len: u32,
|
||||
@@ -153,7 +153,7 @@ pub struct IndentSize {
|
||||
}
|
||||
|
||||
/// A whitespace character that's used for indentation.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
||||
pub enum IndentKind {
|
||||
/// An ASCII space character.
|
||||
#[default]
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
(struct_item
|
||||
(visibility_modifier)? @context
|
||||
"struct" @context
|
||||
name: (_) @name
|
||||
body: (_ "{" @open (_)* "}" @close)) @item
|
||||
name: (_) @name) @item
|
||||
|
||||
(enum_item
|
||||
(visibility_modifier)? @context
|
||||
|
||||
@@ -6,14 +6,14 @@ use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{maybe, ResultExt};
|
||||
use util::{maybe, merge_json_value_into, ResultExt};
|
||||
|
||||
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
@@ -212,11 +212,12 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
adapter: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
let tsdk_path = Self::tsdk_path(adapter).await;
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
let tsdk_path = Self::tsdk_path(delegate).await;
|
||||
let config = serde_json::json!({
|
||||
"tsdk": tsdk_path,
|
||||
"suggest": {
|
||||
@@ -243,10 +244,13 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
"enumMemberValues": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tsserver": {
|
||||
"maxTsServerMemory": 8092
|
||||
},
|
||||
});
|
||||
|
||||
Ok(Some(json!({
|
||||
let mut default_workspace_configuration = serde_json::json!({
|
||||
"typescript": config,
|
||||
"javascript": config,
|
||||
"vtsls": {
|
||||
@@ -258,33 +262,18 @@ impl LspAdapter for VtslsLspAdapter {
|
||||
},
|
||||
"autoUseWorkspaceTsdk": true
|
||||
}
|
||||
})))
|
||||
}
|
||||
});
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
let override_options = cx.update(|cx| {
|
||||
language_server_settings(delegate.as_ref(), SERVER_NAME, cx)
|
||||
.and_then(|s| s.settings.clone())
|
||||
})?;
|
||||
|
||||
if let Some(options) = override_options {
|
||||
return Ok(options);
|
||||
if let Some(override_options) = override_options {
|
||||
merge_json_value_into(override_options, &mut default_workspace_configuration)
|
||||
}
|
||||
|
||||
let config = serde_json::json!({
|
||||
"tsserver": {
|
||||
"maxTsServerMemory": 8092
|
||||
},
|
||||
});
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"typescript": config,
|
||||
"javascript": config
|
||||
}))
|
||||
Ok(default_workspace_configuration)
|
||||
}
|
||||
|
||||
fn language_ids(&self) -> HashMap<String, String> {
|
||||
|
||||
@@ -6,7 +6,7 @@ use clock::ReplicaId;
|
||||
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
||||
use futures::{channel::mpsc, SinkExt};
|
||||
use git::diff::DiffHunk;
|
||||
use gpui::{AppContext, EntityId, EventEmitter, Model, ModelContext};
|
||||
use gpui::{AppContext, EntityId, EventEmitter, Model, ModelContext, Task};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
@@ -1106,64 +1106,24 @@ impl MultiBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_excerpts_with_context_lines(
|
||||
pub fn forget_transaction(
|
||||
&mut self,
|
||||
buffer: Model<Buffer>,
|
||||
ranges: Vec<Range<text::Anchor>>,
|
||||
context_line_count: u32,
|
||||
transaction_id: TransactionId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> mpsc::Receiver<Range<Anchor>> {
|
||||
let (buffer_id, buffer_snapshot) =
|
||||
buffer.update(cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
|
||||
|
||||
let (mut tx, rx) = mpsc::channel(256);
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let mut excerpt_ranges = Vec::new();
|
||||
let mut range_counts = Vec::new();
|
||||
cx.background_executor()
|
||||
.scoped(|scope| {
|
||||
scope.spawn(async {
|
||||
let (ranges, counts) =
|
||||
build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
|
||||
excerpt_ranges = ranges;
|
||||
range_counts = counts;
|
||||
) {
|
||||
if let Some(buffer) = self.as_singleton() {
|
||||
buffer.update(cx, |buffer, _| {
|
||||
buffer.forget_transaction(transaction_id);
|
||||
});
|
||||
} else if let Some(transaction) = self.history.forget(transaction_id) {
|
||||
for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
|
||||
if let Some(state) = self.buffers.borrow_mut().get_mut(&buffer_id) {
|
||||
state.buffer.update(cx, |buffer, _| {
|
||||
buffer.forget_transaction(buffer_transaction_id);
|
||||
});
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut ranges = ranges.into_iter();
|
||||
let mut range_counts = range_counts.into_iter();
|
||||
for excerpt_ranges in excerpt_ranges.chunks(100) {
|
||||
let excerpt_ids = match this.update(&mut cx, |this, cx| {
|
||||
this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx)
|
||||
}) {
|
||||
Ok(excerpt_ids) => excerpt_ids,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.by_ref())
|
||||
{
|
||||
for range in ranges.by_ref().take(range_count) {
|
||||
let start = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id,
|
||||
text_anchor: range.start,
|
||||
};
|
||||
let end = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id,
|
||||
text_anchor: range.end,
|
||||
};
|
||||
if tx.send(start..end).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
rx
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_excerpts<O>(
|
||||
@@ -1215,6 +1175,91 @@ impl MultiBuffer {
|
||||
anchor_ranges
|
||||
}
|
||||
|
||||
pub fn push_multiple_excerpts_with_context_lines(
|
||||
&mut self,
|
||||
buffers_with_ranges: Vec<(Model<Buffer>, Vec<Range<text::Anchor>>)>,
|
||||
context_line_count: u32,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Vec<Range<Anchor>>> {
|
||||
use futures::StreamExt;
|
||||
|
||||
let (excerpt_ranges_tx, mut excerpt_ranges_rx) = mpsc::channel(256);
|
||||
|
||||
let mut buffer_ids = Vec::with_capacity(buffers_with_ranges.len());
|
||||
|
||||
for (buffer, ranges) in buffers_with_ranges {
|
||||
let (buffer_id, buffer_snapshot) =
|
||||
buffer.update(cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
|
||||
|
||||
buffer_ids.push(buffer_id);
|
||||
|
||||
cx.background_executor()
|
||||
.spawn({
|
||||
let mut excerpt_ranges_tx = excerpt_ranges_tx.clone();
|
||||
|
||||
async move {
|
||||
let (excerpt_ranges, counts) =
|
||||
build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
|
||||
excerpt_ranges_tx
|
||||
.send((buffer_id, buffer.clone(), ranges, excerpt_ranges, counts))
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let mut results_by_buffer_id = HashMap::default();
|
||||
while let Some((buffer_id, buffer, ranges, excerpt_ranges, range_counts)) =
|
||||
excerpt_ranges_rx.next().await
|
||||
{
|
||||
results_by_buffer_id
|
||||
.insert(buffer_id, (buffer, ranges, excerpt_ranges, range_counts));
|
||||
}
|
||||
|
||||
let mut multi_buffer_ranges = Vec::default();
|
||||
'outer: for buffer_id in buffer_ids {
|
||||
let Some((buffer, ranges, excerpt_ranges, range_counts)) =
|
||||
results_by_buffer_id.remove(&buffer_id)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut ranges = ranges.into_iter();
|
||||
let mut range_counts = range_counts.into_iter();
|
||||
for excerpt_ranges in excerpt_ranges.chunks(100) {
|
||||
let excerpt_ids = match this.update(&mut cx, |this, cx| {
|
||||
this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx)
|
||||
}) {
|
||||
Ok(excerpt_ids) => excerpt_ids,
|
||||
Err(_) => continue 'outer,
|
||||
};
|
||||
|
||||
for (excerpt_id, range_count) in
|
||||
excerpt_ids.into_iter().zip(range_counts.by_ref())
|
||||
{
|
||||
for range in ranges.by_ref().take(range_count) {
|
||||
let start = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id,
|
||||
text_anchor: range.start,
|
||||
};
|
||||
let end = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id,
|
||||
text_anchor: range.end,
|
||||
};
|
||||
multi_buffer_ranges.push(start..end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
multi_buffer_ranges
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insert_excerpts_after<O>(
|
||||
&mut self,
|
||||
prev_excerpt_id: ExcerptId,
|
||||
@@ -4967,7 +5012,6 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AppContext, Context, TestAppContext};
|
||||
use language::{Buffer, Rope};
|
||||
use parking_lot::RwLock;
|
||||
@@ -5518,41 +5562,67 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
|
||||
let snapshot = buffer.read(cx);
|
||||
let ranges = vec![
|
||||
snapshot.anchor_before(Point::new(3, 2))..snapshot.anchor_before(Point::new(4, 2)),
|
||||
snapshot.anchor_before(Point::new(7, 1))..snapshot.anchor_before(Point::new(7, 3)),
|
||||
snapshot.anchor_before(Point::new(15, 0))
|
||||
..snapshot.anchor_before(Point::new(15, 0)),
|
||||
];
|
||||
multibuffer.stream_excerpts_with_context_lines(buffer.clone(), ranges, 2, cx)
|
||||
});
|
||||
#[gpui::test(iterations = 100)]
|
||||
async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext) {
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(15, 4, 'a'), cx));
|
||||
let snapshot_1 = buffer_1.update(cx, |buffer, _| buffer.snapshot());
|
||||
let snapshot_2 = buffer_2.update(cx, |buffer, _| buffer.snapshot());
|
||||
let ranges_1 = vec![
|
||||
snapshot_1.anchor_before(Point::new(3, 2))..snapshot_1.anchor_before(Point::new(4, 2)),
|
||||
snapshot_1.anchor_before(Point::new(7, 1))..snapshot_1.anchor_before(Point::new(7, 3)),
|
||||
snapshot_1.anchor_before(Point::new(15, 0))
|
||||
..snapshot_1.anchor_before(Point::new(15, 0)),
|
||||
];
|
||||
let ranges_2 = vec![
|
||||
snapshot_2.anchor_before(Point::new(2, 1))..snapshot_2.anchor_before(Point::new(3, 1)),
|
||||
snapshot_2.anchor_before(Point::new(10, 0))
|
||||
..snapshot_2.anchor_before(Point::new(10, 2)),
|
||||
];
|
||||
|
||||
let anchor_ranges = anchor_ranges.collect::<Vec<_>>().await;
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let anchor_ranges = multibuffer
|
||||
.update(cx, |multibuffer, cx| {
|
||||
multibuffer.push_multiple_excerpts_with_context_lines(
|
||||
vec![(buffer_1.clone(), ranges_1), (buffer_2.clone(), ranges_2)],
|
||||
2,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.text(),
|
||||
concat!(
|
||||
"bbb\n", //
|
||||
"bbb\n", // buffer_1
|
||||
"ccc\n", //
|
||||
"ddd\n", //
|
||||
"eee\n", //
|
||||
"ddd\n", // <-- excerpt 1
|
||||
"eee\n", // <-- excerpt 1
|
||||
"fff\n", //
|
||||
"ggg\n", //
|
||||
"hhh\n", //
|
||||
"hhh\n", // <-- excerpt 2
|
||||
"iii\n", //
|
||||
"jjj\n", //
|
||||
//
|
||||
"nnn\n", //
|
||||
"ooo\n", //
|
||||
"ppp\n", //
|
||||
"ppp\n", // <-- excerpt 3
|
||||
"qqq\n", //
|
||||
"rrr", //
|
||||
"rrr\n", //
|
||||
//
|
||||
"aaaa\n", // buffer 2
|
||||
"bbbb\n", //
|
||||
"cccc\n", // <-- excerpt 4
|
||||
"dddd\n", // <-- excerpt 4
|
||||
"eeee\n", //
|
||||
"ffff\n", //
|
||||
//
|
||||
"iiii\n", //
|
||||
"jjjj\n", //
|
||||
"kkkk\n", // <-- excerpt 5
|
||||
"llll\n", //
|
||||
"mmmm", //
|
||||
)
|
||||
);
|
||||
|
||||
@@ -5564,7 +5634,9 @@ mod tests {
|
||||
vec![
|
||||
Point::new(2, 2)..Point::new(3, 2),
|
||||
Point::new(6, 1)..Point::new(6, 3),
|
||||
Point::new(11, 0)..Point::new(11, 0)
|
||||
Point::new(11, 0)..Point::new(11, 0),
|
||||
Point::new(16, 1)..Point::new(17, 1),
|
||||
Point::new(22, 0)..Point::new(22, 2)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ pub struct BufferSearchBar {
|
||||
pending_search: Option<Task<()>>,
|
||||
search_options: SearchOptions,
|
||||
default_options: SearchOptions,
|
||||
configured_options: SearchOptions,
|
||||
query_contains_error: bool,
|
||||
dismissed: bool,
|
||||
search_history: SearchHistory,
|
||||
@@ -517,6 +518,7 @@ impl BufferSearchBar {
|
||||
active_match_index: None,
|
||||
searchable_items_with_matches: Default::default(),
|
||||
default_options: search_options,
|
||||
configured_options: search_options,
|
||||
search_options,
|
||||
pending_search: None,
|
||||
query_contains_error: false,
|
||||
@@ -605,10 +607,11 @@ impl BufferSearchBar {
|
||||
return false;
|
||||
};
|
||||
|
||||
self.default_options = SearchOptions::from_settings(&EditorSettings::get_global(cx).search);
|
||||
|
||||
if self.default_options != self.search_options {
|
||||
self.search_options = self.default_options;
|
||||
self.configured_options =
|
||||
SearchOptions::from_settings(&EditorSettings::get_global(cx).search);
|
||||
if self.dismissed && self.configured_options != self.default_options {
|
||||
self.search_options = self.configured_options;
|
||||
self.default_options = self.configured_options;
|
||||
}
|
||||
|
||||
self.dismissed = false;
|
||||
@@ -627,6 +630,7 @@ impl BufferSearchBar {
|
||||
.map(SearchableItemHandle::supported_options)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn search_suggested(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let search = self
|
||||
.query_suggestion(cx)
|
||||
@@ -1195,10 +1199,11 @@ mod tests {
|
||||
use std::ops::Range;
|
||||
|
||||
use super::*;
|
||||
use editor::{display_map::DisplayRow, DisplayPoint, Editor, MultiBuffer};
|
||||
use gpui::{Context, Hsla, TestAppContext, VisualTestContext};
|
||||
use editor::{display_map::DisplayRow, DisplayPoint, Editor, MultiBuffer, SearchSettings};
|
||||
use gpui::{Context, Hsla, TestAppContext, UpdateGlobal, VisualTestContext};
|
||||
use language::{Buffer, Point};
|
||||
use project::Project;
|
||||
use settings::SettingsStore;
|
||||
use smol::stream::StreamExt as _;
|
||||
use unindent::Unindent as _;
|
||||
|
||||
@@ -2320,4 +2325,119 @@ mod tests {
|
||||
assert!(display_points_of(editor.all_text_background_highlights(cx)).is_empty(),);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_options_changes(cx: &mut TestAppContext) {
|
||||
let (_editor, search_bar, cx) = init_test(cx);
|
||||
update_search_settings(
|
||||
SearchSettings {
|
||||
whole_word: false,
|
||||
case_sensitive: false,
|
||||
include_ignored: false,
|
||||
regex: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
let deploy = Deploy {
|
||||
focus: true,
|
||||
replace_enabled: false,
|
||||
selection_search_enabled: true,
|
||||
};
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::NONE,
|
||||
"Should have no search options enabled by default"
|
||||
);
|
||||
search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::WHOLE_WORD,
|
||||
"Should enable the option toggled"
|
||||
);
|
||||
assert!(
|
||||
!search_bar.dismissed,
|
||||
"Search bar should be present and visible"
|
||||
);
|
||||
search_bar.deploy(&deploy, cx);
|
||||
assert_eq!(
|
||||
search_bar.configured_options,
|
||||
SearchOptions::NONE,
|
||||
"Should have configured search options matching the settings"
|
||||
);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::WHOLE_WORD,
|
||||
"After (re)deploying, the option should still be enabled"
|
||||
);
|
||||
|
||||
search_bar.dismiss(&Dismiss, cx);
|
||||
search_bar.deploy(&deploy, cx);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::NONE,
|
||||
"After hiding and showing the search bar, default options should be used"
|
||||
);
|
||||
|
||||
search_bar.toggle_search_option(SearchOptions::REGEX, cx);
|
||||
search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::REGEX | SearchOptions::WHOLE_WORD,
|
||||
"Should enable the options toggled"
|
||||
);
|
||||
assert!(
|
||||
!search_bar.dismissed,
|
||||
"Search bar should be present and visible"
|
||||
);
|
||||
});
|
||||
|
||||
update_search_settings(
|
||||
SearchSettings {
|
||||
whole_word: false,
|
||||
case_sensitive: true,
|
||||
include_ignored: false,
|
||||
regex: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::REGEX | SearchOptions::WHOLE_WORD,
|
||||
"Should have no search options enabled by default"
|
||||
);
|
||||
|
||||
search_bar.deploy(&deploy, cx);
|
||||
assert_eq!(
|
||||
search_bar.configured_options,
|
||||
SearchOptions::CASE_SENSITIVE,
|
||||
"Should have configured search options matching the settings"
|
||||
);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::REGEX | SearchOptions::WHOLE_WORD,
|
||||
"Toggling a non-dismissed search bar with custom options should not change the default options"
|
||||
);
|
||||
search_bar.dismiss(&Dismiss, cx);
|
||||
search_bar.deploy(&deploy, cx);
|
||||
assert_eq!(
|
||||
search_bar.search_options,
|
||||
SearchOptions::CASE_SENSITIVE,
|
||||
"After hiding and showing the search bar, default options should be used"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn update_search_settings(search_settings: SearchSettings, cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings::<EditorSettings>(cx, |settings| {
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,54 +264,35 @@ impl ProjectSearch {
|
||||
|
||||
let mut limit_reached = false;
|
||||
while let Some(results) = matches.next().await {
|
||||
let tasks = results
|
||||
.into_iter()
|
||||
.map(|result| {
|
||||
let this = this.clone();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
match result {
|
||||
project::search::SearchResult::Buffer { buffer, ranges } => {
|
||||
let mut match_ranges_rx =
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.excerpts.update(cx, |excerpts, cx| {
|
||||
excerpts.stream_excerpts_with_context_lines(
|
||||
buffer,
|
||||
ranges,
|
||||
editor::DEFAULT_MULTIBUFFER_CONTEXT,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut match_ranges = vec![];
|
||||
while let Some(range) = match_ranges_rx.next().await {
|
||||
match_ranges.push(range);
|
||||
}
|
||||
anyhow::Ok((match_ranges, false))
|
||||
}
|
||||
project::search::SearchResult::LimitReached => {
|
||||
anyhow::Ok((vec![], true))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result_ranges = futures::future::join_all(tasks).await;
|
||||
let mut combined_ranges = vec![];
|
||||
for (ranges, result_limit_reached) in result_ranges.into_iter().flatten() {
|
||||
combined_ranges.extend(ranges);
|
||||
if result_limit_reached {
|
||||
limit_reached = result_limit_reached;
|
||||
let mut buffers_with_ranges = Vec::with_capacity(results.len());
|
||||
for result in results {
|
||||
match result {
|
||||
project::search::SearchResult::Buffer { buffer, ranges } => {
|
||||
buffers_with_ranges.push((buffer, ranges));
|
||||
}
|
||||
project::search::SearchResult::LimitReached => {
|
||||
limit_reached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let match_ranges = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.excerpts.update(cx, |excerpts, cx| {
|
||||
excerpts.push_multiple_excerpts_with_context_lines(
|
||||
buffers_with_ranges,
|
||||
editor::DEFAULT_MULTIBUFFER_CONTEXT,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if !combined_ranges.is_empty() {
|
||||
this.no_results = Some(false);
|
||||
this.match_ranges.extend(combined_ranges);
|
||||
cx.notify();
|
||||
}
|
||||
this.no_results = Some(false);
|
||||
this.match_ranges.extend(match_ranges);
|
||||
cx.notify();
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ supermaven_api.workspace = true
|
||||
smol.workspace = true
|
||||
text.workspace = true
|
||||
ui.workspace = true
|
||||
unicode-segmentation.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use text::{ToOffset, ToPoint};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
|
||||
|
||||
@@ -54,33 +55,34 @@ fn completion_state_from_diff(
|
||||
) -> CompletionProposal {
|
||||
let buffer_text = snapshot
|
||||
.text_for_range(delete_range.clone())
|
||||
.collect::<String>()
|
||||
.chars()
|
||||
.collect::<Vec<char>>();
|
||||
.collect::<String>();
|
||||
|
||||
let mut inlays: Vec<InlayProposal> = Vec::new();
|
||||
|
||||
let completion = completion_text.chars().collect::<Vec<char>>();
|
||||
let completion_graphemes: Vec<&str> = completion_text.graphemes(true).collect();
|
||||
let buffer_graphemes: Vec<&str> = buffer_text.graphemes(true).collect();
|
||||
|
||||
let mut offset = position.to_offset(&snapshot);
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
while i < completion.len() && j < buffer_text.len() {
|
||||
while i < completion_graphemes.len() && j < buffer_graphemes.len() {
|
||||
// find the next instance of the buffer text in the completion text.
|
||||
let k = completion[i..].iter().position(|c| *c == buffer_text[j]);
|
||||
let k = completion_graphemes[i..]
|
||||
.iter()
|
||||
.position(|c| *c == buffer_graphemes[j]);
|
||||
match k {
|
||||
Some(k) => {
|
||||
if k != 0 {
|
||||
// the range from the current position to item is an inlay.
|
||||
inlays.push(InlayProposal::Suggestion(
|
||||
snapshot.anchor_after(offset),
|
||||
completion_text[i..i + k].into(),
|
||||
completion_graphemes[i..i + k].join("").into(),
|
||||
));
|
||||
}
|
||||
i += k + 1;
|
||||
j += 1;
|
||||
offset.add_assign(1);
|
||||
offset.add_assign(buffer_graphemes[j - 1].len());
|
||||
}
|
||||
None => {
|
||||
// there are no more matching completions, so drop the remaining
|
||||
@@ -90,11 +92,11 @@ fn completion_state_from_diff(
|
||||
}
|
||||
}
|
||||
|
||||
if j == buffer_text.len() && i < completion.len() {
|
||||
if j == buffer_graphemes.len() && i < completion_graphemes.len() {
|
||||
// there is leftover completion text, so drop it as an inlay.
|
||||
inlays.push(InlayProposal::Suggestion(
|
||||
snapshot.anchor_after(offset),
|
||||
completion_text[i..completion_text.len()].into(),
|
||||
completion_graphemes[i..].join("").into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.154.0"
|
||||
version = "0.154.4"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
stable
|
||||
@@ -35,6 +35,12 @@ Clone down the [Zed repository](https://github.com/zed-industries/zed).
|
||||
brew install cmake
|
||||
```
|
||||
|
||||
- (Optional) Install `mold` to speed up link times
|
||||
|
||||
```sh
|
||||
brew install mold
|
||||
```
|
||||
|
||||
## Backend Dependencies
|
||||
|
||||
If you are developing collaborative features of Zed, you'll need to install the dependencies of zed's `collab` server:
|
||||
|
||||
@@ -68,29 +68,95 @@ Prettier will also be used for TypeScript files by default. To disable this:
|
||||
Zed sets the following initialization options to make the language server send back inlay hints
|
||||
(that is, when Zed has inlay hints enabled in the settings).
|
||||
|
||||
You can override these settings in your configuration file:
|
||||
You can override these settings in your Zed settings file.
|
||||
|
||||
When using `typescript-language-server`:
|
||||
|
||||
```json
|
||||
"lsp": {
|
||||
"$LANGUAGE_SERVER_NAME": {
|
||||
"initialization_options": {
|
||||
"preferences": {
|
||||
"includeInlayParameterNameHints": "all",
|
||||
"includeInlayParameterNameHintsWhenArgumentMatchesName": true,
|
||||
"includeInlayFunctionParameterTypeHints": true,
|
||||
"includeInlayVariableTypeHints": true,
|
||||
"includeInlayVariableTypeHintsWhenTypeMatchesName": true,
|
||||
"includeInlayPropertyDeclarationTypeHints": true,
|
||||
"includeInlayFunctionLikeReturnTypeHints": true,
|
||||
"includeInlayEnumMemberValueHints": true,
|
||||
}
|
||||
{
|
||||
"lsp": {
|
||||
"typescript-language-server": {
|
||||
"initialization_options": {
|
||||
"preferences": {
|
||||
"includeInlayParameterNameHints": "all",
|
||||
"includeInlayParameterNameHintsWhenArgumentMatchesName": true,
|
||||
"includeInlayFunctionParameterTypeHints": true,
|
||||
"includeInlayVariableTypeHints": true,
|
||||
"includeInlayVariableTypeHintsWhenTypeMatchesName": true,
|
||||
"includeInlayPropertyDeclarationTypeHints": true,
|
||||
"includeInlayFunctionLikeReturnTypeHints": true,
|
||||
"includeInlayEnumMemberValueHints": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [typescript-language-server inlayhints documentation](https://github.com/typescript-language-server/typescript-language-server?tab=readme-ov-file#inlay-hints-textdocumentinlayhint) for more information.
|
||||
|
||||
When using `vtsls`:
|
||||
|
||||
```json
|
||||
{
|
||||
"lsp": {
|
||||
"vtsls": {
|
||||
"settings": {
|
||||
// For JavaScript:
|
||||
"javascript": {
|
||||
"inlayHints": {
|
||||
"parameterNames": {
|
||||
"enabled": "all",
|
||||
"suppressWhenArgumentMatchesName": false
|
||||
},
|
||||
"parameterTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"variableTypes": {
|
||||
"enabled": true,
|
||||
"suppressWhenTypeMatchesName": true
|
||||
},
|
||||
"propertyDeclarationTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"functionLikeReturnTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"enumMemberValues": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
// For TypeScript:
|
||||
"typescript": {
|
||||
"inlayHints": {
|
||||
"parameterNames": {
|
||||
"enabled": "all",
|
||||
"suppressWhenArgumentMatchesName": false
|
||||
},
|
||||
"parameterTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"variableTypes": {
|
||||
"enabled": true,
|
||||
"suppressWhenTypeMatchesName": true
|
||||
},
|
||||
"propertyDeclarationTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"functionLikeReturnTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"enumMemberValues": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Zed Yarn documentation](./yarn.md) for a walkthrough of configuring your project to use Yarn.
|
||||
|
||||
25
script/build-docker
Executable file
25
script/build-docker
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Use a docker BASE_IMAGE to test building Zed.
|
||||
# e.g: ./script/bundle-docker ubuntu:20.04
|
||||
#
|
||||
# Increasing resources available to podman may speed this up:
|
||||
# podman machine stop
|
||||
# podman machine set --memory 16384 --cpus 8 --disk-size 200
|
||||
# podman machine start
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE_IMAGE=${BASE_IMAGE:-${1:-}}
|
||||
if [ -z "$BASE_IMAGE" ]; then
|
||||
echo "Usage: $0 BASE_IMAGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export DOCKER_BUILDKIT=1
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
podman build . \
|
||||
-f Dockerfile-distros \
|
||||
-t many \
|
||||
--build-arg BASE_IMAGE="$BASE_IMAGE"
|
||||
77
script/install-cmake
Executable file
77
script/install-cmake
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script installs an up-to-date version of CMake.
|
||||
#
|
||||
# For MacOS use Homebrew to install the latest version.
|
||||
#
|
||||
# For Ubuntu use the official KitWare Apt repository with backports.
|
||||
# See: https://apt.kitware.com/
|
||||
#
|
||||
# For other systems (RHEL 8.x, 9.x, AmazonLinux, SUSE, Fedora, Arch, etc)
|
||||
# use the official CMake installer script from KitWare.
|
||||
#
|
||||
# Note this is similar to how GitHub Actions runners install cmake:
|
||||
# https://github.com/actions/runner-images/blob/main/images/ubuntu/scripts/build/install-cmake.sh
|
||||
#
|
||||
# Upstream: 3.30.4 (2024-09-27)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
if [[ "$(uname -s)" == "darwin" ]]; then
|
||||
brew --version >/dev/null \
|
||||
|| echo "Error: Homebrew is required to install cmake on MacOS." && exit 1
|
||||
echo "Installing cmake via Homebrew (can't pin to old versions)."
|
||||
brew install cmake
|
||||
exit 0
|
||||
elif [ "$(uname -s)" != "Linux" ]; then
|
||||
echo "Error: This script is intended for MacOS/Linux systems only."
|
||||
exit 1
|
||||
elif [ -z "${1:-}" ]; then
|
||||
echo "Usage: $0 [3.30.4]"
|
||||
exit 1
|
||||
fi
|
||||
CMAKE_VERSION="${CMAKE_VERSION:-${1:-3.30.4}}"
|
||||
|
||||
if [ "$(whoami)" = root ]; then SUDO=; else SUDO="$(command -v sudo || command -v doas || true)"; fi
|
||||
|
||||
if cmake --version | grep -q "$CMAKE_VERSION"; then
|
||||
echo "CMake $CMAKE_VERSION is already installed."
|
||||
exit 0
|
||||
elif [ -e /usr/local/bin/cmake ]; then
|
||||
echo "Warning: existing cmake found at /usr/local/bin/cmake. Skipping installation."
|
||||
exit 0
|
||||
elif [ -e /etc/apt/sources.list.d/kitware.list ]; then
|
||||
echo "Warning: existing KitWare repository found. Skipping installation."
|
||||
exit 0
|
||||
elif [ -e /etc/lsb-release ] && grep -qP 'DISTRIB_ID=Ubuntu' /etc/lsb-release; then
|
||||
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc \
|
||||
| $SUDO gpg --dearmor - \
|
||||
| $SUDO tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" \
|
||||
| $SUDO tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
$SUDO apt-get update
|
||||
$SUDO apt-get install -y kitware-archive-keyring cmake==$CMAKE_VERSION
|
||||
else
|
||||
arch="$(uname -m)"
|
||||
if [ "$arch" != "x86_64" ] && [ "$arch" != "aarch64" ]; then
|
||||
echo "Error. Only x86_64 and aarch64 are supported."
|
||||
exit 1
|
||||
fi
|
||||
tempdir=$(mktemp -d)
|
||||
pushd "$tempdir"
|
||||
CMAKE_REPO="https://github.com/Kitware/CMake"
|
||||
CMAKE_INSTALLER="cmake-$CMAKE_VERSION-linux-$arch.sh"
|
||||
curl -fsSL --output cmake-$CMAKE_VERSION-SHA-256.txt \
|
||||
"$CMAKE_REPO/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-SHA-256.txt"
|
||||
curl -fsSL --output $CMAKE_INSTALLER \
|
||||
"$CMAKE_REPO/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-$arch.sh"
|
||||
# workaround for old versions of sha256sum not having --ignore-missing
|
||||
grep -F "cmake-$CMAKE_VERSION-linux-$arch.sh" "cmake-$CMAKE_VERSION-SHA-256.txt" \
|
||||
| sha256sum -c \
|
||||
| grep -qP "^${CMAKE_INSTALLER}: OK"
|
||||
chmod +x cmake-$CMAKE_VERSION-linux-$arch.sh
|
||||
$SUDO ./cmake-$CMAKE_VERSION-linux-$arch.sh --prefix=/usr/local --skip-license
|
||||
popd
|
||||
rm -rf "$tempdir"
|
||||
fi
|
||||
37
script/install-mold
Executable file
37
script/install-mold
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Install `mold` official binaries from GitHub Releases.
|
||||
#
|
||||
# Adapted from the official rui314/setup-mold@v1 action to:
|
||||
# * use environment variables instead of action inputs
|
||||
# * remove make-default support
|
||||
# * use curl instead of wget
|
||||
# * support doas for sudo
|
||||
# * support redhat systems
|
||||
# See: https://github.com/rui314/setup-mold/blob/main/action.yml
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MOLD_VERSION="${MOLD_VERSION:-${1:-}}"
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
echo "Error: This script is intended for Linux systems only."
|
||||
exit 1
|
||||
elif [ -z "$MOLD_VERSION" ]; then
|
||||
echo "Usage: $0 2.34.0"
|
||||
exit 1
|
||||
elif [ -e /usr/local/bin/mold ]; then
|
||||
echo "Warning: existing mold found at /usr/local/bin/mold. Skipping installation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$(whoami)" = root ]; then SUDO=; else SUDO="$(command -v sudo || command -v doas || true)"; fi
|
||||
|
||||
MOLD_REPO="${MOLD_REPO:-https://github.com/rui314/mold}"
|
||||
MOLD_URL="${MOLD_URL:-$MOLD_REPO}/releases/download/v$MOLD_VERSION/mold-$MOLD_VERSION-$(uname -m)-linux.tar.gz"
|
||||
|
||||
echo "Downloading from $MOLD_URL"
|
||||
curl -fsSL --output - "$MOLD_URL" \
|
||||
| $SUDO tar -C /usr/local --strip-components=1 --no-overwrite-dir -xzf -
|
||||
|
||||
# Note this binary depends on the system libatomic.so.1 which is usually
|
||||
# provided as a dependency of gcc so it should be available on most systems.
|
||||
@@ -12,7 +12,7 @@ if [[ -n $(git status --short --untracked-files=no) ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit --features vendored-openssl
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit
|
||||
which jq > /dev/null || brew install jq
|
||||
cargo set-version --package $package --bump $version_increment
|
||||
cargo check --quiet
|
||||
|
||||
112
script/linux
112
script/linux
@@ -1,15 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
set -xeuo pipefail
|
||||
|
||||
# install the wasm toolchain
|
||||
which rustup > /dev/null 2>&1 || curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
# if root or if sudo/unavailable, define an empty variable
|
||||
if [ "$(id -u)" -eq 0 ]
|
||||
then maysudo=''
|
||||
else maysudo="$(command -v sudo || command -v doas || true)"
|
||||
fi
|
||||
|
||||
# if sudo is not installed, define an empty alias
|
||||
maysudo=$(command -v sudo || command -v doas || true)
|
||||
function finalize {
|
||||
# after packages install (curl, etc), get the rust toolchain
|
||||
which rustup > /dev/null 2>&1 || curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
# verify the mold situation
|
||||
if ! command -v mold >/dev/null 2>&1; then
|
||||
echo "Warning: Mold binaries are unavailable on your system." >&2
|
||||
echo " Builds will be slower without mold. Try: scripts/install-mold" >&2
|
||||
fi
|
||||
echo "Finished installing Linux dependencies with script/linux"
|
||||
}
|
||||
|
||||
# Ubuntu, Debian, etc.
|
||||
# https://packages.ubuntu.com/
|
||||
# Ubuntu, Debian, Mint, Kali, Pop!_OS, Raspbian, etc.
|
||||
apt=$(command -v apt-get || true)
|
||||
if [[ -n $apt ]]; then
|
||||
deps=(
|
||||
@@ -20,55 +30,95 @@ if [[ -n $apt ]]; then
|
||||
libwayland-dev
|
||||
libxkbcommon-x11-dev
|
||||
libssl-dev
|
||||
libstdc++-12-dev
|
||||
libzstd-dev
|
||||
libvulkan1
|
||||
libgit2-dev
|
||||
make
|
||||
cmake
|
||||
clang
|
||||
mold
|
||||
jq
|
||||
git
|
||||
curl
|
||||
gettext-base
|
||||
elfutils
|
||||
libsqlite3-dev
|
||||
)
|
||||
if (grep -qP 'PRETTY_NAME="(.+24\.04)' /etc/os-release); then
|
||||
deps+=( mold libstdc++-14-dev )
|
||||
elif (grep -qP 'PRETTY_NAME="((Debian|Raspbian).+12|.+22\.04)' /etc/os-release); then
|
||||
deps+=( mold libstdc++-12-dev )
|
||||
elif (grep -qP 'PRETTY_NAME="((Debian|Raspbian).+11|.+20\.04)' /etc/os-release); then
|
||||
deps+=( libstdc++-10-dev )
|
||||
fi
|
||||
|
||||
$maysudo "$apt" update
|
||||
$maysudo "$apt" install -y "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fedora, CentOS, RHEL, etc.
|
||||
# https://packages.fedoraproject.org/
|
||||
# Fedora, CentOS, RHEL, Alma, Amazon 2023, Oracle, etc.
|
||||
dnf=$(command -v dnf || true)
|
||||
if [[ -n $dnf ]]; then
|
||||
# Old Redhat (yum only): Amazon Linux 2, Oracle Linux 7, etc.
|
||||
yum=$(command -v yum || true)
|
||||
|
||||
if [[ -n $dnf ]] || [[ -n $yum ]]; then
|
||||
pkg_cmd="${dnf:-${yum}}"
|
||||
deps=(
|
||||
gcc
|
||||
g++
|
||||
clang
|
||||
cmake
|
||||
mold
|
||||
alsa-lib-devel
|
||||
fontconfig-devel
|
||||
wayland-devel
|
||||
libxkbcommon-x11-devel
|
||||
openssl-devel
|
||||
libzstd-devel
|
||||
# Perl dependencies are needed for openssl-sys crate see https://docs.rs/openssl/latest/openssl/
|
||||
perl-FindBin
|
||||
perl-IPC-Cmd
|
||||
perl-File-Compare
|
||||
perl-File-Copy
|
||||
vulkan-loader
|
||||
sqlite-devel
|
||||
jq
|
||||
git
|
||||
tar
|
||||
)
|
||||
|
||||
# libxkbcommon-x11-devel is in the crb repo on RHEL and CentOS, not needed for Fedora
|
||||
if ! grep -q "Fedora" /etc/redhat-release; then
|
||||
$maysudo "$dnf" config-manager --set-enabled crb
|
||||
# perl used for building openssl-sys crate. See: https://docs.rs/openssl/latest/openssl/
|
||||
if grep -qP '^ID="(fedora)' /etc/os-release; then
|
||||
deps+=(
|
||||
perl-FindBin
|
||||
perl-IPC-Cmd
|
||||
perl-File-Compare
|
||||
perl-File-Copy
|
||||
mold
|
||||
)
|
||||
elif grep grep -qP '^ID="(rhel|rocky|alma|centos|ol)' /etc/os-release; then
|
||||
deps+=( perl-interpreter )
|
||||
fi
|
||||
|
||||
$maysudo "$dnf" install -y "${deps[@]}"
|
||||
# gcc-c++ is g++ on RHEL8 and 8.x clones
|
||||
if grep -qP '^ID="(rhel|rocky|alma|centos|ol)' /etc/os-release \
|
||||
&& grep -qP '^VERSION_ID="8' /etc/os-release; then
|
||||
deps+=( gcc-c++ )
|
||||
else
|
||||
deps+=( g++ )
|
||||
fi
|
||||
|
||||
# libxkbcommon-x11-devel is in a non-default repo on RHEL 8.x/9.x (except on AmazonLinux)
|
||||
if grep -qP '^VERSION_ID="(8|9)' && grep -qP '^ID="(rhel|rocky|centos|alma|ol)' /etc/os-release; then
|
||||
$maysudo dnf install -y 'dnf-command(config-manager)'
|
||||
if grep -qP '^PRETTY_NAME="(AlmaLinux 8|Rocky Linux 8)' /etc/os-release; then
|
||||
$maysudo dnf config-manager --set-enabled powertools
|
||||
elif grep -qP '^PRETTY_NAME="((AlmaLinux|Rocky|CentOS Stream) 9|Red Hat.+(8|9))' /etc/os-release; then
|
||||
$maysudo dnf config-manager --set-enabled crb
|
||||
elif grep -qP '^PRETTY_NAME="Oracle Linux Server 8' /etc/os-release; then
|
||||
$maysudo dnf config-manager --set-enabled ol8_codeready_builder
|
||||
elif grep -qP '^PRETTY_NAME="Oracle Linux Server 9' /etc/os-release; then
|
||||
$maysudo dnf config-manager --set-enabled ol9_codeready_builder
|
||||
else
|
||||
echo "Unexpected distro" && grep 'PRETTY_NAME' /etc/os-release && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
$maysudo $pkg_cmd install -y "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -89,10 +139,14 @@ if [[ -n $zyp ]]; then
|
||||
openssl-devel
|
||||
libzstd-devel
|
||||
libvulkan1
|
||||
mold
|
||||
sqlite3-devel
|
||||
jq
|
||||
git
|
||||
tar
|
||||
gzip
|
||||
)
|
||||
$maysudo "$zyp" install -y "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -115,8 +169,10 @@ if [[ -n $pacman ]]; then
|
||||
mold
|
||||
sqlite
|
||||
jq
|
||||
git
|
||||
)
|
||||
$maysudo "$pacman" -S --needed --noconfirm "${deps[@]}"
|
||||
$maysudo "$pacman" -Syu --needed --noconfirm "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -143,6 +199,7 @@ if [[ -n $xbps ]]; then
|
||||
sqlite-devel
|
||||
)
|
||||
$maysudo "$xbps" -Syu "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -152,6 +209,7 @@ emerge=$(command -v emerge || true)
|
||||
if [[ -n $emerge ]]; then
|
||||
deps=(
|
||||
app-arch/zstd
|
||||
app-misc/jq
|
||||
dev-libs/openssl
|
||||
dev-libs/wayland
|
||||
dev-util/cmake
|
||||
@@ -164,7 +222,9 @@ if [[ -n $emerge ]]; then
|
||||
dev-db/sqlite
|
||||
)
|
||||
$maysudo "$emerge" -u "${deps[@]}"
|
||||
finalize
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Unsupported Linux distribution in script/linux"
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user