Detailed changes
@@ -31,6 +31,11 @@ jobs:
target: x86_64-apple-darwin
profile: minimal
+ - name: Install Node
+ uses: actions/setup-node@v2
+ with:
+ node-version: '16'
+
- name: Checkout repo
uses: actions/checkout@v2
with:
@@ -6,3 +6,4 @@
/crates/collab/.env.toml
/crates/collab/static/styles.css
/vendor/bin
+/assets/themes/*.json
@@ -1 +0,0 @@
-collaborators = ["nathansobo", "as-cii", "maxbrunsfeld", "iamnbutler", "gibusu", "Kethku"]
@@ -909,6 +909,7 @@ dependencies = [
"fuzzy",
"gpui",
"picker",
+ "project",
"serde_json",
"settings",
"theme",
@@ -18,7 +18,10 @@
"cmd-s": "workspace::Save",
"cmd-=": "zed::IncreaseBufferFontSize",
"cmd--": "zed::DecreaseBufferFontSize",
- "cmd-,": "zed::OpenSettings"
+ "cmd-,": "zed::OpenSettings",
+ "cmd-q": "zed::Quit",
+ "cmd-n": "workspace::OpenNew",
+ "cmd-o": "workspace::Open"
}
},
{
@@ -210,7 +213,6 @@
"bindings": {
"cmd-shift-F": "project_search::Deploy",
"cmd-k cmd-t": "theme_selector::Toggle",
- "cmd-k t": "theme_selector::Reload",
"cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle",
"cmd-p": "file_finder::Toggle",
@@ -470,6 +470,19 @@ impl Server {
state.unregister_project(request.payload.project_id, request.sender_id)?;
(state.user_id_for_connection(request.sender_id)?, project)
};
+
+ broadcast(
+ request.sender_id,
+ project.guests.keys().copied(),
+ |conn_id| {
+ self.peer.send(
+ conn_id,
+ proto::UnregisterProject {
+ project_id: request.payload.project_id,
+ },
+ )
+ },
+ );
for (_, receipts) in project.join_requests {
for receipt in receipts {
self.peer.respond(
@@ -1665,7 +1678,7 @@ mod tests {
use gpui::{
executor::{self, Deterministic},
geometry::vector::vec2f,
- ModelHandle, TestAppContext, ViewHandle,
+ ModelHandle, Task, TestAppContext, ViewHandle,
};
use language::{
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
@@ -1697,7 +1710,7 @@ mod tests {
time::Duration,
};
use theme::ThemeRegistry;
- use workspace::{Item, SplitDirection, ToggleFollow, Workspace, WorkspaceParams};
+ use workspace::{Item, SplitDirection, ToggleFollow, Workspace};
#[cfg(test)]
#[ctor::ctor]
@@ -1930,6 +1943,14 @@ mod tests {
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
.await
.unwrap();
+
+ // When client A (the host) leaves, the project gets unshared and guests are notified.
+ cx_a.update(|_| drop(project_a));
+ deterministic.run_until_parked();
+ project_b2.read_with(cx_b, |project, _| {
+ assert!(project.is_read_only());
+ assert!(project.collaborators().is_empty());
+ });
}
#[gpui::test(iterations = 10)]
@@ -4357,13 +4378,7 @@ mod tests {
// Join the project as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
- let mut params = cx_b.update(WorkspaceParams::test);
- params.languages = lang_registry.clone();
- params.project = project_b.clone();
- params.client = client_b.client.clone();
- params.user_store = client_b.user_store.clone();
-
- let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
+ let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), true, cx)
@@ -4598,13 +4613,7 @@ mod tests {
// Join the worktree as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
- let mut params = cx_b.update(WorkspaceParams::test);
- params.languages = lang_registry.clone();
- params.project = project_b.clone();
- params.client = client_b.client.clone();
- params.user_store = client_b.user_store.clone();
-
- let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
+ let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), true, cx)
@@ -6637,13 +6646,21 @@ mod tests {
})
});
+ let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
+ let app_state = Arc::new(workspace::AppState {
+ client: client.clone(),
+ user_store: user_store.clone(),
+ languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
+ themes: ThemeRegistry::new((), cx.font_cache()),
+ fs: FakeFs::new(cx.background()),
+ build_window_options: || Default::default(),
+ initialize_workspace: |_, _, _| unimplemented!(),
+ });
+
Channel::init(&client);
Project::init(&client);
- cx.update(|cx| {
- workspace::init(&client, cx);
- });
+ cx.update(|cx| workspace::init(app_state.clone(), cx));
- let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
client
.authenticate_and_connect(false, &cx.to_async())
.await
@@ -6882,23 +6899,7 @@ mod tests {
cx: &mut TestAppContext,
) -> ViewHandle<Workspace> {
let (window_id, _) = cx.add_window(|_| EmptyView);
- cx.add_view(window_id, |cx| {
- let fs = project.read(cx).fs().clone();
- Workspace::new(
- &WorkspaceParams {
- fs,
- project: project.clone(),
- user_store: self.user_store.clone(),
- languages: self.language_registry.clone(),
- themes: ThemeRegistry::new((), cx.font_cache().clone()),
- channel_list: cx.add_model(|cx| {
- ChannelList::new(self.user_store.clone(), self.client.clone(), cx)
- }),
- client: self.client.clone(),
- },
- cx,
- )
- })
+ cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx))
}
async fn simulate_host(
@@ -12,6 +12,7 @@ editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
picker = { path = "../picker" }
+project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
theme = { path = "../theme" }
@@ -20,6 +21,7 @@ workspace = { path = "../workspace" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
+project = { path = "../project", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
@@ -299,7 +299,8 @@ mod tests {
use super::*;
use editor::Editor;
use gpui::TestAppContext;
- use workspace::{Workspace, WorkspaceParams};
+ use project::Project;
+ use workspace::{AppState, Workspace};
#[test]
fn test_humanize_action_name() {
@@ -319,15 +320,16 @@ mod tests {
#[gpui::test]
async fn test_command_palette(cx: &mut TestAppContext) {
- let params = cx.update(WorkspaceParams::test);
+ let app_state = cx.update(AppState::test);
cx.update(|cx| {
editor::init(cx);
- workspace::init(¶ms.client, cx);
+ workspace::init(app_state.clone(), cx);
init(cx);
});
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+ let project = Project::test(app_state.fs.clone(), [], cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let editor = cx.add_view(window_id, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);
@@ -23,7 +23,7 @@ use theme::IconButton;
use workspace::{
menu::{Confirm, SelectNext, SelectPrev},
sidebar::SidebarItem,
- AppState, JoinProject, Workspace,
+ JoinProject, Workspace,
};
impl_actions!(
@@ -60,7 +60,6 @@ pub struct ContactsPanel {
filter_editor: ViewHandle<Editor>,
collapsed_sections: Vec<Section>,
selection: Option<usize>,
- app_state: Arc<AppState>,
_maintain_contacts: Subscription,
}
@@ -92,7 +91,7 @@ pub fn init(cx: &mut MutableAppContext) {
impl ContactsPanel {
pub fn new(
- app_state: Arc<AppState>,
+ user_store: ModelHandle<UserStore>,
workspace: WeakViewHandle<Workspace>,
cx: &mut ViewContext<Self>,
) -> Self {
@@ -152,8 +151,8 @@ impl ContactsPanel {
}
});
- cx.subscribe(&app_state.user_store, {
- let user_store = app_state.user_store.downgrade();
+ cx.subscribe(&user_store, {
+ let user_store = user_store.downgrade();
move |_, _, event, cx| {
if let Some((workspace, user_store)) =
workspace.upgrade(cx).zip(user_store.upgrade(cx))
@@ -187,7 +186,6 @@ impl ContactsPanel {
let mut this = Self {
list_state: ListState::new(0, Orientation::Top, 1000., {
let this = cx.weak_handle();
- let app_state = app_state.clone();
move |ix, cx| {
let this = this.upgrade(cx).unwrap();
let this = this.read(cx);
@@ -234,7 +232,6 @@ impl ContactsPanel {
contact.clone(),
current_user_id,
*project_ix,
- app_state.clone(),
theme,
is_last_project_for_contact,
is_selected,
@@ -249,10 +246,8 @@ impl ContactsPanel {
entries: Default::default(),
match_candidates: Default::default(),
filter_editor,
- _maintain_contacts: cx
- .observe(&app_state.user_store, |this, _, cx| this.update_entries(cx)),
- user_store: app_state.user_store.clone(),
- app_state,
+ _maintain_contacts: cx.observe(&user_store, |this, _, cx| this.update_entries(cx)),
+ user_store,
};
this.update_entries(cx);
this
@@ -351,7 +346,6 @@ impl ContactsPanel {
contact: Arc<Contact>,
current_user_id: Option<u64>,
project_index: usize,
- app_state: Arc<AppState>,
theme: &theme::ContactsPanel,
is_last_project: bool,
is_selected: bool,
@@ -456,7 +450,6 @@ impl ContactsPanel {
cx.dispatch_global_action(JoinProject {
contact: contact.clone(),
project_index,
- app_state: app_state.clone(),
});
}
})
@@ -782,7 +775,6 @@ impl ContactsPanel {
.dispatch_global_action(JoinProject {
contact: contact.clone(),
project_index: *project_index,
- app_state: self.app_state.clone(),
}),
_ => {}
}
@@ -987,19 +979,20 @@ impl PartialEq for ContactEntry {
#[cfg(test)]
mod tests {
use super::*;
- use client::{proto, test::FakeServer, ChannelList, Client};
+ use client::{proto, test::FakeServer, Client};
use gpui::TestAppContext;
use language::LanguageRegistry;
+ use project::Project;
use theme::ThemeRegistry;
- use workspace::WorkspaceParams;
+ use workspace::AppState;
#[gpui::test]
async fn test_contact_panel(cx: &mut TestAppContext) {
let (app_state, server) = init(cx).await;
- let workspace_params = cx.update(WorkspaceParams::test);
- let workspace = cx.add_view(0, |cx| Workspace::new(&workspace_params, cx));
+ let project = Project::test(app_state.fs.clone(), [], cx).await;
+ let workspace = cx.add_view(0, |cx| Workspace::new(project, cx));
let panel = cx.add_view(0, |cx| {
- ContactsPanel::new(app_state.clone(), workspace.downgrade(), cx)
+ ContactsPanel::new(app_state.user_store.clone(), workspace.downgrade(), cx)
});
let get_users_request = server.receive::<proto::GetUsers>().await.unwrap();
@@ -1181,13 +1174,6 @@ mod tests {
let mut client = Client::new(http_client.clone());
let server = FakeServer::for_client(100, &mut client, &cx).await;
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
- let channel_list =
- cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
-
- let get_channels = server.receive::<proto::GetChannels>().await.unwrap();
- server
- .respond(get_channels.receipt(), Default::default())
- .await;
(
Arc::new(AppState {
@@ -1196,9 +1182,8 @@ mod tests {
client,
user_store: user_store.clone(),
fs,
- channel_list,
- build_window_options: || unimplemented!(),
- build_workspace: |_, _, _| unimplemented!(),
+ build_window_options: || Default::default(),
+ initialize_workspace: |_, _, _| {},
}),
server,
)
@@ -707,49 +707,42 @@ mod tests {
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
use serde_json::json;
use unindent::Unindent as _;
- use workspace::WorkspaceParams;
+ use workspace::AppState;
#[gpui::test]
async fn test_diagnostics(cx: &mut TestAppContext) {
- let params = cx.update(WorkspaceParams::test);
- let project = params.project.clone();
- let workspace = cx.add_view(0, |cx| Workspace::new(¶ms, cx));
-
- params
+ let app_state = cx.update(AppState::test);
+ app_state
.fs
.as_fake()
.insert_tree(
"/test",
json!({
"consts.rs": "
- const a: i32 = 'a';
- const b: i32 = c;
- "
+ const a: i32 = 'a';
+ const b: i32 = c;
+ "
.unindent(),
"main.rs": "
- fn main() {
- let x = vec![];
- let y = vec![];
- a(x);
- b(y);
- // comment 1
- // comment 2
- c(y);
- d(x);
- }
- "
+ fn main() {
+ let x = vec![];
+ let y = vec![];
+ a(x);
+ b(y);
+ // comment 1
+ // comment 2
+ c(y);
+ d(x);
+ }
+ "
.unindent(),
}),
)
.await;
- project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/test", true, cx)
- })
- .await
- .unwrap();
+ let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
+ let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx));
// Create some diagnostics
project.update(cx, |project, cx| {
@@ -862,7 +862,14 @@ impl Editor {
) -> Self {
let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
- Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
+ Self::new(
+ EditorMode::SingleLine,
+ buffer,
+ None,
+ field_editor_style,
+ None,
+ cx,
+ )
}
pub fn auto_height(
@@ -877,6 +884,7 @@ impl Editor {
buffer,
None,
field_editor_style,
+ None,
cx,
)
}
@@ -887,7 +895,7 @@ impl Editor {
cx: &mut ViewContext<Self>,
) -> Self {
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
- Self::new(EditorMode::Full, buffer, project, None, cx)
+ Self::new(EditorMode::Full, buffer, project, None, None, cx)
}
pub fn for_multibuffer(
@@ -895,7 +903,7 @@ impl Editor {
project: Option<ModelHandle<Project>>,
cx: &mut ViewContext<Self>,
) -> Self {
- Self::new(EditorMode::Full, buffer, project, None, cx)
+ Self::new(EditorMode::Full, buffer, project, None, None, cx)
}
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
@@ -904,6 +912,7 @@ impl Editor {
self.buffer.clone(),
self.project.clone(),
self.get_field_editor_theme,
+ Some(self.selections.clone()),
cx,
);
clone.scroll_position = self.scroll_position;
@@ -917,6 +926,7 @@ impl Editor {
buffer: ModelHandle<MultiBuffer>,
project: Option<ModelHandle<Project>>,
get_field_editor_theme: Option<GetFieldEditorTheme>,
+ selections: Option<SelectionsCollection>,
cx: &mut ViewContext<Self>,
) -> Self {
let display_map = cx.add_model(|cx| {
@@ -937,7 +947,8 @@ impl Editor {
cx.observe(&display_map, Self::on_display_map_changed)
.detach();
- let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
+ let selections = selections
+ .unwrap_or_else(|| SelectionsCollection::new(display_map.clone(), buffer.clone()));
let mut this = Self {
handle: cx.weak_handle(),
@@ -6025,7 +6036,10 @@ mod tests {
use std::{cell::RefCell, rc::Rc, time::Instant};
use text::Point;
use unindent::Unindent;
- use util::test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text};
+ use util::{
+ assert_set_eq,
+ test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
+ };
use workspace::{FollowableItem, ItemHandle};
#[gpui::test]
@@ -6304,6 +6318,26 @@ mod tests {
});
}
+ #[gpui::test]
+ fn test_clone_with_selections(cx: &mut gpui::MutableAppContext) {
+ let (text, selection_ranges) = marked_text_ranges(indoc! {"
+ The qu[ick brown
+ fox jum]ps over
+ the lazy dog
+ "});
+ cx.set_global(Settings::test(cx));
+ let buffer = MultiBuffer::build_simple(&text, cx);
+
+ let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+
+ let cloned_editor = view.update(cx, |view, cx| {
+ view.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
+ view.clone(cx)
+ });
+
+ assert_set_eq!(cloned_editor.selections.ranges(cx), selection_ranges);
+ }
+
#[gpui::test]
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
cx.set_global(Settings::test(cx));
@@ -8815,7 +8849,7 @@ mod tests {
let fs = FakeFs::new(cx.background().clone());
fs.insert_file("/file.rs", Default::default()).await;
- let project = Project::test(fs, ["/file.rs"], cx).await;
+ let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
@@ -8937,7 +8971,7 @@ mod tests {
let fs = FakeFs::new(cx.background().clone());
fs.insert_file("/file.rs", text).await;
- let project = Project::test(fs, ["/file.rs"], cx).await;
+ let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
@@ -9765,7 +9799,7 @@ mod tests {
}
fn build_editor(buffer: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
- Editor::new(EditorMode::Full, buffer, None, None, cx)
+ Editor::new(EditorMode::Full, buffer, None, None, None, cx)
}
fn assert_selection_ranges(
@@ -1517,7 +1517,7 @@ mod tests {
cx.set_global(Settings::test(cx));
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
- Editor::new(EditorMode::Full, buffer, None, None, cx)
+ Editor::new(EditorMode::Full, buffer, None, None, None, cx)
});
let element = EditorElement::new(
editor.downgrade(),
@@ -1539,7 +1539,7 @@ mod tests {
cx.set_global(Settings::test(cx));
let buffer = MultiBuffer::build_simple("", cx);
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
- Editor::new(EditorMode::Full, buffer, None, None, cx)
+ Editor::new(EditorMode::Full, buffer, None, None, None, cx)
});
editor.update(cx, |editor, cx| {
@@ -2479,6 +2479,7 @@ impl History {
self.undo_stack.pop();
false
} else {
+ self.redo_stack.clear();
let transaction = self.undo_stack.last_mut().unwrap();
transaction.last_edit_at = now;
for (buffer_id, transaction_id) in buffer_transactions {
@@ -2511,6 +2512,7 @@ impl History {
};
if !transaction.buffer_transactions.is_empty() {
self.undo_stack.push(transaction);
+ self.redo_stack.clear();
}
}
@@ -3935,6 +3937,16 @@ mod tests {
buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
+ // Redo stack gets cleared after an edit.
+ now += 2 * group_interval;
+ multibuffer.start_transaction_at(now, cx);
+ multibuffer.edit([(0..0, "X")], cx);
+ multibuffer.end_transaction_at(now, cx);
+ assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
+ multibuffer.redo(cx);
+ assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
+ multibuffer.undo(cx);
+ assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
multibuffer.undo(cx);
assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
});
@@ -22,6 +22,7 @@ pub struct PendingSelection {
pub mode: SelectMode,
}
+#[derive(Clone)]
pub struct SelectionsCollection {
display_map: ModelHandle<DisplayMap>,
buffer: ModelHandle<MultiBuffer>,
@@ -258,9 +258,10 @@ mod tests {
use super::*;
use editor::{Editor, Input};
use serde_json::json;
- use std::path::PathBuf;
- use workspace::menu::{Confirm, SelectNext};
- use workspace::{Workspace, WorkspaceParams};
+ use workspace::{
+ menu::{Confirm, SelectNext},
+ AppState, Workspace,
+ };
#[ctor::ctor]
fn init_logger() {
@@ -271,13 +272,13 @@ mod tests {
#[gpui::test]
async fn test_matching_paths(cx: &mut gpui::TestAppContext) {
- cx.update(|cx| {
+ let app_state = cx.update(|cx| {
super::init(cx);
editor::init(cx);
+ AppState::test(cx)
});
- let params = cx.update(WorkspaceParams::test);
- params
+ app_state
.fs
.as_fake()
.insert_tree(
@@ -291,16 +292,8 @@ mod tests {
)
.await;
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
cx.dispatch_action(window_id, Toggle);
let finder = cx.read(|cx| {
@@ -341,32 +334,26 @@ mod tests {
#[gpui::test]
async fn test_matching_cancellation(cx: &mut gpui::TestAppContext) {
- let params = cx.update(WorkspaceParams::test);
- let fs = params.fs.as_fake();
- fs.insert_tree(
- "/dir",
- json!({
- "hello": "",
- "goodbye": "",
- "halogen-light": "",
- "happiness": "",
- "height": "",
- "hi": "",
- "hiccup": "",
- }),
- )
- .await;
-
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/dir", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
+ let app_state = cx.update(AppState::test);
+ app_state
+ .fs
+ .as_fake()
+ .insert_tree(
+ "/dir",
+ json!({
+ "hello": "",
+ "goodbye": "",
+ "halogen-light": "",
+ "happiness": "",
+ "height": "",
+ "hi": "",
+ "hiccup": "",
+ }),
+ )
.await;
+
+ let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@@ -406,23 +393,20 @@ mod tests {
#[gpui::test]
async fn test_single_file_worktrees(cx: &mut gpui::TestAppContext) {
- let params = cx.update(WorkspaceParams::test);
- params
+ let app_state = cx.update(AppState::test);
+ app_state
.fs
.as_fake()
.insert_tree("/root", json!({ "the-parent-dir": { "the-file": "" } }))
.await;
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root/the-parent-dir/the-file", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+ let project = Project::test(
+ app_state.fs.clone(),
+ ["/root/the-parent-dir/the-file".as_ref()],
+ cx,
+ )
+ .await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@@ -451,10 +435,12 @@ mod tests {
finder.read_with(cx, |f, _| assert_eq!(f.matches.len(), 0));
}
- #[gpui::test(retries = 5)]
+ #[gpui::test]
async fn test_multiple_matches_with_same_relative_path(cx: &mut gpui::TestAppContext) {
- let params = cx.update(WorkspaceParams::test);
- params
+ cx.foreground().forbid_parking();
+
+ let app_state = cx.update(AppState::test);
+ app_state
.fs
.as_fake()
.insert_tree(
@@ -466,19 +452,13 @@ mod tests {
)
.await;
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
-
- workspace
- .update(cx, |workspace, cx| {
- workspace.open_paths(
- vec![PathBuf::from("/root/dir1"), PathBuf::from("/root/dir2")],
- cx,
- )
- })
- .await;
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
-
+ let project = Project::test(
+ app_state.fs.clone(),
+ ["/root/dir1".as_ref(), "/root/dir2".as_ref()],
+ cx,
+ )
+ .await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@@ -8,7 +8,7 @@ use std::{
ffi::{c_void, CStr, OsStr},
os::unix::ffi::OsStrExt,
path::{Path, PathBuf},
- slice,
+ ptr, slice,
sync::Arc,
time::Duration,
};
@@ -21,12 +21,28 @@ pub struct Event {
}
pub struct EventStream {
+ lifecycle: Arc<Mutex<Lifecycle>>,
+ state: Box<State>,
+}
+
+struct State {
+ latency: Duration,
+ paths: cf::CFMutableArrayRef,
+ callback: Option<Box<dyn FnMut(Vec<Event>) -> bool>>,
+ last_valid_event_id: Option<fs::FSEventStreamEventId>,
stream: fs::FSEventStreamRef,
- state: Arc<Mutex<Lifecycle>>,
- callback: Box<Option<RunCallback>>,
}
-type RunCallback = Box<dyn FnMut(Vec<Event>) -> bool>;
+impl Drop for State {
+ fn drop(&mut self) {
+ unsafe {
+ cf::CFRelease(self.paths);
+ fs::FSEventStreamStop(self.stream);
+ fs::FSEventStreamInvalidate(self.stream);
+ fs::FSEventStreamRelease(self.stream);
+ }
+ }
+}
enum Lifecycle {
New,
@@ -42,15 +58,6 @@ unsafe impl Send for Lifecycle {}
impl EventStream {
pub fn new(paths: &[&Path], latency: Duration) -> (Self, Handle) {
unsafe {
- let callback = Box::new(None);
- let stream_context = fs::FSEventStreamContext {
- version: 0,
- info: callback.as_ref() as *const _ as *mut c_void,
- retain: None,
- release: None,
- copy_description: None,
- };
-
let cf_paths =
cf::CFArrayCreateMutable(cf::kCFAllocatorDefault, 0, &cf::kCFTypeArrayCallBacks);
assert!(!cf_paths.is_null());
@@ -69,6 +76,20 @@ impl EventStream {
cf::CFRelease(cf_url);
}
+ let mut state = Box::new(State {
+ latency,
+ paths: cf_paths,
+ callback: None,
+ last_valid_event_id: None,
+ stream: ptr::null_mut(),
+ });
+ let stream_context = fs::FSEventStreamContext {
+ version: 0,
+ info: state.as_ref() as *const _ as *mut c_void,
+ retain: None,
+ release: None,
+ copy_description: None,
+ };
let stream = fs::FSEventStreamCreate(
cf::kCFAllocatorDefault,
Self::trampoline,
@@ -80,17 +101,15 @@ impl EventStream {
| fs::kFSEventStreamCreateFlagNoDefer
| fs::kFSEventStreamCreateFlagWatchRoot,
);
- cf::CFRelease(cf_paths);
-
- let state = Arc::new(Mutex::new(Lifecycle::New));
+ state.stream = stream;
+ let lifecycle = Arc::new(Mutex::new(Lifecycle::New));
(
EventStream {
- stream,
- state: state.clone(),
- callback,
+ lifecycle: lifecycle.clone(),
+ state,
},
- Handle(state),
+ Handle(lifecycle),
)
}
}
@@ -99,21 +118,24 @@ impl EventStream {
where
F: FnMut(Vec<Event>) -> bool + 'static,
{
- *self.callback = Some(Box::new(f));
+ self.state.callback = Some(Box::new(f));
unsafe {
let run_loop = cf::CFRunLoopGetCurrent();
{
- let mut state = self.state.lock();
+ let mut state = self.lifecycle.lock();
match *state {
Lifecycle::New => *state = Lifecycle::Running(run_loop),
Lifecycle::Running(_) => unreachable!(),
Lifecycle::Stopped => return,
}
}
- fs::FSEventStreamScheduleWithRunLoop(self.stream, run_loop, cf::kCFRunLoopDefaultMode);
- fs::FSEventStreamStart(self.stream);
+ fs::FSEventStreamScheduleWithRunLoop(
+ self.state.stream,
+ run_loop,
+ cf::kCFRunLoopDefaultMode,
+ );
+ fs::FSEventStreamStart(self.state.stream);
cf::CFRunLoopRun();
- fs::FSEventStreamRelease(self.stream);
}
}
@@ -129,8 +151,8 @@ impl EventStream {
let event_paths = event_paths as *const *const ::std::os::raw::c_char;
let e_ptr = event_flags as *mut u32;
let i_ptr = event_ids as *mut u64;
- let callback_ptr = (info as *mut Option<RunCallback>).as_mut().unwrap();
- let callback = if let Some(callback) = callback_ptr.as_mut() {
+ let state = (info as *mut State).as_mut().unwrap();
+ let callback = if let Some(callback) = state.callback.as_mut() {
callback
} else {
return;
@@ -139,30 +161,83 @@ impl EventStream {
let paths = slice::from_raw_parts(event_paths, num);
let flags = slice::from_raw_parts_mut(e_ptr, num);
let ids = slice::from_raw_parts_mut(i_ptr, num);
+ let mut stream_restarted = false;
+
+ // Sometimes FSEvents reports a "dropped" event, an indication that either the kernel
+ // or our code couldn't keep up with the sheer volume of file-system events that were
+ // generated. If we observed a valid event before this happens, we'll try to read the
+ // file-system journal by stopping the current stream and creating a new one starting at
+ // such event. Otherwise, we'll let invoke the callback with the dropped event, which
+ // will likely perform a re-scan of one of the root directories.
+ if flags
+ .iter()
+ .copied()
+ .filter_map(StreamFlags::from_bits)
+ .any(|flags| {
+ flags.contains(StreamFlags::USER_DROPPED)
+ || flags.contains(StreamFlags::KERNEL_DROPPED)
+ })
+ {
+ if let Some(last_valid_event_id) = state.last_valid_event_id.take() {
+ fs::FSEventStreamStop(state.stream);
+ fs::FSEventStreamInvalidate(state.stream);
+ fs::FSEventStreamRelease(state.stream);
+
+ let stream_context = fs::FSEventStreamContext {
+ version: 0,
+ info,
+ retain: None,
+ release: None,
+ copy_description: None,
+ };
+ let stream = fs::FSEventStreamCreate(
+ cf::kCFAllocatorDefault,
+ Self::trampoline,
+ &stream_context,
+ state.paths,
+ last_valid_event_id,
+ state.latency.as_secs_f64(),
+ fs::kFSEventStreamCreateFlagFileEvents
+ | fs::kFSEventStreamCreateFlagNoDefer
+ | fs::kFSEventStreamCreateFlagWatchRoot,
+ );
+
+ state.stream = stream;
+ fs::FSEventStreamScheduleWithRunLoop(
+ state.stream,
+ cf::CFRunLoopGetCurrent(),
+ cf::kCFRunLoopDefaultMode,
+ );
+ fs::FSEventStreamStart(state.stream);
+ stream_restarted = true;
+ }
+ }
- let mut events = Vec::with_capacity(num);
- for p in 0..num {
- let path_c_str = CStr::from_ptr(paths[p]);
- let path = PathBuf::from(OsStr::from_bytes(path_c_str.to_bytes()));
- if let Some(flag) = StreamFlags::from_bits(flags[p]) {
- if flag.contains(StreamFlags::HISTORY_DONE) {
- events.clear();
+ if !stream_restarted {
+ let mut events = Vec::with_capacity(num);
+ for p in 0..num {
+ if let Some(flag) = StreamFlags::from_bits(flags[p]) {
+ if !flag.contains(StreamFlags::HISTORY_DONE) {
+ let path_c_str = CStr::from_ptr(paths[p]);
+ let path = PathBuf::from(OsStr::from_bytes(path_c_str.to_bytes()));
+ let event = Event {
+ event_id: ids[p],
+ flags: flag,
+ path,
+ };
+ state.last_valid_event_id = Some(event.event_id);
+ events.push(event);
+ }
} else {
- events.push(Event {
- event_id: ids[p],
- flags: flag,
- path,
- });
+ debug_assert!(false, "unknown flag set for fs event: {}", flags[p]);
}
- } else {
- debug_assert!(false, "unknown flag set for fs event: {}", flags[p]);
}
- }
- if !events.is_empty() {
- if !callback(events) {
- fs::FSEventStreamStop(stream_ref);
- cf::CFRunLoopStop(cf::CFRunLoopGetCurrent());
+ if !events.is_empty() {
+ if !callback(events) {
+ fs::FSEventStreamStop(stream_ref);
+ cf::CFRunLoopStop(cf::CFRunLoopGetCurrent());
+ }
}
}
}
@@ -154,7 +154,6 @@ pub struct Menu<'a> {
pub enum MenuItem<'a> {
Action {
name: &'a str,
- keystroke: Option<&'a str>,
action: Box<dyn Action>,
},
Separator,
@@ -193,6 +192,20 @@ impl App {
cx.borrow_mut().quit();
}
}));
+ foreground_platform.on_will_open_menu(Box::new({
+ let cx = app.0.clone();
+ move || {
+ let mut cx = cx.borrow_mut();
+ cx.keystroke_matcher.clear_pending();
+ }
+ }));
+ foreground_platform.on_validate_menu_command(Box::new({
+ let cx = app.0.clone();
+ move |action| {
+ let cx = cx.borrow_mut();
+ !cx.keystroke_matcher.has_pending_keystrokes() && cx.is_action_available(action)
+ }
+ }));
foreground_platform.on_menu_command(Box::new({
let cx = app.0.clone();
move |action| {
@@ -1070,7 +1083,8 @@ impl MutableAppContext {
}
pub fn set_menus(&mut self, menus: Vec<Menu>) {
- self.foreground_platform.set_menus(menus);
+ self.foreground_platform
+ .set_menus(menus, &self.keystroke_matcher);
}
fn prompt(
@@ -1364,6 +1378,26 @@ impl MutableAppContext {
})
}
+ pub fn is_action_available(&self, action: &dyn Action) -> bool {
+ let action_type = action.as_any().type_id();
+ if let Some(window_id) = self.cx.platform.key_window_id() {
+ if let Some((presenter, _)) = self.presenters_and_platform_windows.get(&window_id) {
+ let dispatch_path = presenter.borrow().dispatch_path(&self.cx);
+ for view_id in dispatch_path {
+ if let Some(view) = self.views.get(&(window_id, view_id)) {
+ let view_type = view.as_any().type_id();
+ if let Some(actions) = self.actions.get(&view_type) {
+ if actions.contains_key(&action_type) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ self.global_actions.contains_key(&action_type)
+ }
+
pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) {
let presenter = self
.presenters_and_platform_windows
@@ -215,12 +215,12 @@ where
self.autoscroll(scroll_max, size.y(), item_height);
let start = cmp::min(
- ((self.scroll_top() - self.padding_top) / item_height) as usize,
+ ((self.scroll_top() - self.padding_top) / item_height.max(1.)) as usize,
self.item_count,
);
let end = cmp::min(
self.item_count,
- start + (size.y() / item_height).ceil() as usize + 1,
+ start + (size.y() / item_height.max(1.)).ceil() as usize + 1,
);
if (start..end).contains(&sample_item_ix) {
@@ -123,6 +123,10 @@ impl Matcher {
self.pending.clear();
}
+ pub fn has_pending_keystrokes(&self) -> bool {
+ !self.pending.is_empty()
+ }
+
pub fn push_keystroke(
&mut self,
keystroke: Keystroke,
@@ -14,6 +14,7 @@ use crate::{
rect::{RectF, RectI},
vector::Vector2F,
},
+ keymap,
text_layout::{LineLayout, RunStyle},
Action, ClipboardItem, Menu, Scene,
};
@@ -72,7 +73,9 @@ pub(crate) trait ForegroundPlatform {
fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>);
- fn set_menus(&self, menus: Vec<Menu>);
+ fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
+ fn on_will_open_menu(&self, callback: Box<dyn FnMut()>);
+ fn set_menus(&self, menus: Vec<Menu>, matcher: &keymap::Matcher);
fn prompt_for_paths(
&self,
options: PathPromptOptions,
@@ -8,7 +8,35 @@ use cocoa::{
base::{id, nil, YES},
foundation::NSString as _,
};
-use std::{ffi::CStr, os::raw::c_char};
+use std::{borrow::Cow, ffi::CStr, os::raw::c_char};
+
+pub fn key_to_native(key: &str) -> Cow<str> {
+ use cocoa::appkit::*;
+ let code = match key {
+ "backspace" => 0x7F,
+ "up" => NSUpArrowFunctionKey,
+ "down" => NSDownArrowFunctionKey,
+ "left" => NSLeftArrowFunctionKey,
+ "right" => NSRightArrowFunctionKey,
+ "pageup" => NSPageUpFunctionKey,
+ "pagedown" => NSPageDownFunctionKey,
+ "delete" => NSDeleteFunctionKey,
+ "f1" => NSF1FunctionKey,
+ "f2" => NSF2FunctionKey,
+ "f3" => NSF3FunctionKey,
+ "f4" => NSF4FunctionKey,
+ "f5" => NSF5FunctionKey,
+ "f6" => NSF6FunctionKey,
+ "f7" => NSF7FunctionKey,
+ "f8" => NSF8FunctionKey,
+ "f9" => NSF9FunctionKey,
+ "f10" => NSF10FunctionKey,
+ "f11" => NSF11FunctionKey,
+ "f12" => NSF12FunctionKey,
+ _ => return Cow::Borrowed(key),
+ };
+ Cow::Owned(String::from_utf16(&[code]).unwrap())
+}
impl Event {
pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
@@ -1,7 +1,6 @@
-use super::{BoolExt as _, Dispatcher, FontSystem, Window};
+use super::{event::key_to_native, BoolExt as _, Dispatcher, FontSystem, Window};
use crate::{
- executor,
- keymap::Keystroke,
+ executor, keymap,
platform::{self, CursorStyle},
Action, ClipboardItem, Event, Menu, MenuItem,
};
@@ -90,6 +89,14 @@ unsafe fn build_classes() {
sel!(handleGPUIMenuItem:),
handle_menu_item as extern "C" fn(&mut Object, Sel, id),
);
+ decl.add_method(
+ sel!(validateMenuItem:),
+ validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool,
+ );
+ decl.add_method(
+ sel!(menuWillOpen:),
+ menu_will_open as extern "C" fn(&mut Object, Sel, id),
+ );
decl.add_method(
sel!(application:openURLs:),
open_urls as extern "C" fn(&mut Object, Sel, id, id),
@@ -108,14 +115,22 @@ pub struct MacForegroundPlatformState {
quit: Option<Box<dyn FnMut()>>,
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
+ validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
+ will_open_menu: Option<Box<dyn FnMut()>>,
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
menu_actions: Vec<Box<dyn Action>>,
}
impl MacForegroundPlatform {
- unsafe fn create_menu_bar(&self, menus: Vec<Menu>) -> id {
+ unsafe fn create_menu_bar(
+ &self,
+ menus: Vec<Menu>,
+ delegate: id,
+ keystroke_matcher: &keymap::Matcher,
+ ) -> id {
let menu_bar = NSMenu::new(nil).autorelease();
+ menu_bar.setDelegate_(delegate);
let mut state = self.0.borrow_mut();
state.menu_actions.clear();
@@ -126,6 +141,7 @@ impl MacForegroundPlatform {
let menu_name = menu_config.name;
menu.setTitle_(ns_string(menu_name));
+ menu.setDelegate_(delegate);
for item_config in menu_config.items {
let item;
@@ -134,19 +150,18 @@ impl MacForegroundPlatform {
MenuItem::Separator => {
item = NSMenuItem::separatorItem(nil);
}
- MenuItem::Action {
- name,
- keystroke,
- action,
- } => {
- if let Some(keystroke) = keystroke {
- let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
- panic!(
- "Invalid keystroke for menu item {}:{} - {:?}",
- menu_name, name, err
- )
- });
+ MenuItem::Action { name, action } => {
+ let mut keystroke = None;
+ if let Some(binding) = keystroke_matcher
+ .bindings_for_action_type(action.as_any().type_id())
+ .next()
+ {
+ if binding.keystrokes().len() == 1 {
+ keystroke = binding.keystrokes().first()
+ }
+ }
+ if let Some(keystroke) = keystroke {
let mut mask = NSEventModifierFlags::empty();
for (modifier, flag) in &[
(keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
@@ -162,7 +177,7 @@ impl MacForegroundPlatform {
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
- ns_string(&keystroke.key),
+ ns_string(key_to_native(&keystroke.key).as_ref()),
)
.autorelease();
item.setKeyEquivalentModifierMask_(mask);
@@ -239,10 +254,18 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
self.0.borrow_mut().menu_command = Some(callback);
}
- fn set_menus(&self, menus: Vec<Menu>) {
+ fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
+ self.0.borrow_mut().will_open_menu = Some(callback);
+ }
+
+ fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+ self.0.borrow_mut().validate_menu_command = Some(callback);
+ }
+
+ fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) {
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
- app.setMainMenu_(self.create_menu_bar(menus));
+ app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), keystroke_matcher));
}
}
@@ -740,6 +763,34 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
}
}
+extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
+ unsafe {
+ let mut result = false;
+ let platform = get_foreground_platform(this);
+ let mut platform = platform.0.borrow_mut();
+ if let Some(mut callback) = platform.validate_menu_command.take() {
+ let tag: NSInteger = msg_send![item, tag];
+ let index = tag as usize;
+ if let Some(action) = platform.menu_actions.get(index) {
+ result = callback(action.as_ref());
+ }
+ platform.validate_menu_command = Some(callback);
+ }
+ result
+ }
+}
+
+extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
+ unsafe {
+ let platform = get_foreground_platform(this);
+ let mut platform = platform.0.borrow_mut();
+ if let Some(mut callback) = platform.will_open_menu.take() {
+ callback();
+ platform.will_open_menu = Some(callback);
+ }
+ }
+}
+
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}
@@ -1,7 +1,7 @@
use super::{AppVersion, CursorStyle, WindowBounds};
use crate::{
geometry::vector::{vec2f, Vector2F},
- Action, ClipboardItem,
+ keymap, Action, ClipboardItem,
};
use anyhow::{anyhow, Result};
use parking_lot::Mutex;
@@ -73,8 +73,9 @@ impl super::ForegroundPlatform for ForegroundPlatform {
}
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {}
-
- fn set_menus(&self, _: Vec<crate::Menu>) {}
+ fn on_validate_menu_command(&self, _: Box<dyn FnMut(&dyn Action) -> bool>) {}
+ fn on_will_open_menu(&self, _: Box<dyn FnMut()>) {}
+ fn set_menus(&self, _: Vec<crate::Menu>, _: &keymap::Matcher) {}
fn prompt_for_paths(
&self,
@@ -497,7 +497,7 @@ impl Project {
#[cfg(any(test, feature = "test-support"))]
pub async fn test(
fs: Arc<dyn Fs>,
- root_paths: impl IntoIterator<Item = impl AsRef<Path>>,
+ root_paths: impl IntoIterator<Item = &Path>,
cx: &mut gpui::TestAppContext,
) -> ModelHandle<Project> {
let languages = Arc::new(LanguageRegistry::test());
@@ -528,6 +528,14 @@ impl Project {
&self.languages
}
+ pub fn client(&self) -> Arc<Client> {
+ self.client.clone()
+ }
+
+ pub fn user_store(&self) -> ModelHandle<UserStore> {
+ self.user_store.clone()
+ }
+
#[cfg(any(test, feature = "test-support"))]
pub fn check_invariants(&self, cx: &AppContext) {
if self.is_local() {
@@ -5294,7 +5302,7 @@ mod tests {
)
.unwrap();
- let project = Project::test(Arc::new(RealFs), [root_link_path], cx).await;
+ let project = Project::test(Arc::new(RealFs), [root_link_path.as_ref()], cx).await;
project.read_with(cx, |project, cx| {
let tree = project.worktrees(cx).next().unwrap().read(cx);
@@ -5378,7 +5386,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/the-root"], cx).await;
+ let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
project.update(cx, |project, _| {
project.languages.add(Arc::new(rust_language));
project.languages.add(Arc::new(json_language));
@@ -5714,7 +5722,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir/a.rs", "/dir/b.rs"], cx).await;
+ let project = Project::test(fs, ["/dir/a.rs".as_ref(), "/dir/b.rs".as_ref()], cx).await;
let buffer_a = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
@@ -5825,7 +5833,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let worktree_id =
project.read_with(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
@@ -5947,7 +5955,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@@ -6016,7 +6024,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@@ -6285,7 +6293,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
.await
@@ -6376,7 +6384,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
@@ -6530,7 +6538,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
.await
@@ -6686,7 +6694,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir/b.rs"], cx).await;
+ let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@@ -6780,7 +6788,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
@@ -6838,7 +6846,7 @@ mod tests {
)
.await;
- let project = Project::test(fs, ["/dir"], cx).await;
+ let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
@@ -6944,7 +6952,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
.await
@@ -6973,7 +6981,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/dir/file1"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir/file1".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
.await
@@ -6995,7 +7003,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({})).await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project.update(cx, |project, cx| {
project.create_buffer("", None, cx).unwrap()
});
@@ -7182,7 +7190,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
// Spawn multiple tasks to open paths, repeating some paths.
let (buffer_a_1, buffer_b, buffer_a_2) = project.update(cx, |p, cx| {
@@ -7227,7 +7235,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer1 = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
@@ -7359,7 +7367,7 @@ mod tests {
}),
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/the-file", cx))
.await
@@ -7444,7 +7452,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/the-dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/the-dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/the-dir/a.rs", cx))
.await
@@ -7708,7 +7716,7 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| {
@@ -7827,7 +7835,7 @@ mod tests {
}),
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
assert_eq!(
search(&project, SearchQuery::text("TWO", false, true), cx)
.await
@@ -913,11 +913,14 @@ mod tests {
use project::FakeFs;
use serde_json::json;
use std::{collections::HashSet, path::Path};
- use workspace::WorkspaceParams;
#[gpui::test]
async fn test_visible_list(cx: &mut gpui::TestAppContext) {
cx.foreground().forbid_parking();
+ cx.update(|cx| {
+ let settings = Settings::test(cx);
+ cx.set_global(settings);
+ });
let fs = FakeFs::new(cx.background());
fs.insert_tree(
@@ -956,9 +959,8 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/root1", "/root2"], cx).await;
- let params = cx.update(WorkspaceParams::test);
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+ let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
assert_eq!(
visible_entries_as_strings(&panel, 0..50, cx),
@@ -1005,6 +1007,10 @@ mod tests {
#[gpui::test(iterations = 30)]
async fn test_editing_files(cx: &mut gpui::TestAppContext) {
cx.foreground().forbid_parking();
+ cx.update(|cx| {
+ let settings = Settings::test(cx);
+ cx.set_global(settings);
+ });
let fs = FakeFs::new(cx.background());
fs.insert_tree(
@@ -1043,9 +1049,8 @@ mod tests {
)
.await;
- let project = Project::test(fs.clone(), ["/root1", "/root2"], cx).await;
- let params = cx.update(WorkspaceParams::test);
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+ let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
select_path(&panel, "root1", cx);
@@ -295,7 +295,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "test.rs": "" })).await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let _buffer = project
@@ -848,7 +848,7 @@ mod tests {
}),
)
.await;
- let project = Project::test(fs.clone(), ["/dir"], cx).await;
+ let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
let search_view = cx.add_view(Default::default(), |cx| {
ProjectSearchView::new(search.clone(), cx)
@@ -529,6 +529,16 @@ fn test_history() {
assert!(buffer.end_transaction_at(now).is_none());
buffer.undo();
assert_eq!(buffer.text(), "12cde6");
+
+ // Redo stack gets cleared after performing an edit.
+ buffer.edit([(0..0, "X")]);
+ assert_eq!(buffer.text(), "X12cde6");
+ buffer.redo();
+ assert_eq!(buffer.text(), "X12cde6");
+ buffer.undo();
+ assert_eq!(buffer.text(), "12cde6");
+ buffer.undo();
+ assert_eq!(buffer.text(), "123456");
}
#[test]
@@ -216,6 +216,7 @@ impl History {
self.undo_stack.pop();
None
} else {
+ self.redo_stack.clear();
let entry = self.undo_stack.last_mut().unwrap();
entry.last_edit_at = now;
Some(entry)
@@ -276,6 +277,7 @@ impl History {
last_edit_at: now,
suppress_grouping: false,
});
+ self.redo_stack.clear();
}
fn push_undo(&mut self, op_id: clock::Local) {
@@ -7,7 +7,7 @@ use picker::{Picker, PickerDelegate};
use settings::Settings;
use std::sync::Arc;
use theme::{Theme, ThemeRegistry};
-use workspace::Workspace;
+use workspace::{AppState, Workspace};
pub struct ThemeSelector {
registry: Arc<ThemeRegistry>,
@@ -21,10 +21,14 @@ pub struct ThemeSelector {
actions!(theme_selector, [Toggle, Reload]);
-pub fn init(cx: &mut MutableAppContext) {
- cx.add_action(ThemeSelector::toggle);
- cx.add_action(ThemeSelector::reload);
+pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
Picker::<ThemeSelector>::init(cx);
+ cx.add_action({
+ let theme_registry = app_state.themes.clone();
+ move |workspace, _: &Toggle, cx| {
+ ThemeSelector::toggle(workspace, theme_registry.clone(), cx)
+ }
+ });
}
pub enum Event {
@@ -64,8 +68,11 @@ impl ThemeSelector {
this
}
- fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
- let themes = workspace.themes();
+ fn toggle(
+ workspace: &mut Workspace,
+ themes: Arc<ThemeRegistry>,
+ cx: &mut ViewContext<Workspace>,
+ ) {
workspace.toggle_modal(cx, |_, cx| {
let this = cx.add_view(|cx| Self::new(themes, cx));
cx.subscribe(&this, Self::on_event).detach();
@@ -73,9 +80,9 @@ impl ThemeSelector {
});
}
- fn reload(workspace: &mut Workspace, _: &Reload, cx: &mut ViewContext<Workspace>) {
+ #[cfg(debug_assertions)]
+ pub fn reload(themes: Arc<ThemeRegistry>, cx: &mut MutableAppContext) {
let current_theme_name = cx.global::<Settings>().theme.name.clone();
- let themes = workspace.themes();
themes.clear();
match themes.get(¤t_theme_name) {
Ok(theme) => {
@@ -7,11 +7,12 @@ use editor::{display_map::ToDisplayPoint, Autoscroll};
use gpui::{json::json, keymap::Keystroke, ViewHandle};
use indoc::indoc;
use language::Selection;
+use project::Project;
use util::{
set_eq,
test::{marked_text, marked_text_ranges_by, SetEqError},
};
-use workspace::{WorkspaceHandle, WorkspaceParams};
+use workspace::{AppState, WorkspaceHandle};
use crate::{state::Operator, *};
@@ -30,7 +31,8 @@ impl<'a> VimTestContext<'a> {
settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap();
});
- let params = cx.update(WorkspaceParams::test);
+ let params = cx.update(AppState::test);
+ let project = Project::test(params.fs.clone(), [], cx).await;
cx.update(|cx| {
cx.update_global(|settings: &mut Settings, _| {
@@ -44,9 +46,8 @@ impl<'a> VimTestContext<'a> {
.insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
.await;
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
+ project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
@@ -920,7 +920,7 @@ impl NavHistory {
#[cfg(test)]
mod tests {
use super::*;
- use crate::WorkspaceParams;
+ use crate::AppState;
use gpui::{ModelHandle, TestAppContext, ViewContext};
use project::Project;
use std::sync::atomic::AtomicUsize;
@@ -929,8 +929,9 @@ mod tests {
async fn test_close_items(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
- let params = cx.update(WorkspaceParams::test);
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+ let app_state = cx.update(AppState::test);
+ let project = Project::test(app_state.fs.clone(), None, cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let item1 = cx.add_view(window_id, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
@@ -1019,8 +1020,9 @@ mod tests {
async fn test_prompting_only_on_last_item_for_entry(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
- let params = cx.update(WorkspaceParams::test);
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+ let app_state = cx.update(AppState::test);
+ let project = Project::test(app_state.fs.clone(), [], cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let item = cx.add_view(window_id, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
@@ -1,6 +1,6 @@
use crate::{
sidebar::{Side, ToggleSidebarItem},
- AppState, ToggleFollow,
+ AppState, ToggleFollow, Workspace,
};
use anyhow::Result;
use client::{proto, Client, Contact};
@@ -77,86 +77,87 @@ impl WaitingRoom {
) -> Self {
let project_id = contact.projects[project_index].id;
let client = app_state.client.clone();
- let _join_task = cx.spawn_weak({
- let contact = contact.clone();
- |this, mut cx| async move {
- let project = Project::remote(
- project_id,
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
- &mut cx,
- )
- .await;
+ let _join_task =
+ cx.spawn_weak({
+ let contact = contact.clone();
+ |this, mut cx| async move {
+ let project = Project::remote(
+ project_id,
+ app_state.client.clone(),
+ app_state.user_store.clone(),
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ &mut cx,
+ )
+ .await;
- if let Some(this) = this.upgrade(&cx) {
- this.update(&mut cx, |this, cx| {
- this.waiting = false;
- match project {
- Ok(project) => {
- cx.replace_root_view(|cx| {
- let mut workspace = (app_state.build_workspace)(
- project.clone(),
- &app_state,
- cx,
- );
- workspace.toggle_sidebar_item(
- &ToggleSidebarItem {
- side: Side::Left,
- item_index: 0,
- },
- cx,
- );
- if let Some((host_peer_id, _)) = project
- .read(cx)
- .collaborators()
- .iter()
- .find(|(_, collaborator)| collaborator.replica_id == 0)
- {
- if let Some(follow) = workspace
- .toggle_follow(&ToggleFollow(*host_peer_id), cx)
+ if let Some(this) = this.upgrade(&cx) {
+ this.update(&mut cx, |this, cx| {
+ this.waiting = false;
+ match project {
+ Ok(project) => {
+ cx.replace_root_view(|cx| {
+ let mut workspace = Workspace::new(project, cx);
+ (app_state.initialize_workspace)(
+ &mut workspace,
+ &app_state,
+ cx,
+ );
+ workspace.toggle_sidebar_item(
+ &ToggleSidebarItem {
+ side: Side::Left,
+ item_index: 0,
+ },
+ cx,
+ );
+ if let Some((host_peer_id, _)) =
+ workspace.project.read(cx).collaborators().iter().find(
+ |(_, collaborator)| collaborator.replica_id == 0,
+ )
{
- follow.detach_and_log_err(cx);
+ if let Some(follow) = workspace
+ .toggle_follow(&ToggleFollow(*host_peer_id), cx)
+ {
+ follow.detach_and_log_err(cx);
+ }
}
- }
- workspace
- });
- }
- Err(error @ _) => {
- let login = &contact.user.github_login;
- let message = match error {
- project::JoinProjectError::HostDeclined => {
- format!("@{} declined your request.", login)
- }
- project::JoinProjectError::HostClosedProject => {
- format!(
- "@{} closed their copy of {}.",
- login,
- humanize_list(
- &contact.projects[project_index]
- .worktree_root_names
+ workspace
+ });
+ }
+ Err(error @ _) => {
+ let login = &contact.user.github_login;
+ let message = match error {
+ project::JoinProjectError::HostDeclined => {
+ format!("@{} declined your request.", login)
+ }
+ project::JoinProjectError::HostClosedProject => {
+ format!(
+ "@{} closed their copy of {}.",
+ login,
+ humanize_list(
+ &contact.projects[project_index]
+ .worktree_root_names
+ )
)
- )
- }
- project::JoinProjectError::HostWentOffline => {
- format!("@{} went offline.", login)
- }
- project::JoinProjectError::Other(error) => {
- log::error!("error joining project: {}", error);
- "An error occurred.".to_string()
- }
- };
- this.message = message;
- cx.notify();
+ }
+ project::JoinProjectError::HostWentOffline => {
+ format!("@{} went offline.", login)
+ }
+ project::JoinProjectError::Other(error) => {
+ log::error!("error joining project: {}", error);
+ "An error occurred.".to_string()
+ }
+ };
+ this.message = message;
+ cx.notify();
+ }
}
- }
- })
- }
+ })
+ }
- Ok(())
- }
- });
+ Ok(())
+ }
+ });
Self {
project_id,
@@ -9,8 +9,7 @@ mod waiting_room;
use anyhow::{anyhow, Context, Result};
use client::{
- proto, Authenticate, ChannelList, Client, Contact, PeerId, Subscription, TypedEnvelope, User,
- UserStore,
+ proto, Authenticate, Client, Contact, PeerId, Subscription, TypedEnvelope, User, UserStore,
};
use clock::ReplicaId;
use collections::{hash_map, HashMap, HashSet};
@@ -75,6 +74,8 @@ type FollowableItemBuilders = HashMap<
actions!(
workspace,
[
+ Open,
+ OpenNew,
Unfollow,
Save,
ActivatePreviousPane,
@@ -83,16 +84,9 @@ actions!(
]
);
-#[derive(Clone)]
-pub struct Open(pub Arc<AppState>);
-
-#[derive(Clone)]
-pub struct OpenNew(pub Arc<AppState>);
-
#[derive(Clone)]
pub struct OpenPaths {
pub paths: Vec<PathBuf>,
- pub app_state: Arc<AppState>,
}
#[derive(Clone)]
@@ -102,31 +96,37 @@ pub struct ToggleFollow(pub PeerId);
pub struct JoinProject {
pub contact: Arc<Contact>,
pub project_index: usize,
- pub app_state: Arc<AppState>,
}
-impl_internal_actions!(
- workspace,
- [Open, OpenNew, OpenPaths, ToggleFollow, JoinProject]
-);
+impl_internal_actions!(workspace, [OpenPaths, ToggleFollow, JoinProject]);
-pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
+pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pane::init(cx);
cx.add_global_action(open);
- cx.add_global_action(move |action: &OpenPaths, cx: &mut MutableAppContext| {
- open_paths(&action.paths, &action.app_state, cx).detach();
+ cx.add_global_action({
+ let app_state = Arc::downgrade(&app_state);
+ move |action: &OpenPaths, cx: &mut MutableAppContext| {
+ if let Some(app_state) = app_state.upgrade() {
+ open_paths(&action.paths, &app_state, cx).detach();
+ }
+ }
});
- cx.add_global_action(move |action: &OpenNew, cx: &mut MutableAppContext| {
- open_new(&action.0, cx)
+ cx.add_global_action({
+ let app_state = Arc::downgrade(&app_state);
+ move |_: &OpenNew, cx: &mut MutableAppContext| {
+ if let Some(app_state) = app_state.upgrade() {
+ open_new(&app_state, cx)
+ }
+ }
});
- cx.add_global_action(move |action: &JoinProject, cx: &mut MutableAppContext| {
- join_project(
- action.contact.clone(),
- action.project_index,
- &action.app_state,
- cx,
- );
+ cx.add_global_action({
+ let app_state = Arc::downgrade(&app_state);
+ move |action: &JoinProject, cx: &mut MutableAppContext| {
+ if let Some(app_state) = app_state.upgrade() {
+ join_project(action.contact.clone(), action.project_index, &app_state, cx);
+ }
+ }
});
cx.add_async_action(Workspace::toggle_follow);
@@ -151,6 +151,7 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
workspace.activate_next_pane(cx)
});
+ let client = &app_state.client;
client.add_view_request_handler(Workspace::handle_follow);
client.add_view_message_handler(Workspace::handle_unfollow);
client.add_view_message_handler(Workspace::handle_update_followers);
@@ -188,10 +189,8 @@ pub struct AppState {
pub client: Arc<client::Client>,
pub user_store: ModelHandle<client::UserStore>,
pub fs: Arc<dyn fs::Fs>,
- pub channel_list: ModelHandle<client::ChannelList>,
pub build_window_options: fn() -> WindowOptions<'static>,
- pub build_workspace:
- fn(ModelHandle<Project>, &Arc<AppState>, &mut ViewContext<Workspace>) -> Workspace,
+ pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
}
pub trait Item: View {
@@ -636,20 +635,9 @@ impl Into<AnyViewHandle> for &dyn NotificationHandle {
}
}
-#[derive(Clone)]
-pub struct WorkspaceParams {
- pub project: ModelHandle<Project>,
- pub client: Arc<Client>,
- pub fs: Arc<dyn Fs>,
- pub languages: Arc<LanguageRegistry>,
- pub themes: Arc<ThemeRegistry>,
- pub user_store: ModelHandle<UserStore>,
- pub channel_list: ModelHandle<ChannelList>,
-}
-
-impl WorkspaceParams {
+impl AppState {
#[cfg(any(test, feature = "test-support"))]
- pub fn test(cx: &mut MutableAppContext) -> Self {
+ pub fn test(cx: &mut MutableAppContext) -> Arc<Self> {
let settings = Settings::test(cx);
cx.set_global(settings);
@@ -658,42 +646,16 @@ impl WorkspaceParams {
let http_client = client::test::FakeHttpClient::with_404_response();
let client = Client::new(http_client.clone());
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
- let project = Project::local(
- client.clone(),
- user_store.clone(),
- languages.clone(),
- fs.clone(),
- cx,
- );
- Self {
- project,
- channel_list: cx
- .add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
+ let themes = ThemeRegistry::new((), cx.font_cache().clone());
+ Arc::new(Self {
client,
- themes: ThemeRegistry::new((), cx.font_cache().clone()),
+ themes,
fs,
languages,
user_store,
- }
- }
-
- #[cfg(any(test, feature = "test-support"))]
- pub fn local(app_state: &Arc<AppState>, cx: &mut MutableAppContext) -> Self {
- Self {
- project: Project::local(
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
- cx,
- ),
- client: app_state.client.clone(),
- fs: app_state.fs.clone(),
- themes: app_state.themes.clone(),
- languages: app_state.languages.clone(),
- user_store: app_state.user_store.clone(),
- channel_list: app_state.channel_list.clone(),
- }
+ initialize_workspace: |_, _, _| {},
+ build_window_options: || Default::default(),
+ })
}
}
@@ -708,7 +670,6 @@ pub struct Workspace {
user_store: ModelHandle<client::UserStore>,
remote_entity_subscription: Option<Subscription>,
fs: Arc<dyn Fs>,
- themes: Arc<ThemeRegistry>,
modal: Option<AnyViewHandle>,
center: PaneGroup,
left_sidebar: ViewHandle<Sidebar>,
@@ -744,8 +705,8 @@ enum FollowerItem {
}
impl Workspace {
- pub fn new(params: &WorkspaceParams, cx: &mut ViewContext<Self>) -> Self {
- cx.observe(¶ms.project, |_, project, cx| {
+ pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
+ cx.observe(&project, |_, project, cx| {
if project.read(cx).is_read_only() {
cx.blur();
}
@@ -753,7 +714,7 @@ impl Workspace {
})
.detach();
- cx.subscribe(¶ms.project, move |this, project, event, cx| {
+ cx.subscribe(&project, move |this, project, event, cx| {
match event {
project::Event::RemoteIdChanged(remote_id) => {
this.project_remote_id_changed(*remote_id, cx);
@@ -785,8 +746,11 @@ impl Workspace {
cx.focus(&pane);
cx.emit(Event::PaneAdded(pane.clone()));
- let mut current_user = params.user_store.read(cx).watch_current_user().clone();
- let mut connection_status = params.client.status().clone();
+ let fs = project.read(cx).fs().clone();
+ let user_store = project.read(cx).user_store();
+ let client = project.read(cx).client();
+ let mut current_user = user_store.read(cx).watch_current_user().clone();
+ let mut connection_status = client.status().clone();
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
current_user.recv().await;
connection_status.recv().await;
@@ -826,14 +790,13 @@ impl Workspace {
active_pane: pane.clone(),
status_bar,
notifications: Default::default(),
- client: params.client.clone(),
+ client,
remote_entity_subscription: None,
- user_store: params.user_store.clone(),
- fs: params.fs.clone(),
- themes: params.themes.clone(),
+ user_store,
+ fs,
left_sidebar,
right_sidebar,
- project: params.project.clone(),
+ project,
leader_state: Default::default(),
follower_states_by_leader: Default::default(),
last_leaders_by_pane: Default::default(),
@@ -867,10 +830,6 @@ impl Workspace {
&self.project
}
- pub fn themes(&self) -> Arc<ThemeRegistry> {
- self.themes.clone()
- }
-
pub fn worktrees<'a>(
&self,
cx: &'a AppContext,
@@ -2203,8 +2162,7 @@ impl std::fmt::Debug for OpenPaths {
}
}
-fn open(action: &Open, cx: &mut MutableAppContext) {
- let app_state = action.0.clone();
+fn open(_: &Open, cx: &mut MutableAppContext) {
let mut paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: true,
@@ -2212,7 +2170,7 @@ fn open(action: &Open, cx: &mut MutableAppContext) {
});
cx.spawn(|mut cx| async move {
if let Some(paths) = paths.recv().await.flatten() {
- cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths, app_state }));
+ cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths }));
}
})
.detach();
@@ -2260,14 +2218,17 @@ pub fn open_paths(
.contains(&false);
cx.add_window((app_state.build_window_options)(), |cx| {
- let project = Project::local(
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
+ let mut workspace = Workspace::new(
+ Project::local(
+ app_state.client.clone(),
+ app_state.user_store.clone(),
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ cx,
+ ),
cx,
);
- let mut workspace = (app_state.build_workspace)(project, &app_state, cx);
+ (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
if contains_directory {
workspace.toggle_sidebar_item(
&ToggleSidebarItem {
@@ -2313,14 +2274,18 @@ pub fn join_project(
fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
- let project = Project::local(
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
+ let mut workspace = Workspace::new(
+ Project::local(
+ app_state.client.clone(),
+ app_state.user_store.clone(),
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ cx,
+ ),
cx,
);
- (app_state.build_workspace)(project, &app_state, cx)
+ (app_state.initialize_workspace)(&mut workspace, app_state, cx);
+ workspace
});
- cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone()));
+ cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew);
}
@@ -1,3 +1,31 @@
+use std::process::Command;
+
fn main() {
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.14");
+
+ let output = Command::new("npm")
+ .current_dir("../../styles")
+ .args(["ci"])
+ .output()
+ .expect("failed to run npm");
+ if !output.status.success() {
+ panic!(
+ "failed to install theme dependencies {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ }
+
+ let output = Command::new("npm")
+ .current_dir("../../styles")
+ .args(["run", "build-themes"])
+ .output()
+ .expect("failed to run npm");
+ if !output.status.success() {
+ panic!(
+ "build-themes script failed {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ }
+
+ println!("cargo:rerun-if-changed=../../styles");
}
@@ -12,7 +12,7 @@ use cli::{
use client::{
self,
http::{self, HttpClient},
- ChannelList, UserStore, ZED_SECRET_CLIENT_TOKEN,
+ UserStore, ZED_SECRET_CLIENT_TOKEN,
};
use fs::OpenOptions;
use futures::{
@@ -40,9 +40,9 @@ use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
use util::{ResultExt, TryFutureExt};
use workspace::{self, AppState, OpenNew, OpenPaths};
use zed::{
- self, build_window_options, build_workspace,
+ self, build_window_options,
fs::RealFs,
- languages, menus,
+ initialize_workspace, languages, menus,
settings_file::{settings_from_files, watch_keymap_file, WatchedJsonFile},
};
@@ -133,15 +133,12 @@ fn main() {
let client = client::Client::new(http.clone());
let mut languages = languages::build_language_registry(login_shell_env_loaded);
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
- let channel_list =
- cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
auto_update::init(http, client::ZED_SERVER_URL.clone(), cx);
project::Project::init(&client);
client::Channel::init(&client);
client::init(client.clone(), cx);
command_palette::init(cx);
- workspace::init(&client, cx);
editor::init(cx);
go_to_line::init(cx);
file_finder::init(cx);
@@ -162,6 +159,8 @@ fn main() {
cx.font_cache().clone(),
);
+ cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
+ .detach();
cx.spawn(|cx| watch_keymap_file(keymap_file, cx)).detach();
let settings = cx.background().block(settings_rx.next()).unwrap();
@@ -190,33 +189,33 @@ fn main() {
let app_state = Arc::new(AppState {
languages,
themes,
- channel_list,
client: client.clone(),
user_store,
fs,
build_window_options,
- build_workspace,
+ initialize_workspace,
});
+ workspace::init(app_state.clone(), cx);
journal::init(app_state.clone(), cx);
- theme_selector::init(cx);
+ theme_selector::init(app_state.clone(), cx);
zed::init(&app_state, cx);
- cx.set_menus(menus::menus(&app_state.clone()));
+ cx.set_menus(menus::menus());
if stdout_is_a_pty() {
cx.platform().activate(true);
let paths = collect_path_args();
if paths.is_empty() {
- cx.dispatch_global_action(OpenNew(app_state.clone()));
+ cx.dispatch_global_action(OpenNew);
} else {
- cx.dispatch_global_action(OpenPaths { paths, app_state });
+ cx.dispatch_global_action(OpenPaths { paths });
}
} else {
if let Ok(Some(connection)) = cli_connections_rx.try_next() {
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
.detach();
} else {
- cx.dispatch_global_action(OpenNew(app_state.clone()));
+ cx.dispatch_global_action(OpenNew);
}
cx.spawn(|cx| async move {
while let Some(connection) = cli_connections_rx.next().await {
@@ -440,6 +439,43 @@ fn load_embedded_fonts(app: &App) {
.unwrap();
}
+#[cfg(debug_assertions)]
+async fn watch_themes(
+ fs: Arc<dyn Fs>,
+ themes: Arc<ThemeRegistry>,
+ mut cx: AsyncAppContext,
+) -> Option<()> {
+ let mut events = fs
+ .watch("styles/src".as_ref(), Duration::from_millis(100))
+ .await;
+ while let Some(_) = events.next().await {
+ let output = Command::new("npm")
+ .current_dir("styles")
+ .args(["run", "build-themes"])
+ .output()
+ .await
+ .log_err()?;
+ if output.status.success() {
+ cx.update(|cx| theme_selector::ThemeSelector::reload(themes.clone(), cx))
+ } else {
+ eprintln!(
+ "build-themes script failed {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ }
+ }
+ Some(())
+}
+
+#[cfg(not(debug_assertions))]
+async fn watch_themes(
+ _fs: Arc<dyn Fs>,
+ _themes: Arc<ThemeRegistry>,
+ _cx: AsyncAppContext,
+) -> Option<()> {
+ None
+}
+
fn load_config_files(
app: &App,
fs: Arc<dyn Fs>,
@@ -1,33 +1,27 @@
-use crate::AppState;
use gpui::{Menu, MenuItem};
-use std::sync::Arc;
#[cfg(target_os = "macos")]
-pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
+pub fn menus() -> Vec<Menu<'static>> {
vec![
Menu {
name: "Zed",
items: vec![
MenuItem::Action {
name: "About Zedβ¦",
- keystroke: None,
action: Box::new(super::About),
},
MenuItem::Action {
name: "Check for Updates",
- keystroke: None,
action: Box::new(auto_update::Check),
},
MenuItem::Separator,
MenuItem::Action {
name: "Install CLI",
- keystroke: None,
action: Box::new(super::InstallCommandLineInterface),
},
MenuItem::Separator,
MenuItem::Action {
name: "Quit",
- keystroke: Some("cmd-q"),
action: Box::new(super::Quit),
},
],
@@ -37,14 +31,20 @@ pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
items: vec![
MenuItem::Action {
name: "New",
- keystroke: Some("cmd-n"),
- action: Box::new(workspace::OpenNew(state.clone())),
+ action: Box::new(workspace::OpenNew),
},
MenuItem::Separator,
MenuItem::Action {
name: "Openβ¦",
- keystroke: Some("cmd-o"),
- action: Box::new(workspace::Open(state.clone())),
+ action: Box::new(workspace::Open),
+ },
+ MenuItem::Action {
+ name: "Save",
+ action: Box::new(workspace::Save),
+ },
+ MenuItem::Action {
+ name: "Close Editor",
+ action: Box::new(workspace::CloseActiveItem),
},
],
},
@@ -53,30 +53,160 @@ pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
items: vec![
MenuItem::Action {
name: "Undo",
- keystroke: Some("cmd-z"),
action: Box::new(editor::Undo),
},
MenuItem::Action {
name: "Redo",
- keystroke: Some("cmd-Z"),
action: Box::new(editor::Redo),
},
MenuItem::Separator,
MenuItem::Action {
name: "Cut",
- keystroke: Some("cmd-x"),
action: Box::new(editor::Cut),
},
MenuItem::Action {
name: "Copy",
- keystroke: Some("cmd-c"),
action: Box::new(editor::Copy),
},
MenuItem::Action {
name: "Paste",
- keystroke: Some("cmd-v"),
action: Box::new(editor::Paste),
},
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Find",
+ action: Box::new(search::buffer_search::Deploy { focus: true }),
+ },
+ MenuItem::Action {
+ name: "Find In Project",
+ action: Box::new(search::project_search::Deploy),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Toggle Line Comment",
+ action: Box::new(editor::ToggleComments),
+ },
+ ],
+ },
+ Menu {
+ name: "Selection",
+ items: vec![
+ MenuItem::Action {
+ name: "Select All",
+ action: Box::new(editor::SelectAll),
+ },
+ MenuItem::Action {
+ name: "Expand Selection",
+ action: Box::new(editor::SelectLargerSyntaxNode),
+ },
+ MenuItem::Action {
+ name: "Shrink Selection",
+ action: Box::new(editor::SelectSmallerSyntaxNode),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Add Cursor Above",
+ action: Box::new(editor::AddSelectionAbove),
+ },
+ MenuItem::Action {
+ name: "Add Cursor Below",
+ action: Box::new(editor::AddSelectionBelow),
+ },
+ MenuItem::Action {
+ name: "Select Next Occurrence",
+ action: Box::new(editor::SelectNext {
+ replace_newest: false,
+ }),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Move Line Up",
+ action: Box::new(editor::MoveLineUp),
+ },
+ MenuItem::Action {
+ name: "Move Line Down",
+ action: Box::new(editor::MoveLineDown),
+ },
+ MenuItem::Action {
+ name: "Duplicate Selection",
+ action: Box::new(editor::DuplicateLine),
+ },
+ ],
+ },
+ Menu {
+ name: "View",
+ items: vec![
+ MenuItem::Action {
+ name: "Zoom In",
+ action: Box::new(super::IncreaseBufferFontSize),
+ },
+ MenuItem::Action {
+ name: "Zoom Out",
+ action: Box::new(super::DecreaseBufferFontSize),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Project Browser",
+ action: Box::new(workspace::sidebar::ToggleSidebarItemFocus {
+ side: workspace::sidebar::Side::Left,
+ item_index: 0,
+ }),
+ },
+ MenuItem::Action {
+ name: "Command Palette",
+ action: Box::new(command_palette::Toggle),
+ },
+ MenuItem::Action {
+ name: "Diagnostics",
+ action: Box::new(diagnostics::Deploy),
+ },
+ ],
+ },
+ Menu {
+ name: "Go",
+ items: vec![
+ MenuItem::Action {
+ name: "Back",
+ action: Box::new(workspace::GoBack { pane: None }),
+ },
+ MenuItem::Action {
+ name: "Forward",
+ action: Box::new(workspace::GoForward { pane: None }),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Go to File",
+ action: Box::new(file_finder::Toggle),
+ },
+ MenuItem::Action {
+ name: "Go to Symbol in Project",
+ action: Box::new(project_symbols::Toggle),
+ },
+ MenuItem::Action {
+ name: "Go to Symbol in Editor",
+ action: Box::new(outline::Toggle),
+ },
+ MenuItem::Action {
+ name: "Go to Definition",
+ action: Box::new(editor::GoToDefinition),
+ },
+ MenuItem::Action {
+ name: "Go to References",
+ action: Box::new(editor::FindAllReferences),
+ },
+ MenuItem::Action {
+ name: "Go to Line/Column",
+ action: Box::new(go_to_line::Toggle),
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Next Problem",
+ action: Box::new(editor::GoToNextDiagnostic),
+ },
+ MenuItem::Action {
+ name: "Previous Problem",
+ action: Box::new(editor::GoToPrevDiagnostic),
+ },
],
},
]
@@ -1,13 +1,3 @@
-use crate::{build_window_options, build_workspace, AppState};
-use assets::Assets;
-use client::{test::FakeHttpClient, ChannelList, Client, UserStore};
-use gpui::MutableAppContext;
-use language::LanguageRegistry;
-use project::fs::FakeFs;
-use settings::Settings;
-use std::sync::Arc;
-use theme::ThemeRegistry;
-
#[cfg(test)]
#[ctor::ctor]
fn init_logger() {
@@ -15,32 +5,3 @@ fn init_logger() {
env_logger::init();
}
}
-
-pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
- let settings = Settings::test(cx);
- editor::init(cx);
- cx.set_global(settings);
- let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
- let http = FakeHttpClient::with_404_response();
- let client = Client::new(http.clone());
- let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
- let languages = LanguageRegistry::test();
- languages.add(Arc::new(language::Language::new(
- language::LanguageConfig {
- name: "Rust".into(),
- path_suffixes: vec!["rs".to_string()],
- ..Default::default()
- },
- Some(tree_sitter_rust::language()),
- )));
- Arc::new(AppState {
- themes,
- languages: Arc::new(languages),
- channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
- client,
- user_store,
- fs: FakeFs::new(cx.background().clone()),
- build_window_options,
- build_workspace,
- })
-}
@@ -15,7 +15,7 @@ use gpui::{
actions,
geometry::vector::vec2f,
platform::{WindowBounds, WindowOptions},
- AsyncAppContext, ModelHandle, ViewContext,
+ AsyncAppContext, ViewContext,
};
use lazy_static::lazy_static;
pub use lsp;
@@ -31,7 +31,7 @@ use std::{
};
use util::ResultExt;
pub use workspace;
-use workspace::{AppState, Workspace, WorkspaceParams};
+use workspace::{AppState, Workspace};
actions!(
zed,
@@ -115,13 +115,13 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
settings::KeymapFileContent::load_defaults(cx);
}
-pub fn build_workspace(
- project: ModelHandle<Project>,
+pub fn initialize_workspace(
+ workspace: &mut Workspace,
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
-) -> Workspace {
+) {
cx.subscribe(&cx.handle(), {
- let project = project.clone();
+ let project = workspace.project().clone();
move |_, _, event, cx| {
if let workspace::Event::PaneAdded(pane) = event {
pane.update(cx, |pane, cx| {
@@ -139,22 +139,12 @@ pub fn build_workspace(
})
.detach();
- let workspace_params = WorkspaceParams {
- project,
- client: app_state.client.clone(),
- fs: app_state.fs.clone(),
- languages: app_state.languages.clone(),
- themes: app_state.themes.clone(),
- user_store: app_state.user_store.clone(),
- channel_list: app_state.channel_list.clone(),
- };
- let workspace = Workspace::new(&workspace_params, cx);
- let project = workspace.project().clone();
+ cx.emit(workspace::Event::PaneAdded(workspace.active_pane().clone()));
let theme_names = app_state.themes.list().collect();
let language_names = app_state.languages.language_names();
- project.update(cx, |project, cx| {
+ workspace.project().update(cx, |project, cx| {
let action_names = cx.all_action_names().collect::<Vec<_>>();
project.set_language_server_settings(serde_json::json!({
"json": {
@@ -172,9 +162,10 @@ pub fn build_workspace(
}));
});
- let project_panel = ProjectPanel::new(project, cx);
- let contact_panel =
- cx.add_view(|cx| ContactsPanel::new(app_state.clone(), workspace.weak_handle(), cx));
+ let project_panel = ProjectPanel::new(workspace.project().clone(), cx);
+ let contact_panel = cx.add_view(|cx| {
+ ContactsPanel::new(app_state.user_store.clone(), workspace.weak_handle(), cx)
+ });
workspace.left_sidebar().update(cx, |sidebar, cx| {
sidebar.add_item("icons/folder-tree-solid-14.svg", project_panel.into(), cx)
@@ -196,8 +187,6 @@ pub fn build_workspace(
status_bar.add_right_item(cursor_position, cx);
status_bar.add_right_item(auto_update, cx);
});
-
- workspace
}
pub fn build_window_options() -> WindowOptions<'static> {
@@ -287,14 +276,18 @@ fn open_config_file(
workspace.open_paths(vec![path.to_path_buf()], cx)
} else {
let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
- let project = Project::local(
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
+ let mut workspace = Workspace::new(
+ Project::local(
+ app_state.client.clone(),
+ app_state.user_store.clone(),
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ cx,
+ ),
cx,
);
- (app_state.build_workspace)(project, &app_state, cx)
+ (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
+ workspace
});
workspace.update(cx, |workspace, cx| {
workspace.open_paths(vec![path.to_path_buf()], cx)
@@ -313,43 +306,45 @@ mod tests {
use assets::Assets;
use editor::{Autoscroll, DisplayPoint, Editor};
use gpui::{AssetSource, MutableAppContext, TestAppContext, ViewHandle};
- use project::{Fs, ProjectPath};
+ use project::ProjectPath;
use serde_json::json;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
- use test::test_app_state;
use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME};
- use util::test::temp_tree;
use workspace::{
open_paths, pane, Item, ItemHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle,
};
#[gpui::test]
async fn test_open_paths_action(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
- let dir = temp_tree(json!({
- "a": {
- "aa": null,
- "ab": null,
- },
- "b": {
- "ba": null,
- "bb": null,
- },
- "c": {
- "ca": null,
- "cb": null,
- },
- }));
+ let app_state = init(cx);
+ app_state
+ .fs
+ .as_fake()
+ .insert_tree(
+ "/root",
+ json!({
+ "a": {
+ "aa": null,
+ "ab": null,
+ },
+ "b": {
+ "ba": null,
+ "bb": null,
+ },
+ "c": {
+ "ca": null,
+ "cb": null,
+ },
+ }),
+ )
+ .await;
cx.update(|cx| {
open_paths(
- &[
- dir.path().join("a").to_path_buf(),
- dir.path().join("b").to_path_buf(),
- ],
+ &[PathBuf::from("/root/a"), PathBuf::from("/root/b")],
&app_state,
cx,
)
@@ -357,7 +352,7 @@ mod tests {
.await;
assert_eq!(cx.window_ids().len(), 1);
- cx.update(|cx| open_paths(&[dir.path().join("a").to_path_buf()], &app_state, cx))
+ cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
.await;
assert_eq!(cx.window_ids().len(), 1);
let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
@@ -369,10 +364,7 @@ mod tests {
cx.update(|cx| {
open_paths(
- &[
- dir.path().join("b").to_path_buf(),
- dir.path().join("c").to_path_buf(),
- ],
+ &[PathBuf::from("/root/b"), PathBuf::from("/root/c")],
&app_state,
cx,
)
@@ -383,11 +375,8 @@ mod tests {
#[gpui::test]
async fn test_new_empty_workspace(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
- cx.update(|cx| {
- workspace::init(&app_state.client, cx);
- });
- cx.dispatch_global_action(workspace::OpenNew(app_state.clone()));
+ let app_state = init(cx);
+ cx.dispatch_global_action(workspace::OpenNew);
let window_id = *cx.window_ids().first().unwrap();
let workspace = cx.root_view::<Workspace>(window_id).unwrap();
let editor = workspace.update(cx, |workspace, cx| {
@@ -414,7 +403,7 @@ mod tests {
#[gpui::test]
async fn test_open_entry(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
+ let app_state = init(cx);
app_state
.fs
.as_fake()
@@ -429,18 +418,10 @@ mod tests {
}),
)
.await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
+
let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
let file2 = entries[1].clone();
@@ -535,7 +516,8 @@ mod tests {
#[gpui::test]
async fn test_open_paths(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
+ let app_state = init(cx);
+
let fs = app_state.fs.as_fake();
fs.insert_dir("/dir1").await;
fs.insert_dir("/dir2").await;
@@ -544,17 +526,8 @@ mod tests {
fs.insert_file("/dir2/b.txt", "".into()).await;
fs.insert_file("/dir3/c.txt", "".into()).await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/dir1", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+ let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
// Open a file within an existing worktree.
cx.update(|cx| {
@@ -655,19 +628,15 @@ mod tests {
#[gpui::test]
async fn test_save_conflicting_item(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
- let fs = app_state.fs.as_fake();
- fs.insert_tree("/root", json!({ "a.txt": "" })).await;
-
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
+ let app_state = init(cx);
+ app_state
+ .fs
+ .as_fake()
+ .insert_tree("/root", json!({ "a.txt": "" }))
+ .await;
+
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
// Open a file within an existing worktree.
cx.update(|cx| {
@@ -687,7 +656,11 @@ mod tests {
editor.handle_input(&editor::Input("x".into()), cx)
})
});
- fs.insert_file("/root/a.txt", "changed".to_string()).await;
+ app_state
+ .fs
+ .as_fake()
+ .insert_file("/root/a.txt", "changed".to_string())
+ .await;
editor
.condition(&cx, |editor, cx| editor.has_conflict(cx))
.await;
@@ -704,21 +677,16 @@ mod tests {
#[gpui::test]
async fn test_open_and_save_new_file(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
+ let app_state = init(cx);
app_state.fs.as_fake().insert_dir("/root").await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
+
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ project.update(cx, |project, _| project.languages().add(rust_lang()));
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
// Create a new untitled buffer
- cx.dispatch_action(window_id, OpenNew(app_state.clone()));
+ cx.dispatch_action(window_id, OpenNew);
let editor = workspace.read_with(cx, |workspace, cx| {
workspace
.active_item(cx)
@@ -773,18 +741,11 @@ mod tests {
// Open the same newly-created file in another pane item. The new editor should reuse
// the same buffer.
- cx.dispatch_action(window_id, OpenNew(app_state.clone()));
+ cx.dispatch_action(window_id, OpenNew);
workspace
.update(cx, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
- workspace.open_path(
- ProjectPath {
- worktree_id: worktree.read(cx).id(),
- path: Path::new("the-new-name.rs").into(),
- },
- true,
- cx,
- )
+ workspace.open_path((worktree.read(cx).id(), "the-new-name.rs"), true, cx)
})
.await
.unwrap();
@@ -805,13 +766,15 @@ mod tests {
#[gpui::test]
async fn test_setting_language_when_saving_as_single_file_worktree(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
+ let app_state = init(cx);
app_state.fs.as_fake().insert_dir("/root").await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
+
+ let project = Project::test(app_state.fs.clone(), [], cx).await;
+ project.update(cx, |project, _| project.languages().add(rust_lang()));
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
// Create a new untitled buffer
- cx.dispatch_action(window_id, OpenNew(app_state.clone()));
+ cx.dispatch_action(window_id, OpenNew);
let editor = workspace.read_with(cx, |workspace, cx| {
workspace
.active_item(cx)
@@ -842,10 +805,9 @@ mod tests {
#[gpui::test]
async fn test_pane_actions(cx: &mut TestAppContext) {
- cx.foreground().forbid_parking();
+ init(cx);
- cx.update(|cx| pane::init(cx));
- let app_state = cx.update(test_app_state);
+ let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
@@ -861,17 +823,9 @@ mod tests {
)
.await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
+
let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
@@ -926,7 +880,7 @@ mod tests {
#[gpui::test]
async fn test_navigation(cx: &mut TestAppContext) {
- let app_state = cx.update(test_app_state);
+ let app_state = init(cx);
app_state
.fs
.as_fake()
@@ -941,17 +895,10 @@ mod tests {
}),
)
.await;
- let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
- let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
- params
- .project
- .update(cx, |project, cx| {
- project.find_or_create_local_worktree("/root", true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
+
+ let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
+
let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
let file2 = entries[1].clone();
@@ -990,7 +937,7 @@ mod tests {
editor.newline(&Default::default(), cx);
editor.move_down(&Default::default(), cx);
editor.move_down(&Default::default(), cx);
- editor.save(params.project.clone(), cx)
+ editor.save(project.clone(), cx)
})
.await
.unwrap();
@@ -1104,7 +1051,6 @@ mod tests {
.unwrap();
app_state
.fs
- .as_fake()
.remove_file(Path::new("/root/a/file2"), Default::default())
.await
.unwrap();
@@ -1219,4 +1165,29 @@ mod tests {
}
assert!(has_default_theme);
}
+
+ fn init(cx: &mut TestAppContext) -> Arc<AppState> {
+ cx.foreground().forbid_parking();
+ cx.update(|cx| {
+ let mut app_state = AppState::test(cx);
+ let state = Arc::get_mut(&mut app_state).unwrap();
+ state.initialize_workspace = initialize_workspace;
+ state.build_window_options = build_window_options;
+ workspace::init(app_state.clone(), cx);
+ editor::init(cx);
+ pane::init(cx);
+ app_state
+ })
+ }
+
+ fn rust_lang() -> Arc<language::Language> {
+ Arc::new(language::Language::new(
+ language::LanguageConfig {
+ name: "Rust".into(),
+ path_suffixes: vec!["rs".to_string()],
+ ..Default::default()
+ },
+ Some(tree_sitter_rust::language()),
+ ))
+ }
}
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -e
-
-cd styles
-npm install
-npm run build
@@ -1,8 +0,0 @@
-{
- "watch": [
- "./**/*"
- ],
- "ext": "ts",
- "ignore": [],
- "exec": "ts-node src/buildThemes.ts"
-}
@@ -13,7 +13,6 @@
"@types/node": "^17.0.23",
"case-anything": "^2.1.10",
"chroma-js": "^2.4.2",
- "nodemon": "^2.0.15",
"ts-node": "^10.7.0"
}
},
@@ -36,25 +35,6 @@
"node": ">=12"
}
},
- "node_modules/@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
- "dependencies": {
- "defer-to-connect": "^1.0.1"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
@@ -85,11 +65,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
"integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
},
- "node_modules/abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
- },
"node_modules/acorn": {
"version": "8.7.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
@@ -109,157 +84,11 @@
"node": ">=0.4.0"
}
},
- "node_modules/ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "dependencies": {
- "string-width": "^4.1.0"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "node_modules/binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/boxen": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
- "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
- "dependencies": {
- "ansi-align": "^3.0.0",
- "camelcase": "^6.2.0",
- "chalk": "^4.1.0",
- "cli-boxes": "^2.2.1",
- "string-width": "^4.2.2",
- "type-fest": "^0.20.2",
- "widest-line": "^3.1.0",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dependencies": {
- "fill-range": "^7.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cacheable-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
- "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
- "dependencies": {
- "clone-response": "^1.0.2",
- "get-stream": "^5.1.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^3.0.0",
- "lowercase-keys": "^2.0.0",
- "normalize-url": "^4.1.0",
- "responselike": "^1.0.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cacheable-request/node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cacheable-request/node_modules/lowercase-keys": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
- "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/case-anything": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz",
@@ -271,177 +100,16 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/chalk/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/chalk/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
"node_modules/chroma-js": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz",
"integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A=="
},
- "node_modules/ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
- },
- "node_modules/cli-boxes": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
- "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/clone-response": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
- "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
- "dependencies": {
- "mimic-response": "^1.0.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
- },
- "node_modules/configstore": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
- "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
- "dependencies": {
- "dot-prop": "^5.2.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^3.0.0",
- "unique-string": "^2.0.0",
- "write-file-atomic": "^3.0.0",
- "xdg-basedir": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
},
- "node_modules/crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
- "dependencies": {
- "mimic-response": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/defer-to-connect": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
- "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
- },
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -450,950 +118,113 @@
"node": ">=0.3.1"
}
},
- "node_modules/dot-prop": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
- "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
- "dependencies": {
- "is-obj": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/duplexer3": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
- "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "node_modules/end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/escape-goat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
- "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
- "engines": {
- "node": ">=8"
- }
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "node_modules/ts-node": {
+ "version": "10.7.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz",
+ "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==",
"dependencies": {
- "to-regex-range": "^5.0.1"
+ "@cspotcode/source-map-support": "0.7.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.0",
+ "yn": "3.1.1"
},
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dependencies": {
- "pump": "^3.0.0"
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
},
- "engines": {
- "node": ">=6"
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
}
},
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dependencies": {
- "is-glob": "^4.0.1"
+ "node_modules/typescript": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
+ "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
},
"engines": {
- "node": ">= 6"
+ "node": ">=4.2.0"
}
},
- "node_modules/global-dirs": {
+ "node_modules/v8-compile-cache-lib": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz",
- "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==",
- "dependencies": {
- "ini": "2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz",
+ "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA=="
},
- "node_modules/got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "dependencies": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"engines": {
- "node": ">=8.6"
+ "node": ">=6"
}
+ }
+ },
+ "dependencies": {
+ "@cspotcode/source-map-consumer": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
+ "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg=="
},
- "node_modules/graceful-fs": {
- "version": "4.2.9",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
- "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
- },
- "node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "engines": {
- "node": ">=4"
+ "@cspotcode/source-map-support": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
+ "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
+ "requires": {
+ "@cspotcode/source-map-consumer": "0.8.0"
}
},
- "node_modules/has-yarn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
- "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
- "engines": {
- "node": ">=8"
- }
+ "@tsconfig/node10": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
+ "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg=="
},
- "node_modules/http-cache-semantics": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
- "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
+ "@tsconfig/node12": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
+ "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw=="
},
- "node_modules/ignore-by-default": {
+ "@tsconfig/node14": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
- "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
- },
- "node_modules/import-lazy": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
- "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
- "engines": {
- "node": ">=4"
- }
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
+ "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg=="
},
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/ini": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
- "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dependencies": {
- "ci-info": "^2.0.0"
- },
- "bin": {
- "is-ci": "bin.js"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-installed-globally": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
- "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
- "dependencies": {
- "global-dirs": "^3.0.0",
- "is-path-inside": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-npm": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz",
- "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
- },
- "node_modules/is-yarn-global": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
- "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw=="
- },
- "node_modules/json-buffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
- "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
- },
- "node_modules/keyv": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
- "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
- "dependencies": {
- "json-buffer": "3.0.0"
- }
- },
- "node_modules/latest-version": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
- "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
- "dependencies": {
- "package-json": "^6.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
- },
- "node_modules/mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
- "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "node_modules/nodemon": {
- "version": "2.0.15",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz",
- "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==",
- "hasInstallScript": true,
- "dependencies": {
- "chokidar": "^3.5.2",
- "debug": "^3.2.7",
- "ignore-by-default": "^1.0.1",
- "minimatch": "^3.0.4",
- "pstree.remy": "^1.1.8",
- "semver": "^5.7.1",
- "supports-color": "^5.5.0",
- "touch": "^3.1.0",
- "undefsafe": "^2.0.5",
- "update-notifier": "^5.1.0"
- },
- "bin": {
- "nodemon": "bin/nodemon.js"
- },
- "engines": {
- "node": ">=8.10.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/nodemon"
- }
- },
- "node_modules/nopt": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
- "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
- "dependencies": {
- "abbrev": "1"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/normalize-url": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
- "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/p-cancelable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
- "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/package-json": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
- "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
- "dependencies": {
- "got": "^9.6.0",
- "registry-auth-token": "^4.0.0",
- "registry-url": "^5.0.0",
- "semver": "^6.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/package-json/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/prepend-http": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pstree.remy": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
- "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
- },
- "node_modules/pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/pupa": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz",
- "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==",
- "dependencies": {
- "escape-goat": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
- },
- "node_modules/rc/node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/registry-auth-token": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz",
- "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==",
- "dependencies": {
- "rc": "^1.2.8"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/registry-url": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
- "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
- "dependencies": {
- "rc": "^1.2.8"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/responselike": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
- "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
- "dependencies": {
- "lowercase-keys": "^1.0.0"
- }
- },
- "node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/semver-diff": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
- "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
- "dependencies": {
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/semver-diff/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/to-readable-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
- "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/touch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
- "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
- "dependencies": {
- "nopt": "~1.0.10"
- },
- "bin": {
- "nodetouch": "bin/nodetouch.js"
- }
- },
- "node_modules/ts-node": {
- "version": "10.7.0",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz",
- "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==",
- "dependencies": {
- "@cspotcode/source-map-support": "0.7.0",
- "@tsconfig/node10": "^1.0.7",
- "@tsconfig/node12": "^1.0.7",
- "@tsconfig/node14": "^1.0.0",
- "@tsconfig/node16": "^1.0.2",
- "acorn": "^8.4.1",
- "acorn-walk": "^8.1.1",
- "arg": "^4.1.0",
- "create-require": "^1.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "v8-compile-cache-lib": "^3.0.0",
- "yn": "3.1.1"
- },
- "bin": {
- "ts-node": "dist/bin.js",
- "ts-node-cwd": "dist/bin-cwd.js",
- "ts-node-esm": "dist/bin-esm.js",
- "ts-node-script": "dist/bin-script.js",
- "ts-node-transpile-only": "dist/bin-transpile.js",
- "ts-script": "dist/bin-script-deprecated.js"
- },
- "peerDependencies": {
- "@swc/core": ">=1.2.50",
- "@swc/wasm": ">=1.2.50",
- "@types/node": "*",
- "typescript": ">=2.7"
- },
- "peerDependenciesMeta": {
- "@swc/core": {
- "optional": true
- },
- "@swc/wasm": {
- "optional": true
- }
- }
- },
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
- "dependencies": {
- "is-typedarray": "^1.0.0"
- }
- },
- "node_modules/typescript": {
- "version": "4.6.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
- "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
- "node_modules/undefsafe": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
- "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
- },
- "node_modules/unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dependencies": {
- "crypto-random-string": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/update-notifier": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz",
- "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==",
- "dependencies": {
- "boxen": "^5.0.0",
- "chalk": "^4.1.0",
- "configstore": "^5.0.1",
- "has-yarn": "^2.1.0",
- "import-lazy": "^2.1.0",
- "is-ci": "^2.0.0",
- "is-installed-globally": "^0.4.0",
- "is-npm": "^5.0.0",
- "is-yarn-global": "^0.3.0",
- "latest-version": "^5.1.0",
- "pupa": "^2.1.1",
- "semver": "^7.3.4",
- "semver-diff": "^3.1.1",
- "xdg-basedir": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/yeoman/update-notifier?sponsor=1"
- }
- },
- "node_modules/update-notifier/node_modules/semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/url-parse-lax": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
- "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
- "dependencies": {
- "prepend-http": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/v8-compile-cache-lib": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz",
- "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA=="
- },
- "node_modules/widest-line": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
- "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
- "dependencies": {
- "string-width": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- },
- "node_modules/write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dependencies": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "node_modules/xdg-basedir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
- "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
- "node_modules/yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "engines": {
- "node": ">=6"
- }
- }
- },
- "dependencies": {
- "@cspotcode/source-map-consumer": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
- "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg=="
- },
- "@cspotcode/source-map-support": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
- "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
- "requires": {
- "@cspotcode/source-map-consumer": "0.8.0"
- }
- },
- "@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
- },
- "@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
- "requires": {
- "defer-to-connect": "^1.0.1"
- }
- },
- "@tsconfig/node10": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
- "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg=="
- },
- "@tsconfig/node12": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
- "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw=="
- },
- "@tsconfig/node14": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
- "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg=="
- },
- "@tsconfig/node16": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
- "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA=="
+ "@tsconfig/node16": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
+ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA=="
},
"@types/chroma-js": {
"version": "2.1.3",
@@ -6,8 +6,7 @@
"scripts": {
"build": "npm run build-themes && npm run build-tokens",
"build-themes": "ts-node ./src/buildThemes.ts",
- "build-tokens": "ts-node ./src/buildTokens.ts",
- "watch": "nodemon"
+ "build-tokens": "ts-node ./src/buildTokens.ts"
},
"author": "",
"license": "ISC",
@@ -16,7 +15,6 @@
"@types/node": "^17.0.23",
"case-anything": "^2.1.10",
"chroma-js": "^2.4.2",
- "ts-node": "^10.7.0",
- "nodemon": "^2.0.15"
+ "ts-node": "^10.7.0"
}
}
@@ -1,23 +1,30 @@
import * as fs from "fs";
import * as path from "path";
+import { tmpdir } from 'os';
import app from "./styleTree/app";
import themes from "./themes";
import snakeCase from "./utils/snakeCase";
const themeDirectory = `${__dirname}/../../assets/themes/`;
+const tempDirectory = fs.mkdtempSync(path.join(tmpdir(), 'build-themes'));
// Clear existing themes
for (const file of fs.readdirSync(themeDirectory)) {
- fs.unlinkSync(path.join(themeDirectory, file));
+ if (file.endsWith('.json')) {
+ const name = file.replace(/\.json$/, '');
+ if (!themes.find(theme => theme.name === name)) {
+ fs.unlinkSync(path.join(themeDirectory, file));
+ }
+ }
}
// Write new themes to theme directory
for (let theme of themes) {
let styleTree = snakeCase(app(theme));
let styleTreeJSON = JSON.stringify(styleTree, null, 2);
- let outPath = path.resolve(
- `${__dirname}/../../assets/themes/${theme.name}.json`
- );
- fs.writeFileSync(outPath, styleTreeJSON);
+ let tempPath = path.join(tempDirectory, `${theme.name}.json`);
+ let outPath = path.join(themeDirectory, `${theme.name}.json`);
+ fs.writeFileSync(tempPath, styleTreeJSON);
+ fs.renameSync(tempPath, outPath);
console.log(`- ${outPath} created`);
}
@@ -7,10 +7,12 @@ export default themes;
const themesPath = path.resolve(`${__dirname}/themes`);
for (const fileName of fs.readdirSync(themesPath)) {
+ if (fileName == "template.ts") continue;
const filePath = path.join(themesPath, fileName);
+
if (fs.statSync(filePath).isFile()) {
const theme = require(filePath);
- themes.push(theme.dark);
- themes.push(theme.light);
+ if (theme.dark) themes.push(theme.dark);
+ if (theme.light) themes.push(theme.light);
}
}
@@ -0,0 +1,27 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "andromeda";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#1E2025",
+ "#23262E",
+ "#292E38",
+ "#2E323C",
+ "#ACA8AE",
+ "#CBC9CF",
+ "#E1DDE4",
+ "#F7F7F8",
+ ]),
+ red: colorRamp(chroma("#F92672")),
+ orange: colorRamp(chroma("#F39C12")),
+ yellow: colorRamp(chroma("#FFE66D")),
+ green: colorRamp(chroma("#96E072")),
+ cyan: colorRamp(chroma("#00E8C6")),
+ blue: colorRamp(chroma("#0CA793")),
+ violet: colorRamp(chroma("#8A3FA6")),
+ magenta: colorRamp(chroma("#C74DED")),
+};
+
+export const dark = createTheme(`${name}`, false, ramps);
@@ -0,0 +1,28 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "brush-tree";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#485867",
+ "#5A6D7A",
+ "#6D828E",
+ "#8299A1",
+ "#98AFB5",
+ "#B0C5C8",
+ "#C9DBDC",
+ "#E3EFEF",
+ ]),
+ red: colorRamp(chroma("#b38686")),
+ orange: colorRamp(chroma("#d8bba2")),
+ yellow: colorRamp(chroma("#aab386")),
+ green: colorRamp(chroma("#87b386")),
+ cyan: colorRamp(chroma("#86b3b3")),
+ blue: colorRamp(chroma("#868cb3")),
+ violet: colorRamp(chroma("#b386b2")),
+ magenta: colorRamp(chroma("#b39f9f")),
+};
+
+export const dark = createTheme(`${name}-dark`, false, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);
@@ -4,7 +4,16 @@ import { colorRamp, createTheme } from "./common/base16";
const name = "cave";
const ramps = {
- neutral: chroma.scale(["#19171c", "#26232a", "#585260", "#655f6d", "#7e7887", "#8b8792", "#e2dfe7", "#efecf4"]),
+ neutral: chroma.scale([
+ "#19171c",
+ "#26232a",
+ "#585260",
+ "#655f6d",
+ "#7e7887",
+ "#8b8792",
+ "#e2dfe7",
+ "#efecf4",
+ ]),
red: colorRamp(chroma("#be4678")),
orange: colorRamp(chroma("#aa573c")),
yellow: colorRamp(chroma("#a06e3b")),
@@ -13,7 +22,7 @@ const ramps = {
blue: colorRamp(chroma("#576ddb")),
violet: colorRamp(chroma("#955ae7")),
magenta: colorRamp(chroma("#bf40bf")),
-}
+};
export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);
@@ -1,5 +1,4 @@
-import chroma from "chroma-js";
-import { Scale, Color } from "chroma-js";
+import chroma, { Color, Scale } from "chroma-js";
import { color, ColorToken, fontWeights, NumberToken } from "../../tokens";
import { withOpacity } from "../../utils/color";
import Theme, { buildPlayer, Syntax } from "./theme";
@@ -8,12 +7,15 @@ export function colorRamp(color: Color): Scale {
let hue = color.hsl()[0];
let endColor = chroma.hsl(hue, 0.88, 0.96);
let startColor = chroma.hsl(hue, 0.68, 0.12);
- return chroma
- .scale([startColor, color, endColor])
- .mode("hsl");
+ return chroma.scale([startColor, color, endColor]).mode("hsl");
}
-export function createTheme(name: string, isLight: boolean, ramps: { [rampName: string]: Scale }, blend?: number): Theme {
+export function createTheme(
+ name: string,
+ isLight: boolean,
+ ramps: { [rampName: string]: Scale },
+ blend?: number
+): Theme {
if (isLight) {
for (var rampName in ramps) {
ramps[rampName] = ramps[rampName].domain([1, 0]);
@@ -62,22 +64,22 @@ export function createTheme(name: string, isLight: boolean, ramps: { [rampName:
},
ok: {
base: withOpacity(rampColor(ramps.green, 0.5), 0.15),
- hovered: withOpacity(rampColor(ramps.green, 0.5), 0.20),
+ hovered: withOpacity(rampColor(ramps.green, 0.5), 0.2),
active: withOpacity(rampColor(ramps.green, 0.5), 0.25),
},
error: {
base: withOpacity(rampColor(ramps.red, 0.5), 0.15),
- hovered: withOpacity(rampColor(ramps.red, 0.5), 0.20),
+ hovered: withOpacity(rampColor(ramps.red, 0.5), 0.2),
active: withOpacity(rampColor(ramps.red, 0.5), 0.25),
},
warning: {
base: withOpacity(rampColor(ramps.yellow, 0.5), 0.15),
- hovered: withOpacity(rampColor(ramps.yellow, 0.5), 0.20),
+ hovered: withOpacity(rampColor(ramps.yellow, 0.5), 0.2),
active: withOpacity(rampColor(ramps.yellow, 0.5), 0.25),
},
info: {
base: withOpacity(rampColor(ramps.blue, 0.5), 0.15),
- hovered: withOpacity(rampColor(ramps.blue, 0.5), 0.20),
+ hovered: withOpacity(rampColor(ramps.blue, 0.5), 0.2),
active: withOpacity(rampColor(ramps.blue, 0.5), 0.25),
},
};
@@ -242,4 +244,4 @@ export function createTheme(name: string, isLight: boolean, ramps: { [rampName:
player,
shadowAlpha,
};
-}
+}
@@ -4,8 +4,8 @@ import { withOpacity } from "../../utils/color";
export interface SyntaxHighlightStyle {
color: ColorToken;
weight?: FontWeightToken;
- underline?: boolean,
- italic?: boolean,
+ underline?: boolean;
+ italic?: boolean;
}
export interface Player {
@@ -25,7 +25,7 @@ export function buildPlayer(
cursorColor: withOpacity(color, cursorOpacity || 1.0),
selectionColor: withOpacity(color, selectionOpacity || 0.24),
borderColor: withOpacity(color, borderOpacity || 0.8),
- }
+ };
}
export interface BackgroundColorSet {
@@ -56,7 +56,7 @@ export interface Syntax {
linkText: SyntaxHighlightStyle;
[key: string]: SyntaxHighlightStyle;
-};
+}
export default interface Theme {
name: string;
@@ -86,8 +86,8 @@ export default interface Theme {
muted: ColorToken;
active: ColorToken;
/**
- * Used for rendering borders on top of media like avatars, images, video, etc.
- */
+ * Used for rendering borders on top of media like avatars, images, video, etc.
+ */
onMedia: ColorToken;
ok: ColorToken;
error: ColorToken;
@@ -141,7 +141,7 @@ export default interface Theme {
};
};
- syntax: Syntax,
+ syntax: Syntax;
player: {
1: Player;
@@ -0,0 +1,27 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "rosΓ©-pine-dawn";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#26233a",
+ "#555169",
+ "#575279",
+ "#6e6a86",
+ "#9893a5",
+ "#f2e9de",
+ "#fffaf3",
+ "#faf4ed",
+ ]),
+ red: colorRamp(chroma("#1f1d2e")),
+ orange: colorRamp(chroma("#b4637a")),
+ yellow: colorRamp(chroma("#ea9d34")),
+ green: colorRamp(chroma("#d7827e")),
+ cyan: colorRamp(chroma("#286983")),
+ blue: colorRamp(chroma("#56949f")),
+ violet: colorRamp(chroma("#907aa9")),
+ magenta: colorRamp(chroma("#c5c3ce")),
+};
+
+export const light = createTheme(`${name}`, true, ramps);
@@ -0,0 +1,27 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "rosΓ©-pine";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#191724",
+ "#1f1d2e",
+ "#26233a",
+ "#555169",
+ "#6e6a86",
+ "#e0def4",
+ "#f0f0f3",
+ "#c5c3ce",
+ ]),
+ red: colorRamp(chroma("#e2e1e7")),
+ orange: colorRamp(chroma("#eb6f92")),
+ yellow: colorRamp(chroma("#f6c177")),
+ green: colorRamp(chroma("#ebbcba")),
+ cyan: colorRamp(chroma("#31748f")),
+ blue: colorRamp(chroma("#0CA793")),
+ violet: colorRamp(chroma("#8A3FA6")),
+ magenta: colorRamp(chroma("#C74DED")),
+};
+
+export const dark = createTheme(`${name}`, false, ramps);
@@ -0,0 +1,27 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "sandcastle";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#282c34",
+ "#2c323b",
+ "#3e4451",
+ "#665c54",
+ "#928374",
+ "#a89984",
+ "#d5c4a1",
+ "#fdf4c1",
+ ]),
+ red: colorRamp(chroma("#83a598")),
+ orange: colorRamp(chroma("#a07e3b")),
+ yellow: colorRamp(chroma("#a07e3b")),
+ green: colorRamp(chroma("#528b8b")),
+ cyan: colorRamp(chroma("#83a598")),
+ blue: colorRamp(chroma("#83a598")),
+ violet: colorRamp(chroma("#d75f5f")),
+ magenta: colorRamp(chroma("#a87322")),
+};
+
+export const dark = createTheme(`${name}`, false, ramps);
@@ -4,7 +4,16 @@ import { colorRamp, createTheme } from "./common/base16";
const name = "solarized";
const ramps = {
- neutral: chroma.scale(["#002b36", "#073642", "#586e75", "#657b83", "#839496", "#93a1a1", "#eee8d5", "#fdf6e3"]),
+ neutral: chroma.scale([
+ "#002b36",
+ "#073642",
+ "#586e75",
+ "#657b83",
+ "#839496",
+ "#93a1a1",
+ "#eee8d5",
+ "#fdf6e3",
+ ]),
red: colorRamp(chroma("#dc322f")),
orange: colorRamp(chroma("#cb4b16")),
yellow: colorRamp(chroma("#b58900")),
@@ -13,7 +22,7 @@ const ramps = {
blue: colorRamp(chroma("#268bd2")),
violet: colorRamp(chroma("#6c71c4")),
magenta: colorRamp(chroma("#d33682")),
-}
+};
export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);
@@ -4,7 +4,16 @@ import { colorRamp, createTheme } from "./common/base16";
const name = "sulphurpool";
const ramps = {
- neutral: chroma.scale(["#202746", "#293256", "#5e6687", "#6b7394", "#898ea4", "#979db4", "#dfe2f1", "#f5f7ff"]),
+ neutral: chroma.scale([
+ "#202746",
+ "#293256",
+ "#5e6687",
+ "#6b7394",
+ "#898ea4",
+ "#979db4",
+ "#dfe2f1",
+ "#f5f7ff",
+ ]),
red: colorRamp(chroma("#c94922")),
orange: colorRamp(chroma("#c76b29")),
yellow: colorRamp(chroma("#c08b30")),
@@ -13,7 +22,7 @@ const ramps = {
blue: colorRamp(chroma("#3d8fd1")),
violet: colorRamp(chroma("#6679cc")),
magenta: colorRamp(chroma("#9c637a")),
-}
+};
export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);
@@ -0,0 +1,27 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "summercamp";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#1c1810",
+ "#2a261c",
+ "#3a3527",
+ "#3a3527",
+ "#5f5b45",
+ "#736e55",
+ "#bab696",
+ "#f8f5de",
+ ]),
+ red: colorRamp(chroma("#e35142")),
+ orange: colorRamp(chroma("#fba11b")),
+ yellow: colorRamp(chroma("#f2ff27")),
+ green: colorRamp(chroma("#5ceb5a")),
+ cyan: colorRamp(chroma("#5aebbc")),
+ blue: colorRamp(chroma("#489bf0")),
+ violet: colorRamp(chroma("#FF8080")),
+ magenta: colorRamp(chroma("#F69BE7")),
+};
+
+export const dark = createTheme(`${name}`, false, ramps);
@@ -0,0 +1,28 @@
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+const name = "summerfruit";
+
+const ramps = {
+ neutral: chroma.scale([
+ "#151515",
+ "#202020",
+ "#303030",
+ "#505050",
+ "#B0B0B0",
+ "#D0D0D0",
+ "#E0E0E0",
+ "#FFFFFF",
+ ]),
+ red: colorRamp(chroma("#FF0086")),
+ orange: colorRamp(chroma("#FD8900")),
+ yellow: colorRamp(chroma("#ABA800")),
+ green: colorRamp(chroma("#00C918")),
+ cyan: colorRamp(chroma("#1FAAAA")),
+ blue: colorRamp(chroma("#3777E6")),
+ violet: colorRamp(chroma("#AD00A1")),
+ magenta: colorRamp(chroma("#CC6633")),
+};
+
+export const dark = createTheme(`${name}-dark`, false, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);
@@ -0,0 +1,69 @@
+/**
+ * To create a new theme duplicate this file and move into templates
+ **/
+
+import chroma from "chroma-js";
+import { colorRamp, createTheme } from "./common/base16";
+
+/**
+ * Theme Name
+ *
+ * What the theme will be called in the UI
+ * Also used to generate filenames, etc
+ **/
+
+const name = "themeName";
+
+/**
+ * Theme Colors
+ *
+ * Zed themes are based on [base16](https://github.com/chriskempson/base16)
+ * The first 8 colors ("Neutrals") are used to construct the UI background, panels, etc.
+ * The latter 8 colors ("Accents") are used for syntax themes, semantic colors, and UI states.
+ **/
+
+/**
+ * Color Ramps
+ *
+ * We use (chroma-js)[https://gka.github.io/chroma.js/] to minipulate color in themes and to build color ramps.
+ *
+ * You can use chroma-js operations on the ramps here.
+ * For example, you could use chroma.scale(...).correctLightness if your color ramps seem washed out near the ends.
+ **/
+
+// TODO: Express accents without refering to them directly by color name.
+// See common/base16.ts for where color tokens are used.
+
+const ramps = {
+ neutral: chroma.scale([
+ "#19171c", // Dark: darkest backgrounds, inputs | Light: Lightest text, active states
+ "#26232a",
+ "#585260",
+ "#655f6d",
+ "#7e7887",
+ "#8b8792",
+ "#e2dfe7",
+ "#efecf4", // Light: darkest backgrounds, inputs | Dark: Lightest text, active states
+ ]),
+ red: colorRamp(chroma("#be4678")), // Errors
+ orange: colorRamp(chroma("#aa573c")),
+ yellow: colorRamp(chroma("#a06e3b")), // Warnings
+ green: colorRamp(chroma("#2a9292")), // Positive
+ cyan: colorRamp(chroma("#398bc6")), // Player 1 (Host)
+ blue: colorRamp(chroma("#576ddb")), // Info
+ violet: colorRamp(chroma("#955ae7")),
+ magenta: colorRamp(chroma("#bf40bf")),
+};
+
+/**
+ * Theme Variants
+ *
+ * Currently we only support (and require) dark and light themes
+ * Eventually you will be able to have only a light or dark theme,
+ * and define other variants here.
+ *
+ * createTheme([name], [isLight], [arrayOfRamps])
+ **/
+
+export const dark = createTheme(`${name}-dark`, false, ramps);
+export const light = createTheme(`${name}-light`, true, ramps);