Merge branch 'main' into randomized-tests-operation-script

This commit is contained in:
Max Brunsfeld 2023-04-05 17:10:20 -07:00
commit 2d63ed3ca4
42 changed files with 1183 additions and 1960 deletions

15
Cargo.lock generated
View File

@ -1124,6 +1124,7 @@ dependencies = [
"serde_derive",
"settings",
"smol",
"staff_mode",
"sum_tree",
"tempfile",
"thiserror",
@ -1339,7 +1340,6 @@ dependencies = [
"anyhow",
"async-compression",
"async-tar",
"client",
"collections",
"context_menu",
"futures 0.3.25",
@ -5950,6 +5950,7 @@ dependencies = [
"serde_json",
"serde_path_to_error",
"sqlez",
"staff_mode",
"theme",
"toml",
"tree-sitter",
@ -6360,6 +6361,14 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "staff_mode"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
@ -6678,6 +6687,7 @@ dependencies = [
"postage",
"settings",
"smol",
"staff_mode",
"theme",
"util",
"workspace",
@ -8505,7 +8515,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zed"
version = "0.81.0"
version = "0.82.0"
dependencies = [
"activity_indicator",
"anyhow",
@ -8578,6 +8588,7 @@ dependencies = [
"simplelog",
"smallvec",
"smol",
"staff_mode",
"sum_tree",
"tempdir",
"terminal_view",

View File

@ -54,6 +54,7 @@ members = [
"crates/snippet",
"crates/sqlez",
"crates/sqlez_macros",
"crates/staff_mode",
"crates/sum_tree",
"crates/terminal",
"crates/text",

View File

@ -177,9 +177,9 @@
"focus": false
}
],
"alt-\\": "copilot::NextSuggestion",
"alt-]": "copilot::NextSuggestion",
"alt-[": "copilot::PreviousSuggestion",
"alt-\\": "copilot::Toggle"
"alt-[": "copilot::PreviousSuggestion"
}
},
{

View File

@ -205,9 +205,7 @@
// Different settings for specific languages.
"languages": {
"Plain Text": {
"soft_wrap": "preferred_line_length",
// Copilot can be a little strange on non-code files
"copilot": "off"
"soft_wrap": "preferred_line_length"
},
"Elixir": {
"tab_size": 2
@ -217,9 +215,7 @@
"hard_tabs": true
},
"Markdown": {
"soft_wrap": "preferred_line_length",
// Copilot can be a little strange on non-code files
"copilot": "off"
"soft_wrap": "preferred_line_length"
},
"JavaScript": {
"tab_size": 2
@ -232,9 +228,6 @@
},
"YAML": {
"tab_size": 2
},
"JSON": {
"copilot": "off"
}
},
// LSP Specific settings.

View File

@ -17,6 +17,7 @@ db = { path = "../db" }
gpui = { path = "../gpui" }
util = { path = "../util" }
rpc = { path = "../rpc" }
staff_mode = { path = "../staff_mode" }
sum_tree = { path = "../sum_tree" }
anyhow = "1.0.38"
async-recursion = "0.3"

View File

@ -6,9 +6,10 @@ use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
use postage::{sink::Sink, watch};
use rpc::proto::{RequestMessage, UsersResponse};
use settings::Settings;
use staff_mode::StaffMode;
use std::sync::{Arc, Weak};
use util::http::HttpClient;
use util::{StaffMode, TryFutureExt as _};
use util::TryFutureExt as _;
#[derive(Default, Debug)]
pub struct User {

View File

@ -8,6 +8,16 @@ publish = false
path = "src/copilot.rs"
doctest = false
[features]
test-support = [
"collections/test-support",
"gpui/test-support",
"language/test-support",
"lsp/test-support",
"settings/test-support",
"util/test-support",
]
[dependencies]
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
@ -18,7 +28,6 @@ theme = { path = "../theme" }
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime"}
util = { path = "../util" }
client = { path = "../client" }
async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
async-tar = "0.4.2"
anyhow = "1.0"
@ -29,10 +38,10 @@ smol = "1.2.5"
futures = "0.3"
[dev-dependencies]
collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
client = { path = "../client", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }

View File

@ -1,10 +1,9 @@
mod request;
pub mod request;
mod sign_in;
use anyhow::{anyhow, Context, Result};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use client::Client;
use collections::HashMap;
use futures::{future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
@ -25,40 +24,31 @@ use std::{
sync::Arc,
};
use util::{
fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt,
channel::ReleaseChannel, fs::remove_matching, github::latest_github_release, http::HttpClient,
paths, ResultExt,
};
const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
actions!(copilot_auth, [SignIn, SignOut]);
const COPILOT_NAMESPACE: &'static str = "copilot";
actions!(
copilot,
[NextSuggestion, PreviousSuggestion, Toggle, Reinstall]
);
actions!(copilot, [NextSuggestion, PreviousSuggestion, Reinstall]);
pub fn init(client: Arc<Client>, node_runtime: Arc<NodeRuntime>, cx: &mut MutableAppContext) {
let copilot = cx.add_model(|cx| Copilot::start(client.http_client(), node_runtime, cx));
pub fn init(http: Arc<dyn HttpClient>, node_runtime: Arc<NodeRuntime>, cx: &mut MutableAppContext) {
// Disable Copilot for stable releases.
if *cx.global::<ReleaseChannel>() == ReleaseChannel::Stable {
cx.update_global::<collections::CommandPaletteFilter, _, _>(|filter, _cx| {
filter.filtered_namespaces.insert(COPILOT_NAMESPACE);
filter.filtered_namespaces.insert(COPILOT_AUTH_NAMESPACE);
});
return;
}
let copilot = cx.add_model({
let node_runtime = node_runtime.clone();
move |cx| Copilot::start(http, node_runtime, cx)
});
cx.set_global(copilot.clone());
cx.add_global_action(|_: &SignIn, cx| {
let copilot = Copilot::global(cx).unwrap();
copilot
.update(cx, |copilot, cx| copilot.sign_in(cx))
.detach_and_log_err(cx);
});
cx.add_global_action(|_: &SignOut, cx| {
let copilot = Copilot::global(cx).unwrap();
copilot
.update(cx, |copilot, cx| copilot.sign_out(cx))
.detach_and_log_err(cx);
});
cx.add_global_action(|_: &Reinstall, cx| {
let copilot = Copilot::global(cx).unwrap();
copilot
.update(cx, |copilot, cx| copilot.reinstall(cx))
.detach();
});
cx.observe(&copilot, |handle, cx| {
let status = handle.read(cx).status();
@ -82,6 +72,28 @@ pub fn init(client: Arc<Client>, node_runtime: Arc<NodeRuntime>, cx: &mut Mutabl
.detach();
sign_in::init(cx);
cx.add_global_action(|_: &SignIn, cx| {
if let Some(copilot) = Copilot::global(cx) {
copilot
.update(cx, |copilot, cx| copilot.sign_in(cx))
.detach_and_log_err(cx);
}
});
cx.add_global_action(|_: &SignOut, cx| {
if let Some(copilot) = Copilot::global(cx) {
copilot
.update(cx, |copilot, cx| copilot.sign_out(cx))
.detach_and_log_err(cx);
}
});
cx.add_global_action(|_: &Reinstall, cx| {
if let Some(copilot) = Copilot::global(cx) {
copilot
.update(cx, |copilot, cx| copilot.reinstall(cx))
.detach();
}
});
}
enum CopilotServer {
@ -99,12 +111,8 @@ enum CopilotServer {
#[derive(Clone, Debug)]
enum SignInStatus {
Authorized {
_user: String,
},
Unauthorized {
_user: String,
},
Authorized,
Unauthorized,
SigningIn {
prompt: Option<request::PromptUserDeviceFlow>,
task: Shared<Task<Result<(), Arc<anyhow::Error>>>>,
@ -150,13 +158,6 @@ impl Entity for Copilot {
}
impl Copilot {
pub fn starting_task(&self) -> Option<Shared<Task<()>>> {
match self.server {
CopilotServer::Starting { ref task } => Some(task.clone()),
_ => None,
}
}
pub fn global(cx: &AppContext) -> Option<ModelHandle<Self>> {
if cx.has_global::<ModelHandle<Self>>() {
Some(cx.global::<ModelHandle<Self>>().clone())
@ -219,6 +220,23 @@ impl Copilot {
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn fake(cx: &mut gpui::TestAppContext) -> (ModelHandle<Self>, lsp::FakeLanguageServer) {
let (server, fake_server) =
LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
let this = cx.add_model(|cx| Self {
http: http.clone(),
node_runtime: NodeRuntime::new(http, cx.background().clone()),
server: CopilotServer::Started {
server: Arc::new(server),
status: SignInStatus::Authorized,
subscriptions_by_buffer_id: Default::default(),
},
});
(this, fake_server)
}
fn start_language_server(
http: Arc<dyn HttpClient>,
node_runtime: Arc<NodeRuntime>,
@ -598,14 +616,10 @@ impl Copilot {
) {
if let CopilotServer::Started { status, .. } = &mut self.server {
*status = match lsp_status {
request::SignInStatus::Ok { user }
| request::SignInStatus::MaybeOk { user }
| request::SignInStatus::AlreadySignedIn { user } => {
SignInStatus::Authorized { _user: user }
}
request::SignInStatus::NotAuthorized { user } => {
SignInStatus::Unauthorized { _user: user }
}
request::SignInStatus::Ok { .. }
| request::SignInStatus::MaybeOk { .. }
| request::SignInStatus::AlreadySignedIn { .. } => SignInStatus::Authorized,
request::SignInStatus::NotAuthorized { .. } => SignInStatus::Unauthorized,
request::SignInStatus::NotSignedIn => SignInStatus::SignedOut,
};
cx.notify();

View File

@ -117,7 +117,7 @@ pub struct GetCompletionsResult {
pub completions: Vec<Completion>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Completion {
pub text: String,

View File

@ -23,28 +23,17 @@ pub fn init(cx: &mut MutableAppContext) {
match &status {
crate::Status::SigningIn { prompt } => {
if let Some(code_verification) = code_verification.as_ref() {
code_verification.update(cx, |code_verification, cx| {
code_verification.set_status(status, cx)
if let Some(code_verification_handle) = code_verification.as_mut() {
if cx.has_window(code_verification_handle.window_id()) {
code_verification_handle.update(cx, |code_verification_view, cx| {
code_verification_view.set_status(status, cx)
});
cx.activate_window(code_verification.window_id());
cx.activate_window(code_verification_handle.window_id());
} else {
create_copilot_auth_window(cx, &status, &mut code_verification);
}
} else if let Some(_prompt) = prompt {
let window_size = cx.global::<Settings>().theme.copilot.modal.dimensions();
let window_options = WindowOptions {
bounds: gpui::WindowBounds::Fixed(RectF::new(
Default::default(),
window_size,
)),
titlebar: None,
center: true,
focus: true,
kind: WindowKind::Normal,
is_movable: true,
screen: None,
};
let (_, view) =
cx.add_window(window_options, |_cx| CopilotCodeVerification::new(status));
code_verification = Some(view);
create_copilot_auth_window(cx, &status, &mut code_verification);
}
}
Status::Authorized | Status::Unauthorized => {
@ -67,6 +56,27 @@ pub fn init(cx: &mut MutableAppContext) {
.detach();
}
fn create_copilot_auth_window(
cx: &mut MutableAppContext,
status: &Status,
code_verification: &mut Option<ViewHandle<CopilotCodeVerification>>,
) {
let window_size = cx.global::<Settings>().theme.copilot.modal.dimensions();
let window_options = WindowOptions {
bounds: gpui::WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
titlebar: None,
center: true,
focus: true,
kind: WindowKind::Normal,
is_movable: true,
screen: None,
};
let (_, view) = cx.add_window(window_options, |_cx| {
CopilotCodeVerification::new(status.clone())
});
*code_verification = Some(view);
}
pub struct CopilotCodeVerification {
status: Status,
}

View File

@ -228,12 +228,7 @@ impl CopilotButton {
Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach());
let this_handle = cx.handle().downgrade();
cx.observe_global::<Settings, _>(move |cx| {
if let Some(handle) = this_handle.upgrade(cx) {
handle.update(cx, |_, cx| cx.notify())
}
})
cx.observe_global::<Settings, _>(move |_, cx| cx.notify())
.detach();
Self {
@ -249,19 +244,6 @@ impl CopilotButton {
let mut menu_options = Vec::with_capacity(6);
if let Some((_, view_id)) = self.editor_subscription.as_ref() {
let locally_enabled = self.editor_enabled.unwrap_or(settings.copilot_on(None));
menu_options.push(ContextMenuItem::item_for_view(
if locally_enabled {
"Pause Copilot for this file"
} else {
"Resume Copilot for this file"
},
*view_id,
copilot::Toggle,
));
}
if let Some(language) = &self.language {
let language_enabled = settings.copilot_on(Some(language.as_ref()));
@ -334,11 +316,7 @@ impl CopilotButton {
self.language = language_name.clone();
if let Some(enabled) = editor.copilot_state.user_enabled {
self.editor_enabled = Some(enabled);
} else {
self.editor_enabled = Some(settings.copilot_on(language_name.as_deref()));
}
cx.notify()
}

View File

@ -11,6 +11,7 @@ doctest = false
[features]
test-support = [
"rand",
"copilot/test-support",
"text/test-support",
"language/test-support",
"gpui/test-support",
@ -65,6 +66,7 @@ tree-sitter-javascript = { version = "*", optional = true }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259", optional = true }
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
text = { path = "../text", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }

View File

@ -7,7 +7,7 @@ mod wrap_map;
use crate::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
pub use block_map::{BlockMap, BlockPoint};
use collections::{HashMap, HashSet};
use fold_map::FoldMap;
use fold_map::{FoldMap, FoldOffset};
use gpui::{
color::Color,
fonts::{FontId, HighlightStyle},
@ -238,19 +238,22 @@ impl DisplayMap {
&self,
new_suggestion: Option<Suggestion<T>>,
cx: &mut ModelContext<Self>,
) where
) -> Option<Suggestion<FoldOffset>>
where
T: ToPoint,
{
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.suggestion_map.replace(new_suggestion, snapshot, edits);
let (snapshot, edits, old_suggestion) =
self.suggestion_map.replace(new_suggestion, snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
old_suggestion
}
pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool {

View File

@ -79,7 +79,11 @@ impl SuggestionMap {
new_suggestion: Option<Suggestion<T>>,
fold_snapshot: FoldSnapshot,
fold_edits: Vec<FoldEdit>,
) -> (SuggestionSnapshot, Vec<SuggestionEdit>)
) -> (
SuggestionSnapshot,
Vec<SuggestionEdit>,
Option<Suggestion<FoldOffset>>,
)
where
T: ToPoint,
{
@ -99,7 +103,8 @@ impl SuggestionMap {
let mut snapshot = self.0.lock();
let mut patch = Patch::new(edits);
if let Some(suggestion) = snapshot.suggestion.take() {
let old_suggestion = snapshot.suggestion.take();
if let Some(suggestion) = &old_suggestion {
patch = patch.compose([SuggestionEdit {
old: SuggestionOffset(suggestion.position.0)
..SuggestionOffset(suggestion.position.0 + suggestion.text.len()),
@ -119,7 +124,7 @@ impl SuggestionMap {
snapshot.suggestion = new_suggestion;
snapshot.version += 1;
(snapshot.clone(), patch.into_inner())
(snapshot.clone(), patch.into_inner(), old_suggestion)
}
pub fn sync(
@ -589,7 +594,7 @@ mod tests {
let (suggestion_map, suggestion_snapshot) = SuggestionMap::new(fold_snapshot.clone());
assert_eq!(suggestion_snapshot.text(), "abcdefghi");
let (suggestion_snapshot, _) = suggestion_map.replace(
let (suggestion_snapshot, _, _) = suggestion_map.replace(
Some(Suggestion {
position: 3,
text: "123\n456".into(),
@ -854,7 +859,9 @@ mod tests {
};
log::info!("replacing suggestion with {:?}", new_suggestion);
self.replace(new_suggestion, fold_snapshot, Default::default())
let (snapshot, edits, _) =
self.replace(new_suggestion, fold_snapshot, Default::default());
(snapshot, edits)
}
}
}

View File

@ -53,7 +53,7 @@ pub use language::{char_kind, CharKind};
use language::{
AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
Diagnostic, DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16,
Point, Selection, SelectionGoal, TransactionId,
Point, Rope, Selection, SelectionGoal, TransactionId,
};
use link_go_to_definition::{
hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
@ -95,6 +95,7 @@ const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
@ -391,7 +392,6 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_async_action(Editor::find_all_references);
cx.add_action(Editor::next_copilot_suggestion);
cx.add_action(Editor::previous_copilot_suggestion);
cx.add_action(Editor::toggle_copilot_suggestions);
hover_popover::init(cx);
link_go_to_definition::init(cx);
@ -510,7 +510,7 @@ pub struct Editor {
hover_state: HoverState,
gutter_hovered: bool,
link_go_to_definition_state: LinkGoToDefinitionState,
pub copilot_state: CopilotState,
copilot_state: CopilotState,
_subscriptions: Vec<Subscription>,
}
@ -1013,7 +1013,6 @@ pub struct CopilotState {
pending_refresh: Task<Option<()>>,
completions: Vec<copilot::Completion>,
active_completion_index: usize,
pub user_enabled: Option<bool>,
}
impl Default for CopilotState {
@ -1023,7 +1022,6 @@ impl Default for CopilotState {
pending_refresh: Task::ready(Some(())),
completions: Default::default(),
active_completion_index: 0,
user_enabled: None,
}
}
}
@ -1039,6 +1037,11 @@ impl CopilotState {
let completion = self.completions.get(self.active_completion_index)?;
let excerpt_id = self.excerpt_id?;
let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
if !completion.range.start.is_valid(completion_buffer)
|| !completion.range.end.is_valid(completion_buffer)
{
return None;
}
let mut completion_range = completion.range.to_offset(&completion_buffer);
let prefix_len = Self::common_prefix(
@ -1258,6 +1261,7 @@ impl Editor {
cx.subscribe(&buffer, Self::on_buffer_event),
cx.observe(&display_map, Self::on_display_map_changed),
cx.observe(&blink_manager, |_, _, cx| cx.notify()),
cx.observe_global::<Settings, _>(Self::on_settings_changed),
],
};
this.end_selection(cx);
@ -1461,7 +1465,7 @@ impl Editor {
self.refresh_code_actions(cx);
self.refresh_document_highlights(cx);
refresh_matching_bracket_highlights(self, cx);
self.refresh_copilot_suggestions(cx);
self.hide_copilot_suggestion(cx);
}
self.blink_manager.update(cx, BlinkManager::pause_blinking);
@ -1835,7 +1839,7 @@ impl Editor {
return;
}
if self.clear_copilot_suggestions(cx) {
if self.hide_copilot_suggestion(cx).is_some() {
return;
}
@ -2014,8 +2018,18 @@ impl Editor {
}
drop(snapshot);
let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
if had_active_copilot_suggestion {
this.refresh_copilot_suggestions(cx);
if !this.has_active_copilot_suggestion(cx) {
this.trigger_completion_on_input(&text, cx);
}
} else {
this.trigger_completion_on_input(&text, cx);
this.refresh_copilot_suggestions(cx);
}
});
}
@ -2096,6 +2110,7 @@ impl Editor {
.collect();
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
this.refresh_copilot_suggestions(cx);
});
}
@ -2188,9 +2203,7 @@ impl Editor {
}
fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
if !cx.global::<Settings>().show_completions_on_input
|| self.has_active_copilot_suggestion(cx)
{
if !cx.global::<Settings>().show_completions_on_input {
return;
}
@ -2377,8 +2390,8 @@ impl Editor {
this.completion_tasks.retain(|(id, _)| *id > menu.id);
if this.focused && !menu.matches.is_empty() {
this.show_context_menu(ContextMenu::Completions(menu), cx);
} else {
this.hide_context_menu(cx);
} else if this.hide_context_menu(cx).is_none() {
this.update_visible_copilot_suggestion(cx);
}
});
}
@ -2481,6 +2494,8 @@ impl Editor {
);
});
}
this.refresh_copilot_suggestions(cx);
});
let project = self.project.clone()?;
@ -2775,50 +2790,24 @@ impl Editor {
fn refresh_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
let copilot = Copilot::global(cx)?;
if self.mode != EditorMode::Full {
return None;
}
let settings = cx.global::<Settings>();
if !self
.copilot_state
.user_enabled
.unwrap_or_else(|| settings.copilot_on(None))
{
if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
self.hide_copilot_suggestion(cx);
return None;
}
self.update_visible_copilot_suggestion(cx);
let snapshot = self.buffer.read(cx).snapshot(cx);
let selection = self.selections.newest_anchor();
if !self.copilot_state.user_enabled.is_some() {
let language_name = snapshot
.language_at(selection.start)
.map(|language| language.name());
let copilot_enabled = settings.copilot_on(language_name.as_deref());
if !copilot_enabled {
return None;
}
}
let cursor = if selection.start == selection.end {
selection.start.bias_left(&snapshot)
} else {
return None;
};
self.refresh_active_copilot_suggestion(cx);
if !copilot.read(cx).status().is_authorized() {
let cursor = self.selections.newest_anchor().head();
let language_name = snapshot.language_at(cursor).map(|language| language.name());
if !cx.global::<Settings>().copilot_on(language_name.as_deref()) {
self.hide_copilot_suggestion(cx);
return None;
}
let (buffer, buffer_position) =
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
let (completion, completions_cycling) = copilot.update(&mut cx, |copilot, cx| {
(
copilot.completions(&buffer, buffer_position, cx),
@ -2838,7 +2827,7 @@ impl Editor {
for completion in completions {
this.copilot_state.push_completion(completion);
}
this.refresh_active_copilot_suggestion(cx);
this.update_visible_copilot_suggestion(cx);
}
});
@ -2849,21 +2838,14 @@ impl Editor {
}
fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
// Auto re-enable copilot if you're asking for a suggestion
if self.copilot_state.user_enabled == Some(false) {
cx.notify();
self.copilot_state.user_enabled = Some(true);
}
if self.copilot_state.completions.is_empty() {
if !self.has_active_copilot_suggestion(cx) {
self.refresh_copilot_suggestions(cx);
return;
}
self.copilot_state.active_completion_index =
(self.copilot_state.active_completion_index + 1) % self.copilot_state.completions.len();
self.refresh_active_copilot_suggestion(cx);
self.update_visible_copilot_suggestion(cx);
}
fn previous_copilot_suggestion(
@ -2871,13 +2853,7 @@ impl Editor {
_: &copilot::PreviousSuggestion,
cx: &mut ViewContext<Self>,
) {
// Auto re-enable copilot if you're asking for a suggestion
if self.copilot_state.user_enabled == Some(false) {
cx.notify();
self.copilot_state.user_enabled = Some(true);
}
if self.copilot_state.completions.is_empty() {
if !self.has_active_copilot_suggestion(cx) {
self.refresh_copilot_suggestions(cx);
return;
}
@ -2888,45 +2864,44 @@ impl Editor {
} else {
self.copilot_state.active_completion_index - 1
};
self.refresh_active_copilot_suggestion(cx);
self.update_visible_copilot_suggestion(cx);
}
fn toggle_copilot_suggestions(&mut self, _: &copilot::Toggle, cx: &mut ViewContext<Self>) {
self.copilot_state.user_enabled = match self.copilot_state.user_enabled {
Some(enabled) => Some(!enabled),
None => {
let selection = self.selections.newest_anchor().start;
let language_name = self
.snapshot(cx)
.language_at(selection)
.map(|language| language.name());
let copilot_enabled = cx.global::<Settings>().copilot_on(language_name.as_deref());
Some(!copilot_enabled)
}
};
// We know this can't be None, as we just set it to Some above
if self.copilot_state.user_enabled == Some(true) {
self.refresh_copilot_suggestions(cx);
fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
if let Some(text) = self.hide_copilot_suggestion(cx) {
self.insert_with_autoindent_mode(&text.to_string(), None, cx);
true
} else {
self.clear_copilot_suggestions(cx);
false
}
}
cx.notify();
fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
self.display_map.read(cx).has_suggestion()
}
fn refresh_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor = self.selections.newest_anchor().head();
if self.context_menu.is_some() {
self.display_map
fn hide_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Rope> {
if self.has_active_copilot_suggestion(cx) {
let old_suggestion = self
.display_map
.update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
cx.notify();
old_suggestion.map(|suggestion| suggestion.text)
} else {
None
}
}
fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
let snapshot = self.buffer.read(cx).snapshot(cx);
let selection = self.selections.newest_anchor();
let cursor = selection.head();
if self.context_menu.is_some()
|| !self.completion_tasks.is_empty()
|| selection.start != selection.end
{
self.hide_copilot_suggestion(cx);
} else if let Some(text) = self
.copilot_state
.text_for_active_completion(cursor, &snapshot)
@ -2941,44 +2916,11 @@ impl Editor {
)
});
cx.notify();
} else if self.has_active_copilot_suggestion(cx) {
self.display_map
.update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
cx.notify();
}
}
fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor = self.selections.newest_anchor().head();
if let Some(text) = self
.copilot_state
.text_for_active_completion(cursor, &snapshot)
{
self.insert_with_autoindent_mode(&text.to_string(), None, cx);
self.clear_copilot_suggestions(cx);
true
} else {
false
self.hide_copilot_suggestion(cx);
}
}
fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> bool {
self.display_map
.update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
let was_empty = self.copilot_state.completions.is_empty();
self.copilot_state.completions.clear();
self.copilot_state.active_completion_index = 0;
self.copilot_state.pending_refresh = Task::ready(None);
self.copilot_state.excerpt_id = None;
cx.notify();
!was_empty
}
fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
self.display_map.read(cx).has_suggestion()
}
pub fn render_code_actions_indicator(
&self,
style: &EditorStyle,
@ -3094,7 +3036,7 @@ impl Editor {
self.completion_tasks.clear();
}
self.context_menu = Some(menu);
self.refresh_active_copilot_suggestion(cx);
self.hide_copilot_suggestion(cx);
cx.notify();
}
@ -3102,7 +3044,9 @@ impl Editor {
cx.notify();
self.completion_tasks.clear();
let context_menu = self.context_menu.take();
self.refresh_active_copilot_suggestion(cx);
if context_menu.is_some() {
self.update_visible_copilot_suggestion(cx);
}
context_menu
}
@ -3262,6 +3206,7 @@ impl Editor {
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
this.insert("", cx);
this.refresh_copilot_suggestions(cx);
});
}
@ -3277,6 +3222,7 @@ impl Editor {
})
});
this.insert("", cx);
this.refresh_copilot_suggestions(cx);
});
}
@ -3289,10 +3235,6 @@ impl Editor {
}
pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
if self.accept_copilot_suggestion(cx) {
return;
}
if self.move_to_next_snippet_tabstop(cx) {
return;
}
@ -3322,8 +3264,8 @@ impl Editor {
// If the selection is empty and the cursor is in the leading whitespace before the
// suggested indentation, then auto-indent the line.
let cursor = selection.head();
if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
let current_indent = snapshot.indent_size_for_line(cursor.row);
if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
if cursor.column < suggested_indent.len
&& cursor.column <= current_indent.len
&& current_indent.len <= suggested_indent.len
@ -3342,6 +3284,16 @@ impl Editor {
}
}
// Accept copilot suggestion if there is only one selection and the cursor is not
// in the leading whitespace.
if self.selections.count() == 1
&& cursor.column >= current_indent.len
&& self.has_active_copilot_suggestion(cx)
{
self.accept_copilot_suggestion(cx);
return;
}
// Otherwise, insert a hard or soft tab.
let settings = cx.global::<Settings>();
let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
@ -3365,7 +3317,8 @@ impl Editor {
self.transact(cx, |this, cx| {
this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections))
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
this.refresh_copilot_suggestions(cx);
});
}
@ -4045,6 +3998,7 @@ impl Editor {
}
self.request_autoscroll(Autoscroll::fit(), cx);
self.unmark_text(cx);
self.refresh_copilot_suggestions(cx);
cx.emit(Event::Edited);
}
}
@ -4059,6 +4013,7 @@ impl Editor {
}
self.request_autoscroll(Autoscroll::fit(), cx);
self.unmark_text(cx);
self.refresh_copilot_suggestions(cx);
cx.emit(Event::Edited);
}
}
@ -6466,7 +6421,9 @@ impl Editor {
multi_buffer::Event::Edited => {
self.refresh_active_diagnostics(cx);
self.refresh_code_actions(cx);
self.refresh_copilot_suggestions(cx);
if self.has_active_copilot_suggestion(cx) {
self.update_visible_copilot_suggestion(cx);
}
cx.emit(Event::BufferEdited);
}
multi_buffer::Event::ExcerptsAdded {
@ -6497,6 +6454,10 @@ impl Editor {
cx.notify();
}
fn on_settings_changed(&mut self, cx: &mut ViewContext<Self>) {
self.refresh_copilot_suggestions(cx);
}
pub fn set_searchable(&mut self, searchable: bool) {
self.searchable = searchable;
}

View File

@ -16,7 +16,7 @@ use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegist
use parking_lot::Mutex;
use project::FakeFs;
use settings::EditorSettings;
use std::{cell::RefCell, rc::Rc, time::Instant};
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use unindent::Unindent;
use util::{
assert_set_eq,
@ -4585,81 +4585,6 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
cx.assert_editor_state("editor.closeˇ");
handle_resolve_completion_request(&mut cx, None).await;
apply_additional_edits.await.unwrap();
// Handle completion request passing a marked string specifying where the completion
// should be triggered from using '|' character, what range should be replaced, and what completions
// should be returned using '<' and '>' to delimit the range
async fn handle_completion_request<'a>(
cx: &mut EditorLspTestContext<'a>,
marked_string: &str,
completions: Vec<&'static str>,
) {
let complete_from_marker: TextRangeMarker = '|'.into();
let replace_range_marker: TextRangeMarker = ('<', '>').into();
let (_, mut marked_ranges) = marked_text_ranges_by(
marked_string,
vec![complete_from_marker.clone(), replace_range_marker.clone()],
);
let complete_from_position =
cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
let replace_range =
cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
let completions = completions.clone();
async move {
assert_eq!(params.text_document_position.text_document.uri, url.clone());
assert_eq!(
params.text_document_position.position,
complete_from_position
);
Ok(Some(lsp::CompletionResponse::Array(
completions
.iter()
.map(|completion_text| lsp::CompletionItem {
label: completion_text.to_string(),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: replace_range,
new_text: completion_text.to_string(),
})),
..Default::default()
})
.collect(),
)))
}
})
.next()
.await;
}
async fn handle_resolve_completion_request<'a>(
cx: &mut EditorLspTestContext<'a>,
edits: Option<Vec<(&'static str, &'static str)>>,
) {
let edits = edits.map(|edits| {
edits
.iter()
.map(|(marked_string, new_text)| {
let (_, marked_ranges) = marked_text_ranges(marked_string, false);
let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
lsp::TextEdit::new(replace_range, new_text.to_string())
})
.collect::<Vec<_>>()
});
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
let edits = edits.clone();
async move {
Ok(lsp::CompletionItem {
additional_text_edits: edits,
..Default::default()
})
}
})
.next()
.await;
}
}
#[gpui::test]
@ -5956,6 +5881,288 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test(iterations = 10)]
async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
let (copilot, copilot_lsp) = Copilot::fake(cx);
cx.update(|cx| cx.set_global(copilot));
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"
oneˇ
two
three
"});
// When inserting, ensure autocompletion is favored over Copilot suggestions.
cx.simulate_keystroke(".");
let _ = handle_completion_request(
&mut cx,
indoc! {"
one.|<>
two
three
"},
vec!["completion_a", "completion_b"],
);
handle_copilot_completion_request(
&copilot_lsp,
vec![copilot::request::Completion {
text: "copilot1".into(),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
..Default::default()
}],
vec![],
);
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
cx.update_editor(|editor, cx| {
assert!(editor.context_menu_visible());
assert!(!editor.has_active_copilot_suggestion(cx));
// Confirming a completion inserts it and hides the context menu, without showing
// the copilot suggestion afterwards.
editor
.confirm_completion(&Default::default(), cx)
.unwrap()
.detach();
assert!(!editor.context_menu_visible());
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
});
cx.set_state(indoc! {"
oneˇ
two
three
"});
// When inserting, ensure autocompletion is favored over Copilot suggestions.
cx.simulate_keystroke(".");
let _ = handle_completion_request(
&mut cx,
indoc! {"
one.|<>
two
three
"},
vec!["completion_a", "completion_b"],
);
handle_copilot_completion_request(
&copilot_lsp,
vec![copilot::request::Completion {
text: "one.copilot1".into(),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
..Default::default()
}],
vec![],
);
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
cx.update_editor(|editor, cx| {
assert!(editor.context_menu_visible());
assert!(!editor.has_active_copilot_suggestion(cx));
// When hiding the context menu, the Copilot suggestion becomes visible.
editor.hide_context_menu(cx);
assert!(!editor.context_menu_visible());
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
});
// Ensure existing completion is interpolated when inserting again.
cx.simulate_keystroke("c");
deterministic.run_until_parked();
cx.update_editor(|editor, cx| {
assert!(!editor.context_menu_visible());
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
});
// After debouncing, new Copilot completions should be requested.
handle_copilot_completion_request(
&copilot_lsp,
vec![copilot::request::Completion {
text: "one.copilot2".into(),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
..Default::default()
}],
vec![],
);
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
cx.update_editor(|editor, cx| {
assert!(!editor.context_menu_visible());
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
// Canceling should remove the active Copilot suggestion.
editor.cancel(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
// After canceling, tabbing shouldn't insert the previously shown suggestion.
editor.tab(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.c \ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.c \ntwo\nthree\n");
// When undoing the previously active suggestion is shown again.
editor.undo(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
});
// If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
cx.update_editor(|editor, cx| {
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
// Tabbing when there is an active suggestion inserts it.
editor.tab(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
// When undoing the previously active suggestion is shown again.
editor.undo(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
// Hide suggestion.
editor.cancel(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
});
// If an edit occurs outside of this editor but no suggestion is being shown,
// we won't make it visible.
cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
cx.update_editor(|editor, cx| {
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
});
// Reset the editor to verify how suggestions behave when tabbing on leading indentation.
cx.update_editor(|editor, cx| {
editor.set_text("fn foo() {\n \n}", cx);
editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
});
});
handle_copilot_completion_request(
&copilot_lsp,
vec![copilot::request::Completion {
text: " let x = 4;".into(),
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
..Default::default()
}],
vec![],
);
cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
cx.update_editor(|editor, cx| {
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
assert_eq!(editor.text(cx), "fn foo() {\n \n}");
// Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
editor.tab(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.text(cx), "fn foo() {\n \n}");
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
// Tabbing again accepts the suggestion.
editor.tab(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.text(cx), "fn foo() {\n let x = 4;\n}");
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
});
}
#[gpui::test]
async fn test_copilot_completion_invalidation(
deterministic: Arc<Deterministic>,
cx: &mut gpui::TestAppContext,
) {
let (copilot, copilot_lsp) = Copilot::fake(cx);
cx.update(|cx| cx.set_global(copilot));
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"
one
twˇ
three
"});
handle_copilot_completion_request(
&copilot_lsp,
vec![copilot::request::Completion {
text: "two.foo()".into(),
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
..Default::default()
}],
vec![],
);
cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
cx.update_editor(|editor, cx| {
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
assert_eq!(editor.text(cx), "one\ntw\nthree\n");
editor.backspace(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
assert_eq!(editor.text(cx), "one\nt\nthree\n");
editor.backspace(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
assert_eq!(editor.text(cx), "one\n\nthree\n");
// Deleting across the original suggestion range invalidates it.
editor.backspace(&Default::default(), cx);
assert!(!editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one\nthree\n");
assert_eq!(editor.text(cx), "one\nthree\n");
// Undoing the deletion restores the suggestion.
editor.undo(&Default::default(), cx);
assert!(editor.has_active_copilot_suggestion(cx));
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
assert_eq!(editor.text(cx), "one\n\nthree\n");
});
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@ -5971,3 +6178,106 @@ fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewCo
marked_text
);
}
/// Handle completion request passing a marked string specifying where the completion
/// should be triggered from using '|' character, what range should be replaced, and what completions
/// should be returned using '<' and '>' to delimit the range
fn handle_completion_request<'a>(
cx: &mut EditorLspTestContext<'a>,
marked_string: &str,
completions: Vec<&'static str>,
) -> impl Future<Output = ()> {
let complete_from_marker: TextRangeMarker = '|'.into();
let replace_range_marker: TextRangeMarker = ('<', '>').into();
let (_, mut marked_ranges) = marked_text_ranges_by(
marked_string,
vec![complete_from_marker.clone(), replace_range_marker.clone()],
);
let complete_from_position =
cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
let replace_range =
cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
let completions = completions.clone();
async move {
assert_eq!(params.text_document_position.text_document.uri, url.clone());
assert_eq!(
params.text_document_position.position,
complete_from_position
);
Ok(Some(lsp::CompletionResponse::Array(
completions
.iter()
.map(|completion_text| lsp::CompletionItem {
label: completion_text.to_string(),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: replace_range,
new_text: completion_text.to_string(),
})),
..Default::default()
})
.collect(),
)))
}
});
async move {
request.next().await;
}
}
fn handle_resolve_completion_request<'a>(
cx: &mut EditorLspTestContext<'a>,
edits: Option<Vec<(&'static str, &'static str)>>,
) -> impl Future<Output = ()> {
let edits = edits.map(|edits| {
edits
.iter()
.map(|(marked_string, new_text)| {
let (_, marked_ranges) = marked_text_ranges(marked_string, false);
let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
lsp::TextEdit::new(replace_range, new_text.to_string())
})
.collect::<Vec<_>>()
});
let mut request =
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
let edits = edits.clone();
async move {
Ok(lsp::CompletionItem {
additional_text_edits: edits,
..Default::default()
})
}
});
async move {
request.next().await;
}
}
fn handle_copilot_completion_request(
lsp: &lsp::FakeLanguageServer,
completions: Vec<copilot::request::Completion>,
completions_cycling: Vec<copilot::request::Completion>,
) {
lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
let completions = completions.clone();
async move {
Ok(copilot::request::GetCompletionsResult {
completions: completions.clone(),
})
}
});
lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
let completions_cycling = completions_cycling.clone();
async move {
Ok(copilot::request::GetCompletionsResult {
completions: completions_cycling.clone(),
})
}
});
}

View File

@ -3952,6 +3952,19 @@ impl<'a, T: View> ViewContext<'a, T> {
})
}
pub fn observe_global<G, F>(&mut self, mut callback: F) -> Subscription
where
G: Any,
F: 'static + FnMut(&mut T, &mut ViewContext<T>),
{
let observer = self.weak_handle();
self.app.observe_global::<G, _>(move |cx| {
if let Some(observer) = observer.upgrade(cx) {
observer.update(cx, |observer, cx| callback(observer, cx));
}
})
}
pub fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
where
F: 'static + FnMut(&mut T, ViewHandle<V>, bool, &mut ViewContext<T>),

View File

@ -227,6 +227,7 @@ pub enum Lifecycle<T: Element> {
paint: T::PaintState,
},
}
pub struct ElementBox(ElementRc);
#[derive(Clone)]

View File

@ -65,8 +65,15 @@ impl platform::Screen for Screen {
// This approach is similar to that which winit takes
// https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
let device_description = self.native_screen.deviceDescription();
let key = ns_string("NSScreenNumber");
let device_id_obj = device_description.objectForKey_(key);
if device_id_obj.is_null() {
// Under some circumstances, especially display re-arrangements or display locking, we seem to get a null pointer
// to the device id. See: https://linear.app/zed-industries/issue/Z-257/lock-screen-crash-with-multiple-monitors
return None;
}
let mut device_id: u32 = 0;
CFNumberGetValue(
device_id_obj as CFNumberRef,

View File

@ -335,6 +335,7 @@ impl LanguageServer {
did_change_configuration: Some(DynamicRegistrationClientCapabilities {
dynamic_registration: Some(true),
}),
workspace_folders: Some(true),
..Default::default()
}),
text_document: Some(TextDocumentClientCapabilities {

View File

@ -20,6 +20,7 @@ fs = { path = "../fs" }
anyhow = "1.0.38"
futures = "0.3"
theme = { path = "../theme" }
staff_mode = { path = "../staff_mode" }
util = { path = "../util" }
json_comments = "0.2"
postage = { workspace = true }

View File

@ -177,6 +177,7 @@ pub struct EditorSettings {
pub ensure_final_newline_on_save: Option<bool>,
pub formatter: Option<Formatter>,
pub enable_language_server: Option<bool>,
#[schemars(skip)]
pub copilot: Option<OnOff>,
}
@ -436,6 +437,7 @@ pub struct SettingsFileContent {
#[serde(default)]
pub base_keymap: Option<BaseKeymap>,
#[serde(default)]
#[schemars(skip)]
pub enable_copilot_integration: Option<bool>,
}
@ -779,6 +781,7 @@ pub fn settings_file_json_schema(
settings.option_add_null_type = false;
});
let generator = SchemaGenerator::new(settings);
let mut root_schema = generator.into_root_schema_for::<SettingsFileContent>();
// Create a schema for a theme name.
@ -791,6 +794,7 @@ pub fn settings_file_json_schema(
// Create a schema for a 'languages overrides' object, associating editor
// settings with specific langauges.
assert!(root_schema.definitions.contains_key("EditorSettings"));
let languages_object_schema = SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
object: Some(Box::new(ObjectValidation {

View File

@ -0,0 +1,12 @@
[package]
name = "staff_mode"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/staff_mode.rs"
[dependencies]
gpui = { path = "../gpui" }
anyhow = "1.0.38"

View File

@ -0,0 +1,42 @@
use gpui::MutableAppContext;
#[derive(Debug, Default)]
pub struct StaffMode(pub bool);
impl std::ops::Deref for StaffMode {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// Despite what the type system requires me to tell you, the init function will only be called a once
/// as soon as we know that the staff mode is enabled.
pub fn staff_mode<F: FnMut(&mut MutableAppContext) + 'static>(
cx: &mut MutableAppContext,
mut init: F,
) {
if **cx.default_global::<StaffMode>() {
init(cx)
} else {
let mut once = Some(());
cx.observe_global::<StaffMode, _>(move |cx| {
if **cx.global::<StaffMode>() && once.take().is_some() {
init(cx);
}
})
.detach();
}
}
/// Immediately checks and runs the init function if the staff mode is not enabled.
/// This is only included for symettry with staff_mode() above
pub fn not_staff_mode<F: FnOnce(&mut MutableAppContext) + 'static>(
cx: &mut MutableAppContext,
init: F,
) {
if !**cx.default_global::<StaffMode>() {
init(cx)
}
}

View File

@ -1,4 +1,7 @@
use crate::{BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint, ToPointUtf16};
use crate::{
locator::Locator, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint,
ToPointUtf16,
};
use anyhow::Result;
use std::{cmp::Ordering, fmt::Debug, ops::Range};
use sum_tree::Bias;
@ -86,6 +89,20 @@ impl Anchor {
{
content.summary_for_anchor(self)
}
/// Returns true when the [Anchor] is located inside a visible fragment.
pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
if *self == Anchor::MIN || *self == Anchor::MAX {
true
} else {
let fragment_id = buffer.fragment_id_for_anchor(self);
let mut fragment_cursor = buffer.fragments.cursor::<(Option<&Locator>, usize)>();
fragment_cursor.seek(&Some(fragment_id), Bias::Left, &None);
fragment_cursor
.item()
.map_or(false, |fragment| fragment.visible)
}
}
}
pub trait OffsetRangeExt {

View File

@ -15,6 +15,7 @@ gpui = { path = "../gpui" }
picker = { path = "../picker" }
theme = { path = "../theme" }
settings = { path = "../settings" }
staff_mode = { path = "../staff_mode" }
workspace = { path = "../workspace" }
util = { path = "../util" }
log = { version = "0.4.16", features = ["kv_unstable_serde"] }

View File

@ -5,9 +5,9 @@ use gpui::{
};
use picker::{Picker, PickerDelegate};
use settings::{settings_file::SettingsFile, Settings};
use staff_mode::StaffMode;
use std::sync::Arc;
use theme::{Theme, ThemeMeta, ThemeRegistry};
use util::StaffMode;
use workspace::{AppState, Workspace};
pub struct ThemeSelector {

View File

@ -3,8 +3,12 @@ use std::env;
use lazy_static::lazy_static;
lazy_static! {
pub static ref RELEASE_CHANNEL_NAME: String = env::var("ZED_RELEASE_CHANNEL")
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string());
pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) {
env::var("ZED_RELEASE_CHANNEL")
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string())
} else {
include_str!("../../zed/RELEASE_CHANNEL").to_string()
};
pub static ref RELEASE_CHANNEL: ReleaseChannel = match RELEASE_CHANNEL_NAME.as_str() {
"dev" => ReleaseChannel::Dev,
"preview" => ReleaseChannel::Preview,

View File

@ -17,17 +17,6 @@ pub use backtrace::Backtrace;
use futures::Future;
use rand::{seq::SliceRandom, Rng};
#[derive(Debug, Default)]
pub struct StaffMode(pub bool);
impl std::ops::Deref for StaffMode {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[macro_export]
macro_rules! debug_panic {
( $($fmt_arg:tt)* ) => {

View File

@ -191,16 +191,8 @@ impl View for WelcomePage {
impl WelcomePage {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
let handle = cx.weak_handle();
let settings_subscription = cx.observe_global::<Settings, _>(move |cx| {
if let Some(handle) = handle.upgrade(cx) {
handle.update(cx, |_, cx| cx.notify())
}
});
WelcomePage {
_settings_subscription: settings_subscription,
_settings_subscription: cx.observe_global::<Settings, _>(move |_, cx| cx.notify()),
}
}
}

View File

@ -1,7 +1,16 @@
use std::ops::Range;
use crate::{ItemHandle, Pane};
use gpui::{
elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription,
View, ViewContext, ViewHandle,
elements::*,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
AnyViewHandle, DebugContext, ElementBox, Entity, LayoutContext, MeasurementContext,
MutableAppContext, PaintContext, RenderContext, SizeConstraint, Subscription, View,
ViewContext, ViewHandle,
};
use settings::Settings;
@ -40,7 +49,9 @@ impl View for StatusBar {
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = &cx.global::<Settings>().theme.workspace.status_bar;
Flex::row()
StatusBarElement {
left: Flex::row()
.with_children(self.left_items.iter().map(|i| {
ChildView::new(i.as_any(), cx)
.aligned()
@ -48,14 +59,18 @@ impl View for StatusBar {
.with_margin_right(theme.item_spacing)
.boxed()
}))
.boxed(),
right: Flex::row()
.with_children(self.right_items.iter().rev().map(|i| {
ChildView::new(i.as_any(), cx)
.aligned()
.contained()
.with_margin_left(theme.item_spacing)
.flex_float()
.boxed()
}))
.boxed(),
}
.contained()
.with_style(theme.container)
.constrained()
@ -131,3 +146,74 @@ impl From<&dyn StatusItemViewHandle> for AnyViewHandle {
val.as_any().clone()
}
}
struct StatusBarElement {
left: ElementBox,
right: ElementBox,
}
impl Element for StatusBarElement {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
mut constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let max_width = constraint.max.x();
constraint.min = vec2f(0., constraint.min.y());
let right_size = self.right.layout(constraint, cx);
let constraint = SizeConstraint::new(
vec2f(0., constraint.min.y()),
vec2f(max_width - right_size.x(), constraint.max.y()),
);
self.left.layout(constraint, cx);
(vec2f(max_width, right_size.y()), ())
}
fn paint(
&mut self,
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
cx: &mut PaintContext,
) -> Self::PaintState {
let origin_y = bounds.upper_right().y();
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
let left_origin = vec2f(bounds.lower_left().x(), origin_y);
self.left.paint(left_origin, visible_bounds, cx);
let right_origin = vec2f(bounds.upper_right().x() - self.right.size().x(), origin_y);
self.right.paint(right_origin, visible_bounds, cx);
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &DebugContext,
) -> serde_json::Value {
json!({
"type": "StatusBarElement",
"bounds": bounds.to_json()
})
}
}

View File

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.81.0"
version = "0.82.0"
publish = false
[lib]
@ -55,6 +55,7 @@ project_symbols = { path = "../project_symbols" }
recent_projects = { path = "../recent_projects" }
rpc = { path = "../rpc" }
settings = { path = "../settings" }
staff_mode = { path = "../staff_mode" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
terminal_view = { path = "../terminal_view" }

View File

@ -8,6 +8,7 @@ use node_runtime::NodeRuntime;
use serde_json::json;
use settings::{keymap_file_json_schema, settings_file_json_schema};
use smol::fs;
use staff_mode::StaffMode;
use std::{
any::Any,
ffi::OsString,
@ -17,7 +18,7 @@ use std::{
};
use theme::ThemeRegistry;
use util::http::HttpClient;
use util::{paths, ResultExt, StaffMode};
use util::{paths, ResultExt};
const SERVER_PATH: &'static str =
"node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";

View File

@ -109,10 +109,6 @@
(false)
] @constant.builtin
(interpolation
"#{" @punctuation.special
"}" @punctuation.special) @embedded
(comment) @comment
; Operators
@ -179,3 +175,7 @@
"%w("
"%i("
] @punctuation.bracket
(interpolation
"#{" @punctuation.special
"}" @punctuation.special) @embedded

View File

@ -106,6 +106,9 @@ impl LspAdapter for YamlLspAdapter {
let settings = cx.global::<Settings>();
Some(
future::ready(serde_json::json!({
"yaml": {
"keyOrdering": false
},
"[yaml]": {
"editor.tabSize": settings.tab_size(Some("YAML"))
}

View File

@ -38,9 +38,9 @@ use welcome::{show_welcome_experience, FIRST_OPEN};
use fs::RealFs;
use settings::watched_json::WatchedJsonFile;
use theme::ThemeRegistry;
#[cfg(debug_assertions)]
use util::StaffMode;
use staff_mode::StaffMode;
use theme::ThemeRegistry;
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
use workspace::{
self, dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile,
@ -161,7 +161,7 @@ fn main() {
terminal_view::init(cx);
theme_testbench::init(cx);
recent_projects::init(cx);
copilot::init(client.clone(), node_runtime, cx);
copilot::init(http.clone(), node_runtime, cx);
cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
.detach();

View File

@ -121,7 +121,7 @@ rm ${dmg_source_directory}/Applications
echo "Adding license agreement to DMG"
npm install --global dmg-license minimist
dmg-license script/terms/terms-of-use.json "${dmg_file_path}"
dmg-license script/eula/eula.json "${dmg_file_path}"
if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
echo "Notarizing DMG with Apple"

View File

@ -3,7 +3,7 @@
{
"lang": "en-US",
"type": "rtf",
"file": "terms-of-use.rtf"
"file": "eula.rtf"
}
]
}

118
script/eula/eula.md Normal file
View File

@ -0,0 +1,118 @@
# ZED TERMS AND CONDITIONS
PLEASE READ THESE TERMS AND CONDITIONS CAREFULLY BEFORE USING THE SERVICE OR SOFTWARE OFFERED BY ZED INDUSTRIES, INC. ("ZED", OR "WE"). BY ACCESSING OR USING THE SOLUTION (AS DEFINED BELOW) IN ANY MANNER, YOU ("YOU" OR "CUSTOMER") AGREE TO BE BOUND BY THESE TERMS (THE "AGREEMENT") TO THE EXCLUSION OF ALL OTHER TERMS. YOU REPRESENT AND WARRANT THAT YOU HAVE THE AUTHORITY TO ENTER INTO THIS AGREEMENT; IF YOU ARE ENTERING INTO THIS AGREEMENT ON BEHALF OF AN ORGANIZATION OR ENTITY, REFERENCES TO "CUSTOMER" AND "YOU" IN THIS AGREEMENT, REFER TO THAT ORGANIZATION OR ENTITY. IF YOU DO NOT AGREE TO ALL OF THE FOLLOWING, YOU MAY NOT USE OR ACCESS THE SOLUTION IN ANY MANNER. IF THE TERMS OF THIS AGREEMENT ARE CONSIDERED AN OFFER, ACCEPTANCE IS EXPRESSLY LIMITED TO SUCH TERMS.
## 1. ACCESS TO AND USE OF THE SOLUTION
Subject to the terms and conditions in this Agreement, Zed will make available to You the Zed Editor Software (the "Editor"), and the Zed Collaboration Service (the "Network Based Service"). The Network Based Service may be used in conjunction with the Editor (the Network Based Service and Editor, collectively the "Solution"). The Network Based Service is designed to allow for collaboration, but the use of the Network Based Service is not required to make use of the Editor; Your use of the Network Based Service is optional.
## 2. TERMS APPLICABLE TO THE EDITOR
### 2.1. License Grant
Subject to the terms and conditions of this Agreement, Zed hereby grants to You, and You hereby accept from Zed, a term-limited, non-exclusive, non-transferable, non-assignable and non-sublicensable license to make use of the Editor for Your internal use only, and subject to the use limitations in the table in Section 2.2.
### 2.2 License Limitations
You agree that You shall not: (a) exceed the scope of the licenses granted in Section 2.1; (b) make copies of the Editor; (c) distribute, sublicense, assign, delegate, rent, lease, sell, time-share or otherwise transfer the benefits of, use under, or rights to, the license granted in Section 2.1; (d) reverse engineer, decompile, disassemble or otherwise attempt to learn the source code, structure or algorithms underlying the Editor, except to the extent required to be permitted under applicable law; (e) modify, translate or create derivative works of the Editor; or (f) remove any copyright, trademark, patent or other proprietary notice that appears on the Editor or copies thereof.
## 3. TERMS APPLICABLE TO THE NETWORK BASED SERVICE
### 3.1 Access to and Scope of Network Based Service
If you have elected to use the Network Based Service by enabling or activating the Network Based Service, Zed will use commercially reasonable efforts to make the Network Based Service available to You as set forth in this Agreement. Once you elected to use the Network Based Service, You may access and use the Network Based Service during the Term, subject to Your compliance with the terms and conditions of the Agreement,
### 3.2 Restrictions
You will use the Network Based Service only in accordance with all applicable laws, including, but not limited to, laws related to data (whether applicable within the United States, the European Union, or otherwise). You agree not to (and will not allow any third party to): (i) remove or otherwise alter any proprietary notices or labels from the Network Based Service or any portion thereof; (ii) reverse engineer, decompile, disassemble, or otherwise attempt to discover the underlying structure, ideas, or algorithms of the Network Based Service or any software used to provide or make the Network Based Service available; or (iii) rent, resell or otherwise allow any third party access to or use of the Network Based Service. Zed may suspend Your access to or use of the Network Based Service as follows: (a) immediately if Zed reasonably believes Your use of the Network Based Service may pose a security risk to or may adversely impact the Network Based Service; or (b) if You are in breach of this Agreement.
### 3.3 Customer Data
You are solely responsible for Customer Data including, but not limited to: (a) compliance with all applicable laws and this Agreement; (b) any claims relating to Customer Data; and (c) any claims that Customer Data infringes, misappropriates, or otherwise violates the rights of any third party. You agree and acknowledge that Customer Data may be irretrievably deleted if Your account is terminated. For purposes of this Agreement, "Customer Data" shall mean any data, information or other material provided, uploaded, or submitted by You to the Network Based Service in the course of using the Network Based Service. You shall retain all right, title and interest in and to the Customer Data, including all intellectual property rights therein.
#### 3.3.1 Customer Data Made Available to Zed
To the extent You elect to make Customer Data available to Zed, the same may only be used by Zed according to the Customer Data type and the use rights regarding the same as described herein:
##### 3.3.1.1. Telemetry Data
Customer Data consisting of non-personally-identifiable information that helps Zed understand how the Solution is being used and how it is performing on user machines is classified as "Telemetry Data". By way of example, Telemetry Data may include the file extensions being edited, or the fact that a particular feature is used and how many times. Telemetry Data does not include the content You are editing. You may grant us permission to capture Telemetry Data when installing or activating the Editor, and You may change Your preferences at any time in the settings feature of the Solution. Once You grant Us this permission, We will retain the Telemetry Data indefinitely. If You elect to use the Network Based Service, You agree that We may capture non-personally-identifiable Telemetry Data on Zed's backend systems, and keep such data indefinitely.
##### 3.3.1.2. Crash Reports
Customer Data consisting of data related to the behavior of the Solution prior to a crash or failure, such as stack traces are collected and classified as " Crash Reports". Zed will use commercially reasonable efforts to exclude any personally identifiable information from Crash Reports, but due to the nature of a crash, Zed does not ensure that information such as paths will be excluded from Crash Reports. Crash Reports will be used solely for Zed's internal purposes in connection with diagnosing defects in the Solution that led to the crash. You may grant us permission to capture Crash Reports when installing or activating the Solution, and You may change Your preferences at any time in the settings feature of the Solution. Once You grant us this permission, Zed will retain the Crash Reports indefinitely.
##### 3.3.1.3. User Content
Customer Data consisting of User content created while using the Solution is classified as "User Content". User Content is transmitted from Your environment only if You collaborate with other Zed users by electing to share a project in the Editor. Once You share a project, Zed may transmit User Content consisting of file paths, file contents, and metadata regarding the code returned by language servers. Currently, Zed does not persist any User Content beyond the Your collaboration session. If You unshare a project or disconnect from the Solution, all information associated with such project will be deleted from Zed servers. In the future, Zed may save User Content regarding projects beyond the scope of a single collaboration session. We may share such User Content with those users You elected to grant access to. Zed's access to such User Content is limited to debugging and making improvements to the Solution.
## 4. TERM AND TERMINATION
### 4.1 Term
The term of this Agreement shall commence on the date You first download the Editor or use the Network Based Service (the "Effective Date"), and unless terminated earlier according to this Section 3, will end pursuant to this Section 4 (the "Term").
### 4.2 Termination
This Agreement may be terminated: (a) by either party if the other has materially breached this Agreement; or (b) by Zed at any time and for any reason upon notice to Customer. You acknowledge that Zed is under no obligation to continue to operate the Network Based Service or make the Editor available, and We may end any programs in connection with the same at any time.
### 4.3 Effect of Termination and Survival
Upon any expiration or termination of this Agreement, Customer shall (i) immediately cease use of the Network Based Service, and (ii) return all Zed Confidential Information and other materials provided by Zed. The following provisions will survive termination of this Agreement: Sections 1.4 (Ownership), 3 (Customer Data); 4.3 (Effect of Termination and Survival), Section 5 (Ownership), Section 6 (Indemnification), Section 8 (Limitation of Liability), and Section 9 (Miscellaneous).
## 5. OWNERSHIP
Zed retains all right, title, and interest in and to the Network Based Service, Editor, and any software, products, works or other intellectual property created, used, provided, or made available by Zed under or in connection with the Network Based Service or Editor. Customer may from time to time provide suggestions, comments, or other feedback to Zed with respect to the Network Based Service or Editor ("Feedback"). Customer shall, and hereby does, grant to Zed a nonexclusive, worldwide, perpetual, irrevocable, transferable, sublicensable, royalty-free, fully paid-up license to use and exploit the Feedback for any purpose.
## 6. INDEMNIFICATION
Customer will defend, indemnify, and hold Zed, its affiliates, suppliers and licensors harmless and each of their respective officers, directors, employees and representatives from and against any claims, damages, losses, liabilities, costs, and expenses (including reasonable attorneys' fees) arising out of or relating to any third party claim with respect to: (a) Customer Data; (b) breach of this Agreement or violation of applicable law by Customer; or (c) alleged infringement or misappropriation of third-party's intellectual property rights resulting from Customer Data.
## 7. WARRANTY
Zed does not represent or warrant that the operation of the Network Based Service or Editor (or any portion thereof) will be uninterrupted or error free, or that the Network Based Service or Editor (or any portion thereof) will operate in combination with other hardware, software, systems or data not provided by Zed. CUSTOMER ACKNOWLEDGES THAT, Zed MAKES NO EXPRESS OR IMPLIED REPRESENTATIONS OR WARRANTIES OF ANY KIND WITH RESPECT TO THE SERVICE OR SOFTWARE, OR THEIR CONDITION. ZED HEREBY EXPRESSLY EXCLUDES, ANY AND ALL OTHER EXPRESS OR IMPLIED REPRESENTATIONS OR WARRANTIES, WHETHER UNDER COMMON LAW, STATUTE OR OTHERWISE, INCLUDING WITHOUT LIMITATION ANY AND ALL WARRANTIES AS TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, SATISFACTORY QUALITY OR NON-INFRINGEMENT OF THIRD-PARTY RIGHTS.
## 8. LIMITATIONS OF LIABILITY
IN NO EVENT SHALL ZED BE LIABLE FOR ANY LOST DATA, LOST PROFITS, BUSINESS INTERRUPTION, REPLACEMENT SERVICE OR OTHER SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR INDIRECT DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THEORY OF LIABILITY. ZED'S LIABILITY FOR ALL CLAIMS ARISING UNDER THIS AGREEMENT, WHETHER IN CONTRACT, TORT OR OTHERWISE, SHALL NOT EXCEED ONE THOUSAND US DOLLARS ($1,000).
## 9. MISCELLANEOUS
### 9.1 Export Control
You hereby certify that You will comply with all current US Export Control laws. You agree to defend, indemnify and hold Zed harmless from any liability for Your violation of U.S. Export Control laws.
### 9.2 Compliance with Laws
You shall comply with all applicable laws and regulations in its use of the Solution, including without limitation the unlawful gathering or collecting, or assisting in the gathering or collecting of information in violation of any privacy laws or regulations. You shall, at its own expense, defend, indemnify and hold harmless Zed from and against any and all claims, losses, liabilities, damages, judgments, government or federal sanctions, costs and expenses (including attorneys' fees) incurred by Zed arising from any claim or assertion by any third party of violation of privacy laws or regulations by You or any of its agents, officers, directors or employees.
### 9.3 Assignment
Neither party may transfer and assign its rights and obligations under this Agreement without the prior written consent of the other party. Notwithstanding the foregoing, Zed may transfer and assign its rights under this Agreement without consent from the other party in connection with a change in control, acquisition or sale of all or substantially all of its assets.
### 9.4 Force Majeure
Neither party shall be responsible for failure or delay in performance by events out of their reasonable control, including but not limited to, acts of God, Internet outage, terrorism, war, fires, earthquakes and other disasters (each a "Force Majeure"). Notwithstanding the foregoing: if a Force Majeure continues for more than thirty (30) days, either party may to terminate this agreement by written notice to the other party.
### 9.5 Notice
All notices between the parties shall be in writing and shall be deemed to have been given if personally delivered or sent by registered or certified mail (return receipt), or by recognized courier service.
### 9.6 No Agency
Both parties agree that no agency, partnership, joint venture, or employment is created as a result of this Agreement. You do not have any authority of any kind to bind Zed.
### 9.7 Governing Law
This Agreement shall be governed exclusively by, and construed exclusively in accordance with, the laws of the United States and the State of California, without regard to its conflict of laws provisions. The federal courts of the United States in the Northern District of California and the state courts of the State of California shall have exclusive jurisdiction to adjudicate any dispute arising out of or relating to this Agreement. Each party hereby consents to the jurisdiction of such courts and waives any right it may otherwise have to challenge the appropriateness of such forums, whether on the basis of the doctrine of forum non conveniens or otherwise. The United Nations Convention on Contracts for the International Sale of Goods shall not apply to this Agreement or any Purchase Order issued under this Agreement.
### 9.8 Updated Agreement
Zed reserves the right to update this Agreement at any time. The terms and conditions of the updated version of the Agreement shall apply to the Network Based Service and Editor downloaded, or accessed following the date of publication of the updated version. If You do not agree with any terms of the updated Agreement, You may not use or access the Network Based Service or Editor in any manner. Zed may from time-to-time provide release notes applicable to the Editor or Network Based Service, and such release notes may contain additional use restrictions or terms applicable to Customer Data. Your use of the Editor or Network Based Service after the applicable release notes are made available shall be subject to the additional use restrictions or terms applicable to Customer Data.
### 9.9 Entire Agreement
This Agreement is the complete and exclusive statement of the mutual understanding of the parties and supersedes and cancels all previous written and oral agreements, communications, and other understandings relating to the subject matter of this Agreement, and all waivers and modifications must be in a writing signed by both parties, except as otherwise provided herein. Any term or provision of this Agreement held to be illegal or unenforceable shall be, to the fullest extent possible, interpreted so as to be construed as valid, but in any event the validity or enforceability of the remainder hereof shall not be affected.
**DATE: April 5, 2023**

185
script/eula/eula.rtf Normal file
View File

@ -0,0 +1,185 @@
{\rtf1\ansi\ansicpg1252\cocoartf2639
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\froman\fcharset0 Times-Bold;\f1\froman\fcharset0 Times-Roman;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\deftab720
\pard\pardeftab720\sa321\partightenfactor0
\f0\b\fs48 \cf2 \expnd0\expndtw0\kerning0
\outl0\strokewidth0 \strokec2 ZED TERMS AND CONDITIONS\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 PLEASE READ THESE TERMS AND CONDITIONS CAREFULLY BEFORE USING THE SERVICE OR SOFTWARE OFFERED BY ZED INDUSTRIES, INC. ("ZED", OR "WE"). BY ACCESSING OR USING THE SOLUTION (AS DEFINED BELOW) IN ANY MANNER, YOU ("YOU" OR "CUSTOMER") AGREE TO BE BOUND BY THESE TERMS (THE "AGREEMENT") TO THE EXCLUSION OF ALL OTHER TERMS. YOU REPRESENT AND WARRANT THAT YOU HAVE THE AUTHORITY TO ENTER INTO THIS AGREEMENT; IF YOU ARE ENTERING INTO THIS AGREEMENT ON BEHALF OF AN ORGANIZATION OR ENTITY, REFERENCES TO "CUSTOMER" AND "YOU" IN THIS AGREEMENT, REFER TO THAT ORGANIZATION OR ENTITY. IF YOU DO NOT AGREE TO ALL OF THE FOLLOWING, YOU MAY NOT USE OR ACCESS THE SOLUTION IN ANY MANNER. IF THE TERMS OF THIS AGREEMENT ARE CONSIDERED AN OFFER, ACCEPTANCE IS EXPRESSLY LIMITED TO SUCH TERMS.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 1. ACCESS TO AND USE OF THE SOLUTION\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Subject to the terms and conditions in this Agreement, Zed will make available to You the Zed Editor Software (the "Editor"), and the Zed Collaboration Service (the "Network Based Service"). The Network Based Service may be used in conjunction with the Editor (the Network Based Service and Editor, collectively the "Solution"). The Network Based Service is designed to allow for collaboration, but the use of the Network Based Service is not required to make use of the Editor; Your use of the Network Based Service is optional.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 2. TERMS APPLICABLE TO THE EDITOR\
\pard\pardeftab720\sa280\partightenfactor0
\fs28 \cf2 2.1. License Grant\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Subject to the terms and conditions of this Agreement, Zed hereby grants to You, and You hereby accept from Zed, a term-limited, non-exclusive, non-transferable, non-assignable and non-sublicensable license to make use of the Editor for Your internal use only, and subject to the use limitations in the table in Section 2.2.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 2.2 License Limitations\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 You agree that You shall not: (a) exceed the scope of the licenses granted in Section 2.1; (b) make copies of the Editor; (c) distribute, sublicense, assign, delegate, rent, lease, sell, time-share or otherwise transfer the benefits of, use under, or rights to, the license granted in Section 2.1; (d) reverse engineer, decompile, disassemble or otherwise attempt to learn the source code, structure or algorithms underlying the Editor, except to the extent required to be permitted under applicable law; (e) modify, translate or create derivative works of the Editor; or (f) remove any copyright, trademark, patent or other proprietary notice that appears on the Editor or copies thereof.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 3. TERMS APPLICABLE TO THE NETWORK BASED SERVICE\
\pard\pardeftab720\sa280\partightenfactor0
\fs28 \cf2 3.1 Access to and Scope of Network Based Service\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 If you have elected to use the Network Based Service by enabling or activating the Network Based Service, Zed will use commercially reasonable efforts to make the Network Based Service available to You as set forth in this Agreement. Once you elected to use the Network Based Service, You may access and use the Network Based Service during the Term, subject to Your compliance with the terms and conditions of the Agreement,\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 3.2 Restrictions\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 You will use the Network Based Service only in accordance with all applicable laws, including, but not limited to, laws related to data (whether applicable within the United States, the European Union, or otherwise). You agree not to (and will not allow any third party to): (i) remove or otherwise alter any proprietary notices or labels from the Network Based Service or any portion thereof; (ii) reverse engineer, decompile, disassemble, or otherwise attempt to discover the underlying structure, ideas, or algorithms of the Network Based Service or any software used to provide or make the Network Based Service available; or (iii) rent, resell or otherwise allow any third party access to or use of the Network Based Service. Zed may suspend Your access to or use of the Network Based Service as follows: (a) immediately if Zed reasonably believes Your use of the Network Based Service may pose a security risk to or may adversely impact the Network Based Service; or (b) if You are in breach of this Agreement.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 3.3 Customer Data\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 You are solely responsible for Customer Data including, but not limited to: (a) compliance with all applicable laws and this Agreement; (b) any claims relating to Customer Data; and (c) any claims that Customer Data infringes, misappropriates, or otherwise violates the rights of any third party. You agree and acknowledge that Customer Data may be irretrievably deleted if Your account is terminated. For purposes of this Agreement, "Customer Data" shall mean any data, information or other material provided, uploaded, or submitted by You to the Network Based Service in the course of using the Network Based Service. You shall retain all right, title and interest in and to the Customer Data, including all intellectual property rights therein.\
\pard\pardeftab720\sa319\partightenfactor0
\f0\b \cf2 3.3.1 Customer Data Made Available to Zed\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0 \cf2 To the extent You elect to make Customer Data available to Zed, the same may only be used by Zed according to the Customer Data type and the use rights regarding the same as described herein:\
\pard\pardeftab720\sa332\partightenfactor0
\f0\b\fs20 \cf2 3.3.1.1. Telemetry Data\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Customer Data consisting of non-personally-identifiable information that helps Zed understand how the Solution is being used and how it is performing on user machines is classified as "Telemetry Data". By way of example, Telemetry Data may include the file extensions being edited, or the fact that a particular feature is used and how many times. Telemetry Data does not include the content You are editing. You may grant us permission to capture Telemetry Data when installing or activating the Editor, and You may change Your preferences at any time in the settings feature of the Solution. Once You grant Us this permission, We will retain the Telemetry Data indefinitely. If You elect to use the Network Based Service, You agree that We may capture non-personally-identifiable Telemetry Data on Zed's backend systems, and keep such data indefinitely.\
\pard\pardeftab720\sa332\partightenfactor0
\f0\b\fs20 \cf2 3.3.1.2. Crash Reports\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Customer Data consisting of data related to the behavior of the Solution prior to a crash or failure, such as stack traces are collected and classified as " Crash Reports". Zed will use commercially reasonable efforts to exclude any personally identifiable information from Crash Reports, but due to the nature of a crash, Zed does not ensure that information such as paths will be excluded from Crash Reports. Crash Reports will be used solely for Zed's internal purposes in connection with diagnosing defects in the Solution that led to the crash. You may grant us permission to capture Crash Reports when installing or activating the Solution, and You may change Your preferences at any time in the settings feature of the Solution. Once You grant us this permission, Zed will retain the Crash Reports indefinitely.\
\pard\pardeftab720\sa332\partightenfactor0
\f0\b\fs20 \cf2 3.3.1.3. User Content\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Customer Data consisting of User content created while using the Solution is classified as "User Content". User Content is transmitted from Your environment only if You collaborate with other Zed users by electing to share a project in the Editor. Once You share a project, Zed may transmit User Content consisting of file paths, file contents, and metadata regarding the code returned by language servers. Currently, Zed does not persist any User Content beyond the Your collaboration session. If You unshare a project or disconnect from the Solution, all information associated with such project will be deleted from Zed servers. In the future, Zed may save User Content regarding projects beyond the scope of a single collaboration session. We may share such User Content with those users You elected to grant access to. Zed's access to such User Content is limited to debugging and making improvements to the Solution.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 4. TERM AND TERMINATION\
\pard\pardeftab720\sa280\partightenfactor0
\fs28 \cf2 4.1 Term\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 The term of this Agreement shall commence on the date You first download the Editor or use the Network Based Service (the "Effective Date"), and unless terminated earlier according to this Section 3, will end pursuant to this Section 4 (the "Term").\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 4.2 Termination\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 This Agreement may be terminated: (a) by either party if the other has materially breached this Agreement; or (b) by Zed at any time and for any reason upon notice to Customer. You acknowledge that Zed is under no obligation to continue to operate the Network Based Service or make the Editor available, and We may end any programs in connection with the same at any time.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 4.3 Effect of Termination and Survival\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Upon any expiration or termination of this Agreement, Customer shall (i) immediately cease use of the Network Based Service, and (ii) return all Zed Confidential Information and other materials provided by Zed. The following provisions will survive termination of this Agreement: Sections 1.4 (Ownership), 3 (Customer Data); 4.3 (Effect of Termination and Survival), Section 5 (Ownership), Section 6 (Indemnification), Section 8 (Limitation of Liability), and Section 9 (Miscellaneous).\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 5. OWNERSHIP\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Zed retains all right, title, and interest in and to the Network Based Service, Editor, and any software, products, works or other intellectual property created, used, provided, or made available by Zed under or in connection with the Network Based Service or Editor. Customer may from time to time provide suggestions, comments, or other feedback to Zed with respect to the Network Based Service or Editor ("Feedback"). Customer shall, and hereby does, grant to Zed a nonexclusive, worldwide, perpetual, irrevocable, transferable, sublicensable, royalty-free, fully paid-up license to use and exploit the Feedback for any purpose.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 6. INDEMNIFICATION\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Customer will defend, indemnify, and hold Zed, its affiliates, suppliers and licensors harmless and each of their respective officers, directors, employees and representatives from and against any claims, damages, losses, liabilities, costs, and expenses (including reasonable attorneys' fees) arising out of or relating to any third party claim with respect to: (a) Customer Data; (b) breach of this Agreement or violation of applicable law by Customer; or (c) alleged infringement or misappropriation of third-party's intellectual property rights resulting from Customer Data.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 7. WARRANTY\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Zed does not represent or warrant that the operation of the Network Based Service or Editor (or any portion thereof) will be uninterrupted or error free, or that the Network Based Service or Editor (or any portion thereof) will operate in combination with other hardware, software, systems or data not provided by Zed. CUSTOMER ACKNOWLEDGES THAT, Zed MAKES NO EXPRESS OR IMPLIED REPRESENTATIONS OR WARRANTIES OF ANY KIND WITH RESPECT TO THE SERVICE OR SOFTWARE, OR THEIR CONDITION. ZED HEREBY EXPRESSLY EXCLUDES, ANY AND ALL OTHER EXPRESS OR IMPLIED REPRESENTATIONS OR WARRANTIES, WHETHER UNDER COMMON LAW, STATUTE OR OTHERWISE, INCLUDING WITHOUT LIMITATION ANY AND ALL WARRANTIES AS TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, SATISFACTORY QUALITY OR NON-INFRINGEMENT OF THIRD-PARTY RIGHTS.\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 8. LIMITATIONS OF LIABILITY\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 IN NO EVENT SHALL ZED BE LIABLE FOR ANY LOST DATA, LOST PROFITS, BUSINESS INTERRUPTION, REPLACEMENT SERVICE OR OTHER SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR INDIRECT DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THEORY OF LIABILITY. ZED'S LIABILITY FOR ALL CLAIMS ARISING UNDER THIS AGREEMENT, WHETHER IN CONTRACT, TORT OR OTHERWISE, SHALL NOT EXCEED ONE THOUSAND US DOLLARS ($1,000).\
\pard\pardeftab720\sa298\partightenfactor0
\f0\b\fs36 \cf2 9. MISCELLANEOUS\
\pard\pardeftab720\sa280\partightenfactor0
\fs28 \cf2 9.1 Export Control\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 You hereby certify that You will comply with all current US Export Control laws. You agree to defend, indemnify and hold Zed harmless from any liability for Your violation of U.S. Export Control laws.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.2 Compliance with Laws\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 You shall comply with all applicable laws and regulations in its use of the Solution, including without limitation the unlawful gathering or collecting, or assisting in the gathering or collecting of information in violation of any privacy laws or regulations. You shall, at its own expense, defend, indemnify and hold harmless Zed from and against any and all claims, losses, liabilities, damages, judgments, government or federal sanctions, costs and expenses (including attorneys' fees) incurred by Zed arising from any claim or assertion by any third party of violation of privacy laws or regulations by You or any of its agents, officers, directors or employees.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.3 Assignment\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Neither party may transfer and assign its rights and obligations under this Agreement without the prior written consent of the other party. Notwithstanding the foregoing, Zed may transfer and assign its rights under this Agreement without consent from the other party in connection with a change in control, acquisition or sale of all or substantially all of its assets.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.4 Force Majeure\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Neither party shall be responsible for failure or delay in performance by events out of their reasonable control, including but not limited to, acts of God, Internet outage, terrorism, war, fires, earthquakes and other disasters (each a "Force Majeure"). Notwithstanding the foregoing: if a Force Majeure continues for more than thirty (30) days, either party may to terminate this agreement by written notice to the other party.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.5 Notice\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 All notices between the parties shall be in writing and shall be deemed to have been given if personally delivered or sent by registered or certified mail (return receipt), or by recognized courier service.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.6 No Agency\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Both parties agree that no agency, partnership, joint venture, or employment is created as a result of this Agreement. You do not have any authority of any kind to bind Zed.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.7 Governing Law\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 This Agreement shall be governed exclusively by, and construed exclusively in accordance with, the laws of the United States and the State of California, without regard to its conflict of laws provisions. The federal courts of the United States in the Northern District of California and the state courts of the State of California shall have exclusive jurisdiction to adjudicate any dispute arising out of or relating to this Agreement. Each party hereby consents to the jurisdiction of such courts and waives any right it may otherwise have to challenge the appropriateness of such forums, whether on the basis of the doctrine of forum non conveniens or otherwise. The United Nations Convention on Contracts for the International Sale of Goods shall not apply to this Agreement or any Purchase Order issued under this Agreement.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.8 Updated Agreement\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 Zed reserves the right to update this Agreement at any time. The terms and conditions of the updated version of the Agreement shall apply to the Network Based Service and Editor downloaded, or accessed following the date of publication of the updated version. If You do not agree with any terms of the updated Agreement, You may not use or access the Network Based Service or Editor in any manner. Zed may from time-to-time provide release notes applicable to the Editor or Network Based Service, and such release notes may contain additional use restrictions or terms applicable to Customer Data. Your use of the Editor or Network Based Service after the applicable release notes are made available shall be subject to the additional use restrictions or terms applicable to Customer Data.\
\pard\pardeftab720\sa280\partightenfactor0
\f0\b\fs28 \cf2 9.9 Entire Agreement\
\pard\pardeftab720\sa240\partightenfactor0
\f1\b0\fs24 \cf2 This Agreement is the complete and exclusive statement of the mutual understanding of the parties and supersedes and cancels all previous written and oral agreements, communications, and other understandings relating to the subject matter of this Agreement, and all waivers and modifications must be in a writing signed by both parties, except as otherwise provided herein. Any term or provision of this Agreement held to be illegal or unenforceable shall be, to the fullest extent possible, interpreted so as to be construed as valid, but in any event the validity or enforceability of the remainder hereof shall not be affected.\
\pard\pardeftab720\sa240\partightenfactor0
\f0\b \cf2 DATE: April 5, 2023
\f1\b0 \
}

12
script/generate-eula-rtf Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
set -e
if ! command -v pandoc &> /dev/null
then
brew install pandoc # Install pandoc using Homebrew
fi
pandoc ./script/eula/eula.md -f markdown-smart -t html -o ./script/eula/eula.html
textutil -convert rtf ./script/eula/eula.html -output ./script/eula/eula.rtf
rm ./script/eula/eula.html

File diff suppressed because it is too large Load Diff