Compare commits

...

6 Commits

Author SHA1 Message Date
Mikayla
f67815df05 better documentation for the pixel format 2024-12-17 10:56:31 -08:00
Mikayla
f2ca21ae44 Add rendering of self screen capture to test app
Fix bugs in screen share stream configuration
2024-12-15 22:47:39 -08:00
Antonio Scandurra
01e5ac0a49 Maintain inline completion order, simplifying how we track pending completions (#21977)
Release Notes:

- N/A
2024-12-13 17:24:07 +01:00
Thorsten Ball
306f1c6838 zeta: Increase context lines to 32 (#21968)
Release Notes:

- N/A

Co-authored-by: Antonio <antonio@zed.dev>
2024-12-13 15:41:43 +01:00
Thorsten Ball
2f722e63a1 Highlight whitespace-only inline completions with background (#21954)
Noticed that whitespace-only insertions are really hard to make out, so
this changes it to make them visible by giving them a green background.

![screenshot-2024-12-13-10 49
09@2x](https://github.com/user-attachments/assets/10d83067-46f2-4cb5-97fa-0f44d254890d)


Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-12-13 13:40:34 +01:00
Jaagup Averin
6838b6203a python: Refine highlighting (#21389)
Fixes:
* Types in binary unions as per [PEP
604](https://peps.python.org/pep-0604/) not highlighted;
   * `except*` keyword not highlighted;
* Classes beginning with `_` not recognized as such, however `_` is a
valid first character for private classes; additionally the regex for
parsing constant/class names appeared inconsistent and incomplete so was
adjusted;
   * Builtin types such as `float`, `dict`, etc not recognized as types;
   * **Update:** decorators with arguments not recognized as decorators;
* **Update:** docstrings after type alias assignments not recognized as
docstrings;
* **Update:** `and/in/is/not/or/is not/not in` not capturable as
keywords;
* **Update:** decorators with "nesting" (@x.y.z) not recognized as
decorators;

Before:

![new_before](https://github.com/user-attachments/assets/6f05262e-be3b-41bf-aee6-26438c2bf254)

After:

![new_after](https://github.com/user-attachments/assets/408c481c-5eb9-40c9-8e18-52ebf5a121d3)

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-12-13 12:40:16 +01:00
20 changed files with 208 additions and 107 deletions

1
Cargo.lock generated
View File

@@ -16424,6 +16424,7 @@ name = "zeta"
version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec",
"call",
"client",
"clock",

View File

@@ -11,8 +11,8 @@ use futures::{
use fuzzy::StringMatchCandidate;
use gpui::{
actions, point, size, transparent_black, Action, AppContext, BackgroundExecutor, Bounds,
EventEmitter, Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
TitlebarOptions, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task, TextStyle, TitlebarOptions,
UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
};
use heed::{
types::{SerdeBincode, SerdeJson, Str},
@@ -928,10 +928,8 @@ impl PromptLibrary {
status: cx.theme().status().clone(),
inlay_hints_style:
editor::make_inlay_hints_style(cx),
suggestions_style: HighlightStyle {
color: Some(cx.theme().status().predictive),
..HighlightStyle::default()
},
inline_completion_styles:
editor::make_suggestion_styles(cx),
..EditorStyle::default()
},
)),

View File

@@ -42,10 +42,10 @@ serde_derive.workspace = true
settings.workspace = true
util.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
[target.'cfg(any())'.dependencies]
livekit_client_macos = { workspace = true }
[target.'cfg(not(target_os = "macos"))'.dependencies]
[target.'cfg(all())'.dependencies]
livekit_client = { workspace = true }
[dev-dependencies]

View File

@@ -1,13 +1,13 @@
pub mod call_settings;
#[cfg(target_os = "macos")]
#[cfg(any())]
mod macos;
#[cfg(target_os = "macos")]
#[cfg(any())]
pub use macos::*;
#[cfg(not(target_os = "macos"))]
#[cfg(all())]
mod cross_platform;
#[cfg(not(target_os = "macos"))]
#[cfg(all())]
pub use cross_platform::*;

View File

@@ -1441,7 +1441,7 @@ impl Room {
let sources = sources.await??;
let source = sources.first().ok_or_else(|| anyhow!("no display found"))?;
let (track, stream) = capture_local_video_track(&**source).await?;
let (track, stream) = capture_local_video_track(&**source, None).await?;
let publication = participant
.publish_track(

View File

@@ -2078,17 +2078,7 @@ async fn test_mute_deafen(
audio_tracks_playing: participant
.audio_tracks
.values()
.map({
#[cfg(target_os = "macos")]
{
|track| track.is_playing()
}
#[cfg(not(target_os = "macos"))]
{
|(track, _)| track.rtc_track().enabled()
}
})
.map(|(track, _)| track.rtc_track().enabled())
.collect(),
})
.collect::<Vec<_>>()

View File

@@ -535,10 +535,16 @@ pub(crate) struct Highlights<'a> {
pub styles: HighlightStyles,
}
#[derive(Clone, Copy, Debug)]
pub struct InlineCompletionStyles {
pub insertion: HighlightStyle,
pub whitespace: HighlightStyle,
}
#[derive(Default, Debug, Clone, Copy)]
pub struct HighlightStyles {
pub inlay_hint: Option<HighlightStyle>,
pub suggestion: Option<HighlightStyle>,
pub inline_completion: Option<InlineCompletionStyles>,
}
#[derive(Clone)]
@@ -859,7 +865,7 @@ impl DisplaySnapshot {
language_aware,
HighlightStyles {
inlay_hint: Some(editor_style.inlay_hints_style),
suggestion: Some(editor_style.suggestions_style),
inline_completion: Some(editor_style.inline_completion_styles),
},
)
.flat_map(|chunk| {

View File

@@ -62,9 +62,9 @@ impl Inlay {
}
}
pub fn suggestion<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
pub fn inline_completion<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
Self {
id: InlayId::Suggestion(id),
id: InlayId::InlineCompletion(id),
position,
text: text.into(),
}
@@ -346,7 +346,15 @@ impl<'a> Iterator for InlayChunks<'a> {
}
let mut highlight_style = match inlay.id {
InlayId::Suggestion(_) => self.highlight_styles.suggestion,
InlayId::InlineCompletion(_) => {
self.highlight_styles.inline_completion.map(|s| {
if inlay.text.chars().all(|c| c.is_whitespace()) {
s.whitespace
} else {
s.insertion
}
})
}
InlayId::Hint(_) => self.highlight_styles.inlay_hint,
};
let next_inlay_highlight_endpoint;
@@ -693,7 +701,7 @@ impl InlayMap {
let inlay_id = if i % 2 == 0 {
InlayId::Hint(post_inc(next_inlay_id))
} else {
InlayId::Suggestion(post_inc(next_inlay_id))
InlayId::InlineCompletion(post_inc(next_inlay_id))
};
log::info!(
"creating inlay {:?} at buffer offset {} with bias {:?} and text {:?}",
@@ -1389,7 +1397,7 @@ mod tests {
text: "|123|".into(),
},
Inlay {
id: InlayId::Suggestion(post_inc(&mut next_inlay_id)),
id: InlayId::InlineCompletion(post_inc(&mut next_inlay_id)),
position: buffer.read(cx).snapshot(cx).anchor_after(3),
text: "|456|".into(),
},
@@ -1605,7 +1613,7 @@ mod tests {
text: "|456|".into(),
},
Inlay {
id: InlayId::Suggestion(post_inc(&mut next_inlay_id)),
id: InlayId::InlineCompletion(post_inc(&mut next_inlay_id)),
position: buffer.read(cx).snapshot(cx).anchor_before(7),
text: "\n|567|\n".into(),
},

View File

@@ -259,14 +259,14 @@ pub fn render_parsed_markdown(
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum InlayId {
Suggestion(usize),
InlineCompletion(usize),
Hint(usize),
}
impl InlayId {
fn id(&self) -> usize {
match self {
Self::Suggestion(id) => *id,
Self::InlineCompletion(id) => *id,
Self::Hint(id) => *id,
}
}
@@ -405,7 +405,7 @@ pub struct EditorStyle {
pub syntax: Arc<SyntaxTheme>,
pub status: StatusColors,
pub inlay_hints_style: HighlightStyle,
pub suggestions_style: HighlightStyle,
pub inline_completion_styles: InlineCompletionStyles,
pub unnecessary_code_fade: f32,
}
@@ -422,7 +422,10 @@ impl Default for EditorStyle {
// style and retrieve them directly from the theme.
status: StatusColors::dark(),
inlay_hints_style: HighlightStyle::default(),
suggestions_style: HighlightStyle::default(),
inline_completion_styles: InlineCompletionStyles {
insertion: HighlightStyle::default(),
whitespace: HighlightStyle::default(),
},
unnecessary_code_fade: Default::default(),
}
}
@@ -440,6 +443,19 @@ pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
}
}
pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
InlineCompletionStyles {
insertion: HighlightStyle {
color: Some(cx.theme().status().predictive),
..HighlightStyle::default()
},
whitespace: HighlightStyle {
background_color: Some(cx.theme().status().created_background),
..HighlightStyle::default()
},
}
}
type CompletionId = usize;
enum InlineCompletion {
@@ -4735,7 +4751,7 @@ impl Editor {
{
let mut inlays = Vec::new();
for (range, new_text) in &edits {
let inlay = Inlay::suggestion(
let inlay = Inlay::inline_completion(
post_inc(&mut self.next_inlay_id),
range.start,
new_text.as_str(),
@@ -9901,10 +9917,9 @@ impl Editor {
font_weight: Some(FontWeight::BOLD),
..make_inlay_hints_style(cx)
},
suggestions_style: HighlightStyle {
color: Some(cx.theme().status().predictive),
..HighlightStyle::default()
},
inline_completion_styles: make_suggestion_styles(
cx,
),
..EditorStyle::default()
},
))
@@ -13905,10 +13920,7 @@ impl Render for Editor {
syntax: cx.theme().syntax().clone(),
status: cx.theme().status().clone(),
inlay_hints_style: make_inlay_hints_style(cx),
suggestions_style: HighlightStyle {
color: Some(cx.theme().status().predictive),
..HighlightStyle::default()
},
inline_completion_styles: make_suggestion_styles(cx),
unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
},
)

View File

@@ -841,12 +841,12 @@ mod tests {
.flat_map(|offset| {
[
Inlay {
id: InlayId::Suggestion(post_inc(&mut id)),
id: InlayId::InlineCompletion(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Left),
text: "test".into(),
},
Inlay {
id: InlayId::Suggestion(post_inc(&mut id)),
id: InlayId::InlineCompletion(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Right),
text: "test".into(),
},

View File

@@ -239,7 +239,7 @@ pub trait PlatformDisplay: Send + Sync + Debug {
/// A source of on-screen video content that can be captured.
pub trait ScreenCaptureSource {
/// Returns the video resolution of this source.
fn resolution(&self) -> Result<Size<Pixels>>;
fn resolution(&self) -> Size<DevicePixels>;
/// Start capture video from this source, invoking the given callback
/// with each frame.
@@ -253,6 +253,7 @@ pub trait ScreenCaptureSource {
pub trait ScreenCaptureStream {}
/// A frame of video captured from a screen.
#[derive(Clone)]
pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
/// An opaque identifier for a hardware display

View File

@@ -1,6 +1,6 @@
use crate::{
platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
px, size, Pixels, Size,
size, DevicePixels, Size,
};
use anyhow::{anyhow, Result};
use block::ConcreteBlock;
@@ -9,6 +9,10 @@ use cocoa::{
foundation::NSArray,
};
use core_foundation::base::TCFType;
use core_graphics::display::{
CGDirectDisplayID, CGDisplayCopyDisplayMode, CGDisplayModeGetPixelHeight,
CGDisplayModeGetPixelWidth, CGDisplayModeRelease,
};
use ctor::ctor;
use futures::channel::oneshot;
use media::core_media::{CMSampleBuffer, CMSampleBufferRef};
@@ -25,6 +29,7 @@ use std::{cell::RefCell, ffi::c_void, mem, ptr, rc::Rc};
#[derive(Clone)]
pub struct MacScreenCaptureSource {
sc_display: id,
size: Size<DevicePixels>,
}
pub struct MacScreenCaptureStream {
@@ -43,12 +48,8 @@ const FRAME_CALLBACK_IVAR: &str = "frame_callback";
const SCStreamOutputTypeScreen: NSInteger = 0;
impl ScreenCaptureSource for MacScreenCaptureSource {
fn resolution(&self) -> Result<Size<Pixels>> {
unsafe {
let width: i64 = msg_send![self.sc_display, width];
let height: i64 = msg_send![self.sc_display, height];
Ok(size(px(width as f32), px(height as f32)))
}
fn resolution(&self) -> Size<DevicePixels> {
self.size
}
fn stream(
@@ -61,13 +62,21 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
let configuration: id = msg_send![class!(SCStreamConfiguration), alloc];
let delegate: id = msg_send![DELEGATE_CLASS, alloc];
let output: id = msg_send![OUTPUT_CLASS, alloc];
let excluded_windows = NSArray::array(nil);
let filter: id = msg_send![filter, initWithDisplay:self.sc_display excludingWindows:excluded_windows];
let configuration: id = msg_send![configuration, init];
let delegate: id = msg_send![delegate, init];
let output: id = msg_send![output, init];
// ASCII for '420f': https://developer.apple.com/documentation/screencapturekit/scstreamconfiguration/pixelformat?language=objc
let format = u32::from_be_bytes([52u8, 50u8, 48u8, 102u8]);
let _: () = msg_send![configuration, setShowsCursor:YES];
let _: () = msg_send![configuration, setWidth:self.size.width];
let _: () = msg_send![configuration, setHeight:self.size.height];
let _: () = msg_send![configuration, setPixelFormat:format];
let _: () = msg_send![configuration, setQueueDepth:5i32];
output.as_mut().unwrap().set_ivar(
FRAME_CALLBACK_IVAR,
Box::into_raw(Box::new(frame_callback)) as *mut c_void,
@@ -94,6 +103,7 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
sc_stream: stream,
sc_stream_output: output,
};
Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>)
} else {
let message: id = msg_send![error, localizedDescription];
@@ -159,8 +169,16 @@ pub(crate) fn get_sources() -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptur
let mut result = Vec::new();
for i in 0..displays.count() {
let display = displays.objectAtIndex(i);
let display: id = msg_send![display, retain];
let display_id: CGDirectDisplayID = msg_send![display, displayID];
let display_mode_ref = CGDisplayCopyDisplayMode(display_id);
let width = CGDisplayModeGetPixelWidth(display_mode_ref);
let height = CGDisplayModeGetPixelHeight(display_mode_ref);
CGDisplayModeRelease(display_mode_ref);
let source = MacScreenCaptureSource {
sc_display: msg_send![display, retain],
sc_display: display,
size: size(DevicePixels(width as i32), DevicePixels(height as i32)),
};
result.push(Box::new(source) as Box<dyn ScreenCaptureSource>);
}

View File

@@ -1,7 +1,8 @@
use crate::{
px, size, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame, ScreenCaptureSource,
ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams,
size, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame,
ScreenCaptureSource, ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance,
WindowParams,
};
use anyhow::Result;
use collections::VecDeque;
@@ -46,8 +47,8 @@ pub struct TestScreenCaptureSource {}
pub struct TestScreenCaptureStream {}
impl ScreenCaptureSource for TestScreenCaptureSource {
fn resolution(&self) -> Result<crate::Size<crate::Pixels>> {
Ok(size(px(1.), px(1.)))
fn resolution(&self) -> crate::Size<crate::DevicePixels> {
size(DevicePixels(1), DevicePixels(1))
}
fn stream(

View File

@@ -1,18 +1,23 @@
; Identifier naming conventions; these "soft conventions" should stay at the top of the file as they're often overridden
; CamelCase for classes
((identifier) @type.class
(#match? @type.class "^_*[A-Z][A-Za-z0-9_]*$"))
; ALL_CAPS for constants:
((identifier) @constant
(#match? @constant "^_*[A-Z][A-Z0-9_]*$"))
(attribute attribute: (identifier) @property)
(type (identifier) @type)
(generic_type (identifier) @type)
(comment) @comment
(string) @string
(escape_sequence) @string.escape
; Type alias
(type_alias_statement "type" @keyword)
; Identifier naming conventions
((identifier) @type.class
(#match? @type.class "^[A-Z]"))
((identifier) @constant
(#match? @constant "^_*[A-Z][A-Z\\d_]*$"))
; TypeVar with constraints in type parameters
(type
(tuple (identifier) @type)
@@ -26,15 +31,20 @@
; Function calls
(decorator
"@" @punctuation.special
(identifier) @function.decorator)
(call
function: (attribute attribute: (identifier) @function.method.call))
(call
function: (identifier) @function.call)
(decorator
"@" @punctuation.special
[
(identifier) @function.decorator
(attribute attribute: (identifier) @function.decorator)
(call function: (identifier) @function.decorator.call)
(call (attribute attribute: (identifier) @function.decorator.call))
])
; Function and class definitions
(function_definition
@@ -47,9 +57,9 @@
(call
function: (identifier) @type.class.call
(#match? @type.class.call "^[A-Z][A-Z0-9_]*[a-z]"))
(#match? @type.class.call "^_*[A-Z][A-Za-z0-9_]*$"))
; Builtin functions
; Builtins
((call
function: (identifier) @function.builtin)
@@ -57,6 +67,9 @@
@function.builtin
"^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$"))
((identifier) @type.builtin
(#any-of? @type.builtin "int" "float" "complex" "bool" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview" "set" "frozenset" "dict"))
; Literals
[
@@ -79,10 +92,6 @@
(#match? @variable.special "^self|cls$")
]
(comment) @comment
(string) @string
(escape_sequence) @string.escape
[
"("
")"
@@ -114,7 +123,10 @@
. (expression_statement (string) @string.doc))
(module
(expression_statement (assignment))
[
(expression_statement (assignment))
(type_alias_statement)
]
. (expression_statement (string) @string.doc))
(class_definition
@@ -163,6 +175,9 @@
">>"
"|"
"~"
] @operator
[
"and"
"in"
"is"
@@ -170,7 +185,7 @@
"or"
"is not"
"not in"
] @operator
] @keyword.operator
[
"as"
@@ -185,6 +200,7 @@
"elif"
"else"
"except"
"except*"
"exec"
"finally"
"for"

View File

@@ -49,6 +49,7 @@ livekit.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation.workspace = true
coreaudio-rs = "0.12.1"
media.workspace = true
[dev-dependencies]
collections = { workspace = true, features = ["test-support"] }

View File

@@ -3,11 +3,12 @@
// it causes compile errors.
#![cfg_attr(target_os = "macos", allow(unused_imports))]
use futures::StreamExt;
use gpui::{
actions, bounds, div, point,
prelude::{FluentBuilder as _, IntoElement},
px, rgb, size, AsyncAppContext, Bounds, InteractiveElement, KeyBinding, Menu, MenuItem,
ParentElement, Pixels, Render, ScreenCaptureStream, SharedString,
ParentElement, Pixels, Render, ScreenCaptureFrame, ScreenCaptureStream, SharedString,
StatefulInteractiveElement as _, Styled, Task, View, ViewContext, VisualContext, WindowBounds,
WindowHandle, WindowOptions,
};
@@ -22,6 +23,7 @@ use livekit_client::{
track::{LocalTrack, RemoteTrack, RemoteVideoTrack, TrackSource},
AudioStream, RemoteVideoTrackView, Room, RoomEvent, RoomOptions,
};
use media::core_video::CVImageBuffer;
#[cfg(not(target_os = "windows"))]
use postage::stream::Stream;
@@ -108,6 +110,7 @@ struct LivekitWindow {
screen_share_track: Option<LocalTrackPublication>,
microphone_stream: Option<AudioStream>,
screen_share_stream: Option<Box<dyn ScreenCaptureStream>>,
latest_self_frame: Option<ScreenCaptureFrame>,
#[cfg(not(target_os = "windows"))]
remote_participants: Vec<(ParticipantIdentity, ParticipantState)>,
_events_task: Task<()>,
@@ -156,6 +159,7 @@ impl LivekitWindow {
microphone_stream: None,
screen_share_track: None,
screen_share_stream: None,
latest_self_frame: None,
remote_participants: Vec::new(),
_events_task,
}
@@ -312,7 +316,25 @@ impl LivekitWindow {
cx.spawn(|this, mut cx| async move {
let sources = sources.await.unwrap()?;
let source = sources.into_iter().next().unwrap();
let (track, stream) = capture_local_video_track(&*source).await?;
let (self_stream_tx, mut self_stream_rx) = futures::channel::mpsc::unbounded();
let (track, stream) =
capture_local_video_track(&*source, Some(self_stream_tx)).await?;
cx.spawn({
let this = this.clone();
|mut cx| async move {
while let Some(frame) = self_stream_rx.next().await {
this.update(&mut cx, |this, cx| {
this.latest_self_frame = Some(frame);
cx.notify();
})
.ok();
}
}
})
.detach();
let publication = participant
.publish_track(
LocalTrack::Video(track),
@@ -394,6 +416,11 @@ impl Render for LivekitWindow {
.on_click(cx.listener(|this, _, cx| this.toggle_screen_share(cx))),
]),
)
.children(
self.latest_self_frame
.as_ref()
.map(|frame| gpui::surface(frame.0.clone()).size_full()),
)
.child(
div()
.id("remote-participants")
@@ -403,7 +430,7 @@ impl Render for LivekitWindow {
.flex_grow()
.children(self.remote_participants.iter().map(|(identity, state)| {
div()
.h(px(300.0))
.size_full()
.flex()
.flex_col()
.m_2()

View File

@@ -142,8 +142,9 @@ pub fn init(
#[cfg(not(target_os = "windows"))]
pub async fn capture_local_video_track(
capture_source: &dyn ScreenCaptureSource,
show_capture: Option<futures::channel::mpsc::UnboundedSender<ScreenCaptureFrame>>,
) -> Result<(track::LocalVideoTrack, Box<dyn ScreenCaptureStream>)> {
let resolution = capture_source.resolution()?;
let resolution = capture_source.resolution();
let track_source = NativeVideoSource::new(VideoResolution {
width: resolution.width.0 as u32,
height: resolution.height.0 as u32,
@@ -153,6 +154,10 @@ pub async fn capture_local_video_track(
.stream({
let track_source = track_source.clone();
Box::new(move |frame| {
if let Some(show_capture) = show_capture.as_ref() {
show_capture.unbounded_send(frame.clone()).unwrap();
}
if let Some(buffer) = video_frame_buffer_to_webrtc(frame) {
track_source.capture_frame(&VideoFrame {
rotation: VideoRotation::VideoRotation0,

View File

@@ -1,11 +1,11 @@
#[cfg(target_os = "macos")]
#[cfg(any())]
mod macos;
#[cfg(target_os = "macos")]
#[cfg(any())]
pub use macos::*;
#[cfg(not(target_os = "macos"))]
#[cfg(all())]
mod cross_platform;
#[cfg(not(target_os = "macos"))]
#[cfg(all())]
pub use cross_platform::*;

View File

@@ -18,6 +18,7 @@ test-support = []
[dependencies]
anyhow.workspace = true
arrayvec.workspace = true
client.workspace = true
collections.workspace = true
editor.workspace = true

View File

@@ -3,6 +3,7 @@ mod rate_completion_modal;
pub use rate_completion_modal::*;
use anyhow::{anyhow, Context as _, Result};
use arrayvec::ArrayVec;
use client::Client;
use collections::{HashMap, HashSet, VecDeque};
use futures::AsyncReadExt;
@@ -798,7 +799,7 @@ fn prompt_for_excerpt(
}
fn excerpt_range_for_position(point: Point, snapshot: &BufferSnapshot) -> Range<usize> {
const CONTEXT_LINES: u32 = 16;
const CONTEXT_LINES: u32 = 32;
let mut context_lines_before = CONTEXT_LINES;
let mut context_lines_after = CONTEXT_LINES;
@@ -899,10 +900,15 @@ impl CurrentInlineCompletion {
}
}
struct PendingCompletion {
id: usize,
_task: Task<Result<()>>,
}
pub struct ZetaInlineCompletionProvider {
zeta: Model<Zeta>,
first_pending_completion: Option<Task<Result<()>>>,
last_pending_completion: Option<Task<Result<()>>>,
pending_completions: ArrayVec<PendingCompletion, 2>,
next_pending_completion_id: usize,
current_completion: Option<CurrentInlineCompletion>,
}
@@ -912,8 +918,8 @@ impl ZetaInlineCompletionProvider {
pub fn new(zeta: Model<Zeta>) -> Self {
Self {
zeta,
first_pending_completion: None,
last_pending_completion: None,
pending_completions: ArrayVec::new(),
next_pending_completion_id: 0,
current_completion: None,
}
}
@@ -944,7 +950,9 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
debounce: bool,
cx: &mut ModelContext<Self>,
) {
let is_first = self.first_pending_completion.is_none();
let pending_completion_id = self.next_pending_completion_id;
self.next_pending_completion_id += 1;
let task = cx.spawn(|this, mut cx| async move {
if debounce {
cx.background_executor().timer(Self::DEBOUNCE_TIMEOUT).await;
@@ -965,9 +973,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
}
this.update(&mut cx, |this, cx| {
this.first_pending_completion = None;
if !is_first {
this.last_pending_completion = None;
if this.pending_completions[0].id == pending_completion_id {
this.pending_completions.remove(0);
} else {
this.pending_completions.clear();
}
if let Some(new_completion) = completion {
@@ -993,10 +1002,19 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
})
});
if is_first {
self.first_pending_completion = Some(task);
} else {
self.last_pending_completion = Some(task);
// We always maintain at most two pending completions. When we already
// have two, we replace the newest one.
if self.pending_completions.len() <= 1 {
self.pending_completions.push(PendingCompletion {
id: pending_completion_id,
_task: task,
});
} else if self.pending_completions.len() == 2 {
self.pending_completions.pop();
self.pending_completions.push(PendingCompletion {
id: pending_completion_id,
_task: task,
});
}
}
@@ -1011,13 +1029,11 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
}
fn accept(&mut self, _cx: &mut ModelContext<Self>) {
self.first_pending_completion.take();
self.last_pending_completion.take();
self.pending_completions.clear();
}
fn discard(&mut self, _cx: &mut ModelContext<Self>) {
self.first_pending_completion.take();
self.last_pending_completion.take();
self.pending_completions.clear();
self.current_completion.take();
}