Cargo.lock 🔗
@@ -9005,6 +9005,7 @@ dependencies = [
"fs2",
"gpui2",
"indexmap 1.9.3",
+ "itertools 0.11.0",
"parking_lot 0.11.2",
"refineable",
"schemars",
Nathan Sobo created
Cargo.lock | 1
crates/editor2/src/editor.rs | 1117 ++++++++++++-----------
crates/editor2/src/element.rs | 26
crates/go_to_line2/src/go_to_line.rs | 8
crates/gpui2/src/app.rs | 2
crates/gpui2/src/element.rs | 5
crates/gpui2/src/gpui2.rs | 2
crates/gpui2/src/interactive.rs | 7
crates/gpui2/src/style.rs | 4
crates/gpui2/src/window.rs | 39
crates/gpui2/src/window_input_handler.rs | 89 +
crates/settings2/src/keymap_file.rs | 6
crates/storybook2/src/story_selector.rs | 2
crates/theme2/Cargo.toml | 3
crates/theme2/src/colors.rs | 175 +++
crates/theme2/src/default_colors.rs | 165 ++-
crates/theme2/src/default_theme.rs | 6
crates/theme2/src/players.rs | 170 +++
crates/theme2/src/story.rs | 38
crates/theme2/src/theme2.rs | 7
crates/theme_importer/src/theme_printer.rs | 3
crates/ui2/src/components/tab.rs | 2
crates/workspace2/src/modal_layer.rs | 14
crates/workspace2/src/workspace2.rs | 8
24 files changed, 1,217 insertions(+), 682 deletions(-)
@@ -9005,6 +9005,7 @@ dependencies = [
"fs2",
"gpui2",
"indexmap 1.9.3",
+ "itertools 0.11.0",
"parking_lot 0.11.2",
"refineable",
"schemars",
@@ -36,10 +36,10 @@ pub use element::{
use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
- actions, div, px, AnyElement, AppContext, BackgroundExecutor, Context, DispatchContext, Div,
- Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render,
- Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView,
- WindowContext,
+ actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, Context,
+ DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight,
+ HighlightStyle, Hsla, InputHandler, Model, Pixels, PlatformInputHandler, Render, Styled,
+ Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -56,6 +56,7 @@ use language::{
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
use movement::TextLayoutDetails;
+use multi_buffer::ToOffsetUtf16;
pub use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
ToPoint,
@@ -67,7 +68,7 @@ use rpc::proto::*;
use scroll::{
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
};
-use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
+use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use smallvec::SmallVec;
@@ -82,7 +83,7 @@ use std::{
};
pub use sum_tree::Bias;
use sum_tree::TreeMap;
-use text::Rope;
+use text::{OffsetUtf16, Rope};
use theme::{
ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
};
@@ -592,7 +593,6 @@ pub struct EditorStyle {
pub background: Hsla,
pub local_player: PlayerColor,
pub text: TextStyle,
- pub line_height_scalar: f32,
pub scrollbar_width: Pixels,
pub syntax: Arc<SyntaxTheme>,
pub diagnostic_style: DiagnosticStyle,
@@ -1794,14 +1794,11 @@ impl InlayHintRefreshReason {
}
impl Editor {
- // pub fn single_line(
- // field_editor_style: Option<Arc<GetFieldEditorTheme>>,
- // cx: &mut ViewContext<Self>,
- // ) -> Self {
- // let buffer = cx.build_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
- // let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
- // Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
- // }
+ pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
+ let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
+ let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+ Self::new(EditorMode::SingleLine, buffer, None, cx)
+ }
// pub fn multi_line(
// field_editor_style: Option<Arc<GetFieldEditorTheme>>,
@@ -2772,197 +2769,197 @@ impl Editor {
// cx.propagate();
// }
- // pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
- // let text: Arc<str> = text.into();
+ pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
+ let text: Arc<str> = text.into();
- // if self.read_only {
- // return;
- // }
+ if self.read_only {
+ return;
+ }
- // let selections = self.selections.all_adjusted(cx);
- // let mut brace_inserted = false;
- // let mut edits = Vec::new();
- // let mut new_selections = Vec::with_capacity(selections.len());
- // let mut new_autoclose_regions = Vec::new();
- // let snapshot = self.buffer.read(cx).read(cx);
+ let selections = self.selections.all_adjusted(cx);
+ let mut brace_inserted = false;
+ let mut edits = Vec::new();
+ let mut new_selections = Vec::with_capacity(selections.len());
+ let mut new_autoclose_regions = Vec::new();
+ let snapshot = self.buffer.read(cx).read(cx);
- // for (selection, autoclose_region) in
- // self.selections_with_autoclose_regions(selections, &snapshot)
- // {
- // if let Some(scope) = snapshot.language_scope_at(selection.head()) {
- // // Determine if the inserted text matches the opening or closing
- // // bracket of any of this language's bracket pairs.
- // let mut bracket_pair = None;
- // let mut is_bracket_pair_start = false;
- // if !text.is_empty() {
- // // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
- // // and they are removing the character that triggered IME popup.
- // for (pair, enabled) in scope.brackets() {
- // if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
- // bracket_pair = Some(pair.clone());
- // is_bracket_pair_start = true;
- // break;
- // } else if pair.end.as_str() == text.as_ref() {
- // bracket_pair = Some(pair.clone());
- // break;
- // }
- // }
- // }
+ for (selection, autoclose_region) in
+ self.selections_with_autoclose_regions(selections, &snapshot)
+ {
+ if let Some(scope) = snapshot.language_scope_at(selection.head()) {
+ // Determine if the inserted text matches the opening or closing
+ // bracket of any of this language's bracket pairs.
+ let mut bracket_pair = None;
+ let mut is_bracket_pair_start = false;
+ if !text.is_empty() {
+ // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
+ // and they are removing the character that triggered IME popup.
+ for (pair, enabled) in scope.brackets() {
+ if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
+ bracket_pair = Some(pair.clone());
+ is_bracket_pair_start = true;
+ break;
+ } else if pair.end.as_str() == text.as_ref() {
+ bracket_pair = Some(pair.clone());
+ break;
+ }
+ }
+ }
- // if let Some(bracket_pair) = bracket_pair {
- // if selection.is_empty() {
- // if is_bracket_pair_start {
- // let prefix_len = bracket_pair.start.len() - text.len();
-
- // // If the inserted text is a suffix of an opening bracket and the
- // // selection is preceded by the rest of the opening bracket, then
- // // insert the closing bracket.
- // let following_text_allows_autoclose = snapshot
- // .chars_at(selection.start)
- // .next()
- // .map_or(true, |c| scope.should_autoclose_before(c));
- // let preceding_text_matches_prefix = prefix_len == 0
- // || (selection.start.column >= (prefix_len as u32)
- // && snapshot.contains_str_at(
- // Point::new(
- // selection.start.row,
- // selection.start.column - (prefix_len as u32),
- // ),
- // &bracket_pair.start[..prefix_len],
- // ));
- // if following_text_allows_autoclose && preceding_text_matches_prefix {
- // let anchor = snapshot.anchor_before(selection.end);
- // new_selections.push((selection.map(|_| anchor), text.len()));
- // new_autoclose_regions.push((
- // anchor,
- // text.len(),
- // selection.id,
- // bracket_pair.clone(),
- // ));
- // edits.push((
- // selection.range(),
- // format!("{}{}", text, bracket_pair.end).into(),
- // ));
- // brace_inserted = true;
- // continue;
- // }
- // }
+ if let Some(bracket_pair) = bracket_pair {
+ if selection.is_empty() {
+ if is_bracket_pair_start {
+ let prefix_len = bracket_pair.start.len() - text.len();
+
+ // If the inserted text is a suffix of an opening bracket and the
+ // selection is preceded by the rest of the opening bracket, then
+ // insert the closing bracket.
+ let following_text_allows_autoclose = snapshot
+ .chars_at(selection.start)
+ .next()
+ .map_or(true, |c| scope.should_autoclose_before(c));
+ let preceding_text_matches_prefix = prefix_len == 0
+ || (selection.start.column >= (prefix_len as u32)
+ && snapshot.contains_str_at(
+ Point::new(
+ selection.start.row,
+ selection.start.column - (prefix_len as u32),
+ ),
+ &bracket_pair.start[..prefix_len],
+ ));
+ if following_text_allows_autoclose && preceding_text_matches_prefix {
+ let anchor = snapshot.anchor_before(selection.end);
+ new_selections.push((selection.map(|_| anchor), text.len()));
+ new_autoclose_regions.push((
+ anchor,
+ text.len(),
+ selection.id,
+ bracket_pair.clone(),
+ ));
+ edits.push((
+ selection.range(),
+ format!("{}{}", text, bracket_pair.end).into(),
+ ));
+ brace_inserted = true;
+ continue;
+ }
+ }
- // if let Some(region) = autoclose_region {
- // // If the selection is followed by an auto-inserted closing bracket,
- // // then don't insert that closing bracket again; just move the selection
- // // past the closing bracket.
- // let should_skip = selection.end == region.range.end.to_point(&snapshot)
- // && text.as_ref() == region.pair.end.as_str();
- // if should_skip {
- // let anchor = snapshot.anchor_after(selection.end);
- // new_selections
- // .push((selection.map(|_| anchor), region.pair.end.len()));
- // continue;
- // }
- // }
- // }
- // // If an opening bracket is 1 character long and is typed while
- // // text is selected, then surround that text with the bracket pair.
- // else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
- // edits.push((selection.start..selection.start, text.clone()));
- // edits.push((
- // selection.end..selection.end,
- // bracket_pair.end.as_str().into(),
- // ));
- // brace_inserted = true;
- // new_selections.push((
- // Selection {
- // id: selection.id,
- // start: snapshot.anchor_after(selection.start),
- // end: snapshot.anchor_before(selection.end),
- // reversed: selection.reversed,
- // goal: selection.goal,
- // },
- // 0,
- // ));
- // continue;
- // }
- // }
- // }
+ if let Some(region) = autoclose_region {
+ // If the selection is followed by an auto-inserted closing bracket,
+ // then don't insert that closing bracket again; just move the selection
+ // past the closing bracket.
+ let should_skip = selection.end == region.range.end.to_point(&snapshot)
+ && text.as_ref() == region.pair.end.as_str();
+ if should_skip {
+ let anchor = snapshot.anchor_after(selection.end);
+ new_selections
+ .push((selection.map(|_| anchor), region.pair.end.len()));
+ continue;
+ }
+ }
+ }
+ // If an opening bracket is 1 character long and is typed while
+ // text is selected, then surround that text with the bracket pair.
+ else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
+ edits.push((selection.start..selection.start, text.clone()));
+ edits.push((
+ selection.end..selection.end,
+ bracket_pair.end.as_str().into(),
+ ));
+ brace_inserted = true;
+ new_selections.push((
+ Selection {
+ id: selection.id,
+ start: snapshot.anchor_after(selection.start),
+ end: snapshot.anchor_before(selection.end),
+ reversed: selection.reversed,
+ goal: selection.goal,
+ },
+ 0,
+ ));
+ continue;
+ }
+ }
+ }
- // // If not handling any auto-close operation, then just replace the selected
- // // text with the given input and move the selection to the end of the
- // // newly inserted text.
- // let anchor = snapshot.anchor_after(selection.end);
- // new_selections.push((selection.map(|_| anchor), 0));
- // edits.push((selection.start..selection.end, text.clone()));
- // }
+ // If not handling any auto-close operation, then just replace the selected
+ // text with the given input and move the selection to the end of the
+ // newly inserted text.
+ let anchor = snapshot.anchor_after(selection.end);
+ new_selections.push((selection.map(|_| anchor), 0));
+ edits.push((selection.start..selection.end, text.clone()));
+ }
- // drop(snapshot);
- // self.transact(cx, |this, cx| {
- // this.buffer.update(cx, |buffer, cx| {
- // buffer.edit(edits, this.autoindent_mode.clone(), cx);
- // });
+ drop(snapshot);
+ self.transact(cx, |this, cx| {
+ this.buffer.update(cx, |buffer, cx| {
+ buffer.edit(edits, this.autoindent_mode.clone(), cx);
+ });
- // let new_anchor_selections = new_selections.iter().map(|e| &e.0);
- // let new_selection_deltas = new_selections.iter().map(|e| e.1);
- // let snapshot = this.buffer.read(cx).read(cx);
- // let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
- // .zip(new_selection_deltas)
- // .map(|(selection, delta)| Selection {
- // id: selection.id,
- // start: selection.start + delta,
- // end: selection.end + delta,
- // reversed: selection.reversed,
- // goal: SelectionGoal::None,
- // })
- // .collect::<Vec<_>>();
+ let new_anchor_selections = new_selections.iter().map(|e| &e.0);
+ let new_selection_deltas = new_selections.iter().map(|e| e.1);
+ let snapshot = this.buffer.read(cx).read(cx);
+ let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
+ .zip(new_selection_deltas)
+ .map(|(selection, delta)| Selection {
+ id: selection.id,
+ start: selection.start + delta,
+ end: selection.end + delta,
+ reversed: selection.reversed,
+ goal: SelectionGoal::None,
+ })
+ .collect::<Vec<_>>();
- // let mut i = 0;
- // for (position, delta, selection_id, pair) in new_autoclose_regions {
- // let position = position.to_offset(&snapshot) + delta;
- // let start = snapshot.anchor_before(position);
- // let end = snapshot.anchor_after(position);
- // while let Some(existing_state) = this.autoclose_regions.get(i) {
- // match existing_state.range.start.cmp(&start, &snapshot) {
- // Ordering::Less => i += 1,
- // Ordering::Greater => break,
- // Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
- // Ordering::Less => i += 1,
- // Ordering::Equal => break,
- // Ordering::Greater => break,
- // },
- // }
- // }
- // this.autoclose_regions.insert(
- // i,
- // AutocloseRegion {
- // selection_id,
- // range: start..end,
- // pair,
- // },
- // );
- // }
+ let mut i = 0;
+ for (position, delta, selection_id, pair) in new_autoclose_regions {
+ let position = position.to_offset(&snapshot) + delta;
+ let start = snapshot.anchor_before(position);
+ let end = snapshot.anchor_after(position);
+ while let Some(existing_state) = this.autoclose_regions.get(i) {
+ match existing_state.range.start.cmp(&start, &snapshot) {
+ Ordering::Less => i += 1,
+ Ordering::Greater => break,
+ Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
+ Ordering::Less => i += 1,
+ Ordering::Equal => break,
+ Ordering::Greater => break,
+ },
+ }
+ }
+ this.autoclose_regions.insert(
+ i,
+ AutocloseRegion {
+ selection_id,
+ range: start..end,
+ pair,
+ },
+ );
+ }
- // drop(snapshot);
- // let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
- // this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
+ drop(snapshot);
+ let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
+ this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
- // if !brace_inserted && EditorSettings>(cx).use_on_type_format {
- // if let Some(on_type_format_task) =
- // this.trigger_on_type_formatting(text.to_string(), cx)
- // {
- // on_type_format_task.detach_and_log_err(cx);
- // }
- // }
+ if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
+ if let Some(on_type_format_task) =
+ this.trigger_on_type_formatting(text.to_string(), cx)
+ {
+ on_type_format_task.detach_and_log_err(cx);
+ }
+ }
- // if had_active_copilot_suggestion {
- // this.refresh_copilot_suggestions(true, cx);
- // if !this.has_active_copilot_suggestion(cx) {
- // this.trigger_completion_on_input(&text, cx);
- // }
- // } else {
- // this.trigger_completion_on_input(&text, cx);
- // this.refresh_copilot_suggestions(true, cx);
- // }
- // });
- // }
+ if had_active_copilot_suggestion {
+ this.refresh_copilot_suggestions(true, cx);
+ if !this.has_active_copilot_suggestion(cx) {
+ this.trigger_completion_on_input(&text, cx);
+ }
+ } else {
+ this.trigger_completion_on_input(&text, cx);
+ this.refresh_copilot_suggestions(true, cx);
+ }
+ });
+ }
// pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
// self.transact(cx, |this, cx| {
@@ -3262,22 +3259,22 @@ impl Editor {
});
}
- // fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
- // if !EditorSettings>(cx).show_completions_on_input {
- // return;
- // }
+ fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
+ if !EditorSettings::get_global(cx).show_completions_on_input {
+ return;
+ }
- // let selection = self.selections.newest_anchor();
- // if self
- // .buffer
- // .read(cx)
- // .is_completion_trigger(selection.head(), text, cx)
- // {
- // self.show_completions(&ShowCompletions, cx);
- // } else {
- // self.hide_context_menu(cx);
- // }
- // }
+ let selection = self.selections.newest_anchor();
+ if self
+ .buffer
+ .read(cx)
+ .is_completion_trigger(selection.head(), text, cx)
+ {
+ self.show_completions(&ShowCompletions, cx);
+ } else {
+ self.hide_context_menu(cx);
+ }
+ }
// /// If any empty selections is touching the start of its innermost containing autoclose
// /// region, expand it to select the brackets.
@@ -3308,37 +3305,37 @@ impl Editor {
// self.change_selections(None, cx, |selections| selections.select(new_selections));
// }
- // /// Iterate the given selections, and for each one, find the smallest surrounding
- // /// autoclose region. This uses the ordering of the selections and the autoclose
- // /// regions to avoid repeated comparisons.
- // fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
- // &'a self,
- // selections: impl IntoIterator<Item = Selection<D>>,
- // buffer: &'a MultiBufferSnapshot,
- // ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
- // let mut i = 0;
- // let mut regions = self.autoclose_regions.as_slice();
- // selections.into_iter().map(move |selection| {
- // let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
-
- // let mut enclosing = None;
- // while let Some(pair_state) = regions.get(i) {
- // if pair_state.range.end.to_offset(buffer) < range.start {
- // regions = ®ions[i + 1..];
- // i = 0;
- // } else if pair_state.range.start.to_offset(buffer) > range.end {
- // break;
- // } else {
- // if pair_state.selection_id == selection.id {
- // enclosing = Some(pair_state);
- // }
- // i += 1;
- // }
- // }
+ /// Iterate the given selections, and for each one, find the smallest surrounding
+ /// autoclose region. This uses the ordering of the selections and the autoclose
+ /// regions to avoid repeated comparisons.
+ fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
+ &'a self,
+ selections: impl IntoIterator<Item = Selection<D>>,
+ buffer: &'a MultiBufferSnapshot,
+ ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
+ let mut i = 0;
+ let mut regions = self.autoclose_regions.as_slice();
+ selections.into_iter().map(move |selection| {
+ let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
+
+ let mut enclosing = None;
+ while let Some(pair_state) = regions.get(i) {
+ if pair_state.range.end.to_offset(buffer) < range.start {
+ regions = ®ions[i + 1..];
+ i = 0;
+ } else if pair_state.range.start.to_offset(buffer) > range.end {
+ break;
+ } else {
+ if pair_state.selection_id == selection.id {
+ enclosing = Some(pair_state);
+ }
+ i += 1;
+ }
+ }
- // (selection.clone(), enclosing)
- // })
- // }
+ (selection.clone(), enclosing)
+ })
+ }
/// Remove any autoclose regions that no longer contain their selection.
fn invalidate_autoclose_regions(
@@ -3540,51 +3537,51 @@ impl Editor {
cx.notify();
}
- // fn trigger_on_type_formatting(
- // &self,
- // input: String,
- // cx: &mut ViewContext<Self>,
- // ) -> Option<Task<Result<()>>> {
- // if input.len() != 1 {
- // return None;
- // }
+ fn trigger_on_type_formatting(
+ &self,
+ input: String,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<Task<Result<()>>> {
+ if input.len() != 1 {
+ return None;
+ }
- // let project = self.project.as_ref()?;
- // let position = self.selections.newest_anchor().head();
- // let (buffer, buffer_position) = self
- // .buffer
- // .read(cx)
- // .text_anchor_for_position(position.clone(), cx)?;
-
- // // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
- // // hence we do LSP request & edit on host side only — add formats to host's history.
- // let push_to_lsp_host_history = true;
- // // If this is not the host, append its history with new edits.
- // let push_to_client_history = project.read(cx).is_remote();
-
- // let on_type_formatting = project.update(cx, |project, cx| {
- // project.on_type_format(
- // buffer.clone(),
- // buffer_position,
- // input,
- // push_to_lsp_host_history,
- // cx,
- // )
- // });
- // Some(cx.spawn(|editor, mut cx| async move {
- // if let Some(transaction) = on_type_formatting.await? {
- // if push_to_client_history {
- // buffer.update(&mut cx, |buffer, _| {
- // buffer.push_transaction(transaction, Instant::now());
- // });
- // }
- // editor.update(&mut cx, |editor, cx| {
- // editor.refresh_document_highlights(cx);
- // })?;
- // }
- // Ok(())
- // }))
- // }
+ let project = self.project.as_ref()?;
+ let position = self.selections.newest_anchor().head();
+ let (buffer, buffer_position) = self
+ .buffer
+ .read(cx)
+ .text_anchor_for_position(position.clone(), cx)?;
+
+ // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
+ // hence we do LSP request & edit on host side only — add formats to host's history.
+ let push_to_lsp_host_history = true;
+ // If this is not the host, append its history with new edits.
+ let push_to_client_history = project.read(cx).is_remote();
+
+ let on_type_formatting = project.update(cx, |project, cx| {
+ project.on_type_format(
+ buffer.clone(),
+ buffer_position,
+ input,
+ push_to_lsp_host_history,
+ cx,
+ )
+ });
+ Some(cx.spawn(|editor, mut cx| async move {
+ if let Some(transaction) = on_type_formatting.await? {
+ if push_to_client_history {
+ buffer.update(&mut cx, |buffer, _| {
+ buffer.push_transaction(transaction, Instant::now());
+ });
+ }
+ editor.update(&mut cx, |editor, cx| {
+ editor.refresh_document_highlights(cx);
+ })?;
+ }
+ Ok(())
+ }))
+ }
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
if self.pending_rename.is_some() {
@@ -8137,15 +8134,14 @@ impl Editor {
}
fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
- todo!()
- // self.end_selection(cx);
- // if let Some(tx_id) = self
- // .buffer
- // .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
- // {
- // self.selection_history
- // .insert_transaction(tx_id, self.selections.disjoint_anchors());
- // }
+ self.end_selection(cx);
+ if let Some(tx_id) = self
+ .buffer
+ .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
+ {
+ self.selection_history
+ .insert_transaction(tx_id, self.selections.disjoint_anchors());
+ }
}
fn end_transaction_at(
@@ -8153,22 +8149,21 @@ impl Editor {
now: Instant,
cx: &mut ViewContext<Self>,
) -> Option<TransactionId> {
- todo!()
- // if let Some(tx_id) = self
- // .buffer
- // .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
- // {
- // if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
- // *end_selections = Some(self.selections.disjoint_anchors());
- // } else {
- // error!("unexpectedly ended a transaction that wasn't started by this editor");
- // }
-
- // cx.emit(Event::Edited);
- // Some(tx_id)
- // } else {
- // None
- // }
+ if let Some(tx_id) = self
+ .buffer
+ .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
+ {
+ if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
+ *end_selections = Some(self.selections.disjoint_anchors());
+ } else {
+ log::error!("unexpectedly ended a transaction that wasn't started by this editor");
+ }
+
+ cx.emit(Event::Edited);
+ Some(tx_id)
+ } else {
+ None
+ }
}
// pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
@@ -8366,9 +8361,9 @@ impl Editor {
// .max_point()
// }
- // pub fn text(&self, cx: &AppContext) -> String {
- // self.buffer.read(cx).read(cx).text()
- // }
+ pub fn text(&self, cx: &AppContext) -> String {
+ self.buffer.read(cx).read(cx).text()
+ }
// pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
// self.transact(cx, |this, cx| {
@@ -8691,17 +8686,17 @@ impl Editor {
// results
// }
- // pub fn highlight_text<T: 'static>(
- // &mut self,
- // ranges: Vec<Range<Anchor>>,
- // style: HighlightStyle,
- // cx: &mut ViewContext<Self>,
- // ) {
- // self.display_map.update(cx, |map, _| {
- // map.highlight_text(TypeId::of::<T>(), ranges, style)
- // });
- // cx.notify();
- // }
+ pub fn highlight_text<T: 'static>(
+ &mut self,
+ ranges: Vec<Range<Anchor>>,
+ style: HighlightStyle,
+ cx: &mut ViewContext<Self>,
+ ) {
+ self.display_map.update(cx, |map, _| {
+ map.highlight_text(TypeId::of::<T>(), ranges, style)
+ });
+ cx.notify();
+ }
// pub fn highlight_inlays<T: 'static>(
// &mut self,
@@ -8715,12 +8710,12 @@ impl Editor {
// cx.notify();
// }
- // pub fn text_highlights<'a, T: 'static>(
- // &'a self,
- // cx: &'a AppContext,
- // ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
- // self.display_map.read(cx).text_highlights(TypeId::of::<T>())
- // }
+ pub fn text_highlights<'a, T: 'static>(
+ &'a self,
+ cx: &'a AppContext,
+ ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
+ self.display_map.read(cx).text_highlights(TypeId::of::<T>())
+ }
pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
let cleared = self
@@ -8937,43 +8932,43 @@ impl Editor {
// .detach_and_log_err(cx);
// }
- // fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
- // let snapshot = self.buffer.read(cx).read(cx);
- // let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
- // Some(
- // ranges
- // .iter()
- // .map(move |range| {
- // range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
- // })
- // .collect(),
- // )
- // }
+ fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
+ let snapshot = self.buffer.read(cx).read(cx);
+ let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
+ Some(
+ ranges
+ .iter()
+ .map(move |range| {
+ range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
+ })
+ .collect(),
+ )
+ }
- // fn selection_replacement_ranges(
- // &self,
- // range: Range<OffsetUtf16>,
- // cx: &AppContext,
- // ) -> Vec<Range<OffsetUtf16>> {
- // let selections = self.selections.all::<OffsetUtf16>(cx);
- // let newest_selection = selections
- // .iter()
- // .max_by_key(|selection| selection.id)
- // .unwrap();
- // let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
- // let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
- // let snapshot = self.buffer.read(cx).read(cx);
- // selections
- // .into_iter()
- // .map(|mut selection| {
- // selection.start.0 =
- // (selection.start.0 as isize).saturating_add(start_delta) as usize;
- // selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
- // snapshot.clip_offset_utf16(selection.start, Bias::Left)
- // ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
- // })
- // .collect()
- // }
+ fn selection_replacement_ranges(
+ &self,
+ range: Range<OffsetUtf16>,
+ cx: &AppContext,
+ ) -> Vec<Range<OffsetUtf16>> {
+ let selections = self.selections.all::<OffsetUtf16>(cx);
+ let newest_selection = selections
+ .iter()
+ .max_by_key(|selection| selection.id)
+ .unwrap();
+ let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
+ let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
+ let snapshot = self.buffer.read(cx).read(cx);
+ selections
+ .into_iter()
+ .map(|mut selection| {
+ selection.start.0 =
+ (selection.start.0 as isize).saturating_add(start_delta) as usize;
+ selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
+ snapshot.clip_offset_utf16(selection.start, Bias::Left)
+ ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
+ })
+ .collect()
+ }
fn report_copilot_event(
&self,
@@ -9188,6 +9183,10 @@ impl Editor {
// });
// supports
// }
+
+ fn focus(&self, cx: &mut WindowContext) {
+ cx.focus(&self.focus_handle)
+ }
}
pub trait CollaborationHub {
@@ -9371,14 +9370,13 @@ impl Render for Editor {
font_size: settings.buffer_font_size.into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
- line_height: Default::default(),
+ line_height: relative(settings.buffer_line_height.value()),
underline: None,
};
EditorElement::new(EditorStyle {
background: cx.theme().colors().editor_background,
local_player: cx.theme().players().local(),
text: text_style,
- line_height_scalar: settings.buffer_line_height.value(),
scrollbar_width: px(12.),
syntax: cx.theme().syntax().clone(),
diagnostic_style: cx.theme().diagnostic_style(),
@@ -9485,214 +9483,229 @@ impl Render for Editor {
// false
// }
-//
-// fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
-// Some(
-// self.buffer
-// .read(cx)
-// .read(cx)
-// .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
-// .collect(),
-// )
-// }
-// fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
-// // Prevent the IME menu from appearing when holding down an alphabetic key
-// // while input is disabled.
-// if !self.input_enabled {
-// return None;
-// }
+impl InputHandler for Editor {
+ fn text_for_range(
+ &self,
+ range_utf16: Range<usize>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<String> {
+ Some(
+ self.buffer
+ .read(cx)
+ .read(cx)
+ .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
+ .collect(),
+ )
+ }
+
+ fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
+ // Prevent the IME menu from appearing when holding down an alphabetic key
+ // while input is disabled.
+ if !self.input_enabled {
+ return None;
+ }
-// let range = self.selections.newest::<OffsetUtf16>(cx).range();
-// Some(range.start.0..range.end.0)
-// }
+ let range = self.selections.newest::<OffsetUtf16>(cx).range();
+ Some(range.start.0..range.end.0)
+ }
-// fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
-// let snapshot = self.buffer.read(cx).read(cx);
-// let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
-// Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
-// }
+ fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
+ let snapshot = self.buffer.read(cx).read(cx);
+ let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
+ Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
+ }
-// fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
-// self.clear_highlights::<InputComposition>(cx);
-// self.ime_transaction.take();
-// }
+ fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
+ self.clear_highlights::<InputComposition>(cx);
+ self.ime_transaction.take();
+ }
-// fn replace_text_in_range(
-// &mut self,
-// range_utf16: Option<Range<usize>>,
-// text: &str,
-// cx: &mut ViewContext<Self>,
-// ) {
-// if !self.input_enabled {
-// cx.emit(Event::InputIgnored { text: text.into() });
-// return;
-// }
+ fn replace_text_in_range(
+ &mut self,
+ range_utf16: Option<Range<usize>>,
+ text: &str,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if !self.input_enabled {
+ cx.emit(Event::InputIgnored { text: text.into() });
+ return;
+ }
-// self.transact(cx, |this, cx| {
-// let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
-// let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
-// Some(this.selection_replacement_ranges(range_utf16, cx))
-// } else {
-// this.marked_text_ranges(cx)
-// };
+ self.transact(cx, |this, cx| {
+ let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
+ let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
+ Some(this.selection_replacement_ranges(range_utf16, cx))
+ } else {
+ this.marked_text_ranges(cx)
+ };
-// let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
-// let newest_selection_id = this.selections.newest_anchor().id;
-// this.selections
-// .all::<OffsetUtf16>(cx)
-// .iter()
-// .zip(ranges_to_replace.iter())
-// .find_map(|(selection, range)| {
-// if selection.id == newest_selection_id {
-// Some(
-// (range.start.0 as isize - selection.head().0 as isize)
-// ..(range.end.0 as isize - selection.head().0 as isize),
-// )
-// } else {
-// None
-// }
-// })
-// });
+ let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
+ let newest_selection_id = this.selections.newest_anchor().id;
+ this.selections
+ .all::<OffsetUtf16>(cx)
+ .iter()
+ .zip(ranges_to_replace.iter())
+ .find_map(|(selection, range)| {
+ if selection.id == newest_selection_id {
+ Some(
+ (range.start.0 as isize - selection.head().0 as isize)
+ ..(range.end.0 as isize - selection.head().0 as isize),
+ )
+ } else {
+ None
+ }
+ })
+ });
-// cx.emit(Event::InputHandled {
-// utf16_range_to_replace: range_to_replace,
-// text: text.into(),
-// });
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: range_to_replace,
+ text: text.into(),
+ });
-// if let Some(new_selected_ranges) = new_selected_ranges {
-// this.change_selections(None, cx, |selections| {
-// selections.select_ranges(new_selected_ranges)
-// });
-// }
+ if let Some(new_selected_ranges) = new_selected_ranges {
+ this.change_selections(None, cx, |selections| {
+ selections.select_ranges(new_selected_ranges)
+ });
+ }
-// this.handle_input(text, cx);
-// });
+ this.handle_input(text, cx);
+ });
-// if let Some(transaction) = self.ime_transaction {
-// self.buffer.update(cx, |buffer, cx| {
-// buffer.group_until_transaction(transaction, cx);
-// });
-// }
+ if let Some(transaction) = self.ime_transaction {
+ self.buffer.update(cx, |buffer, cx| {
+ buffer.group_until_transaction(transaction, cx);
+ });
+ }
-// self.unmark_text(cx);
-// }
+ self.unmark_text(cx);
+ }
-// fn replace_and_mark_text_in_range(
-// &mut self,
-// range_utf16: Option<Range<usize>>,
-// text: &str,
-// new_selected_range_utf16: Option<Range<usize>>,
-// cx: &mut ViewContext<Self>,
-// ) {
-// if !self.input_enabled {
-// cx.emit(Event::InputIgnored { text: text.into() });
-// return;
-// }
+ fn replace_and_mark_text_in_range(
+ &mut self,
+ range_utf16: Option<Range<usize>>,
+ text: &str,
+ new_selected_range_utf16: Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if !self.input_enabled {
+ cx.emit(Event::InputIgnored { text: text.into() });
+ return;
+ }
-// let transaction = self.transact(cx, |this, cx| {
-// let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
-// let snapshot = this.buffer.read(cx).read(cx);
-// if let Some(relative_range_utf16) = range_utf16.as_ref() {
-// for marked_range in &mut marked_ranges {
-// marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
-// marked_range.start.0 += relative_range_utf16.start;
-// marked_range.start =
-// snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
-// marked_range.end =
-// snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
-// }
-// }
-// Some(marked_ranges)
-// } else if let Some(range_utf16) = range_utf16 {
-// let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
-// Some(this.selection_replacement_ranges(range_utf16, cx))
-// } else {
-// None
-// };
+ let transaction = self.transact(cx, |this, cx| {
+ let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
+ let snapshot = this.buffer.read(cx).read(cx);
+ if let Some(relative_range_utf16) = range_utf16.as_ref() {
+ for marked_range in &mut marked_ranges {
+ marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
+ marked_range.start.0 += relative_range_utf16.start;
+ marked_range.start =
+ snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
+ marked_range.end =
+ snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
+ }
+ }
+ Some(marked_ranges)
+ } else if let Some(range_utf16) = range_utf16 {
+ let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
+ Some(this.selection_replacement_ranges(range_utf16, cx))
+ } else {
+ None
+ };
-// let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
-// let newest_selection_id = this.selections.newest_anchor().id;
-// this.selections
-// .all::<OffsetUtf16>(cx)
-// .iter()
-// .zip(ranges_to_replace.iter())
-// .find_map(|(selection, range)| {
-// if selection.id == newest_selection_id {
-// Some(
-// (range.start.0 as isize - selection.head().0 as isize)
-// ..(range.end.0 as isize - selection.head().0 as isize),
-// )
-// } else {
-// None
-// }
-// })
-// });
+ let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
+ let newest_selection_id = this.selections.newest_anchor().id;
+ this.selections
+ .all::<OffsetUtf16>(cx)
+ .iter()
+ .zip(ranges_to_replace.iter())
+ .find_map(|(selection, range)| {
+ if selection.id == newest_selection_id {
+ Some(
+ (range.start.0 as isize - selection.head().0 as isize)
+ ..(range.end.0 as isize - selection.head().0 as isize),
+ )
+ } else {
+ None
+ }
+ })
+ });
-// cx.emit(Event::InputHandled {
-// utf16_range_to_replace: range_to_replace,
-// text: text.into(),
-// });
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: range_to_replace,
+ text: text.into(),
+ });
-// if let Some(ranges) = ranges_to_replace {
-// this.change_selections(None, cx, |s| s.select_ranges(ranges));
-// }
+ if let Some(ranges) = ranges_to_replace {
+ this.change_selections(None, cx, |s| s.select_ranges(ranges));
+ }
-// let marked_ranges = {
-// let snapshot = this.buffer.read(cx).read(cx);
-// this.selections
-// .disjoint_anchors()
-// .iter()
-// .map(|selection| {
-// selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
-// })
-// .collect::<Vec<_>>()
-// };
+ let marked_ranges = {
+ let snapshot = this.buffer.read(cx).read(cx);
+ this.selections
+ .disjoint_anchors()
+ .iter()
+ .map(|selection| {
+ selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
+ })
+ .collect::<Vec<_>>()
+ };
-// if text.is_empty() {
-// this.unmark_text(cx);
-// } else {
-// this.highlight_text::<InputComposition>(
-// marked_ranges.clone(),
-// this.style(cx).composition_mark,
-// cx,
-// );
-// }
+ if text.is_empty() {
+ this.unmark_text(cx);
+ } else {
+ this.highlight_text::<InputComposition>(
+ marked_ranges.clone(),
+ HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
+ cx,
+ );
+ }
-// this.handle_input(text, cx);
-
-// if let Some(new_selected_range) = new_selected_range_utf16 {
-// let snapshot = this.buffer.read(cx).read(cx);
-// let new_selected_ranges = marked_ranges
-// .into_iter()
-// .map(|marked_range| {
-// let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
-// let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
-// let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
-// snapshot.clip_offset_utf16(new_start, Bias::Left)
-// ..snapshot.clip_offset_utf16(new_end, Bias::Right)
-// })
-// .collect::<Vec<_>>();
-
-// drop(snapshot);
-// this.change_selections(None, cx, |selections| {
-// selections.select_ranges(new_selected_ranges)
-// });
-// }
-// });
+ this.handle_input(text, cx);
-// self.ime_transaction = self.ime_transaction.or(transaction);
-// if let Some(transaction) = self.ime_transaction {
-// self.buffer.update(cx, |buffer, cx| {
-// buffer.group_until_transaction(transaction, cx);
-// });
-// }
+ if let Some(new_selected_range) = new_selected_range_utf16 {
+ let snapshot = this.buffer.read(cx).read(cx);
+ let new_selected_ranges = marked_ranges
+ .into_iter()
+ .map(|marked_range| {
+ let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
+ let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
+ let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
+ snapshot.clip_offset_utf16(new_start, Bias::Left)
+ ..snapshot.clip_offset_utf16(new_end, Bias::Right)
+ })
+ .collect::<Vec<_>>();
-// if self.text_highlights::<InputComposition>(cx).is_none() {
-// self.ime_transaction.take();
-// }
-// }
-// }
+ drop(snapshot);
+ this.change_selections(None, cx, |selections| {
+ selections.select_ranges(new_selected_ranges)
+ });
+ }
+ });
+
+ self.ime_transaction = self.ime_transaction.or(transaction);
+ if let Some(transaction) = self.ime_transaction {
+ self.buffer.update(cx, |buffer, cx| {
+ buffer.group_until_transaction(transaction, cx);
+ });
+ }
+
+ if self.text_highlights::<InputComposition>(cx).is_none() {
+ self.ime_transaction.take();
+ }
+ }
+
+ fn bounds_for_range(
+ &self,
+ range_utf16: Range<usize>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<gpui::Bounds<f32>> {
+ // todo!()
+ // See how we did it before: `rect_for_range`
+ None
+ }
+}
// fn build_style(
// settings: &ThemeSettings,
@@ -8,10 +8,11 @@ use crate::{
use anyhow::Result;
use collections::{BTreeMap, HashMap};
use gpui::{
- black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, BorrowWindow,
- Bounds, ContentMask, Corners, DispatchContext, DispatchPhase, Edges, Element, ElementId,
- Entity, Hsla, KeyDownEvent, KeyListener, KeyMatch, Line, Pixels, ScrollWheelEvent, ShapedGlyph,
- Size, StatefulInteraction, Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext,
+ black, hsla, point, px, relative, size, transparent_black, Action, AnyElement,
+ BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase,
+ Edges, Element, ElementId, Entity, Hsla, KeyDownEvent, KeyListener, KeyMatch, Line, Pixels,
+ ScrollWheelEvent, ShapedGlyph, Size, StatefulInteraction, Style, TextRun, TextStyle,
+ TextSystem, ViewContext, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -1594,7 +1595,7 @@ impl EditorElement {
&mut self,
editor: &mut Editor,
cx: &mut ViewContext<'_, Editor>,
- bounds: Bounds<Pixels>,
+ mut bounds: Bounds<Pixels>,
) -> LayoutState {
// let mut size = constraint.max;
// if size.x.is_infinite() {
@@ -1605,7 +1606,7 @@ impl EditorElement {
let style = self.style.clone();
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
let font_size = style.text.font_size.to_pixels(cx.rem_size());
- let line_height = (font_size * style.line_height_scalar).round();
+ let line_height = style.text.line_height_in_pixels(cx.rem_size());
let em_width = cx
.text_system()
.typographic_bounds(font_id, font_size, 'm')
@@ -1672,8 +1673,7 @@ impl EditorElement {
// .min(line_height * max_lines as f32),
// )
} else if let EditorMode::SingleLine = editor_mode {
- todo!()
- // size.set_y(line_height.max(constraint.min_along(Axis::Vertical)))
+ bounds.size.height = line_height.min(bounds.size.height);
}
// todo!()
// else if size.y.is_infinite() {
@@ -2593,7 +2593,11 @@ impl Element<Editor> for EditorElement {
let rem_size = cx.rem_size();
let mut style = Style::default();
style.size.width = relative(1.).into();
- style.size.height = relative(1.).into();
+ style.size.height = match editor.mode {
+ EditorMode::SingleLine => self.style.text.line_height_in_pixels(cx.rem_size()).into(),
+ EditorMode::AutoHeight { .. } => todo!(),
+ EditorMode::Full => relative(1.).into(),
+ };
cx.request_layout(&style, None)
}
@@ -2619,6 +2623,10 @@ impl Element<Editor> for EditorElement {
}
});
+ if editor.focus_handle.is_focused(cx) {
+ cx.handle_text_input();
+ }
+
cx.with_content_mask(ContentMask { bounds }, |cx| {
let gutter_bounds = Bounds {
origin: bounds.origin,
@@ -1,12 +1,10 @@
-use gpui::{div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext};
-use serde::Deserialize;
+use gpui::{actions, div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext};
use workspace::ModalRegistry;
-// actions!(go_to_line, [Toggle]);
-#[derive(Clone, Default, PartialEq, Deserialize)]
-struct Toggle;
+actions!(Toggle);
pub fn init(cx: &mut AppContext) {
+ cx.register_action_type::<Toggle>();
cx.global_mut::<ModalRegistry>()
.register_modal(Toggle, |_, cx| {
// if let Some(editor) = workspace
@@ -153,7 +153,7 @@ type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
// }
pub struct AppContext {
- this: Weak<AppCell>,
+ pub(crate) this: Weak<AppCell>,
pub(crate) platform: Rc<dyn Platform>,
app_metadata: AppMetadata,
text_system: Arc<TextSystem>,
@@ -134,7 +134,10 @@ where
.layout(state, frame_state.as_mut().unwrap(), cx);
}
}
- _ => panic!("must call initialize before layout"),
+ ElementRenderPhase::Start => panic!("must call initialize before layout"),
+ ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::Painted => {
+ panic!("element rendered twice")
+ }
};
self.phase = ElementRenderPhase::LayoutRequested {
@@ -24,6 +24,7 @@ mod text_system;
mod util;
mod view;
mod window;
+mod window_input_handler;
mod private {
/// A mechanism for restricting implementations of a trait to only those in GPUI.
@@ -65,6 +66,7 @@ pub use text_system::*;
pub use util::arc_cow::ArcCow;
pub use view::*;
pub use window::*;
+pub use window_input_handler::*;
use derive_more::{Deref, DerefMut};
use std::{
@@ -397,9 +397,10 @@ pub trait ElementInteraction<V: 'static>: 'static {
None
}),
));
- let result = stateful.stateless.initialize(cx, f);
- stateful.key_listeners.pop();
- result
+
+ cx.with_key_dispatch_context(stateful.dispatch_context.clone(), |cx| {
+ cx.with_key_listeners(mem::take(&mut stateful.key_listeners), f)
+ })
})
} else {
let stateless = self.as_stateless_mut();
@@ -189,6 +189,10 @@ impl TextStyle {
}
}
+ pub fn line_height_in_pixels(&self, rem_size: Pixels) -> Pixels {
+ self.line_height.to_pixels(self.font_size, rem_size)
+ }
+
pub fn to_run(&self, len: usize) -> TextRun {
TextRun {
len,
@@ -2,13 +2,14 @@ use crate::{
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId,
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
- GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
- KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
- MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
- PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
- RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
- Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
- VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+ GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, InputHandler, IsZero, KeyListener,
+ KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
+ PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
+ Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
+ SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
+ TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
+ WindowBounds, WindowInputHandler, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Result};
use collections::HashMap;
@@ -191,6 +192,7 @@ pub struct Window {
default_prevented: bool,
mouse_position: Point<Pixels>,
requested_cursor_style: Option<CursorStyle>,
+ requested_input_handler: Option<Box<dyn PlatformInputHandler>>,
scale_factor: f32,
bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
@@ -253,7 +255,7 @@ impl Window {
handle
.update(&mut cx, |_, cx| cx.dispatch_event(event))
.log_err()
- .unwrap_or(true)
+ .unwrap_or(false)
})
});
@@ -285,6 +287,7 @@ impl Window {
default_prevented: true,
mouse_position,
requested_cursor_style: None,
+ requested_input_handler: None,
scale_factor,
bounds,
bounds_observers: SubscriberSet::new(),
@@ -1008,6 +1011,9 @@ impl<'a> WindowContext<'a> {
.take()
.unwrap_or(CursorStyle::Arrow);
self.platform.set_cursor_style(cursor_style);
+ if let Some(handler) = self.window.requested_input_handler.take() {
+ self.window.platform_window.set_input_handler(handler);
+ }
self.window.dirty = false;
}
@@ -1152,6 +1158,7 @@ impl<'a> WindowContext<'a> {
.insert(any_mouse_event.type_id(), handlers);
}
} else if let Some(any_key_event) = event.keyboard_event() {
+ let mut did_handle_action = false;
let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack);
let key_event_type = any_key_event.type_id();
let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new();
@@ -1172,6 +1179,7 @@ impl<'a> WindowContext<'a> {
self.dispatch_action(action, &key_dispatch_stack[..ix]);
}
if !self.app.propagate_event {
+ did_handle_action = true;
break;
}
}
@@ -1200,6 +1208,7 @@ impl<'a> WindowContext<'a> {
}
if !self.app.propagate_event {
+ did_handle_action = true;
break;
}
}
@@ -1213,6 +1222,7 @@ impl<'a> WindowContext<'a> {
drop(context_stack);
self.window.key_dispatch_stack = key_dispatch_stack;
+ return did_handle_action;
}
true
@@ -1995,6 +2005,19 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}
}
+impl<V> ViewContext<'_, V>
+where
+ V: InputHandler + 'static,
+{
+ pub fn handle_text_input(&mut self) {
+ self.window.requested_input_handler = Some(Box::new(WindowInputHandler {
+ cx: self.app.this.clone(),
+ window: self.window_handle(),
+ handler: self.view().downgrade(),
+ }));
+ }
+}
+
impl<V> ViewContext<'_, V>
where
V: EventEmitter,
@@ -0,0 +1,89 @@
+use crate::{AnyWindowHandle, AppCell, Context, PlatformInputHandler, ViewContext, WeakView};
+use std::{ops::Range, rc::Weak};
+
+pub struct WindowInputHandler<V>
+where
+ V: InputHandler,
+{
+ pub cx: Weak<AppCell>,
+ pub window: AnyWindowHandle,
+ pub handler: WeakView<V>,
+}
+
+impl<V: InputHandler + 'static> PlatformInputHandler for WindowInputHandler<V> {
+ fn selected_text_range(&self) -> Option<std::ops::Range<usize>> {
+ self.update(|view, cx| view.selected_text_range(cx))
+ .flatten()
+ }
+
+ fn marked_text_range(&self) -> Option<std::ops::Range<usize>> {
+ self.update(|view, cx| view.marked_text_range(cx)).flatten()
+ }
+
+ fn text_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<String> {
+ self.update(|view, cx| view.text_for_range(range_utf16, cx))
+ .flatten()
+ }
+
+ fn replace_text_in_range(
+ &mut self,
+ replacement_range: Option<std::ops::Range<usize>>,
+ text: &str,
+ ) {
+ self.update(|view, cx| view.replace_text_in_range(replacement_range, text, cx));
+ }
+
+ fn replace_and_mark_text_in_range(
+ &mut self,
+ range_utf16: Option<std::ops::Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<std::ops::Range<usize>>,
+ ) {
+ self.update(|view, cx| {
+ view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
+ });
+ }
+
+ fn unmark_text(&mut self) {
+ self.update(|view, cx| view.unmark_text(cx));
+ }
+
+ fn bounds_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<crate::Bounds<f32>> {
+ self.update(|view, cx| view.bounds_for_range(range_utf16, cx))
+ .flatten()
+ }
+}
+
+impl<V: InputHandler + 'static> WindowInputHandler<V> {
+ fn update<T>(&self, f: impl FnOnce(&mut V, &mut ViewContext<V>) -> T) -> Option<T> {
+ let cx = self.cx.upgrade()?;
+ let mut cx = cx.borrow_mut();
+ cx.update_window(self.window, |_, cx| self.handler.update(cx, f).ok())
+ .ok()?
+ }
+}
+
+pub trait InputHandler: Sized {
+ fn text_for_range(&self, range: Range<usize>, cx: &mut ViewContext<Self>) -> Option<String>;
+ fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
+ fn replace_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ text: &str,
+ cx: &mut ViewContext<Self>,
+ );
+ fn replace_and_mark_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ );
+ fn bounds_for_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<crate::Bounds<f32>>;
+}
@@ -1,7 +1,7 @@
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{anyhow, Context, Result};
use collections::BTreeMap;
-use gpui::{AppContext, KeyBinding, SharedString};
+use gpui::{actions, Action, AppContext, KeyBinding, SharedString};
use schemars::{
gen::{SchemaGenerator, SchemaSettings},
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
@@ -137,8 +137,10 @@ impl KeymapFile {
}
}
+actions!(NoAction);
+
fn no_action() -> Box<dyn gpui::Action> {
- todo!()
+ NoAction.boxed_clone()
}
#[cfg(test)]
@@ -38,6 +38,7 @@ pub enum ComponentStory {
Palette,
Panel,
ProjectPanel,
+ Players,
RecentProjects,
Scroll,
Tab,
@@ -79,6 +80,7 @@ impl ComponentStory {
Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(),
Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(),
Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(),
+ Self::Players => cx.build_view(|_| theme2::PlayerStory).into(),
Self::Panel => cx.build_view(|cx| ui::PanelStory).into(),
Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(),
Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(),
@@ -5,6 +5,8 @@ edition = "2021"
publish = false
[features]
+default = ["stories"]
+stories = ["dep:itertools"]
test-support = [
"gpui/test-support",
"fs/test-support",
@@ -30,6 +32,7 @@ settings = { package = "settings2", path = "../settings2" }
toml.workspace = true
uuid.workspace = true
util = { path = "../util" }
+itertools = { version = "0.11.0", optional = true }
[dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
@@ -3,7 +3,7 @@ use std::sync::Arc;
use gpui::Hsla;
use refineable::Refineable;
-use crate::SyntaxTheme;
+use crate::{PlayerColors, SyntaxTheme};
#[derive(Clone)]
pub struct SystemColors {
@@ -13,33 +13,6 @@ pub struct SystemColors {
pub mac_os_traffic_light_green: Hsla,
}
-#[derive(Debug, Clone, Copy)]
-pub struct PlayerColor {
- pub cursor: Hsla,
- pub background: Hsla,
- pub selection: Hsla,
-}
-
-#[derive(Clone)]
-pub struct PlayerColors(pub Vec<PlayerColor>);
-
-impl PlayerColors {
- pub fn local(&self) -> PlayerColor {
- // todo!("use a valid color");
- *self.0.first().unwrap()
- }
-
- pub fn absent(&self) -> PlayerColor {
- // todo!("use a valid color");
- *self.0.last().unwrap()
- }
-
- pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
- let len = self.0.len() - 1;
- self.0[(participant_index as usize % len) + 1]
- }
-}
-
#[derive(Refineable, Clone, Debug)]
#[refineable(debug)]
pub struct StatusColors {
@@ -71,71 +44,213 @@ pub struct GitStatusColors {
#[refineable(debug, deserialize)]
pub struct ThemeColors {
pub border: Hsla,
+ /// Border color. Used for deemphasized borders, like a visual divider between two sections
pub border_variant: Hsla,
+ /// Border color. Used for focused elements, like keyboard focused list item.
pub border_focused: Hsla,
+ /// Border color. Used for selected elements, like an active search filter or selected checkbox.
pub border_selected: Hsla,
+ /// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
pub border_transparent: Hsla,
+ /// Border color. Used for disabled elements, like a disabled input or button.
pub border_disabled: Hsla,
+ /// Border color. Used for elevated surfaces, like a context menu, popup, or dialog.
pub elevated_surface_background: Hsla,
+ /// Background Color. Used for grounded surfaces like a panel or tab.
pub surface_background: Hsla,
+ /// Background Color. Used for the app background and blank panels or windows.
pub background: Hsla,
+ /// Background Color. Used for the background of an element that should have a different background than the surface it's on.
+ ///
+ /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
+ ///
+ /// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
pub element_background: Hsla,
+ /// Background Color. Used for the hover state of an element that should have a different background than the surface it's on.
+ ///
+ /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
pub element_hover: Hsla,
+ /// Background Color. Used for the active state of an element that should have a different background than the surface it's on.
+ ///
+ /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
pub element_active: Hsla,
+ /// Background Color. Used for the selected state of an element that should have a different background than the surface it's on.
+ ///
+ /// Selected states are triggered by the element being selected (or "activated") by the user.
+ ///
+ /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
pub element_selected: Hsla,
+ /// Background Color. Used for the disabled state of an element that should have a different background than the surface it's on.
+ ///
+ /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
pub element_disabled: Hsla,
- pub element_placeholder: Hsla,
- pub element_drop_target: Hsla,
+ /// Background Color. Used for the area that shows where a dragged element will be dropped.
+ pub drop_target_background: Hsla,
+ /// Border Color. Used to show the area that shows where a dragged element will be dropped.
+ // pub drop_target_border: Hsla,
+ /// Used for the background of a ghost element that should have the same background as the surface it's on.
+ ///
+ /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
+ ///
+ /// For an element that should have a different background than the surface it's on, use `element_background`.
pub ghost_element_background: Hsla,
+ /// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
+ ///
+ /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
pub ghost_element_hover: Hsla,
+ /// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
+ ///
+ /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
pub ghost_element_active: Hsla,
+ /// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
+ ///
+ /// Selected states are triggered by the element being selected (or "activated") by the user.
+ ///
+ /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
pub ghost_element_selected: Hsla,
+ /// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
+ ///
+ /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
pub ghost_element_disabled: Hsla,
+ /// Text Color. Default text color used for most text.
pub text: Hsla,
+ /// Text Color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
pub text_muted: Hsla,
+ /// Text Color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
pub text_placeholder: Hsla,
+ /// Text Color. Color used for text denoting disabled elements. Typically, the color is faded or grayed out to emphasize the disabled state.
pub text_disabled: Hsla,
+ /// Text Color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
pub text_accent: Hsla,
+ /// Fill Color. Used for the default fill color of an icon.
pub icon: Hsla,
+ /// Fill Color. Used for the muted or deemphasized fill color of an icon.
+ ///
+ /// This might be used to show an icon in an inactive pane, or to demphasize a series of icons to give them less visual weight.
pub icon_muted: Hsla,
+ /// Fill Color. Used for the disabled fill color of an icon.
+ ///
+ /// Disabled states are shown when a user cannot interact with an element, like a icon button.
pub icon_disabled: Hsla,
+ /// Fill Color. Used for the placeholder fill color of an icon.
+ ///
+ /// This might be used to show an icon in an input that disappears when the user enters text.
pub icon_placeholder: Hsla,
+ /// Fill Color. Used for the accent fill color of an icon.
+ ///
+ /// This might be used to show when a toggleable icon button is selected.
pub icon_accent: Hsla,
+
+ // ===
+ // UI Elements
+ // ===
pub status_bar_background: Hsla,
pub title_bar_background: Hsla,
pub toolbar_background: Hsla,
pub tab_bar_background: Hsla,
pub tab_inactive_background: Hsla,
pub tab_active_background: Hsla,
+ // pub panel_background: Hsla,
+ // pub pane_focused_border: Hsla,
+ // /// The color of the scrollbar thumb.
+ // pub scrollbar_thumb_background: Hsla,
+ // /// The color of the scrollbar thumb when hovered over.
+ // pub scrollbar_thumb_hover_background: Hsla,
+ // /// The border color of the scrollbar thumb.
+ // pub scrollbar_thumb_border: Hsla,
+ // /// The background color of the scrollbar track.
+ // pub scrollbar_track_background: Hsla,
+ // /// The border color of the scrollbar track.
+ // pub scrollbar_track_border: Hsla,
+ // /// The opacity of the scrollbar status marks, like diagnostic states and git status..
+ // pub scrollbar_status_opacity: Hsla,
+
+ // ===
+ // Editor
+ // ===
pub editor_background: Hsla,
+ // pub editor_inactive_background: Hsla,
pub editor_gutter_background: Hsla,
pub editor_subheader_background: Hsla,
pub editor_active_line_background: Hsla,
pub editor_highlighted_line_background: Hsla,
+ /// Text Color. Used for the text of the line number in the editor gutter.
pub editor_line_number: Hsla,
+ /// Text Color. Used for the text of the line number in the editor gutter when the line is highlighted.
pub editor_active_line_number: Hsla,
+ /// Text Color. Used to mark invisible characters in the editor.
+ ///
+ /// Example: spaces, tabs, carriage returns, etc.
pub editor_invisible: Hsla,
pub editor_wrap_guide: Hsla,
pub editor_active_wrap_guide: Hsla,
pub editor_document_highlight_read_background: Hsla,
pub editor_document_highlight_write_background: Hsla,
+
+ // ===
+ // Terminal
+ // ===
+ /// Terminal Background Color
pub terminal_background: Hsla,
+ /// Bright Black Color for ANSI Terminal
pub terminal_ansi_bright_black: Hsla,
+ /// Bright Red Color for ANSI Terminal
pub terminal_ansi_bright_red: Hsla,
+ /// Bright Green Color for ANSI Terminal
pub terminal_ansi_bright_green: Hsla,
+ /// Bright Yellow Color for ANSI Terminal
pub terminal_ansi_bright_yellow: Hsla,
+ /// Bright Blue Color for ANSI Terminal
pub terminal_ansi_bright_blue: Hsla,
+ /// Bright Magenta Color for ANSI Terminal
pub terminal_ansi_bright_magenta: Hsla,
+ /// Bright Cyan Color for ANSI Terminal
pub terminal_ansi_bright_cyan: Hsla,
+ /// Bright White Color for ANSI Terminal
pub terminal_ansi_bright_white: Hsla,
+ /// Black Color for ANSI Terminal
pub terminal_ansi_black: Hsla,
+ /// Red Color for ANSI Terminal
pub terminal_ansi_red: Hsla,
+ /// Green Color for ANSI Terminal
pub terminal_ansi_green: Hsla,
+ /// Yellow Color for ANSI Terminal
pub terminal_ansi_yellow: Hsla,
+ /// Blue Color for ANSI Terminal
pub terminal_ansi_blue: Hsla,
+ /// Magenta Color for ANSI Terminal
pub terminal_ansi_magenta: Hsla,
+ /// Cyan Color for ANSI Terminal
pub terminal_ansi_cyan: Hsla,
+ /// White Color for ANSI Terminal
pub terminal_ansi_white: Hsla,
+ // new colors
+
+ // ===
+ // Elevation
+ // ===
+ // elevation_0_shadow
+ // elevation_0_shadow_color
+ // elevation_1_shadow
+ // elevation_1_shadow_color
+ // elevation_2_shadow
+ // elevation_2_shadow_color
+ // elevation_3_shadow
+ // elevation_3_shadow_color
+ // elevation_4_shadow
+ // elevation_4_shadow_color
+ // elevation_5_shadow
+ // elevation_5_shadow_color
+
+ // ===
+ // UI Text
+ // ===
+ // pub headline: Hsla,
+ // pub paragraph: Hsla,
+ // pub link: Hsla,
+ // pub link_hover: Hsla,
+ // pub code_block_background: Hsla,
+ // pub code_block_border: Hsla,
}
#[derive(Refineable, Clone)]
@@ -3,12 +3,106 @@ use std::num::ParseIntError;
use gpui::{hsla, Hsla, Rgba};
use crate::{
- colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors},
+ colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors},
scale::{ColorScaleSet, ColorScales},
syntax::SyntaxTheme,
- ColorScale,
+ ColorScale, PlayerColor, PlayerColors,
};
+impl Default for PlayerColors {
+ fn default() -> Self {
+ Self(vec![
+ PlayerColor {
+ cursor: blue().dark().step_9(),
+ background: blue().dark().step_5(),
+ selection: blue().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: orange().dark().step_9(),
+ background: orange().dark().step_5(),
+ selection: orange().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: pink().dark().step_9(),
+ background: pink().dark().step_5(),
+ selection: pink().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: lime().dark().step_9(),
+ background: lime().dark().step_5(),
+ selection: lime().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: purple().dark().step_9(),
+ background: purple().dark().step_5(),
+ selection: purple().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: amber().dark().step_9(),
+ background: amber().dark().step_5(),
+ selection: amber().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: jade().dark().step_9(),
+ background: jade().dark().step_5(),
+ selection: jade().dark().step_3(),
+ },
+ PlayerColor {
+ cursor: red().dark().step_9(),
+ background: red().dark().step_5(),
+ selection: red().dark().step_3(),
+ },
+ ])
+ }
+}
+
+impl PlayerColors {
+ pub fn default_light() -> Self {
+ Self(vec![
+ PlayerColor {
+ cursor: blue().light().step_9(),
+ background: blue().light().step_4(),
+ selection: blue().light().step_3(),
+ },
+ PlayerColor {
+ cursor: orange().light().step_9(),
+ background: orange().light().step_4(),
+ selection: orange().light().step_3(),
+ },
+ PlayerColor {
+ cursor: pink().light().step_9(),
+ background: pink().light().step_4(),
+ selection: pink().light().step_3(),
+ },
+ PlayerColor {
+ cursor: lime().light().step_9(),
+ background: lime().light().step_4(),
+ selection: lime().light().step_3(),
+ },
+ PlayerColor {
+ cursor: purple().light().step_9(),
+ background: purple().light().step_4(),
+ selection: purple().light().step_3(),
+ },
+ PlayerColor {
+ cursor: amber().light().step_9(),
+ background: amber().light().step_4(),
+ selection: amber().light().step_3(),
+ },
+ PlayerColor {
+ cursor: jade().light().step_9(),
+ background: jade().light().step_4(),
+ selection: jade().light().step_3(),
+ },
+ PlayerColor {
+ cursor: red().light().step_9(),
+ background: red().light().step_4(),
+ selection: red().light().step_3(),
+ },
+ ])
+ }
+}
+
fn neutral() -> ColorScaleSet {
slate()
}
@@ -27,17 +121,17 @@ impl Default for SystemColors {
impl Default for StatusColors {
fn default() -> Self {
Self {
- conflict: red().dark().step_11(),
- created: grass().dark().step_11(),
- deleted: red().dark().step_11(),
- error: red().dark().step_11(),
- hidden: neutral().dark().step_11(),
- ignored: neutral().dark().step_11(),
- info: blue().dark().step_11(),
- modified: yellow().dark().step_11(),
- renamed: blue().dark().step_11(),
- success: grass().dark().step_11(),
- warning: yellow().dark().step_11(),
+ conflict: red().dark().step_9(),
+ created: grass().dark().step_9(),
+ deleted: red().dark().step_9(),
+ error: red().dark().step_9(),
+ hidden: neutral().dark().step_9(),
+ ignored: neutral().dark().step_9(),
+ info: blue().dark().step_9(),
+ modified: yellow().dark().step_9(),
+ renamed: blue().dark().step_9(),
+ success: grass().dark().step_9(),
+ warning: yellow().dark().step_9(),
}
}
}
@@ -45,43 +139,16 @@ impl Default for StatusColors {
impl Default for GitStatusColors {
fn default() -> Self {
Self {
- conflict: orange().dark().step_11(),
- created: grass().dark().step_11(),
- deleted: red().dark().step_11(),
- ignored: neutral().dark().step_11(),
- modified: yellow().dark().step_11(),
- renamed: blue().dark().step_11(),
+ conflict: orange().dark().step_9(),
+ created: grass().dark().step_9(),
+ deleted: red().dark().step_9(),
+ ignored: neutral().dark().step_9(),
+ modified: yellow().dark().step_9(),
+ renamed: blue().dark().step_9(),
}
}
}
-impl Default for PlayerColors {
- fn default() -> Self {
- Self(vec![
- PlayerColor {
- cursor: hsla(0.0, 0.0, 0.0, 1.0),
- background: hsla(0.0, 0.0, 0.0, 1.0),
- selection: hsla(0.0, 0.0, 0.0, 1.0),
- },
- PlayerColor {
- cursor: hsla(0.0, 0.0, 0.0, 1.0),
- background: hsla(0.0, 0.0, 0.0, 1.0),
- selection: hsla(0.0, 0.0, 0.0, 1.0),
- },
- PlayerColor {
- cursor: hsla(0.0, 0.0, 0.0, 1.0),
- background: hsla(0.0, 0.0, 0.0, 1.0),
- selection: hsla(0.0, 0.0, 0.0, 1.0),
- },
- PlayerColor {
- cursor: hsla(0.0, 0.0, 0.0, 1.0),
- background: hsla(0.0, 0.0, 0.0, 1.0),
- selection: hsla(0.0, 0.0, 0.0, 1.0),
- },
- ])
- }
-}
-
impl SyntaxTheme {
pub fn default_light() -> Self {
Self {
@@ -220,8 +287,7 @@ impl ThemeColors {
element_active: neutral().light().step_5(),
element_selected: neutral().light().step_5(),
element_disabled: neutral().light_alpha().step_3(),
- element_placeholder: neutral().light().step_11(),
- element_drop_target: blue().light_alpha().step_2(),
+ drop_target_background: blue().light_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().light().step_4(),
ghost_element_active: neutral().light().step_5(),
@@ -293,8 +359,7 @@ impl ThemeColors {
element_active: neutral().dark().step_5(),
element_selected: neutral().dark().step_5(),
element_disabled: neutral().dark_alpha().step_3(),
- element_placeholder: neutral().dark().step_11(),
- element_drop_target: blue().dark_alpha().step_2(),
+ drop_target_background: blue().dark_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().dark().step_4(),
ghost_element_active: neutral().dark().step_5(),
@@ -1,8 +1,8 @@
use std::sync::Arc;
use crate::{
- colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles},
- default_color_scales, Appearance, SyntaxTheme, Theme, ThemeFamily,
+ colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors, ThemeStyles},
+ default_color_scales, Appearance, PlayerColors, SyntaxTheme, Theme, ThemeFamily,
};
fn zed_pro_daylight() -> Theme {
@@ -15,7 +15,7 @@ fn zed_pro_daylight() -> Theme {
colors: ThemeColors::default_light(),
status: StatusColors::default(),
git: GitStatusColors::default(),
- player: PlayerColors::default(),
+ player: PlayerColors::default_light(),
syntax: Arc::new(SyntaxTheme::default_light()),
},
}
@@ -0,0 +1,170 @@
+use gpui::Hsla;
+
+#[derive(Debug, Clone, Copy)]
+pub struct PlayerColor {
+ pub cursor: Hsla,
+ pub background: Hsla,
+ pub selection: Hsla,
+}
+
+/// A collection of colors that are used to color players in the editor.
+///
+/// The first color is always the local player's color, usually a blue.
+///
+/// The rest of the default colors crisscross back and forth on the
+/// color wheel so that the colors are as distinct as possible.
+#[derive(Clone)]
+pub struct PlayerColors(pub Vec<PlayerColor>);
+
+impl PlayerColors {
+ pub fn local(&self) -> PlayerColor {
+ // todo!("use a valid color");
+ *self.0.first().unwrap()
+ }
+
+ pub fn absent(&self) -> PlayerColor {
+ // todo!("use a valid color");
+ *self.0.last().unwrap()
+ }
+
+ pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
+ let len = self.0.len() - 1;
+ self.0[(participant_index as usize % len) + 1]
+ }
+}
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::{ActiveTheme, Story};
+ use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
+
+ pub struct PlayerStory;
+
+ impl Render for PlayerStory {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx).child(
+ div()
+ .flex()
+ .flex_col()
+ .gap_4()
+ .child(Story::title_for::<_, PlayerColors>(cx))
+ .child(Story::label(cx, "Player Colors"))
+ .child(
+ div()
+ .flex()
+ .flex_col()
+ .gap_1()
+ .child(
+ div().flex().gap_1().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div().w_8().h_8().rounded_md().bg(player.cursor)
+ }),
+ ),
+ )
+ .child(div().flex().gap_1().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div().w_8().h_8().rounded_md().bg(player.background)
+ }),
+ ))
+ .child(div().flex().gap_1().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div().w_8().h_8().rounded_md().bg(player.selection)
+ }),
+ )),
+ )
+ .child(Story::label(cx, "Avatar Rings"))
+ .child(div().flex().gap_1().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div()
+ .my_1()
+ .rounded_full()
+ .border_2()
+ .border_color(player.cursor)
+ .child(
+ img()
+ .rounded_full()
+ .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .size_6()
+ .bg(gpui::red()),
+ )
+ }),
+ ))
+ .child(Story::label(cx, "Player Backgrounds"))
+ .child(div().flex().gap_1().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div()
+ .my_1()
+ .rounded_xl()
+ .flex()
+ .items_center()
+ .h_8()
+ .py_0p5()
+ .px_1p5()
+ .bg(player.background)
+ .child(
+ div().relative().neg_mx_1().rounded_full().z_index(3)
+ .border_2()
+ .border_color(player.background)
+ .size(px(28.))
+ .child(
+ img()
+ .rounded_full()
+ .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .size(px(24.))
+ .bg(gpui::red()),
+ ),
+ ).child(
+ div().relative().neg_mx_1().rounded_full().z_index(2)
+ .border_2()
+ .border_color(player.background)
+ .size(px(28.))
+ .child(
+ img()
+ .rounded_full()
+ .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .size(px(24.))
+ .bg(gpui::red()),
+ ),
+ ).child(
+ div().relative().neg_mx_1().rounded_full().z_index(1)
+ .border_2()
+ .border_color(player.background)
+ .size(px(28.))
+ .child(
+ img()
+ .rounded_full()
+ .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .size(px(24.))
+ .bg(gpui::red()),
+ ),
+ )
+ }),
+ ))
+ .child(Story::label(cx, "Player Selections"))
+ .child(div().flex().flex_col().gap_px().children(
+ cx.theme().players().0.clone().iter_mut().map(|player| {
+ div()
+ .flex()
+ .child(
+ div()
+ .flex()
+ .flex_none()
+ .rounded_sm()
+ .px_0p5()
+ .text_color(cx.theme().colors().text)
+ .bg(player.selection)
+ .child("The brown fox jumped over the lazy dog."),
+ )
+ .child(div().flex_1())
+ }),
+ )),
+ )
+ }
+ }
+}
@@ -0,0 +1,38 @@
+use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
+
+use crate::ActiveTheme;
+
+pub struct Story {}
+
+impl Story {
+ pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
+ div()
+ .size_full()
+ .flex()
+ .flex_col()
+ .pt_2()
+ .px_4()
+ .font("Zed Mono")
+ .bg(cx.theme().colors().background)
+ }
+
+ pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Component<V> {
+ div()
+ .text_xl()
+ .text_color(cx.theme().colors().text)
+ .child(title.to_owned())
+ }
+
+ pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Component<V> {
+ Self::title(cx, std::any::type_name::<T>())
+ }
+
+ pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Component<V> {
+ div()
+ .mt_4()
+ .mb_2()
+ .text_xs()
+ .text_color(cx.theme().colors().text)
+ .child(label.to_owned())
+ }
+}
@@ -1,6 +1,7 @@
mod colors;
mod default_colors;
mod default_theme;
+mod players;
mod registry;
mod scale;
mod settings;
@@ -14,6 +15,7 @@ use ::settings::Settings;
pub use colors::*;
pub use default_colors::*;
pub use default_theme::*;
+pub use players::*;
pub use registry::*;
pub use scale::*;
pub use settings::*;
@@ -120,3 +122,8 @@ pub struct DiagnosticStyle {
pub hint: Hsla,
pub ignored: Hsla,
}
+
+#[cfg(feature = "stories")]
+mod story;
+#[cfg(feature = "stories")]
+pub use story::*;
@@ -140,8 +140,7 @@ impl<'a> Debug for ThemeColorsRefinementPrinter<'a> {
("element_active", self.0.element_active),
("element_selected", self.0.element_selected),
("element_disabled", self.0.element_disabled),
- ("element_placeholder", self.0.element_placeholder),
- ("element_drop_target", self.0.element_drop_target),
+ ("drop_target_background", self.0.drop_target_background),
("ghost_element_background", self.0.ghost_element_background),
("ghost_element_hover", self.0.ghost_element_hover),
("ghost_element_active", self.0.ghost_element_active),
@@ -127,7 +127,7 @@ impl Tab {
div()
.id(self.id.clone())
.on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
- .drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().element_drop_target))
+ .drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
.on_drop(|_view, state: View<TabDragState>, cx| {
eprintln!("{:?}", state.read(cx));
})
@@ -1,8 +1,8 @@
use std::{any::TypeId, sync::Arc};
use gpui::{
- div, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render,
- StatelessInteractive, View, ViewContext,
+ div, AnyView, AppContext, DispatchPhase, Div, ParentElement, Render, StatelessInteractive,
+ View, ViewContext,
};
use crate::Workspace;
@@ -28,10 +28,6 @@ struct ToggleModal {
name: String,
}
-// complete change of plan?
-// on_action(ToggleModal{ name})
-// register_modal(name, |workspace, cx| { ... })
-
impl ModalRegistry {
pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
where
@@ -40,12 +36,10 @@ impl ModalRegistry {
{
let build_view = Arc::new(build_view);
- dbg!("yonder");
self.registered_modals.push((
TypeId::of::<A>(),
Box::new(move |mut div| {
let build_view = build_view.clone();
- dbg!("this point");
div.on_action(
move |workspace: &mut Workspace,
@@ -75,9 +69,7 @@ impl ModalLayer {
Self { open_modal: None }
}
- pub fn render(&self, cx: &ViewContext<Workspace>) -> impl Component<Workspace> {
- dbg!("rendering ModalLayer");
-
+ pub fn render(&self, workspace: &Workspace, cx: &ViewContext<Workspace>) -> Div<Workspace> {
let mut div = div();
// div, c workspace.toggle_modal()div.on_action()) {
@@ -2694,7 +2694,7 @@ impl Workspace {
.any(|item| item.has_conflict(cx) || item.is_dirty(cx));
if is_edited != self.window_edited {
self.window_edited = is_edited;
- todo!()
+ // todo!()
// cx.set_window_edited(self.window_edited)
}
}
@@ -3707,7 +3707,9 @@ impl Render for Workspace {
.bg(cx.theme().colors().background)
.child(self.render_titlebar(cx))
.child(
- div()
+ self.modal_layer
+ .read(cx)
+ .render(self, cx)
.flex_1()
.w_full()
.flex()
@@ -3840,8 +3842,6 @@ impl Render for Workspace {
// .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
// ),
)
- // .child(self.modal_layer.clone())
- .child(self.modal_layer.read(cx).render(cx))
}
}