Compare commits
1 Commits
alas-memor
...
navigate-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69e7499af |
15
Cargo.lock
generated
15
Cargo.lock
generated
@@ -395,6 +395,7 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"languages",
|
||||
"log",
|
||||
"lsp",
|
||||
@@ -462,6 +463,7 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"log",
|
||||
"lsp",
|
||||
"markdown",
|
||||
@@ -516,6 +518,7 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"languages",
|
||||
"log",
|
||||
"multi_buffer",
|
||||
@@ -7011,7 +7014,6 @@ dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"client",
|
||||
"collections",
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
@@ -7027,7 +7029,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smol",
|
||||
"strum",
|
||||
"telemetry_events",
|
||||
"thiserror 1.0.69",
|
||||
"ui",
|
||||
"util",
|
||||
@@ -7054,10 +7055,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"aws-config",
|
||||
"aws-credential-types",
|
||||
"aws_http_client",
|
||||
"bedrock",
|
||||
"client",
|
||||
"collections",
|
||||
"copilot",
|
||||
@@ -7069,7 +7066,6 @@ dependencies = [
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
"gpui",
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"language_model",
|
||||
"lmstudio",
|
||||
@@ -7085,9 +7081,10 @@ dependencies = [
|
||||
"settings",
|
||||
"smol",
|
||||
"strum",
|
||||
"telemetry_events",
|
||||
"theme",
|
||||
"thiserror 1.0.69",
|
||||
"tiktoken-rs",
|
||||
"tokio",
|
||||
"ui",
|
||||
"util",
|
||||
]
|
||||
@@ -17193,7 +17190,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"inline_completion",
|
||||
"language",
|
||||
"language_model",
|
||||
"language_models",
|
||||
"log",
|
||||
"menu",
|
||||
"migrator",
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="283.6413 127.3453 56 55.9999" width="16px" height="16px">
|
||||
<path d="M 808.592 158.131 C 807.489 158.131 806.592 157.234 806.592 156.131 C 806.592 155.028 807.489 154.131 808.592 154.131 C 809.695 154.131 810.592 155.028 810.592 156.131 C 810.592 157.234 809.695 158.131 808.592 158.131 Z M 776.705 185.039 L 773.457 183.145 L 780.122 178.979 L 779.062 177.283 L 771.505 182.006 L 765.592 178.557 L 765.592 169.666 L 771.147 165.963 L 770.037 164.299 L 764.551 167.956 L 758.592 164.551 L 758.592 159.711 L 765.088 155.999 L 764.096 154.263 L 758.592 157.408 L 758.592 153.711 L 764.592 150.283 L 770.592 153.711 L 770.592 157.565 L 766.077 160.274 L 767.107 161.988 L 771.592 159.297 L 776.077 161.988 L 777.107 160.274 L 772.592 157.565 L 772.592 153.666 L 778.147 149.963 C 778.425 149.777 778.592 149.465 778.592 149.131 L 778.592 142.131 L 776.592 142.131 L 776.592 148.596 L 771.551 151.956 L 765.592 148.551 L 765.592 139.705 L 770.592 136.789 L 770.592 145.131 L 772.592 145.131 L 772.592 135.622 L 776.705 133.223 L 784.592 135.852 L 784.592 164.565 L 770.077 173.274 L 771.107 174.988 L 784.592 166.897 L 784.592 182.41 L 776.705 185.039 Z M 806.592 169.131 C 806.592 170.234 805.695 171.131 804.592 171.131 C 803.489 171.131 802.592 170.234 802.592 169.131 C 802.592 168.028 803.489 167.131 804.592 167.131 C 805.695 167.131 806.592 168.028 806.592 169.131 Z M 796.592 179.131 C 796.592 180.234 795.695 181.131 794.592 181.131 C 793.489 181.131 792.592 180.234 792.592 179.131 C 792.592 178.028 793.489 177.131 794.592 177.131 C 795.695 177.131 796.592 178.028 796.592 179.131 Z M 795.592 139.131 C 795.592 138.028 796.489 137.131 797.592 137.131 C 798.695 137.131 799.592 138.028 799.592 139.131 C 799.592 140.234 798.695 141.131 797.592 141.131 C 796.489 141.131 795.592 140.234 795.592 139.131 Z M 808.592 152.131 C 806.733 152.131 805.181 153.411 804.734 155.131 L 786.592 155.131 L 786.592 150.131 L 797.592 150.131 C 798.145 150.131 798.592 149.683 798.592 149.131 L 798.592 142.989 C 800.312 142.542 801.592 140.989 801.592 139.131 C 801.592 136.925 799.798 135.131 797.592 135.131 C 795.386 135.131 793.592 136.925 793.592 139.131 C 793.592 140.989 794.872 142.542 796.592 142.989 L 796.592 148.131 L 786.592 148.131 L 786.592 135.131 C 786.592 134.7 786.317 134.319 785.908 134.182 L 776.908 131.182 C 776.634 131.092 776.336 131.122 776.088 131.267 L 764.088 138.267 C 763.78 138.446 763.592 138.776 763.592 139.131 L 763.592 148.551 L 757.096 152.263 C 756.784 152.441 756.592 152.772 756.592 153.131 L 756.592 165.131 C 756.592 165.49 756.784 165.821 757.096 165.999 L 763.592 169.711 L 763.592 179.131 C 763.592 179.486 763.78 179.816 764.088 179.995 L 776.088 186.995 C 776.242 187.085 776.417 187.131 776.592 187.131 C 776.698 187.131 776.805 187.114 776.908 187.08 L 785.908 184.08 C 786.317 183.943 786.592 183.562 786.592 183.131 L 786.592 171.131 L 793.592 171.131 L 793.592 175.273 C 791.872 175.72 790.592 177.273 790.592 179.131 C 790.592 181.337 792.386 183.131 794.592 183.131 C 796.798 183.131 798.592 181.337 798.592 179.131 C 798.592 177.273 797.312 175.72 795.592 175.273 L 795.592 170.131 C 795.592 169.579 795.145 169.131 794.592 169.131 L 786.592 169.131 L 786.592 164.131 L 799.092 164.131 L 801.23 166.981 C 800.831 167.603 800.592 168.338 800.592 169.131 C 800.592 171.337 802.386 173.131 804.592 173.131 C 806.798 173.131 808.592 171.337 808.592 169.131 C 808.592 166.925 806.798 165.131 804.592 165.131 C 803.908 165.131 803.274 165.319 802.711 165.623 L 800.392 162.531 C 800.203 162.279 799.906 162.131 799.592 162.131 L 786.592 162.131 L 786.592 157.131 L 804.734 157.131 C 805.181 158.851 806.733 160.131 808.592 160.131 C 810.798 160.131 812.592 158.337 812.592 156.131 C 812.592 153.925 810.798 152.131 808.592 152.131 Z" fill-rule="evenodd" fill-opacity="1" style="" id="object-0" transform="matrix(1, 0, 0, 1, -472.9506530761719, -3.7858259677886963)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -43,6 +43,7 @@ indoc.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
menu.workspace = true
|
||||
|
||||
@@ -32,10 +32,11 @@ use gpui::{
|
||||
};
|
||||
use language::{line_diff, Buffer, IndentKind, Point, Selection, TransactionId};
|
||||
use language_model::{
|
||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelTextStream, Role,
|
||||
};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use language_models::report_assistant_event;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
|
||||
@@ -16,10 +16,10 @@ use gpui::{
|
||||
};
|
||||
use language::Buffer;
|
||||
use language_model::{
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use language_models::report_assistant_event;
|
||||
use prompt_library::PromptBuilder;
|
||||
use settings::{update_settings_file, Settings};
|
||||
use std::{
|
||||
|
||||
@@ -46,6 +46,7 @@ itertools.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
markdown.workspace = true
|
||||
|
||||
@@ -9,9 +9,10 @@ use futures::{channel::mpsc, future::LocalBoxFuture, join, SinkExt, Stream, Stre
|
||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
|
||||
use language::{line_diff, Buffer, IndentKind, Point, TransactionId};
|
||||
use language_model::{
|
||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelTextStream, Role,
|
||||
};
|
||||
use language_models::report_assistant_event;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use prompt_library::PromptBuilder;
|
||||
|
||||
@@ -24,7 +24,8 @@ use gpui::{
|
||||
UpdateGlobal, WeakEntity, Window,
|
||||
};
|
||||
use language::{Buffer, Point, Selection, TransactionId};
|
||||
use language_model::{report_assistant_event, LanguageModelRegistry};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use language_models::report_assistant_event;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::inline_prompt_editor::CodegenStatus;
|
||||
use client::telemetry::Telemetry;
|
||||
use futures::{channel::mpsc, SinkExt, StreamExt};
|
||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
|
||||
use language_model::{report_assistant_event, LanguageModelRegistry, LanguageModelRequest};
|
||||
use language_model::{LanguageModelRegistry, LanguageModelRequest};
|
||||
use language_models::report_assistant_event;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
use terminal::Terminal;
|
||||
|
||||
@@ -13,9 +13,9 @@ use fs::Fs;
|
||||
use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
|
||||
use language::Buffer;
|
||||
use language_model::{
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_models::report_assistant_event;
|
||||
use prompt_library::PromptBuilder;
|
||||
use std::sync::Arc;
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
|
||||
@@ -10,9 +10,9 @@ use gpui::{App, Context, EventEmitter, SharedString, Task};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
|
||||
LanguageModelToolUseId, MaxMonthlySpendReachedError, MessageContent, PaymentRequiredError,
|
||||
Role, StopReason,
|
||||
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||
};
|
||||
use language_models::provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use util::{post_inc, TryFutureExt as _};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -30,6 +30,7 @@ indexed_docs.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
open_ai.workspace = true
|
||||
|
||||
@@ -19,10 +19,13 @@ use gpui::{
|
||||
};
|
||||
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
|
||||
use language_model::{
|
||||
report_assistant_event, LanguageModel, LanguageModelCacheConfiguration,
|
||||
LanguageModelCompletionEvent, LanguageModelImage, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelToolUseId, MaxMonthlySpendReachedError,
|
||||
MessageContent, PaymentRequiredError, Role, StopReason,
|
||||
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
|
||||
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||
};
|
||||
use language_models::{
|
||||
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
|
||||
report_assistant_event,
|
||||
};
|
||||
use open_ai::Model as OpenAiModel;
|
||||
use paths::contexts_dir;
|
||||
|
||||
@@ -359,7 +359,6 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
|
||||
schemars::schema::SchemaObject {
|
||||
enum_values: Some(vec![
|
||||
"anthropic".into(),
|
||||
"bedrock".into(),
|
||||
"google".into(),
|
||||
"lmstudio".into(),
|
||||
"ollama".into(),
|
||||
|
||||
@@ -524,7 +524,7 @@ impl ScrollbarMarkerState {
|
||||
#[derive(Clone, Debug)]
|
||||
struct RunnableTasks {
|
||||
templates: Vec<(TaskSourceKind, TaskTemplate)>,
|
||||
offset: multi_buffer::Anchor,
|
||||
offset: MultiBufferOffset,
|
||||
// We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
|
||||
column: u32,
|
||||
// Values of all named captures, including those starting with '_'
|
||||
@@ -551,6 +551,8 @@ struct ResolvedTasks {
|
||||
templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
|
||||
position: Anchor,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct MultiBufferOffset(usize);
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
struct BufferOffset(usize);
|
||||
|
||||
@@ -9531,6 +9533,56 @@ impl Editor {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn move_to_previous_multibuffer_header(
|
||||
&mut self,
|
||||
_: &MoveToStartOfExcerpt,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if matches!(self.mode, EditorMode::SingleLine { .. }) {
|
||||
cx.propagate();
|
||||
return;
|
||||
}
|
||||
|
||||
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
selection.collapse_to(
|
||||
movement::multibuffer_header(
|
||||
map,
|
||||
selection.head(),
|
||||
workspace::searchable::Direction::Prev,
|
||||
),
|
||||
SelectionGoal::None,
|
||||
)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn move_to_next_multibuffer_header(
|
||||
&mut self,
|
||||
_: &MoveToEndOfExcerpt,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if matches!(self.mode, EditorMode::SingleLine { .. }) {
|
||||
cx.propagate();
|
||||
return;
|
||||
}
|
||||
|
||||
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
selection.collapse_to(
|
||||
movement::multibuffer_header(
|
||||
map,
|
||||
selection.head(),
|
||||
workspace::searchable::Direction::Next,
|
||||
),
|
||||
SelectionGoal::None,
|
||||
)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn select_to_end_of_paragraph(
|
||||
&mut self,
|
||||
_: &SelectToEndOfParagraph,
|
||||
@@ -10846,9 +10898,7 @@ impl Editor {
|
||||
(runnable.buffer_id, row),
|
||||
RunnableTasks {
|
||||
templates: tasks,
|
||||
offset: snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(runnable.run_range.start),
|
||||
offset: MultiBufferOffset(runnable.run_range.start),
|
||||
context_range,
|
||||
column: point.column,
|
||||
extra_variables: runnable.extra_captures,
|
||||
@@ -15340,39 +15390,27 @@ impl Editor {
|
||||
let selections = self.selections.all::<usize>(cx);
|
||||
let multi_buffer = self.buffer.read(cx);
|
||||
for selection in selections {
|
||||
for (snapshot, range, _, anchor) in multi_buffer
|
||||
for (buffer, mut range, _) in multi_buffer
|
||||
.snapshot(cx)
|
||||
.range_to_buffer_ranges_with_deleted_hunks(selection.range())
|
||||
.range_to_buffer_ranges(selection.range())
|
||||
{
|
||||
if let Some(anchor) = anchor {
|
||||
// selection is in a deleted hunk
|
||||
let Some(buffer_id) = anchor.buffer_id else {
|
||||
continue;
|
||||
};
|
||||
let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
|
||||
continue;
|
||||
};
|
||||
let offset = text::ToOffset::to_offset(
|
||||
&anchor.text_anchor,
|
||||
&buffer_handle.read(cx).snapshot(),
|
||||
);
|
||||
let range = offset..offset;
|
||||
new_selections_by_buffer
|
||||
.entry(buffer_handle)
|
||||
.or_insert((Vec::new(), None))
|
||||
.0
|
||||
.push(range)
|
||||
} else {
|
||||
let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
new_selections_by_buffer
|
||||
.entry(buffer_handle)
|
||||
.or_insert((Vec::new(), None))
|
||||
.0
|
||||
.push(range)
|
||||
// When editing branch buffers, jump to the corresponding location
|
||||
// in their base buffer.
|
||||
let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
|
||||
let buffer = buffer_handle.read(cx);
|
||||
if let Some(base_buffer) = buffer.base_buffer() {
|
||||
range = buffer.range_to_version(range, &base_buffer.read(cx).version());
|
||||
buffer_handle = base_buffer;
|
||||
}
|
||||
|
||||
if selection.reversed {
|
||||
mem::swap(&mut range.start, &mut range.end);
|
||||
}
|
||||
new_selections_by_buffer
|
||||
.entry(buffer_handle)
|
||||
.or_insert((Vec::new(), None))
|
||||
.0
|
||||
.push(range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15337,12 +15337,11 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
|
||||
});
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
editor.tasks.insert(
|
||||
(buffer.read(cx).remote_id(), 3),
|
||||
RunnableTasks {
|
||||
templates: vec![],
|
||||
offset: snapshot.anchor_before(43),
|
||||
offset: MultiBufferOffset(43),
|
||||
column: 0,
|
||||
extra_variables: HashMap::default(),
|
||||
context_range: BufferOffset(43)..BufferOffset(85),
|
||||
@@ -15352,7 +15351,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
|
||||
(buffer.read(cx).remote_id(), 8),
|
||||
RunnableTasks {
|
||||
templates: vec![],
|
||||
offset: snapshot.anchor_before(86),
|
||||
offset: MultiBufferOffset(86),
|
||||
column: 0,
|
||||
extra_variables: HashMap::default(),
|
||||
context_range: BufferOffset(86)..BufferOffset(191),
|
||||
|
||||
@@ -50,7 +50,7 @@ use language::{
|
||||
use lsp::DiagnosticSeverity;
|
||||
use multi_buffer::{
|
||||
Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow,
|
||||
RowInfo,
|
||||
RowInfo, ToOffset,
|
||||
};
|
||||
use project::project_settings::{self, GitGutterSetting, ProjectSettings};
|
||||
use settings::Settings;
|
||||
@@ -2062,22 +2062,21 @@ impl EditorElement {
|
||||
None
|
||||
};
|
||||
|
||||
let offset_range_start =
|
||||
snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left);
|
||||
|
||||
let offset_range_end =
|
||||
snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
|
||||
let offset_range_start = snapshot
|
||||
.display_point_to_anchor(DisplayPoint::new(range.start, 0), Bias::Left)
|
||||
.to_offset(&snapshot.buffer_snapshot);
|
||||
let offset_range_end = snapshot
|
||||
.display_point_to_anchor(DisplayPoint::new(range.end, 0), Bias::Right)
|
||||
.to_offset(&snapshot.buffer_snapshot);
|
||||
|
||||
editor
|
||||
.tasks
|
||||
.iter()
|
||||
.filter_map(|(_, tasks)| {
|
||||
let multibuffer_point = tasks.offset.to_point(&snapshot.buffer_snapshot);
|
||||
if multibuffer_point < offset_range_start
|
||||
|| multibuffer_point > offset_range_end
|
||||
{
|
||||
if tasks.offset.0 < offset_range_start || tasks.offset.0 >= offset_range_end {
|
||||
return None;
|
||||
}
|
||||
let multibuffer_point = tasks.offset.0.to_point(&snapshot.buffer_snapshot);
|
||||
let multibuffer_row = MultiBufferRow(multibuffer_point.row);
|
||||
let buffer_folded = snapshot
|
||||
.buffer_snapshot
|
||||
|
||||
@@ -467,6 +467,31 @@ pub fn end_of_excerpt(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multibuffer_header(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
direction: Direction,
|
||||
) -> DisplayPoint {
|
||||
let point = map.display_point_to_point(display_point, Bias::Left);
|
||||
// It seems likely that the better way to implement this is to reuse block logic via
|
||||
// `map.blocks_in_range` and add support for `reversed_blocks_in_range`. I haven't evaluated in
|
||||
// depth whether this will work.
|
||||
//
|
||||
// Before thinking of that, implementation plan was to:
|
||||
//
|
||||
// * For `Direction::Prev`, use `reversed_excerpts_at` to iterate over the excerpts in reverse
|
||||
// order.
|
||||
//
|
||||
// * For `Direction::Next`, use `excerpts_at` to iterate over the excerpts.
|
||||
//
|
||||
// * Find boundaries by checking when `buffer_id` changes similar to the block_map logic
|
||||
// [here](https://github.com/zed-industries/zed/blob/e5b61949148cd87e08ae38e80949bce9b4ede9f7/crates/editor/src/display_map/block_map.rs#L845).
|
||||
//
|
||||
// Another alternative might be to use `excerpt_before` and `excerpt_after` methods to walk the
|
||||
// excerpts.
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Scans for a boundary preceding the given start point `from` until a boundary is found,
|
||||
/// indicated by the given predicate returning true.
|
||||
/// The predicate is called with the character to the left and right of the candidate boundary location.
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::{
|
||||
git_panel_settings::GitPanelSettings, git_status_icon, repository_selector::RepositorySelector,
|
||||
};
|
||||
use crate::{picker_prompt, project_diff, ProjectDiff};
|
||||
use collections::HashMap;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::commit_tooltip::CommitTooltip;
|
||||
use editor::{
|
||||
@@ -29,7 +30,7 @@ use settings::Settings as _;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use std::{collections::HashSet, sync::Arc, time::Duration, usize};
|
||||
use std::{collections::HashSet, path::PathBuf, sync::Arc, time::Duration, usize};
|
||||
use strum::{IntoEnumIterator, VariantNames};
|
||||
use time::OffsetDateTime;
|
||||
use ui::{
|
||||
@@ -154,6 +155,8 @@ impl GitListEntry {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct GitStatusEntry {
|
||||
pub(crate) depth: usize,
|
||||
pub(crate) display_name: String,
|
||||
pub(crate) repo_path: RepoPath,
|
||||
pub(crate) status: FileStatus,
|
||||
pub(crate) is_staged: Option<bool>,
|
||||
@@ -187,6 +190,7 @@ pub struct GitPanel {
|
||||
current_modifiers: Modifiers,
|
||||
add_coauthors: bool,
|
||||
entries: Vec<GitListEntry>,
|
||||
entries_by_path: collections::HashMap<RepoPath, usize>,
|
||||
focus_handle: FocusHandle,
|
||||
fs: Arc<dyn Fs>,
|
||||
hide_scrollbar_task: Option<Task<()>>,
|
||||
@@ -311,6 +315,7 @@ impl GitPanel {
|
||||
current_modifiers: window.modifiers(),
|
||||
add_coauthors: true,
|
||||
entries: Vec::new(),
|
||||
entries_by_path: HashMap::default(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
fs,
|
||||
hide_scrollbar_task: None,
|
||||
@@ -339,80 +344,6 @@ impl GitPanel {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn entry_by_path(&self, path: &RepoPath) -> Option<usize> {
|
||||
fn binary_search<F>(mut low: usize, mut high: usize, is_target: F) -> Option<usize>
|
||||
where
|
||||
F: Fn(usize) -> std::cmp::Ordering,
|
||||
{
|
||||
while low < high {
|
||||
let mid = low + (high - low) / 2;
|
||||
match is_target(mid) {
|
||||
std::cmp::Ordering::Equal => return Some(mid),
|
||||
std::cmp::Ordering::Less => low = mid + 1,
|
||||
std::cmp::Ordering::Greater => high = mid,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
if self.conflicted_count > 0 {
|
||||
let conflicted_start = 1;
|
||||
if let Some(ix) = binary_search(
|
||||
conflicted_start,
|
||||
conflicted_start + self.conflicted_count,
|
||||
|ix| {
|
||||
self.entries[ix]
|
||||
.status_entry()
|
||||
.unwrap()
|
||||
.repo_path
|
||||
.cmp(&path)
|
||||
},
|
||||
) {
|
||||
return Some(ix);
|
||||
}
|
||||
}
|
||||
if self.tracked_count > 0 {
|
||||
let tracked_start = if self.conflicted_count > 0 {
|
||||
1 + self.conflicted_count
|
||||
} else {
|
||||
0
|
||||
} + 1;
|
||||
if let Some(ix) =
|
||||
binary_search(tracked_start, tracked_start + self.tracked_count, |ix| {
|
||||
self.entries[ix]
|
||||
.status_entry()
|
||||
.unwrap()
|
||||
.repo_path
|
||||
.cmp(&path)
|
||||
})
|
||||
{
|
||||
return Some(ix);
|
||||
}
|
||||
}
|
||||
if self.new_count > 0 {
|
||||
let untracked_start = if self.conflicted_count > 0 {
|
||||
1 + self.conflicted_count
|
||||
} else {
|
||||
0
|
||||
} + if self.tracked_count > 0 {
|
||||
1 + self.tracked_count
|
||||
} else {
|
||||
0
|
||||
} + 1;
|
||||
if let Some(ix) =
|
||||
binary_search(untracked_start, untracked_start + self.new_count, |ix| {
|
||||
self.entries[ix]
|
||||
.status_entry()
|
||||
.unwrap()
|
||||
.repo_path
|
||||
.cmp(&path)
|
||||
})
|
||||
{
|
||||
return Some(ix);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn select_entry_by_path(
|
||||
&mut self,
|
||||
path: ProjectPath,
|
||||
@@ -425,10 +356,10 @@ impl GitPanel {
|
||||
let Some(repo_path) = git_repo.read(cx).project_path_to_repo_path(&path) else {
|
||||
return;
|
||||
};
|
||||
let Some(ix) = self.entry_by_path(&repo_path) else {
|
||||
let Some(ix) = self.entries_by_path.get(&repo_path) else {
|
||||
return;
|
||||
};
|
||||
self.selected_entry = Some(ix);
|
||||
self.selected_entry = Some(*ix);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -552,6 +483,31 @@ impl GitPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn calculate_depth_and_difference(
|
||||
repo_path: &RepoPath,
|
||||
visible_entries: &HashSet<RepoPath>,
|
||||
) -> (usize, usize) {
|
||||
let ancestors = repo_path.ancestors().skip(1);
|
||||
for ancestor in ancestors {
|
||||
if let Some(parent_entry) = visible_entries.get(ancestor) {
|
||||
let entry_component_count = repo_path.components().count();
|
||||
let parent_component_count = parent_entry.components().count();
|
||||
|
||||
let difference = entry_component_count - parent_component_count;
|
||||
|
||||
let parent_depth = parent_entry
|
||||
.ancestors()
|
||||
.skip(1) // Skip the parent itself
|
||||
.filter(|ancestor| visible_entries.contains(*ancestor))
|
||||
.count();
|
||||
|
||||
return (parent_depth + 1, difference);
|
||||
}
|
||||
}
|
||||
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
fn scroll_to_selected_entry(&mut self, cx: &mut Context<Self>) {
|
||||
if let Some(selected_entry) = self.selected_entry {
|
||||
self.scroll_handle
|
||||
@@ -1592,6 +1548,7 @@ impl GitPanel {
|
||||
|
||||
fn update_visible_entries(&mut self, cx: &mut Context<Self>) {
|
||||
self.entries.clear();
|
||||
self.entries_by_path.clear();
|
||||
let mut changed_entries = Vec::new();
|
||||
let mut new_entries = Vec::new();
|
||||
let mut conflict_entries = Vec::new();
|
||||
@@ -1604,9 +1561,13 @@ impl GitPanel {
|
||||
|
||||
// First pass - collect all paths
|
||||
let repo = repo.read(cx);
|
||||
let path_set = HashSet::from_iter(repo.status().map(|entry| entry.repo_path));
|
||||
|
||||
// Second pass - create entries with proper depth calculation
|
||||
for entry in repo.status() {
|
||||
let (depth, difference) =
|
||||
Self::calculate_depth_and_difference(&entry.repo_path, &path_set);
|
||||
|
||||
let is_conflict = repo.has_conflict(&entry.repo_path);
|
||||
let is_new = entry.status.is_created();
|
||||
let is_staged = entry.status.is_staged();
|
||||
@@ -1619,7 +1580,28 @@ impl GitPanel {
|
||||
continue;
|
||||
}
|
||||
|
||||
let display_name = if difference > 1 {
|
||||
// Show partial path for deeply nested files
|
||||
entry
|
||||
.repo_path
|
||||
.as_ref()
|
||||
.iter()
|
||||
.skip(entry.repo_path.components().count() - difference)
|
||||
.collect::<PathBuf>()
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
} else {
|
||||
// Just show filename
|
||||
entry
|
||||
.repo_path
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().into_owned())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let entry = GitStatusEntry {
|
||||
depth,
|
||||
display_name,
|
||||
repo_path: entry.repo_path.clone(),
|
||||
status: entry.status,
|
||||
is_staged,
|
||||
@@ -1634,6 +1616,11 @@ impl GitPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Sort entries by path to maintain consistent order
|
||||
conflict_entries.sort_by(|a, b| a.repo_path.cmp(&b.repo_path));
|
||||
changed_entries.sort_by(|a, b| a.repo_path.cmp(&b.repo_path));
|
||||
new_entries.sort_by(|a, b| a.repo_path.cmp(&b.repo_path));
|
||||
|
||||
if conflict_entries.len() > 0 {
|
||||
self.entries.push(GitListEntry::Header(GitHeaderEntry {
|
||||
header: Section::Conflict,
|
||||
@@ -1663,6 +1650,12 @@ impl GitPanel {
|
||||
.extend(new_entries.into_iter().map(GitListEntry::GitStatusEntry));
|
||||
}
|
||||
|
||||
for (ix, entry) in self.entries.iter().enumerate() {
|
||||
if let Some(status_entry) = entry.status_entry() {
|
||||
self.entries_by_path
|
||||
.insert(status_entry.repo_path.clone(), ix);
|
||||
}
|
||||
}
|
||||
self.update_counts(repo);
|
||||
|
||||
self.select_first_entry_if_none(cx);
|
||||
@@ -2218,8 +2211,8 @@ impl GitPanel {
|
||||
) -> Option<AnyElement> {
|
||||
let repo = self.active_repository.as_ref()?.read(cx);
|
||||
let repo_path = repo.worktree_id_path_to_repo_path(file.worktree_id(cx), file.path())?;
|
||||
let ix = self.entry_by_path(&repo_path)?;
|
||||
let entry = self.entries.get(ix)?;
|
||||
let ix = self.entries_by_path.get(&repo_path)?;
|
||||
let entry = self.entries.get(*ix)?;
|
||||
|
||||
let is_staged = self.entry_is_staged(entry.status_entry()?);
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ test-support = []
|
||||
anthropic = { workspace = true, features = ["schemars"] }
|
||||
anyhow.workspace = true
|
||||
base64.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
futures.workspace = true
|
||||
google_ai = { workspace = true, features = ["schemars"] }
|
||||
@@ -35,7 +34,6 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
smol.workspace = true
|
||||
strum.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
thiserror.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
@@ -3,17 +3,20 @@ mod rate_limiter;
|
||||
mod registry;
|
||||
mod request;
|
||||
mod role;
|
||||
mod telemetry;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod fake_provider;
|
||||
|
||||
use anyhow::Result;
|
||||
use client::Client;
|
||||
use futures::FutureExt;
|
||||
use futures::{future::BoxFuture, stream::BoxStream, StreamExt, TryStreamExt as _};
|
||||
use gpui::{AnyElement, AnyView, App, AsyncApp, SharedString, Task, Window};
|
||||
pub use model::*;
|
||||
use proto::Plan;
|
||||
pub use rate_limiter::*;
|
||||
pub use registry::*;
|
||||
pub use request::*;
|
||||
pub use role::*;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
@@ -21,18 +24,10 @@ use std::{future::Future, sync::Arc};
|
||||
use thiserror::Error;
|
||||
use ui::IconName;
|
||||
|
||||
pub use crate::model::*;
|
||||
pub use crate::rate_limiter::*;
|
||||
pub use crate::registry::*;
|
||||
pub use crate::request::*;
|
||||
pub use crate::role::*;
|
||||
pub use crate::telemetry::*;
|
||||
|
||||
pub const ZED_CLOUD_PROVIDER_ID: &str = "zed.dev";
|
||||
|
||||
pub fn init(client: Arc<Client>, cx: &mut App) {
|
||||
pub fn init(cx: &mut App) {
|
||||
registry::init(cx);
|
||||
RefreshLlmTokenListener::register(client.clone(), cx);
|
||||
}
|
||||
|
||||
/// The availability of a [`LanguageModel`].
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use client::Client;
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, ReadGlobal as _,
|
||||
};
|
||||
use proto::{Plan, TypedEnvelope};
|
||||
use proto::Plan;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol::lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
|
||||
use strum::EnumIter;
|
||||
use thiserror::Error;
|
||||
use ui::IconName;
|
||||
|
||||
use crate::LanguageModelAvailability;
|
||||
@@ -112,92 +102,3 @@ impl CloudModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct PaymentRequiredError;
|
||||
|
||||
impl fmt::Display for PaymentRequiredError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Payment required to use this language model. Please upgrade your account."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct MaxMonthlySpendReachedError;
|
||||
|
||||
impl fmt::Display for MaxMonthlySpendReachedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Maximum spending limit reached for this month. For more usage, increase your spending limit."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct LlmApiToken(Arc<RwLock<Option<String>>>);
|
||||
|
||||
impl LlmApiToken {
|
||||
pub async fn acquire(&self, client: &Arc<Client>) -> Result<String> {
|
||||
let lock = self.0.upgradable_read().await;
|
||||
if let Some(token) = lock.as_ref() {
|
||||
Ok(token.to_string())
|
||||
} else {
|
||||
Self::fetch(RwLockUpgradableReadGuard::upgrade(lock).await, client).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn refresh(&self, client: &Arc<Client>) -> Result<String> {
|
||||
Self::fetch(self.0.write().await, client).await
|
||||
}
|
||||
|
||||
async fn fetch<'a>(
|
||||
mut lock: RwLockWriteGuard<'a, Option<String>>,
|
||||
client: &Arc<Client>,
|
||||
) -> Result<String> {
|
||||
let response = client.request(proto::GetLlmToken {}).await?;
|
||||
*lock = Some(response.token.clone());
|
||||
Ok(response.token.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct GlobalRefreshLlmTokenListener(Entity<RefreshLlmTokenListener>);
|
||||
|
||||
impl Global for GlobalRefreshLlmTokenListener {}
|
||||
|
||||
pub struct RefreshLlmTokenEvent;
|
||||
|
||||
pub struct RefreshLlmTokenListener {
|
||||
_llm_token_subscription: client::Subscription,
|
||||
}
|
||||
|
||||
impl EventEmitter<RefreshLlmTokenEvent> for RefreshLlmTokenListener {}
|
||||
|
||||
impl RefreshLlmTokenListener {
|
||||
pub fn register(client: Arc<Client>, cx: &mut App) {
|
||||
let listener = cx.new(|cx| RefreshLlmTokenListener::new(client, cx));
|
||||
cx.set_global(GlobalRefreshLlmTokenListener(listener));
|
||||
}
|
||||
|
||||
pub fn global(cx: &App) -> Entity<Self> {
|
||||
GlobalRefreshLlmTokenListener::global(cx).0.clone()
|
||||
}
|
||||
|
||||
fn new(client: Arc<Client>, cx: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
_llm_token_subscription: client
|
||||
.add_message_handler(cx.weak_entity(), Self::handle_refresh_llm_token),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_refresh_llm_token(
|
||||
this: Entity<Self>,
|
||||
_: TypedEnvelope<proto::RefreshLlmToken>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |_this, cx| cx.emit(RefreshLlmTokenEvent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,6 @@ path = "src/language_models.rs"
|
||||
[dependencies]
|
||||
anthropic = { workspace = true, features = ["schemars"] }
|
||||
anyhow.workspace = true
|
||||
aws-config = { workspace = true, features = ["behavior-version-latest"] }
|
||||
aws-credential-types = { workspace = true, features = ["hardcoded-credentials"] }
|
||||
aws_http_client.workspace = true
|
||||
bedrock.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
credentials_provider.workspace = true
|
||||
@@ -29,7 +25,6 @@ fs.workspace = true
|
||||
futures.workspace = true
|
||||
google_ai = { workspace = true, features = ["schemars"] }
|
||||
gpui.workspace = true
|
||||
gpui_tokio.workspace = true
|
||||
http_client.workspace = true
|
||||
language_model.workspace = true
|
||||
lmstudio = { workspace = true, features = ["schemars"] }
|
||||
@@ -45,9 +40,10 @@ serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
strum.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
theme.workspace = true
|
||||
thiserror.workspace = true
|
||||
tiktoken-rs.workspace = true
|
||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@ use gpui::{App, Context, Entity};
|
||||
use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
|
||||
use provider::deepseek::DeepSeekLanguageModelProvider;
|
||||
|
||||
mod logging;
|
||||
pub mod provider;
|
||||
mod settings;
|
||||
|
||||
use crate::provider::anthropic::AnthropicLanguageModelProvider;
|
||||
use crate::provider::bedrock::BedrockLanguageModelProvider;
|
||||
use crate::provider::cloud::CloudLanguageModelProvider;
|
||||
pub use crate::provider::cloud::LlmApiToken;
|
||||
pub use crate::provider::cloud::RefreshLlmTokenListener;
|
||||
use crate::provider::copilot_chat::CopilotChatLanguageModelProvider;
|
||||
use crate::provider::google::GoogleLanguageModelProvider;
|
||||
use crate::provider::lmstudio::LmStudioLanguageModelProvider;
|
||||
@@ -19,6 +21,7 @@ use crate::provider::mistral::MistralLanguageModelProvider;
|
||||
use crate::provider::ollama::OllamaLanguageModelProvider;
|
||||
use crate::provider::open_ai::OpenAiLanguageModelProvider;
|
||||
pub use crate::settings::*;
|
||||
pub use logging::report_assistant_event;
|
||||
|
||||
pub fn init(user_store: Entity<UserStore>, client: Arc<Client>, fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
crate::settings::init(fs, cx);
|
||||
@@ -36,6 +39,8 @@ fn register_language_model_providers(
|
||||
) {
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
|
||||
RefreshLlmTokenListener::register(client.clone(), cx);
|
||||
|
||||
registry.register_provider(
|
||||
AnthropicLanguageModelProvider::new(client.http_client(), cx),
|
||||
cx,
|
||||
@@ -64,10 +69,6 @@ fn register_language_model_providers(
|
||||
MistralLanguageModelProvider::new(client.http_client(), cx),
|
||||
cx,
|
||||
);
|
||||
registry.register_provider(
|
||||
BedrockLanguageModelProvider::new(client.http_client(), cx),
|
||||
cx,
|
||||
);
|
||||
registry.register_provider(CopilotChatLanguageModelProvider::new(cx), cx);
|
||||
|
||||
cx.observe_flag::<feature_flags::LanguageModels, _>(move |enabled, cx| {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
use util::ResultExt;
|
||||
|
||||
pub const ANTHROPIC_PROVIDER_ID: &str = "anthropic";
|
||||
use crate::provider::anthropic::PROVIDER_ID as ANTHROPIC_PROVIDER_ID;
|
||||
|
||||
pub fn report_assistant_event(
|
||||
event: AssistantEvent,
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod anthropic;
|
||||
pub mod bedrock;
|
||||
pub mod cloud;
|
||||
pub mod copilot_chat;
|
||||
pub mod deepseek;
|
||||
|
||||
@@ -27,7 +27,7 @@ use theme::ThemeSettings;
|
||||
use ui::{prelude::*, Icon, IconName, Tooltip};
|
||||
use util::{maybe, ResultExt};
|
||||
|
||||
const PROVIDER_ID: &str = language_model::ANTHROPIC_PROVIDER_ID;
|
||||
pub const PROVIDER_ID: &str = "anthropic";
|
||||
const PROVIDER_NAME: &str = "Anthropic";
|
||||
|
||||
#[derive(Default, Clone, Debug, PartialEq)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,10 @@ use futures::{
|
||||
future::BoxFuture, stream::BoxStream, AsyncBufReadExt, FutureExt, Stream, StreamExt,
|
||||
TryStreamExt as _,
|
||||
};
|
||||
use gpui::{AnyElement, AnyView, App, AsyncApp, Context, Entity, Subscription, Task};
|
||||
use gpui::{
|
||||
AnyElement, AnyView, App, AsyncApp, Context, Entity, EventEmitter, Global, ReadGlobal,
|
||||
Subscription, Task,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClient, Method, Response, StatusCode};
|
||||
use language_model::{
|
||||
AuthenticateError, CloudModel, LanguageModel, LanguageModelCacheConfiguration, LanguageModelId,
|
||||
@@ -19,19 +22,24 @@ use language_model::{
|
||||
ZED_CLOUD_PROVIDER_ID,
|
||||
};
|
||||
use language_model::{
|
||||
LanguageModelAvailability, LanguageModelCompletionEvent, LanguageModelProvider, LlmApiToken,
|
||||
MaxMonthlySpendReachedError, PaymentRequiredError, RefreshLlmTokenListener,
|
||||
LanguageModelAvailability, LanguageModelCompletionEvent, LanguageModelProvider,
|
||||
};
|
||||
use proto::TypedEnvelope;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use smol::io::{AsyncReadExt, BufReader};
|
||||
use smol::{
|
||||
io::{AsyncReadExt, BufReader},
|
||||
lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard},
|
||||
};
|
||||
use std::fmt;
|
||||
use std::{
|
||||
future,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use thiserror::Error;
|
||||
use ui::{prelude::*, TintColor};
|
||||
|
||||
use crate::provider::anthropic::{
|
||||
@@ -93,6 +101,44 @@ pub struct AvailableModel {
|
||||
pub extra_beta_headers: Vec<String>,
|
||||
}
|
||||
|
||||
struct GlobalRefreshLlmTokenListener(Entity<RefreshLlmTokenListener>);
|
||||
|
||||
impl Global for GlobalRefreshLlmTokenListener {}
|
||||
|
||||
pub struct RefreshLlmTokenEvent;
|
||||
|
||||
pub struct RefreshLlmTokenListener {
|
||||
_llm_token_subscription: client::Subscription,
|
||||
}
|
||||
|
||||
impl EventEmitter<RefreshLlmTokenEvent> for RefreshLlmTokenListener {}
|
||||
|
||||
impl RefreshLlmTokenListener {
|
||||
pub fn register(client: Arc<Client>, cx: &mut App) {
|
||||
let listener = cx.new(|cx| RefreshLlmTokenListener::new(client, cx));
|
||||
cx.set_global(GlobalRefreshLlmTokenListener(listener));
|
||||
}
|
||||
|
||||
pub fn global(cx: &App) -> Entity<Self> {
|
||||
GlobalRefreshLlmTokenListener::global(cx).0.clone()
|
||||
}
|
||||
|
||||
fn new(client: Arc<Client>, cx: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
_llm_token_subscription: client
|
||||
.add_message_handler(cx.weak_entity(), Self::handle_refresh_llm_token),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_refresh_llm_token(
|
||||
this: Entity<Self>,
|
||||
_: TypedEnvelope<proto::RefreshLlmToken>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |_this, cx| cx.emit(RefreshLlmTokenEvent))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CloudLanguageModelProvider {
|
||||
client: Arc<Client>,
|
||||
state: gpui::Entity<State>,
|
||||
@@ -429,6 +475,33 @@ pub struct CloudLanguageModel {
|
||||
request_limiter: RateLimiter,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct LlmApiToken(Arc<RwLock<Option<String>>>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct PaymentRequiredError;
|
||||
|
||||
impl fmt::Display for PaymentRequiredError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Payment required to use this language model. Please upgrade your account."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct MaxMonthlySpendReachedError;
|
||||
|
||||
impl fmt::Display for MaxMonthlySpendReachedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Maximum spending limit reached for this month. For more usage, increase your spending limit."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CloudLanguageModel {
|
||||
async fn perform_llm_completion(
|
||||
client: Arc<Client>,
|
||||
@@ -774,6 +847,30 @@ fn response_lines<T: DeserializeOwned>(
|
||||
)
|
||||
}
|
||||
|
||||
impl LlmApiToken {
|
||||
pub async fn acquire(&self, client: &Arc<Client>) -> Result<String> {
|
||||
let lock = self.0.upgradable_read().await;
|
||||
if let Some(token) = lock.as_ref() {
|
||||
Ok(token.to_string())
|
||||
} else {
|
||||
Self::fetch(RwLockUpgradableReadGuard::upgrade(lock).await, client).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn refresh(&self, client: &Arc<Client>) -> Result<String> {
|
||||
Self::fetch(self.0.write().await, client).await
|
||||
}
|
||||
|
||||
async fn fetch<'a>(
|
||||
mut lock: RwLockWriteGuard<'a, Option<String>>,
|
||||
client: &Arc<Client>,
|
||||
) -> Result<String> {
|
||||
let response = client.request(proto::GetLlmToken {}).await?;
|
||||
*lock = Some(response.token.clone());
|
||||
Ok(response.token.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct ConfigurationView {
|
||||
state: gpui::Entity<State>,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use settings::{update_settings_file, Settings, SettingsSources};
|
||||
use crate::provider::{
|
||||
self,
|
||||
anthropic::AnthropicSettings,
|
||||
bedrock::AmazonBedrockSettings,
|
||||
cloud::{self, ZedDotDevSettings},
|
||||
copilot_chat::CopilotChatSettings,
|
||||
deepseek::DeepSeekSettings,
|
||||
@@ -58,7 +57,6 @@ pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
#[derive(Default)]
|
||||
pub struct AllLanguageModelSettings {
|
||||
pub anthropic: AnthropicSettings,
|
||||
pub bedrock: AmazonBedrockSettings,
|
||||
pub ollama: OllamaSettings,
|
||||
pub openai: OpenAiSettings,
|
||||
pub zed_dot_dev: ZedDotDevSettings,
|
||||
|
||||
@@ -4968,6 +4968,23 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn excerpts_at<T>(&self, position: T) -> impl Iterator<Item = MultiBufferExcerpt<'_>>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn reversed_excerpts_at<T>(
|
||||
&self,
|
||||
position: T,
|
||||
) -> impl Iterator<Item = MultiBufferExcerpt<'_>>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn excerpt_before(&self, id: ExcerptId) -> Option<MultiBufferExcerpt<'_>> {
|
||||
let start_locator = self.excerpt_locator_for_id(id);
|
||||
let mut excerpts = self
|
||||
|
||||
@@ -18,7 +18,7 @@ use file_icons::FileIcons;
|
||||
use git::status::GitSummary;
|
||||
use gpui::{
|
||||
actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action,
|
||||
AnyElement, App, ArcCow, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div,
|
||||
AnyElement, App, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div,
|
||||
DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, Hsla,
|
||||
InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, ScrollStrategy, Stateful,
|
||||
@@ -2700,7 +2700,7 @@ impl ProjectPanel {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let path = ArcCow::Borrowed(Path::new(path_name));
|
||||
let path = Arc::from(Path::new(path_name));
|
||||
let depth = 0;
|
||||
(depth, path)
|
||||
} else if entry.is_file() {
|
||||
@@ -2712,7 +2712,7 @@ impl ProjectPanel {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let path = ArcCow::Borrowed(Path::new(path_name));
|
||||
let path = Arc::from(Path::new(path_name));
|
||||
let depth = entry.path.ancestors().count() - 1;
|
||||
(depth, path)
|
||||
} else {
|
||||
@@ -2732,11 +2732,11 @@ impl ProjectPanel {
|
||||
.ok()
|
||||
.and_then(|suffix| {
|
||||
let full_path = Path::new(root_folded_entry.file_name()?);
|
||||
Some(ArcCow::Owned(Arc::<Path>::from(full_path.join(suffix))))
|
||||
Some(Arc::<Path>::from(full_path.join(suffix)))
|
||||
})
|
||||
})
|
||||
.or_else(|| entry.path.file_name().map(Path::new).map(ArcCow::Borrowed))
|
||||
.unwrap_or_else(|| ArcCow::Owned(entry.path.clone()));
|
||||
.or_else(|| entry.path.file_name().map(Path::new).map(Arc::from))
|
||||
.unwrap_or_else(|| entry.path.clone());
|
||||
let depth = path.components().count();
|
||||
(depth, path)
|
||||
};
|
||||
|
||||
@@ -125,7 +125,6 @@ impl IconSize {
|
||||
pub enum IconName {
|
||||
Ai,
|
||||
AiAnthropic,
|
||||
AiBedrock,
|
||||
AiAnthropicHosted,
|
||||
AiDeepSeek,
|
||||
AiGoogle,
|
||||
|
||||
@@ -303,9 +303,6 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
||||
Vim::action(editor, cx, |vim, _: &Quotes, window, cx| {
|
||||
vim.object(Object::Quotes, window, cx)
|
||||
});
|
||||
Vim::action(editor, cx, |vim, _: &BackQuotes, window, cx| {
|
||||
vim.object(Object::BackQuotes, window, cx)
|
||||
});
|
||||
Vim::action(editor, cx, |vim, _: &AnyQuotes, window, cx| {
|
||||
vim.object(Object::AnyQuotes, window, cx)
|
||||
});
|
||||
|
||||
@@ -206,19 +206,15 @@ pub struct RepositoryEntry {
|
||||
pub current_merge_conflicts: TreeSet<RepoPath>,
|
||||
}
|
||||
|
||||
impl Deref for RepositoryEntry {
|
||||
type Target = WorkDirectory;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.work_directory
|
||||
}
|
||||
}
|
||||
|
||||
impl RepositoryEntry {
|
||||
pub fn relativize(&self, path: &Path) -> Result<RepoPath> {
|
||||
self.work_directory.relativize(path)
|
||||
}
|
||||
|
||||
pub fn unrelativize(&self, path: &RepoPath) -> Option<Arc<Path>> {
|
||||
self.work_directory.unrelativize(path)
|
||||
}
|
||||
|
||||
pub fn directory_contains(&self, path: impl AsRef<Path>) -> bool {
|
||||
self.work_directory.directory_contains(path)
|
||||
}
|
||||
|
||||
pub fn branch(&self) -> Option<&Branch> {
|
||||
self.current_branch.as_ref()
|
||||
}
|
||||
@@ -2838,7 +2834,7 @@ impl Snapshot {
|
||||
pub fn repository_for_path(&self, path: &Path) -> Option<&RepositoryEntry> {
|
||||
self.repositories
|
||||
.iter()
|
||||
.filter(|repo| repo.directory_contains(path))
|
||||
.filter(|repo| repo.work_directory.directory_contains(path))
|
||||
.last()
|
||||
}
|
||||
|
||||
@@ -6027,13 +6023,7 @@ impl<'a> GitTraversal<'a> {
|
||||
};
|
||||
|
||||
// Update our state if we changed repositories.
|
||||
if reset
|
||||
|| self
|
||||
.repo_location
|
||||
.as_ref()
|
||||
.map(|(prev_repo, _)| &prev_repo.work_directory)
|
||||
!= Some(&repo.work_directory)
|
||||
{
|
||||
if reset || self.repo_location.as_ref().map(|(prev_repo, _)| prev_repo) != Some(&repo) {
|
||||
self.repo_location = Some((repo, repo.statuses_by_path.cursor::<PathProgress>(&())));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,5 @@
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -436,7 +436,7 @@ fn main() {
|
||||
cx,
|
||||
);
|
||||
supermaven::init(app_state.client.clone(), cx);
|
||||
language_model::init(app_state.client.clone(), cx);
|
||||
language_model::init(cx);
|
||||
language_models::init(
|
||||
app_state.user_store.clone(),
|
||||
app_state.client.clone(),
|
||||
|
||||
@@ -4214,7 +4214,6 @@ mod tests {
|
||||
|
||||
app_state.languages.add(markdown_language());
|
||||
|
||||
gpui_tokio::init(cx);
|
||||
vim_mode_setting::init(cx);
|
||||
theme::init(theme::LoadThemes::JustBase, cx);
|
||||
audio::init((), cx);
|
||||
@@ -4238,7 +4237,7 @@ mod tests {
|
||||
cx,
|
||||
);
|
||||
image_viewer::init(cx);
|
||||
language_model::init(app_state.client.clone(), cx);
|
||||
language_model::init(cx);
|
||||
language_models::init(
|
||||
app_state.user_store.clone(),
|
||||
app_state.client.clone(),
|
||||
|
||||
@@ -33,7 +33,7 @@ http_client.workspace = true
|
||||
indoc.workspace = true
|
||||
inline_completion.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
menu.workspace = true
|
||||
migrator.workspace = true
|
||||
|
||||
@@ -31,7 +31,7 @@ use input_excerpt::excerpt_for_cursor_position;
|
||||
use language::{
|
||||
text_diff, Anchor, Buffer, BufferSnapshot, EditPreview, OffsetRangeExt, ToOffset, ToPoint,
|
||||
};
|
||||
use language_model::{LlmApiToken, RefreshLlmTokenListener};
|
||||
use language_models::LlmApiToken;
|
||||
use postage::watch;
|
||||
use project::Project;
|
||||
use release_channel::AppVersion;
|
||||
@@ -244,7 +244,7 @@ impl Zeta {
|
||||
user_store: Entity<UserStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let refresh_llm_token_listener = RefreshLlmTokenListener::global(cx);
|
||||
let refresh_llm_token_listener = language_models::RefreshLlmTokenListener::global(cx);
|
||||
|
||||
let data_collection_choice = Self::load_data_collection_choices();
|
||||
let data_collection_choice = cx.new(|_| data_collection_choice);
|
||||
@@ -1649,6 +1649,7 @@ mod tests {
|
||||
use http_client::FakeHttpClient;
|
||||
use indoc::indoc;
|
||||
use language::Point;
|
||||
use language_models::RefreshLlmTokenListener;
|
||||
use rpc::proto;
|
||||
use settings::SettingsStore;
|
||||
|
||||
|
||||
@@ -6,17 +6,15 @@ CARGO_ABOUT_VERSION="0.6.6"
|
||||
OUTPUT_FILE="${1:-$(pwd)/assets/licenses.md}"
|
||||
TEMPLATE_FILE="script/licenses/template.md.hbs"
|
||||
|
||||
echo -n "" > "$OUTPUT_FILE"
|
||||
> $OUTPUT_FILE
|
||||
|
||||
{
|
||||
echo -e "# ###### THEME LICENSES ######\n"
|
||||
cat assets/themes/LICENSES
|
||||
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
|
||||
cat assets/themes/LICENSES >> $OUTPUT_FILE
|
||||
|
||||
echo -e "\n# ###### ICON LICENSES ######\n"
|
||||
cat assets/icons/LICENSES
|
||||
echo -e "\n# ###### ICON LICENSES ######\n" >> $OUTPUT_FILE
|
||||
cat assets/icons/LICENSES >> $OUTPUT_FILE
|
||||
|
||||
echo -e "\n# ###### CODE LICENSES ######\n"
|
||||
} >> "$OUTPUT_FILE"
|
||||
echo -e "\n# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE
|
||||
|
||||
if ! cargo install --list | grep "cargo-about v$CARGO_ABOUT_VERSION" > /dev/null; then
|
||||
echo "Installing cargo-about@$CARGO_ABOUT_VERSION..."
|
||||
@@ -30,14 +28,14 @@ echo "Generating cargo licenses"
|
||||
cargo about generate \
|
||||
--fail \
|
||||
-c script/licenses/zed-licenses.toml \
|
||||
"$TEMPLATE_FILE" >> "$OUTPUT_FILE"
|
||||
"${TEMPLATE_FILE}" >> $OUTPUT_FILE
|
||||
|
||||
sed -i.bak 's/"/"/g' "$OUTPUT_FILE"
|
||||
sed -i.bak 's/'/'\''/g' "$OUTPUT_FILE" # The ` '\'' ` thing ends the string, appends a single quote, and re-opens the string
|
||||
sed -i.bak 's/=/=/g' "$OUTPUT_FILE"
|
||||
sed -i.bak 's/`/`/g' "$OUTPUT_FILE"
|
||||
sed -i.bak 's/</</g' "$OUTPUT_FILE"
|
||||
sed -i.bak 's/>/>/g' "$OUTPUT_FILE"
|
||||
sed -i.bak 's/"/"/g' $OUTPUT_FILE
|
||||
sed -i.bak 's/'/'\''/g' $OUTPUT_FILE # The ` '\'' ` thing ends the string, appends a single quote, and re-opens the string
|
||||
sed -i.bak 's/=/=/g' $OUTPUT_FILE
|
||||
sed -i.bak 's/`/`/g' $OUTPUT_FILE
|
||||
sed -i.bak 's/</</g' $OUTPUT_FILE
|
||||
sed -i.bak 's/>/>/g' $OUTPUT_FILE
|
||||
|
||||
rm -rf "${OUTPUT_FILE}.bak"
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ echo "Generating cargo licenses"
|
||||
cargo about generate \
|
||||
--fail \
|
||||
-c script/licenses/zed-licenses.toml \
|
||||
"$TEMPLATE_FILE" \
|
||||
script/licenses/template.csv.hbs \
|
||||
| awk 'NR==1{print;next} NF{print | "sort"}' \
|
||||
> "$OUTPUT_FILE"
|
||||
> $OUTPUT_FILE
|
||||
|
||||
echo "generate-licenses-csv completed. See $OUTPUT_FILE"
|
||||
|
||||
Reference in New Issue
Block a user