Compare commits

...

26 Commits

Author SHA1 Message Date
Peter Tripp
03592ac595 Fix tests. 2024-09-24 09:42:36 -04:00
Thorsten Ball
0d7eb4baf3 zed 0.153.7 2024-09-24 15:15:09 +02:00
Thorsten Ball
fa274616dc project search: Fix search results not being highlighted (#18273)
Closes #18254
Closes #18219
Closes #17690

This fixes the project search not highlighting all results.

The problem was relatively simple, even though it took a while to find
it: we inserted multiple excerpts concurrently and the order in the
multi-buffer ended up being wrong. Sorting the resulting `match_ranges`
fixed the problem, but as it turns out, we can do a better job by moving
the concurrency into the method on the MultiBuffer.

Performance is the same, but now the problem is fixed.

Release Notes:

- Fixed search results in project-wide search not being highlighted
consistently and navigation sometimes being broken (#18254, #18219,

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-09-24 15:11:05 +02:00
Kirill Bulatov
d062dd29ff Properly use default search options in the buffer search bar (#18271)
Only replace current search options if the search was dismissed and the
new options are different from the default ones.

Follow-up of https://github.com/zed-industries/zed/pull/17179
Closes https://github.com/zed-industries/zed/issues/18166


Release Notes:

- Fixed buffer search options toggling unexpectedly on redeploys
([#18166](https://github.com/zed-industries/zed/issues/18166))
2024-09-24 15:40:02 +03:00
CharlesChen0823
aa23c358ae assistant: Fix offset calculation not in char boundary (#18069)
Closes #17825 

Release Notes:

- N/A
2024-09-19 10:04:17 -06:00
Joseph T Lyons
7234166077 v0.153.x stable 2024-09-19 11:02:29 -04:00
Amin Ahmed Khan
4105d27545 Fix OpenAI key URL (#17675)
Update the create Open AI Key URL

Release Notes:

- Fixed a link in the Assistant panel to the OpenAI console.
2024-09-18 22:27:17 -04:00
Peter Tripp
5f08341c9b Fix arm buildjet (#18023)
Run `apt-get update` before `apt-get install` on Linux. Hopefully will fix building on Linux Arm.
2024-09-18 15:31:45 -04:00
Peter Tripp
9a261cd028 zed 0.153.6 2024-09-18 13:46:20 -04:00
Peter Tripp
afeca3480f Move remaining self-hosted jobs to BuildJet (#18018) 2024-09-18 13:45:48 -04:00
Peter Tripp
58a12ef252 Update typos-cli to v1.24.6. Add scripts/check-spelling. Fix typos (#17961) 2024-09-18 12:10:34 -04:00
Max Brunsfeld
d3d1a8f55d Use BuildJet for linux CI 2024-09-18 09:03:23 -07:00
Thorsten Ball
553edc2662 zed 0.153.5 2024-09-18 12:49:56 +02:00
Thorsten Ball
5fff524870 go: Fix tasks when running tests/benchs in packages (#17998)
Turns out that #17645 reintroduced another regression and didn't catch
all the regressions in #17108.

Releases Notes:

- Fixed Go tasks not working properly when running tests or benchmarks
in subfolders/packages.

Co-authored-by: Piotr <piotr@zed.dev>
2024-09-18 12:49:05 +02:00
Max Brunsfeld
47779a9334 zed 0.153.4 2024-09-16 16:04:04 -07:00
Max Brunsfeld
ecc1ba168a Fix keystroke observer leak in vim crate (#17913)
Release Notes:

- Fixed a performance problem that happened when using vim mode after
opening and closing many editors

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Nathan <nathan@zed.dev>
2024-09-16 16:03:11 -07:00
Zed Bot
cfe5dd8d04 Bump to 0.153.3 for @ConradIrwin 2024-09-13 13:03:13 -07:00
gcp-cherry-pick-bot[bot]
c58a552161 project: Use login shell to get environment per project (cherry-pick #17717) (#17720)
Cherry-picked project: Use login shell to get environment per project
(#17717)

This is a follow-up to #17075 to spawn a login shell when getting the
environment for projects.

The reason why we didn't do it before is that we only used the
environment for certain language servers and not a lot of other things,
like tasks.

But with #17075 we now use the project more often and use it as the
_base_ environment for tasks/terminals.

Before the change, terminals and tasks would inherit the Zed process'
environment, including PATH and so on. After the change, we would set
the environment, overwriting the PATH instead of merging. But the
non-login shell environment is a subset of the login-shell environment.


Release Notes:

- Fixed environment variables used per project in terminals/tasks
overwriting the base environment and not making use of a login-shell
environment.

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-09-12 14:46:31 -04:00
Joseph T Lyons
efe773a796 zed 0.153.2 2024-09-12 10:00:43 -04:00
gcp-cherry-pick-bot[bot]
4c81907cc2 Use a bigger prefix for numeric sorting (cherry-pick #17752) (#17754)
Cherry-picked Use a bigger prefix for numeric sorting (#17752)

Release Notes:

- Fixed sorting of files with YYYYmmddHHMMSS prefix

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-09-12 09:42:50 -04:00
gcp-cherry-pick-bot[bot]
5880b24c79 bump eslint memory usage (cherry-pick #17724) (#17731)
Cherry-picked bump eslint memory usage (#17724)

Release Notes:

- Increased memory limit for eslint to reduce crashes

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-09-11 17:23:40 -04:00
Peter Tripp
576b38d598 zed 0.153.1 2024-09-10 17:52:25 -04:00
Piotr Osiewicz
7e4bc1235a pane: Fix pinned tabs being persisted after closing (#17666)
Release Notes:

- Fixed tabs staying pinned after closing unrelated tabs
2024-09-10 17:51:21 -04:00
Peter Tripp
39f57fa538 Revert tokenizer for custom OpenAI models (#17660)
Fix for custom openai models tokenizer settings.
2024-09-10 17:51:17 -04:00
Conrad Irwin
7058a91b82 Correctly merge settings for vtsls (#17657)
Release Notes:

- Fixed vtsls initialization_options in project settings files
2024-09-10 17:51:12 -04:00
Joseph T Lyons
f9ec8405c5 v0.153.x preview 2024-09-10 14:40:26 -04:00
32 changed files with 464 additions and 248 deletions

View File

@@ -15,8 +15,7 @@ concurrency:
jobs:
bump_patch_version:
runs-on:
- self-hosted
- test
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

View File

@@ -39,16 +39,7 @@ jobs:
run: git clean -df
- name: Check spelling
run: |
if ! cargo install --list | grep "typos-cli v$TYPOS_CLI_VERSION" > /dev/null; then
echo "Installing typos-cli@$TYPOS_CLI_VERSION..."
cargo install "typos-cli@$TYPOS_CLI_VERSION"
else
echo "typos-cli@$TYPOS_CLI_VERSION is already installed."
fi
typos
env:
TYPOS_CLI_VERSION: "1.23.3"
run: script/check-spelling
- name: Run style checks
uses: ./.github/actions/check_style
@@ -110,8 +101,7 @@ jobs:
timeout-minutes: 60
name: (Linux) Run Clippy and tests
runs-on:
- self-hosted
- deploy
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
@@ -121,6 +111,14 @@ jobs:
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install Linux dependencies
run: ./script/linux
- name: cargo clippy
run: ./script/clippy
@@ -271,24 +269,20 @@ jobs:
timeout-minutes: 60
name: Create a Linux bundle
runs-on:
- self-hosted
- deploy
- buildjet-16vcpu-ubuntu-2204
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
needs: [linux_tests]
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Install Linux dependencies
run: ./script/linux
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
@@ -343,7 +337,7 @@ jobs:
timeout-minutes: 60
name: Create arm64 Linux bundle
runs-on:
- hosted-linux-arm-1
- buildjet-16vcpu-ubuntu-2204-arm
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
needs: [linux_tests]
env:
@@ -354,26 +348,9 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: "Setup jq"
uses: dcarbone/install-jq-action@8867ddb4788346d7c22b72ea2e2ffe4d514c7bcb # v2
- name: Set up Clang
run: |
sudo apt-get update
sudo apt-get install -y llvm-15 clang-15 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-15/bin" >> $GITHUB_PATH
- uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
with:
mold-version: 2.32.0
- name: rustup
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Install Linux dependencies
run: ./script/linux
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}

View File

@@ -61,8 +61,7 @@ jobs:
- style
- tests
runs-on:
- self-hosted
- deploy
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
@@ -92,8 +91,7 @@ jobs:
needs:
- publish
runs-on:
- self-hosted
- deploy
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Sign into Kubernetes

View File

@@ -19,8 +19,7 @@ jobs:
tests:
name: Run randomized tests
runs-on:
- self-hosted
- randomized-tests
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Install Node
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4

View File

@@ -100,8 +100,7 @@ jobs:
name: Create a Linux *.tar.gz bundle for x86
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- deploy
- buildjet-16vcpu-ubuntu-2204
needs: tests
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}

2
Cargo.lock generated
View File

@@ -14197,7 +14197,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.153.0"
version = "0.153.7"
dependencies = [
"activity_indicator",
"anyhow",

View File

@@ -220,7 +220,8 @@ impl PromptBuilder {
let before_range = 0..range.start;
let truncated_before = if before_range.len() > MAX_CTX {
is_truncated = true;
range.start - MAX_CTX..range.start
let start = buffer.clip_offset(range.start - MAX_CTX, text::Bias::Right);
start..range.start
} else {
before_range
};
@@ -228,7 +229,8 @@ impl PromptBuilder {
let after_range = range.end..buffer.len();
let truncated_after = if after_range.len() > MAX_CTX {
is_truncated = true;
range.end..range.end + MAX_CTX
let end = buffer.clip_offset(range.end + MAX_CTX, text::Bias::Left);
range.end..end
} else {
after_range
};

View File

@@ -104,7 +104,7 @@ pub enum ChannelRole {
/// Admin can read/write and change permissions.
#[sea_orm(string_value = "admin")]
Admin,
/// Member can read/write, but not change pemissions.
/// Member can read/write, but not change permissions.
#[sea_orm(string_value = "member")]
#[default]
Member,

View File

@@ -337,7 +337,7 @@ impl InlayHintCache {
/// If needed, queries LSP for new inlay hints, using the invalidation strategy given.
/// To reduce inlay hint jumping, attempts to query a visible range of the editor(s) first,
/// followed by the delayed queries of the same range above and below the visible one.
/// This way, concequent refresh invocations are less likely to trigger LSP queries for the invisible ranges.
/// This way, subsequent refresh invocations are less likely to trigger LSP queries for the invisible ranges.
pub(super) fn spawn_hint_refresh(
&mut self,
reason_description: &'static str,

View File

@@ -204,7 +204,8 @@ impl App {
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
type KeystrokeObserver = Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) + 'static>;
pub(crate) type KeystrokeObserver =
Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) -> bool + 'static>;
type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
@@ -1050,7 +1051,7 @@ impl AppContext {
/// and that this API will not be invoked if the event's propagation is stopped.
pub fn observe_keystrokes(
&mut self,
f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
mut f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
) -> Subscription {
fn inner(
keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
@@ -1060,7 +1061,14 @@ impl AppContext {
activate();
subscription
}
inner(&mut self.keystroke_observers, Box::new(f))
inner(
&mut self.keystroke_observers,
Box::new(move |event, cx| {
f(event, cx);
true
}),
)
}
/// Register key bindings.

View File

@@ -4,16 +4,17 @@ use crate::{
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
FileDropEvent, Flatten, FontId, GPUSpecs, Global, GlobalElementId, GlyphId, Hsla, InputHandler,
IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
LineLayoutIndex, Model, ModelContext, Modifiers, ModifiersChangedEvent, MonochromeSprite,
MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams,
Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
WindowOptions, WindowParams, WindowTextSystem, SUBPIXEL_VARIANTS,
IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent,
KeystrokeObserver, LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers,
ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, ScaledPixels, Scene,
Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet, Subscription,
TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline,
UnderlineStyle, View, VisualContext, WeakView, WindowAppearance, WindowBackgroundAppearance,
WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
use collections::{FxHashMap, FxHashSet};
@@ -1043,8 +1044,7 @@ impl<'a> WindowContext<'a> {
action: action.as_ref().map(|action| action.boxed_clone()),
},
self,
);
true
)
});
}
@@ -4250,6 +4250,36 @@ impl<'a, V: 'static> ViewContext<'a, V> {
subscription
}
/// Register a callback to be invoked when a keystroke is received by the application
/// in any window. Note that this fires after all other action and event mechanisms have resolved
/// and that this API will not be invoked if the event's propagation is stopped.
pub fn observe_keystrokes(
&mut self,
mut f: impl FnMut(&mut V, &KeystrokeEvent, &mut ViewContext<V>) + 'static,
) -> Subscription {
fn inner(
keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
handler: KeystrokeObserver,
) -> Subscription {
let (subscription, activate) = keystroke_observers.insert((), handler);
activate();
subscription
}
let view = self.view.downgrade();
inner(
&mut self.keystroke_observers,
Box::new(move |event, cx| {
if let Some(view) = view.upgrade() {
view.update(cx, |view, cx| f(view, event, cx));
true
} else {
false
}
}),
)
}
/// Register a callback to be invoked when the window's pending input changes.
pub fn observe_pending_input(
&mut self,

View File

@@ -3022,7 +3022,7 @@ impl BufferSnapshot {
let mut start = text.len();
let end = start + buffer_range.len();
// When multiple names are captured, then the matcheable text
// When multiple names are captured, then the matchable text
// includes the whitespace in between the names.
if !name_ranges.is_empty() {
start -= 1;

View File

@@ -392,7 +392,7 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
let buffer = cx.new_model(|cx| Buffer::local(text, cx));
// Spawn a task to format the buffer's whitespace.
// Pause so that the foratting task starts running.
// Pause so that the formatting task starts running.
let format = buffer.update(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
smol::future::yield_now().await;

View File

@@ -370,7 +370,11 @@ pub fn count_open_ai_tokens(
})
.collect::<Vec<_>>();
tiktoken_rs::num_tokens_from_messages(model.id(), &messages)
if let open_ai::Model::Custom { .. } = model {
tiktoken_rs::num_tokens_from_messages("gpt-4", &messages)
} else {
tiktoken_rs::num_tokens_from_messages(model.id(), &messages)
}
})
.boxed()
}
@@ -487,7 +491,7 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
const OPENAI_CONSOLE_URL: &str = "https://console.anthropic.com/settings/keys";
const OPENAI_CONSOLE_URL: &str = "https://platform.openai.com/api-keys";
const INSTRUCTIONS: [&str; 6] = [
"To use the assistant panel or inline assistant, you need to add your OpenAI API key.",
" - You can create an API key at: ",

View File

@@ -521,7 +521,6 @@ impl ContextProvider for GoContextProvider {
command: "go".into(),
args: vec![
"test".into(),
GO_PACKAGE_TASK_VARIABLE.template_value(),
"-run".into(),
format!("^{}\\$", VariableName::Symbol.template_value(),),
],
@@ -532,7 +531,7 @@ impl ContextProvider for GoContextProvider {
TaskTemplate {
label: format!("go test {}", GO_PACKAGE_TASK_VARIABLE.template_value()),
command: "go".into(),
args: vec!["test".into(), GO_PACKAGE_TASK_VARIABLE.template_value()],
args: vec!["test".into()],
cwd: package_cwd.clone(),
..TaskTemplate::default()
},
@@ -574,7 +573,6 @@ impl ContextProvider for GoContextProvider {
command: "go".into(),
args: vec![
"test".into(),
GO_PACKAGE_TASK_VARIABLE.template_value(),
"-benchmem".into(),
"-run=^$".into(),
"-bench".into(),

View File

@@ -58,7 +58,11 @@ fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
}
fn eslint_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
vec![server_path.into(), "--stdio".into()]
vec![
"--max-old-space-size=8192".into(),
server_path.into(),
"--stdio".into(),
]
}
pub struct TypeScriptLspAdapter {

View File

@@ -7,14 +7,14 @@ use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime;
use project::project_settings::{BinarySettings, ProjectSettings};
use serde_json::{json, Value};
use settings::Settings;
use settings::{Settings, SettingsLocation};
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()]
@@ -274,17 +274,29 @@ impl LspAdapter for VtslsLspAdapter {
cx: &mut AsyncAppContext,
) -> Result<Value> {
let override_options = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(SERVER_NAME)
.and_then(|s| s.initialization_options.clone())
ProjectSettings::get(
Some(SettingsLocation {
worktree_id: adapter.worktree_id(),
path: adapter.worktree_root_path(),
}),
cx,
)
.lsp
.get(SERVER_NAME)
.and_then(|s| s.initialization_options.clone())
})?;
if let Some(options) = override_options {
return Ok(options);
}
self.initialization_options(adapter)
let mut initialization_options = self
.initialization_options(adapter)
.await
.map(|o| o.unwrap())
.map(|o| o.unwrap())?;
if let Some(override_options) = override_options {
merge_json_value_into(override_options, &mut initialization_options)
}
Ok(initialization_options)
}
fn language_ids(&self) -> HashMap<String, String> {

View File

@@ -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,
@@ -4925,7 +4970,6 @@ where
#[cfg(test)]
mod tests {
use super::*;
use futures::StreamExt;
use gpui::{AppContext, Context, TestAppContext};
use language::{Buffer, Rope};
use parking_lot::RwLock;
@@ -5477,40 +5521,66 @@ 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)
});
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", //
)
);
@@ -5522,7 +5592,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)
]
);
}

View File

@@ -509,7 +509,7 @@ impl<D: PickerDelegate> Picker<D> {
.on_mouse_up(
MouseButton::Right,
cx.listener(move |this, event: &MouseUpEvent, cx| {
// We specficially want to use the platform key here, as
// We specifically want to use the platform key here, as
// ctrl will already be held down for the tab switcher.
this.handle_click(ix, event.modifiers.platform, cx)
}),

View File

@@ -219,7 +219,7 @@ async fn load_shell_environment(
);
let output = smol::process::Command::new(&shell)
.args(["-i", "-c", &command])
.args(["-l", "-i", "-c", &command])
.envs(direnv_environment)
.output()
.await

View File

@@ -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);
});
});
});
}
}

View File

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

View File

@@ -1619,7 +1619,7 @@ fn task_summary(task: &TaskState, error_code: Option<i32>) -> (bool, String, Str
/// the cursor's `point` is not updated to the new line and column values
///
/// * ??? there could be more consequences, and any further "proper" streaming from the PTY might bug and/or panic.
/// Still, concequent `append_text_to_term` invocations are possible and display the contents correctly.
/// Still, subsequent `append_text_to_term` invocations are possible and display the contents correctly.
///
/// Despite the quirks, this is the simplest approach to appending text to the terminal: its alternative, `grid_mut` manipulations,
/// do not properly set the scrolling state and display odd text after appending; also those manipulations are more tedious and error-prone.

View File

@@ -45,7 +45,7 @@ impl RenderOnce for AnyIcon {
/// The decoration for an icon.
///
/// For example, this can show an indicator, an "x",
/// or a diagonal strkethrough to indicate something is disabled.
/// or a diagonal strikethrough to indicate something is disabled.
#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
pub enum IconDecoration {
Strikethrough,

View File

@@ -644,7 +644,7 @@ impl<T: Ord + Clone> RangeExt<T> for RangeInclusive<T> {
/// This is useful for turning regular alphanumerically sorted sequences as `1-abc, 10, 11-def, .., 2, 21-abc`
/// into `1-abc, 2, 10, 11-def, .., 21-abc`
#[derive(Debug, PartialEq, Eq)]
pub struct NumericPrefixWithSuffix<'a>(Option<u32>, &'a str);
pub struct NumericPrefixWithSuffix<'a>(Option<u64>, &'a str);
impl<'a> NumericPrefixWithSuffix<'a> {
pub fn from_numeric_prefixed_str(str: &'a str) -> Self {

View File

@@ -24,7 +24,7 @@ use editor::{
};
use gpui::{
actions, impl_actions, Action, AppContext, Entity, EventEmitter, KeyContext, KeystrokeEvent,
Render, View, ViewContext, WeakView,
Render, Subscription, View, ViewContext, WeakView,
};
use insert::NormalBefore;
use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
@@ -166,6 +166,8 @@ pub(crate) struct Vim {
pub search: SearchState,
editor: WeakView<Editor>,
_subscriptions: Vec<Subscription>,
}
// Hack: Vim intercepts events dispatched to a window and updates the view in response.
@@ -189,36 +191,32 @@ impl Vim {
pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
let editor = cx.view().clone();
cx.new_view(|cx: &mut ViewContext<Vim>| {
cx.subscribe(&editor, |vim, _, event, cx| {
vim.handle_editor_event(event, cx)
})
.detach();
cx.new_view(|cx| Vim {
mode: Mode::Normal,
last_mode: Mode::Normal,
pre_count: None,
post_count: None,
operator_stack: Vec::new(),
replacements: Vec::new(),
let listener = cx.listener(Vim::observe_keystrokes);
cx.observe_keystrokes(listener).detach();
marks: HashMap::default(),
stored_visual_mode: None,
change_list: Vec::new(),
change_list_position: None,
current_tx: None,
current_anchor: None,
undo_modes: HashMap::default(),
Vim {
mode: Mode::Normal,
last_mode: Mode::Normal,
pre_count: None,
post_count: None,
operator_stack: Vec::new(),
replacements: Vec::new(),
selected_register: None,
search: SearchState::default(),
marks: HashMap::default(),
stored_visual_mode: None,
change_list: Vec::new(),
change_list_position: None,
current_tx: None,
current_anchor: None,
undo_modes: HashMap::default(),
selected_register: None,
search: SearchState::default(),
editor: editor.downgrade(),
}
editor: editor.downgrade(),
_subscriptions: vec![
cx.observe_keystrokes(Self::observe_keystrokes),
cx.subscribe(&editor, |this, _, event, cx| {
this.handle_editor_event(event, cx)
}),
],
})
}

View File

@@ -1364,6 +1364,9 @@ impl Pane {
self.activation_history
.retain(|entry| entry.entity_id != self.items[item_index].item_id());
if self.is_tab_pinned(item_index) {
self.pinned_tab_count -= 1;
}
if item_index == self.active_item_index {
let index_to_activate = self
.activation_history

View File

@@ -2,7 +2,7 @@
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.153.0"
version = "0.153.7"
publish = false
license = "GPL-3.0-or-later"
authors = ["Zed Team <hi@zed.dev>"]

View File

@@ -1 +1 @@
dev
stable

View File

@@ -176,7 +176,7 @@ pub fn monitor_main_thread_hangs(
let background_executor = cx.background_executor();
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
// Initialize SIGUSR2 handler to send a backrace to a channel.
// Initialize SIGUSR2 handler to send a backtrace to a channel.
let (backtrace_tx, backtrace_rx) = mpsc::channel();
static BACKTRACE: Mutex<Vec<backtrace::Frame>> = Mutex::new(Vec::new());
static BACKTRACE_SENDER: OnceLock<mpsc::Sender<()>> = OnceLock::new();

11
script/check-spelling Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
TYPOS_CLI_VERSION=1.24.6
if ! cargo install --list | grep "typos-cli v$TYPOS_CLI_VERSION" > /dev/null; then
echo "Installing typos-cli@$TYPOS_CLI_VERSION..."
cargo install "typos-cli@$TYPOS_CLI_VERSION"
else
echo "typos-cli@$TYPOS_CLI_VERSION is already installed."
fi
typos

View File

@@ -32,6 +32,7 @@ if [[ -n $apt ]]; then
elfutils
libsqlite3-dev
)
$maysudo "$apt" update
$maysudo "$apt" install -y "${deps[@]}"
exit 0
fi