Compare commits
26 Commits
quick-comm
...
v0.153.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03592ac595 | ||
|
|
0d7eb4baf3 | ||
|
|
fa274616dc | ||
|
|
d062dd29ff | ||
|
|
aa23c358ae | ||
|
|
7234166077 | ||
|
|
4105d27545 | ||
|
|
5f08341c9b | ||
|
|
9a261cd028 | ||
|
|
afeca3480f | ||
|
|
58a12ef252 | ||
|
|
d3d1a8f55d | ||
|
|
553edc2662 | ||
|
|
5fff524870 | ||
|
|
47779a9334 | ||
|
|
ecc1ba168a | ||
|
|
cfe5dd8d04 | ||
|
|
c58a552161 | ||
|
|
efe773a796 | ||
|
|
4c81907cc2 | ||
|
|
5880b24c79 | ||
|
|
576b38d598 | ||
|
|
7e4bc1235a | ||
|
|
39f57fa538 | ||
|
|
7058a91b82 | ||
|
|
f9ec8405c5 |
3
.github/workflows/bump_patch_version.yml
vendored
3
.github/workflows/bump_patch_version.yml
vendored
@@ -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
|
||||
|
||||
55
.github/workflows/ci.yml
vendored
55
.github/workflows/ci.yml
vendored
@@ -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') }}
|
||||
|
||||
6
.github/workflows/deploy_collab.yml
vendored
6
.github/workflows/deploy_collab.yml
vendored
@@ -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
|
||||
|
||||
3
.github/workflows/randomized_tests.yml
vendored
3
.github/workflows/randomized_tests.yml
vendored
@@ -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
|
||||
|
||||
3
.github/workflows/release_nightly.yml
vendored
3
.github/workflows/release_nightly.yml
vendored
@@ -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
2
Cargo.lock
generated
@@ -14197,7 +14197,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.153.0"
|
||||
version = "0.153.7"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: ",
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()?;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
stable
|
||||
@@ -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
11
script/check-spelling
Executable 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
|
||||
@@ -32,6 +32,7 @@ if [[ -n $apt ]]; then
|
||||
elfutils
|
||||
libsqlite3-dev
|
||||
)
|
||||
$maysudo "$apt" update
|
||||
$maysudo "$apt" install -y "${deps[@]}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user