mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 11:51:41 +03:00
Wip/wdanilo/widgets 182746060 (#3678)
This commit is contained in:
parent
0d74ab6124
commit
61546a7ade
70
Cargo.lock
generated
70
Cargo.lock
generated
@ -936,6 +936,12 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be0fdd54b507df8f22012890aadd099979befdba27713c767993f8380112ca7c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@ -2246,6 +2252,7 @@ dependencies = [
|
||||
"enso-web",
|
||||
"failure",
|
||||
"futures 0.3.21",
|
||||
"gen-iter",
|
||||
"ifmt",
|
||||
"itertools 0.10.3",
|
||||
"lazy_static",
|
||||
@ -2493,7 +2500,6 @@ dependencies = [
|
||||
"enso-types",
|
||||
"enso-web",
|
||||
"ensogl-text-embedded-fonts",
|
||||
"ensogl-text-msdf",
|
||||
"enum_dispatch",
|
||||
"failure",
|
||||
"itertools 0.10.3",
|
||||
@ -2608,17 +2614,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ensogl-example-glyph-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ensogl-core",
|
||||
"ensogl-text",
|
||||
"ensogl-text-embedded-fonts",
|
||||
"ensogl-text-msdf",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ensogl-example-grid-view"
|
||||
version = "0.1.0"
|
||||
@ -2775,7 +2770,6 @@ dependencies = [
|
||||
"ensogl-example-dom-symbols",
|
||||
"ensogl-example-drop-manager",
|
||||
"ensogl-example-easing-animator",
|
||||
"ensogl-example-glyph-system",
|
||||
"ensogl-example-grid-view",
|
||||
"ensogl-example-list-view",
|
||||
"ensogl-example-mouse-events",
|
||||
@ -2942,6 +2936,7 @@ dependencies = [
|
||||
"ensogl-text-msdf",
|
||||
"ordered-float",
|
||||
"owned_ttf_parser",
|
||||
"rustybuzz",
|
||||
"serde",
|
||||
"wasm-bindgen-test",
|
||||
"xi-rope",
|
||||
@ -2975,6 +2970,7 @@ dependencies = [
|
||||
"enso-build-utilities",
|
||||
"enso-prelude",
|
||||
"enso-types",
|
||||
"enso-web",
|
||||
"ensogl-text-embedded-fonts",
|
||||
"ensogl-text-font-family",
|
||||
"failure",
|
||||
@ -3340,6 +3336,12 @@ dependencies = [
|
||||
"enso-prelude",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gen-iter"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1668ac3c7b8cc5f1e31565ed509d8d70aa1a81bd7f508b620725b78c6e1d7049"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
@ -3820,7 +3822,7 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c"
|
||||
dependencies = [
|
||||
"base64 0.11.0",
|
||||
"base64 0.13.0",
|
||||
"bytes 1.1.0",
|
||||
"http",
|
||||
"httpdate 1.0.2",
|
||||
@ -5956,6 +5958,22 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
||||
|
||||
[[package]]
|
||||
name = "rustybuzz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a617c811f5c9a7060fe511d35d13bf5b9f0463ce36d63ce666d05779df2b4eba"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"smallvec 1.8.0",
|
||||
"ttf-parser",
|
||||
"unicode-bidi-mirroring",
|
||||
"unicode-ccc",
|
||||
"unicode-general-category",
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
@ -7162,6 +7180,24 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi-mirroring"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ccc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07547e3ee45e28326cc23faac56d44f58f16ab23e413db526debce3b0bfd2742"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
@ -7177,6 +7213,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-script"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dd944fd05f2f0b5c674917aea8a4df6af84f2d8de3fe8d988b95d28fb8fb09"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
|
@ -3,6 +3,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -3,6 +3,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
|
@ -9,6 +9,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Code for module-level double representation processing.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
use crate::alias_analysis;
|
||||
use crate::definition;
|
||||
@ -828,7 +828,7 @@ pub fn lookup_method(
|
||||
pub fn definition_span(
|
||||
ast: &known::Module,
|
||||
id: &definition::Id,
|
||||
) -> FallibleResult<enso_text::Range<Bytes>> {
|
||||
) -> FallibleResult<enso_text::Range<Byte>> {
|
||||
let location = locate(ast, id)?;
|
||||
ast.range_of_descendant_at(&location.crumbs)
|
||||
}
|
||||
|
@ -403,7 +403,6 @@ mod tests {
|
||||
|
||||
impl Case {
|
||||
fn run(&self, parser: &Parser) {
|
||||
let logger = DefaultTraceLogger::new("Collapsing_Test");
|
||||
let ast = parser.parse_module(self.initial_method_code, default()).unwrap();
|
||||
let main = module::locate_child(&ast, &self.refactored_name).unwrap();
|
||||
let graph = graph::GraphInfo::from_definition(main.item.clone());
|
||||
@ -417,14 +416,14 @@ mod tests {
|
||||
let new_method = collapsed.new_method.ast(0, parser).unwrap();
|
||||
let placement = module::Placement::Before(self.refactored_name.clone());
|
||||
let new_main = &collapsed.updated_definition.ast;
|
||||
info!(logger, "Generated method:\n{new_method}");
|
||||
info!(logger, "Updated method:\n{new_method}");
|
||||
info!("Generated method:\n{new_method}");
|
||||
info!("Updated method:\n{new_method}");
|
||||
let mut module = module::Info { ast: ast.clone_ref() };
|
||||
let main_crumb = Crumb::from(main.crumb());
|
||||
module.ast = module.ast.set(&main_crumb, new_main.ast().clone()).unwrap();
|
||||
module.add_method(collapsed.new_method, placement, parser).unwrap();
|
||||
ast::test_utils::assert_unique_ids(module.ast.as_ref());
|
||||
info!(logger, "Updated method:\n{&module.ast}");
|
||||
info!("Updated method:\n{}", &module.ast);
|
||||
assert_eq!(new_method.repr(), self.expected_generated);
|
||||
assert_eq!(new_main.repr(), self.expected_refactored);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! A module with functions used to support working with text representation of the language.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::unit::*;
|
||||
|
||||
use ast::IdMap;
|
||||
@ -14,7 +15,7 @@ use ast::IdMap;
|
||||
/// Update IdMap to reflect the recent code change.
|
||||
pub fn apply_code_change_to_id_map(
|
||||
id_map: &mut IdMap,
|
||||
change: &enso_text::text::Change<Bytes, String>,
|
||||
change: &enso_text::text::Change<Byte, String>,
|
||||
code: &str,
|
||||
) {
|
||||
// TODO [mwu]
|
||||
@ -29,27 +30,26 @@ pub fn apply_code_change_to_id_map(
|
||||
let inserted = change.text.as_str();
|
||||
let new_code = change.applied(code).unwrap_or_else(|_| code.to_owned());
|
||||
let non_white = |c: char| !c.is_whitespace();
|
||||
let logger = enso_logger::DefaultWarningLogger::new("apply_code_change_to_id_map");
|
||||
let vector = &mut id_map.vec;
|
||||
let inserted_size: Bytes = inserted.len().into();
|
||||
let inserted_size: ByteDiff = inserted.len().into();
|
||||
|
||||
info!(logger, "Old code:\n```\n{code}\n```");
|
||||
info!(logger, "New code:\n```\n{new_code}\n```");
|
||||
info!(logger, "Updating the ID map with the following text edit: {change:?}.");
|
||||
info!("Old code:\n```\n{code}\n```");
|
||||
info!("New code:\n```\n{new_code}\n```");
|
||||
info!("Updating the ID map with the following text edit: {change:?}.");
|
||||
|
||||
// Remove all entries fully covered by the removed span.
|
||||
vector.drain_filter(|(range, _)| removed.contains_range(range));
|
||||
|
||||
// If the edited section ends up being the trailing part of AST node, how many bytes should be
|
||||
// trimmed from the id. Precalculated, as is constant in the loop below.
|
||||
let to_trim_back: Bytes = {
|
||||
let to_trim_back: ByteDiff = {
|
||||
let last_non_white = inserted.rfind(non_white);
|
||||
let inserted_len = || inserted.len();
|
||||
let length_to_last_non_white = |index| inserted.len() - index - 1;
|
||||
last_non_white.map_or_else(inserted_len, length_to_last_non_white).into()
|
||||
};
|
||||
// As above but for the front side.
|
||||
let to_trim_front: Bytes = {
|
||||
let to_trim_front: ByteDiff = {
|
||||
let first_non_white = inserted.find(non_white);
|
||||
first_non_white.unwrap_or(inserted.len()).into()
|
||||
};
|
||||
@ -62,31 +62,31 @@ pub fn apply_code_change_to_id_map(
|
||||
// This is needed for edits like: `foo f` => `foo` — the earlier `foo` in `foo f` also has a
|
||||
// id map entry, however we want it to be consistently shadowed by the id from the whole App
|
||||
// expression.
|
||||
let mut preferred: HashMap<enso_text::Range<Bytes>, ast::Id> = default();
|
||||
let mut preferred: HashMap<enso_text::Range<Byte>, ast::Id> = default();
|
||||
|
||||
for (range, id) in vector.iter_mut() {
|
||||
let mut trim_front = false;
|
||||
let mut trim_back = false;
|
||||
let initial_range = *range;
|
||||
info!(logger, "Processing @{range}: `{&code[*range]}`.");
|
||||
info!("Processing @{range}: `{}`.", &code[*range]);
|
||||
if range.start > removed.end {
|
||||
debug!(logger, "Node after the edited region.");
|
||||
debug!("Node after the edited region.");
|
||||
// AST node starts after edited region — it will be simply shifted.
|
||||
let between_range: enso_text::Range<_> = (removed.end..range.start).into();
|
||||
let between_range: enso_text::Range<Byte> = (removed.end..range.start).into();
|
||||
let code_between = &code[between_range];
|
||||
*range = range.moved_left(removed.size()).moved_right(inserted_size);
|
||||
|
||||
// If there are only spaces between current AST symbol and insertion, extend the symbol.
|
||||
// This is for cases like line with `foo ` being changed into `foo j`.
|
||||
debug!(logger, "Between: `{code_between}`.");
|
||||
debug!("Between: `{code_between}`.");
|
||||
if all_spaces(code_between) && inserted_non_white {
|
||||
debug!(logger, "Will extend the node leftwards.");
|
||||
debug!("Will extend the node leftwards.");
|
||||
range.start -= inserted_size + between_range.size();
|
||||
trim_front = true;
|
||||
}
|
||||
} else if range.start >= removed.start {
|
||||
// AST node starts inside the edited region. It does not have to end inside it.
|
||||
debug!(logger, "Node overlapping with the end of the edited region.");
|
||||
debug!("Node overlapping with the end of the edited region.");
|
||||
let removed_before = range.start - removed.start;
|
||||
*range = range.moved_left(removed_before);
|
||||
range.end -= removed.size() - removed_before;
|
||||
@ -94,7 +94,7 @@ pub fn apply_code_change_to_id_map(
|
||||
trim_front = true;
|
||||
} else if range.end >= removed.start {
|
||||
// AST node starts before the edited region and reaches (or possibly goes past) its end.
|
||||
debug!(logger, "Node overlapping with the beginning of the edited region.");
|
||||
debug!("Node overlapping with the beginning of the edited region.");
|
||||
if range.end <= removed.end {
|
||||
trim_back = true;
|
||||
}
|
||||
@ -102,35 +102,35 @@ pub fn apply_code_change_to_id_map(
|
||||
range.end -= removed_chars;
|
||||
range.end += inserted_size;
|
||||
} else {
|
||||
debug!(logger, "Node before the edited region.");
|
||||
debug!("Node before the edited region.");
|
||||
// If there are only spaces between current AST symbol and insertion, extend the symbol.
|
||||
// This is for cases like line with `foo ` being changed into `foo j`.
|
||||
let between_range: enso_text::Range<_> = (range.end..removed.start).into();
|
||||
let between_range: enso_text::Range<Byte> = (range.end..removed.start).into();
|
||||
let between = &code[between_range];
|
||||
if all_spaces(between) && inserted_non_white {
|
||||
debug!(logger, "Will extend ");
|
||||
debug!("Will extend ");
|
||||
range.end += between_range.size() + inserted_size;
|
||||
trim_back = true;
|
||||
}
|
||||
}
|
||||
|
||||
if trim_front && to_trim_front > 0.bytes() {
|
||||
if trim_front && to_trim_front > 0.byte_diff() {
|
||||
range.start += to_trim_front;
|
||||
debug!(logger, "Trimming front {to_trim_front.as_usize()} chars.");
|
||||
debug!("Trimming front {} chars.", to_trim_front.as_usize());
|
||||
}
|
||||
|
||||
if trim_back {
|
||||
if to_trim_back > 0.bytes() {
|
||||
if to_trim_back > 0.byte_diff() {
|
||||
range.end += -to_trim_back;
|
||||
debug!(logger, "Trimming back {to_trim_back.as_usize()} chars.");
|
||||
debug!("Trimming back {} chars.", to_trim_back.as_usize());
|
||||
}
|
||||
let new_repr = &new_code[*range];
|
||||
// Trim trailing spaces
|
||||
let space_count = spaces_size(new_repr.chars().rev());
|
||||
let spaces_len: Bytes = (space_count.as_usize() * ' '.len_utf8()).into();
|
||||
if spaces_len > 0.bytes() {
|
||||
debug!(logger, "Additionally trimming {space_count.as_usize()} trailing spaces.");
|
||||
debug!(logger, "The would-be code: `{new_repr}`.");
|
||||
let spaces_len: ByteDiff = (space_count.value * ' '.len_utf8()).into();
|
||||
if spaces_len > 0.byte_diff() {
|
||||
debug!("Additionally trimming {} trailing spaces.", space_count);
|
||||
debug!("The would-be code: `{new_repr}`.");
|
||||
range.end -= spaces_len;
|
||||
}
|
||||
}
|
||||
@ -141,14 +141,12 @@ pub fn apply_code_change_to_id_map(
|
||||
preferred.insert(*range, *id);
|
||||
}
|
||||
|
||||
info!(logger, || {
|
||||
let old_fragment = &code[initial_range];
|
||||
let new_fragment = &new_code[*range];
|
||||
iformat!(
|
||||
"Processing for id {id}: {initial_range} ->\t{range}.\n
|
||||
let old_fragment = &code[initial_range];
|
||||
let new_fragment = &new_code[*range];
|
||||
info!(
|
||||
"Processing for id {id}: {initial_range} ->\t{range}.\n
|
||||
Code: `{old_fragment}` => `{new_fragment}`"
|
||||
)
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
// If non-preferred entry collides with the preferred one, remove the former.
|
||||
@ -164,7 +162,7 @@ pub fn apply_code_change_to_id_map(
|
||||
// ===============
|
||||
|
||||
/// Returns the chars count of leading space characters sequence.
|
||||
fn spaces_size(itr: impl Iterator<Item = char>) -> Chars {
|
||||
fn spaces_size(itr: impl Iterator<Item = char>) -> Utf16CodeUnit {
|
||||
itr.take_while(|c| *c == ' ').fold(0, |acc, _| acc + 1).into()
|
||||
}
|
||||
|
||||
@ -198,7 +196,7 @@ mod test {
|
||||
/// The initial enso program code.
|
||||
pub code: String,
|
||||
/// The edit made to the initial code.
|
||||
pub change: enso_text::Change<Bytes, String>,
|
||||
pub change: enso_text::Change<Byte, String>,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
@ -291,13 +289,13 @@ mod test {
|
||||
let case = Case::from_markdown("foo«aa⎀bb»c");
|
||||
assert_eq!(case.code, "fooaac");
|
||||
assert_eq!(case.change.text, "bb");
|
||||
assert_eq!(case.change.range, 3.bytes()..5.bytes());
|
||||
assert_eq!(case.change.range, 3.byte()..5.byte());
|
||||
assert_eq!(case.resulting_code(), "foobbc");
|
||||
|
||||
let case = Case::from_markdown("foo«aa»c");
|
||||
assert_eq!(case.code, "fooaac");
|
||||
assert_eq!(case.change.text, "");
|
||||
assert_eq!(case.change.range, 3.bytes()..5.bytes());
|
||||
assert_eq!(case.change.range, 3.byte()..5.byte());
|
||||
assert_eq!(case.resulting_code(), "fooc");
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
enso-data-structures = { path = "../../../../lib/rust/data-structures" }
|
||||
enso-logger = { path = "../../../../lib/rust/logger" }
|
||||
enso-prelude = { path = "../../../../lib/rust/prelude", features = [
|
||||
"serde",
|
||||
"serde_json"
|
||||
] }
|
||||
enso-shapely = { path = "../../../../lib/rust/shapely" }
|
||||
|
@ -113,7 +113,6 @@ impl Client {
|
||||
/// Function that does early processing of the peer's message and decides how it shall be
|
||||
/// handled. Returns a function so that it may be passed to the `Handler`.
|
||||
fn processor(
|
||||
logger: Logger,
|
||||
) -> impl FnMut(TransportEvent) -> Disposition<Uuid, FromServerPayloadOwned, Notification> + 'static
|
||||
{
|
||||
move |event: TransportEvent| {
|
||||
@ -125,7 +124,7 @@ impl Client {
|
||||
Ok(message) => message,
|
||||
Err(e) => return Disposition::error(e),
|
||||
};
|
||||
debug!(logger, "Deserialized incoming binary message: {message:?}");
|
||||
debug!("Deserialized incoming binary message: {message:?}");
|
||||
let correlation_id = message.correlation_id;
|
||||
match message.0.payload {
|
||||
FromServerPayloadOwned::VisualizationUpdate { context, data } =>
|
||||
@ -149,7 +148,7 @@ impl Client {
|
||||
/// * `init` must be called or it needs to be wrapped into `Connection`.
|
||||
pub fn new(parent: impl AnyLogger, transport: impl Transport + 'static) -> Client {
|
||||
let logger = Logger::new_sub(parent, "binary-protocol-client");
|
||||
let processor = Self::processor(logger.clone_ref());
|
||||
let processor = Self::processor();
|
||||
Client { logger: logger.clone_ref(), handler: Handler::new(transport, logger, processor) }
|
||||
}
|
||||
|
||||
@ -167,10 +166,8 @@ impl Client {
|
||||
{
|
||||
let message = MessageToServerRef::new(payload);
|
||||
let id = message.message_id;
|
||||
|
||||
let logger = self.logger.clone_ref();
|
||||
let completer = move |reply| {
|
||||
info!(logger, "Completing request {id} with a reply: {reply:?}");
|
||||
info!("Completing request {id} with a reply: {reply:?}");
|
||||
if let FromServerPayloadOwned::Error { code, message, data } = reply {
|
||||
let code = code as i64;
|
||||
let error = json_rpc::messages::Error { code, message, data };
|
||||
@ -193,19 +190,19 @@ impl Client {
|
||||
|
||||
impl API for Client {
|
||||
fn init(&self, client_id: Uuid) -> StaticBoxFuture<FallibleResult> {
|
||||
info!(self.logger, "Initializing binary connection as client with id {client_id}.");
|
||||
info!("Initializing binary connection as client with id {client_id}.");
|
||||
let payload = ToServerPayload::InitSession { client_id };
|
||||
self.make_request(payload, Self::expect_success)
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &Path, contents: &[u8]) -> StaticBoxFuture<FallibleResult> {
|
||||
info!(self.logger, "Writing file {path} with {contents.len()} bytes.");
|
||||
info!("Writing file {} with {} bytes.", path, contents.len());
|
||||
let payload = ToServerPayload::WriteFile { path, contents };
|
||||
self.make_request(payload, Self::expect_success)
|
||||
}
|
||||
|
||||
fn read_file(&self, path: &Path) -> StaticBoxFuture<FallibleResult<Vec<u8>>> {
|
||||
info!(self.logger, "Reading file {path}.");
|
||||
info!("Reading file {path}.");
|
||||
let payload = ToServerPayload::ReadFile { path };
|
||||
self.make_request(payload, move |result| {
|
||||
if let FromServerPayloadOwned::FileContentsReply { contents } = result {
|
||||
@ -223,7 +220,7 @@ impl API for Client {
|
||||
overwrite: bool,
|
||||
bytes: &[u8],
|
||||
) -> StaticBoxFuture<FallibleResult<Sha3_224>> {
|
||||
info!(self.logger, "Writing {bytes.len()} bytes to {path} at offset {byte_offset}");
|
||||
info!("Writing {} bytes to {path} at offset {byte_offset}", bytes.len());
|
||||
let payload = ToServerPayload::WriteBytes { path, byte_offset, overwrite, bytes };
|
||||
self.make_request(payload, move |result| {
|
||||
if let FromServerPayloadOwned::WriteBytesReply { checksum } = result {
|
||||
|
@ -12,10 +12,10 @@ use futures::channel::oneshot;
|
||||
/// their answer.
|
||||
/// `Id` identifies the request.
|
||||
/// `Reply` represents the answer.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct OngoingCalls<Id, Reply>
|
||||
where Id: Hash + Eq {
|
||||
logger: Logger,
|
||||
ongoing_calls: HashMap<Id, oneshot::Sender<Reply>>,
|
||||
}
|
||||
|
||||
@ -23,11 +23,8 @@ impl<Id, Reply> OngoingCalls<Id, Reply>
|
||||
where Id: Copy + Debug + Display + Hash + Eq + Send + Sync + 'static
|
||||
{
|
||||
/// Creates a new, empty ongoing request storage.
|
||||
pub fn new(parent: impl AnyLogger) -> OngoingCalls<Id, Reply> {
|
||||
OngoingCalls {
|
||||
logger: Logger::new_sub(parent, "ongoing_calls"),
|
||||
ongoing_calls: HashMap::new(),
|
||||
}
|
||||
pub fn new() -> OngoingCalls<Id, Reply> {
|
||||
default()
|
||||
}
|
||||
|
||||
/// Removes the request from the storage and returns it (if present).
|
||||
@ -35,9 +32,9 @@ where Id: Copy + Debug + Display + Hash + Eq + Send + Sync + 'static
|
||||
pub fn remove_request(&mut self, id: &Id) -> Option<oneshot::Sender<Reply>> {
|
||||
let ret = self.ongoing_calls.remove(id);
|
||||
if ret.is_some() {
|
||||
info!(self.logger, "Removing request {id}");
|
||||
info!("Removing request {id}");
|
||||
} else {
|
||||
info!(self.logger, "Failed to remove non-present request {id}");
|
||||
info!("Failed to remove non-present request {id}");
|
||||
}
|
||||
ret
|
||||
}
|
||||
@ -45,7 +42,7 @@ where Id: Copy + Debug + Display + Hash + Eq + Send + Sync + 'static
|
||||
/// Inserts a new request with given id and completer (i.e. the channel capable of accepting
|
||||
/// the peer's reply and completing the request).
|
||||
pub fn insert_request(&mut self, id: Id, completer: oneshot::Sender<Reply>) {
|
||||
info!(self.logger, "Storing a new request {id}");
|
||||
info!("Storing a new request {id}");
|
||||
// There will be no previous request, since Ids are assumed to be unique.
|
||||
// Still, if there was, we can just safely drop it.
|
||||
self.ongoing_calls.insert(id, completer);
|
||||
@ -74,7 +71,7 @@ where Id: Copy + Debug + Display + Hash + Eq + Send + Sync + 'static
|
||||
|
||||
/// Removes all awaiting requests. Their futures will signal cancellation.
|
||||
pub fn clear(&mut self) {
|
||||
info!(self.logger, "Clearing all the requests.");
|
||||
info!("Clearing all the requests.");
|
||||
self.ongoing_calls.clear()
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ where
|
||||
transport: Box::new(transport),
|
||||
logger: logger.clone_ref(),
|
||||
sender: None,
|
||||
ongoing_calls: OngoingCalls::new(logger),
|
||||
ongoing_calls: OngoingCalls::new(),
|
||||
processor: Box::new(processor),
|
||||
}
|
||||
}
|
||||
@ -114,7 +114,7 @@ where
|
||||
|
||||
/// Feeds the reply to complete the corresponding open request.
|
||||
fn process_reply(&mut self, id: Id, reply: Reply) {
|
||||
info!(self.logger, "Processing reply to request {id}: {reply:?}");
|
||||
info!("Processing reply to request {id}: {reply:?}");
|
||||
if let Err(error) = self.ongoing_calls.complete_request(id, reply) {
|
||||
self.emit_error(error);
|
||||
}
|
||||
@ -122,7 +122,7 @@ where
|
||||
|
||||
/// Helper that wraps error into an appropriate event value and emits it.
|
||||
fn emit_error(&mut self, error: impl Into<failure::Error> + Debug) {
|
||||
info!(self.logger, "Emitting error: {error:?}");
|
||||
info!("Emitting error: {error:?}");
|
||||
let event = Event::Error(error.into());
|
||||
self.emit_event(event);
|
||||
}
|
||||
@ -133,12 +133,12 @@ where
|
||||
/// Main entry point for input data while running. Should be connected to the `Transport`s
|
||||
/// output event stream.
|
||||
pub fn process_event(&mut self, event: TransportEvent) {
|
||||
debug!(self.logger, "Processing incoming transport event", || {
|
||||
debug!(self.logger, "Transport event contents: {event:?}.");
|
||||
debug_span!("Processing incoming transport event").in_scope(|| {
|
||||
debug!("Transport event contents: {event:?}.");
|
||||
match event {
|
||||
TransportEvent::TextMessage(_) | TransportEvent::BinaryMessage(_) => {
|
||||
let disposition = (self.processor)(event);
|
||||
debug!(self.logger, "Disposition: {disposition:?}");
|
||||
debug!("Disposition: {disposition:?}");
|
||||
match disposition {
|
||||
Disposition::HandleReply { id, reply } => self.process_reply(id, reply),
|
||||
Disposition::EmitEvent { event } => self.emit_event(event),
|
||||
@ -159,10 +159,10 @@ where
|
||||
where
|
||||
F: FnOnce(Reply) -> FallibleResult<R>,
|
||||
{
|
||||
debug!(self.logger, "Making a new RPC call", || {
|
||||
debug_span!("Making a new RPC call").in_scope(|| {
|
||||
let id = message.id();
|
||||
let ret = self.ongoing_calls.open_new_request(id, f);
|
||||
debug!(self.logger, "Sending message {message:?}");
|
||||
debug!("Sending message {message:?}");
|
||||
let sending_result = message.send(self.transport.as_mut());
|
||||
if sending_result.is_err() {
|
||||
// If we failed to send the request, it should be immediately removed.
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::language_server::*;
|
||||
|
||||
use enso_text::Utf16CodeUnit;
|
||||
use strum_macros::IntoStaticStr;
|
||||
|
||||
|
||||
@ -480,17 +481,17 @@ pub struct Position {
|
||||
pub character: usize,
|
||||
}
|
||||
|
||||
impls! { From + &From <enso_text::Location> for Position { |location|
|
||||
impls! { From + &From <enso_text::Location<Utf16CodeUnit>> for Position { |location|
|
||||
Position {
|
||||
line: location.line.as_usize(),
|
||||
character: location.column.as_usize(),
|
||||
line: location.line.value,
|
||||
character: location.offset.value,
|
||||
}
|
||||
}}
|
||||
|
||||
impls! { From + &From <Position> for enso_text::Location { |position|
|
||||
impls! { From + &From <Position> for enso_text::Location<Utf16CodeUnit> { |position|
|
||||
enso_text::Location {
|
||||
line: position.line.into(),
|
||||
column: position.character.into(),
|
||||
offset: position.character.into(),
|
||||
}
|
||||
}}
|
||||
|
||||
@ -508,14 +509,14 @@ pub struct TextRange {
|
||||
pub end: Position,
|
||||
}
|
||||
|
||||
impls! { From + &From <enso_text::Range<enso_text::Location>> for TextRange { |range|
|
||||
impls! { From + &From <enso_text::Range<enso_text::Location<Utf16CodeUnit>>> for TextRange { |range|
|
||||
TextRange {
|
||||
start : range.start.into(),
|
||||
end : range.end.into(),
|
||||
}
|
||||
}}
|
||||
|
||||
impls! { From + &From <TextRange> for enso_text::Range<enso_text::Location> { |range|
|
||||
impls! { From + &From <TextRange> for enso_text::Range<enso_text::Location<Utf16CodeUnit>> { |range|
|
||||
enso_text::Range::new(range.start.into(), range.end.into())
|
||||
}}
|
||||
|
||||
@ -542,12 +543,14 @@ impl TextEdit {
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use engine_protocol::language_server::{TextEdit, Position, TextRange};
|
||||
/// // Note that 🌊 has two UTF-16 code units.
|
||||
/// let source = "\n333<->🌊12345\n";
|
||||
/// let target = "\n333x🔥12345\n";
|
||||
/// let expected_removed_len = "<->🌊".encode_utf16().count();
|
||||
/// let diff = TextEdit::from_prefix_postfix_differences(source, target);
|
||||
/// let edit_range = TextRange {
|
||||
/// start: Position { line: 1, character: 3 },
|
||||
/// end: Position { line: 1, character: 7 },
|
||||
/// end: Position { line: 1, character: 3 + expected_removed_len },
|
||||
/// };
|
||||
/// assert_eq!(diff, TextEdit { range: edit_range, text: "x🔥".to_string() });
|
||||
///
|
||||
@ -570,25 +573,31 @@ impl TextEdit {
|
||||
/// assert_eq!(diff, TextEdit { range: edit_range, text: "".to_string() });
|
||||
/// ```
|
||||
pub fn from_prefix_postfix_differences(
|
||||
source: impl Into<enso_text::Text>,
|
||||
target: impl Into<enso_text::Text>,
|
||||
source: impl Into<enso_text::Rope>,
|
||||
target: impl Into<enso_text::Rope>,
|
||||
) -> TextEdit {
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::Range;
|
||||
|
||||
let source = source.into();
|
||||
let target = target.into();
|
||||
let source_len = source.len().to_diff();
|
||||
let target_len = target.len().to_diff();
|
||||
let common_lengths = source.common_prefix_and_suffix(&target);
|
||||
|
||||
let source_start_byte = common_lengths.prefix;
|
||||
let source_end_byte = Bytes::from(source.len()) - common_lengths.suffix;
|
||||
let source_start_byte = 0.byte() + common_lengths.prefix;
|
||||
let source_end_byte = 0.byte() + source_len - common_lengths.suffix;
|
||||
|
||||
let source_start_position = source.location_of_byte_offset_snapped(source_start_byte);
|
||||
let source_end_position = source.location_of_byte_offset_snapped(source_end_byte);
|
||||
let source_start_position = source.offset_to_location_snapped(source_start_byte);
|
||||
let source_start_position =
|
||||
source.utf16_code_unit_location_of_location(source_start_position);
|
||||
let source_end_position = source.offset_to_location_snapped(source_end_byte);
|
||||
let source_end_position = source.utf16_code_unit_location_of_location(source_end_position);
|
||||
let source_text_range = Range::new(source_start_position, source_end_position);
|
||||
|
||||
let target_len: Bytes = target.len().into();
|
||||
let target_range = common_lengths.prefix..(target_len - common_lengths.suffix);
|
||||
let start = 0.byte() + common_lengths.prefix;
|
||||
let end = 0.byte() + target_len - common_lengths.suffix;
|
||||
let target_range = start..end;
|
||||
let target_text = target.sub(target_range).to_string();
|
||||
|
||||
TextEdit { range: source_text_range.into(), text: target_text }
|
||||
@ -876,14 +885,14 @@ pub struct SuggestionEntryScope {
|
||||
pub end: Position,
|
||||
}
|
||||
|
||||
impls! { From + &From <RangeInclusive<enso_text::Location>> for SuggestionEntryScope { |range|
|
||||
impls! { From + &From <RangeInclusive<enso_text::Location<Utf16CodeUnit>>> for SuggestionEntryScope { |range|
|
||||
SuggestionEntryScope {
|
||||
start : range.start().into(),
|
||||
end : range.end().into(),
|
||||
}
|
||||
}}
|
||||
|
||||
impls! { From + &From <SuggestionEntryScope> for RangeInclusive<enso_text::Location> { |this|
|
||||
impls! { From + &From <SuggestionEntryScope> for RangeInclusive<enso_text::Location<Utf16CodeUnit>> { |this|
|
||||
this.start.into()..=this.end.into()
|
||||
}}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![deny(unconditional_recursion)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
|
@ -34,6 +34,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![deny(unconditional_recursion)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
|
@ -5,6 +5,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![deny(unconditional_recursion)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
|
@ -2,8 +2,7 @@
|
||||
//! possible in a constant time.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
use crate::enumerate_non_empty_lines;
|
||||
use crate::known;
|
||||
@ -1465,8 +1464,8 @@ pub trait TraversableAst: Sized {
|
||||
}
|
||||
|
||||
/// Calculate the span of the descendent AST node described by given crumbs.
|
||||
fn range_of_descendant_at(&self, crumbs: &[Crumb]) -> FallibleResult<text::Range<Bytes>> {
|
||||
let mut position = 0.bytes();
|
||||
fn range_of_descendant_at(&self, crumbs: &[Crumb]) -> FallibleResult<text::Range<Byte>> {
|
||||
let mut position = 0.byte();
|
||||
let mut ast = self.my_ast()?;
|
||||
for crumb in crumbs {
|
||||
let child = ast.get(crumb)?;
|
||||
@ -2238,7 +2237,7 @@ mod tests {
|
||||
assert_eq!(two.repr(), "2");
|
||||
|
||||
let two_span = ast.range_of_descendant_at(&crumbs_to_two).unwrap();
|
||||
assert_eq!(two_span, 4.bytes()..5.bytes());
|
||||
assert_eq!(two_span, 4.byte()..5.byte());
|
||||
assert_eq!(&expected_code[two_span], "2");
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,12 @@
|
||||
//! source file: the parser gives the id of particular span to the AST node representing that span.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
use crate::Id;
|
||||
|
||||
use enso_text::rope::xi_rope;
|
||||
use enso_text::rope::xi_rope::rope::Utf16CodeUnitsMetric;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
@ -21,20 +23,20 @@ use uuid::Uuid;
|
||||
/// A mapping between text position and immutable ID.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct IdMap {
|
||||
pub vec: Vec<(enso_text::Range<Bytes>, Id)>,
|
||||
pub vec: Vec<(enso_text::Range<Byte>, Id)>,
|
||||
}
|
||||
|
||||
impl IdMap {
|
||||
/// Create a new instance.
|
||||
pub fn new(vec: Vec<(enso_text::Range<Bytes>, Id)>) -> IdMap {
|
||||
pub fn new(vec: Vec<(enso_text::Range<Byte>, Id)>) -> IdMap {
|
||||
IdMap { vec }
|
||||
}
|
||||
/// Assigns Span to given ID.
|
||||
pub fn insert(&mut self, span: impl Into<enso_text::Range<Bytes>>, id: Id) {
|
||||
pub fn insert(&mut self, span: impl Into<enso_text::Range<Byte>>, id: Id) {
|
||||
self.vec.push((span.into(), id));
|
||||
}
|
||||
/// Generate random Uuid for span.
|
||||
pub fn generate(&mut self, span: impl Into<enso_text::Range<Bytes>>) {
|
||||
pub fn generate(&mut self, span: impl Into<enso_text::Range<Byte>>) {
|
||||
self.vec.push((span.into(), Uuid::new_v4()));
|
||||
}
|
||||
}
|
||||
@ -85,18 +87,17 @@ impl JsonIdMap {
|
||||
/// Create from the [`IdMap`] structure.
|
||||
///
|
||||
/// The code is needed for transforming byte offsets to codepoint offsets.
|
||||
pub fn from_id_map(id_map: &IdMap, code: &str) -> Self {
|
||||
let char_offsets = code.char_indices().map(|(idx, _)| idx).collect_vec();
|
||||
pub fn from_id_map(id_map: &IdMap, code: &enso_text::Rope) -> Self {
|
||||
// let char_offsets = code.char_indices().map(|(idx, _)| idx).collect_vec();
|
||||
let mut cursor = xi_rope::Cursor::new(&code.rope, 0);
|
||||
let char_offsets = iter::once(0).chain(cursor.iter::<Utf16CodeUnitsMetric>()).collect_vec();
|
||||
let mapped_vec = id_map.vec.iter().map(|(range, id)| {
|
||||
let byte_start = range.start.as_usize();
|
||||
let byte_end = range.end.as_usize();
|
||||
let start: Chars = char_offsets.binary_search(&byte_start).unwrap_both().into();
|
||||
let end: Chars = char_offsets.binary_search(&byte_end).unwrap_both().into();
|
||||
let byte_start = range.start.value as usize;
|
||||
let byte_end = range.end.value as usize;
|
||||
let start = char_offsets.binary_search(&byte_start).unwrap_both();
|
||||
let end = char_offsets.binary_search(&byte_end).unwrap_both();
|
||||
let size = end - start;
|
||||
let span = Span {
|
||||
index: Index { value: start.as_usize() },
|
||||
size: Size { value: size.as_usize() },
|
||||
};
|
||||
let span = Span { index: Index { value: start }, size: Size { value: size } };
|
||||
(span, *id)
|
||||
});
|
||||
Self { vec: mapped_vec.collect() }
|
||||
|
@ -7,6 +7,22 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use crate::prelude::*;
|
||||
use ast_macros::*;
|
||||
use enso_shapely::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::unit::*;
|
||||
|
||||
use serde::de::Deserializer;
|
||||
use serde::de::Visitor;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::ser::Serializer;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
// ==============
|
||||
@ -71,24 +87,10 @@ pub mod constants {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub use crumbs::Crumb;
|
||||
pub use crumbs::Crumbs;
|
||||
pub use id_map::IdMap;
|
||||
|
||||
use ast_macros::*;
|
||||
use enso_shapely::*;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::unit::*;
|
||||
use serde::de::Deserializer;
|
||||
use serde::de::Visitor;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::ser::Serializer;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// A sequence of AST nodes, typically the "token soup".
|
||||
pub type Stream<T> = Vec<T>;
|
||||
|
||||
@ -283,7 +285,7 @@ impl Ast {
|
||||
}
|
||||
|
||||
/// Just wraps shape, id and len into Ast node.
|
||||
fn from_ast_id_len(shape: Shape<Ast>, id: Option<Id>, char_count: Chars) -> Ast {
|
||||
fn from_ast_id_len(shape: Shape<Ast>, id: Option<Id>, char_count: usize) -> Ast {
|
||||
let with_length = WithLength { wrapped: shape, length: char_count };
|
||||
let with_id = WithID { wrapped: with_length, id };
|
||||
Ast { wrapped: Rc::new(with_id) }
|
||||
@ -328,10 +330,10 @@ impl Ast {
|
||||
///
|
||||
/// Returned index is the position of the first character of child's text representation within
|
||||
/// the text representation of this AST node.
|
||||
pub fn child_offset(&self, child: &Ast) -> FallibleResult<Bytes> {
|
||||
pub fn child_offset(&self, child: &Ast) -> FallibleResult<Byte> {
|
||||
let searched_token = Token::Ast(child);
|
||||
let mut found_child = false;
|
||||
let mut position = 0.bytes();
|
||||
let mut position = 0.byte();
|
||||
self.shape().feed_to(&mut |token: Token| {
|
||||
if searched_token == token {
|
||||
found_child = true
|
||||
@ -347,7 +349,7 @@ impl Ast {
|
||||
}
|
||||
|
||||
/// Get the span (relative to self) for a child node identified by given crumb.
|
||||
pub fn span_of_child_at(&self, crumb: &Crumb) -> FallibleResult<enso_text::Range<Bytes>> {
|
||||
pub fn span_of_child_at(&self, crumb: &Crumb) -> FallibleResult<enso_text::Range<Byte>> {
|
||||
let child = self.get(crumb)?;
|
||||
let offset = self.child_offset(child)?;
|
||||
Ok(enso_text::Range::new(offset, offset + child.len()))
|
||||
@ -384,7 +386,7 @@ impl Serialize for Ast {
|
||||
if self.id.is_some() {
|
||||
state.serialize_field(ID, &self.id)?;
|
||||
}
|
||||
state.serialize_field(LENGTH, &self.length.as_usize())?;
|
||||
state.serialize_field(LENGTH, &self.length)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
@ -420,7 +422,7 @@ impl<'de> Visitor<'de> for AstDeserializationVisitor {
|
||||
let shape = shape.ok_or_else(|| serde::de::Error::missing_field(SHAPE))?;
|
||||
let id = id.unwrap_or(None); // allow missing `id` field
|
||||
let len = len.ok_or_else(|| serde::de::Error::missing_field(LENGTH))?;
|
||||
Ok(Ast::from_ast_id_len(shape, id, len.into()))
|
||||
Ok(Ast::from_ast_id_len(shape, id, len))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1064,15 +1066,15 @@ pub trait HasIdMap {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct IdMapBuilder {
|
||||
id_map: IdMap,
|
||||
offset: Bytes,
|
||||
offset: Byte,
|
||||
}
|
||||
|
||||
impl TokenConsumer for IdMapBuilder {
|
||||
fn feed(&mut self, token: Token) {
|
||||
match token {
|
||||
Token::Off(val) => self.offset += Bytes::from(' '.len_utf8() * val),
|
||||
Token::Chr(_) => self.offset += 1.bytes(),
|
||||
Token::Str(val) => self.offset += Bytes::from(val.len()),
|
||||
Token::Off(val) => self.offset += Byte::from(' '.len_utf8() * val),
|
||||
Token::Chr(_) => self.offset += 1.byte(),
|
||||
Token::Str(val) => self.offset += Byte::from(val.len()),
|
||||
Token::Ast(val) => {
|
||||
let begin = self.offset;
|
||||
val.shape().feed_to(self);
|
||||
@ -1106,7 +1108,7 @@ pub trait HasRepr {
|
||||
/// May be implemented in a quicker way than building string. Must meet the constraint
|
||||
/// `x.len() == x.repr().len()` for any `x: impl HasRepr`.
|
||||
fn len(&self) -> Bytes {
|
||||
self.repr().len().into()
|
||||
self.repr().len().bytes()
|
||||
}
|
||||
|
||||
/// Check if the representation is empty.
|
||||
@ -1118,8 +1120,8 @@ pub trait HasRepr {
|
||||
///
|
||||
/// May be implemented in a quicker way than building string. Must meet the constraint
|
||||
/// `x.char_count() == x.repr().chars().count()` for any `x: impl HasRepr`.
|
||||
fn char_count(&self) -> Chars {
|
||||
self.repr().chars().count().into()
|
||||
fn char_count(&self) -> usize {
|
||||
self.repr().chars().count()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1147,9 +1149,9 @@ struct LengthBuilder {
|
||||
impl TokenConsumer for LengthBuilder {
|
||||
fn feed(&mut self, token: Token) {
|
||||
match token {
|
||||
Token::Off(val) => self.length += Bytes::from(' '.len_utf8() * val),
|
||||
Token::Chr(chr) => self.length += Bytes::from(chr.len_utf8()),
|
||||
Token::Str(val) => self.length += Bytes::from(val.len()),
|
||||
Token::Off(val) => self.length += (' '.len_utf8() * val).bytes(),
|
||||
Token::Chr(chr) => self.length += chr.len_utf8().bytes(),
|
||||
Token::Str(val) => self.length += val.len().bytes(),
|
||||
Token::Ast(val) => val.shape().feed_to(self),
|
||||
}
|
||||
}
|
||||
@ -1157,16 +1159,16 @@ impl TokenConsumer for LengthBuilder {
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
struct CharCountBuilder {
|
||||
char_count: Chars,
|
||||
char_count: usize,
|
||||
}
|
||||
|
||||
|
||||
impl TokenConsumer for CharCountBuilder {
|
||||
fn feed(&mut self, token: Token) {
|
||||
match token {
|
||||
Token::Off(val) => self.char_count += Chars::from(val),
|
||||
Token::Chr(_) => self.char_count += 1.chars(),
|
||||
Token::Str(val) => self.char_count += Chars::from(val.chars().count()),
|
||||
Token::Off(val) => self.char_count += val,
|
||||
Token::Chr(_) => self.char_count += 1,
|
||||
Token::Str(val) => self.char_count += val.chars().count(),
|
||||
Token::Ast(val) => val.shape().feed_to(self),
|
||||
}
|
||||
}
|
||||
@ -1185,7 +1187,7 @@ impl<T: HasTokens> HasRepr for T {
|
||||
consumer.length
|
||||
}
|
||||
|
||||
fn char_count(&self) -> Chars {
|
||||
fn char_count(&self) -> usize {
|
||||
let mut consumer = CharCountBuilder::default();
|
||||
self.feed_to(&mut consumer);
|
||||
consumer.char_count
|
||||
@ -1235,7 +1237,7 @@ where T: HasRepr
|
||||
self.deref().len()
|
||||
}
|
||||
|
||||
fn char_count(&self) -> Chars {
|
||||
fn char_count(&self) -> usize {
|
||||
self.deref().char_count()
|
||||
}
|
||||
}
|
||||
@ -1244,19 +1246,19 @@ where T: HasRepr
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TraverserWithOffset<F> {
|
||||
offset: Chars,
|
||||
offset: usize,
|
||||
callback: F,
|
||||
}
|
||||
|
||||
impl<F> TraverserWithOffset<F> {
|
||||
pub fn new(callback: F) -> TraverserWithOffset<F> {
|
||||
let offset = 0.chars();
|
||||
let offset = 0;
|
||||
TraverserWithOffset { offset, callback }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> TokenConsumer for TraverserWithOffset<F>
|
||||
where F: FnMut(Chars, &Ast)
|
||||
where F: FnMut(usize, &Ast)
|
||||
{
|
||||
fn feed(&mut self, token: Token) {
|
||||
if let Token::Ast(val) = token {
|
||||
@ -1269,13 +1271,13 @@ where F: FnMut(Chars, &Ast)
|
||||
}
|
||||
|
||||
/// Visits each Ast node, while keeping track of its index.
|
||||
pub fn traverse_with_offset(ast: &impl HasTokens, f: impl FnMut(Chars, &Ast)) {
|
||||
pub fn traverse_with_offset(ast: &impl HasTokens, f: impl FnMut(usize, &Ast)) {
|
||||
let mut traverser = TraverserWithOffset::new(f);
|
||||
ast.feed_to(&mut traverser);
|
||||
}
|
||||
|
||||
/// Visits each Ast node, while keeping track of its span.
|
||||
pub fn traverse_with_span(ast: &impl HasTokens, mut f: impl FnMut(enso_text::Range<Chars>, &Ast)) {
|
||||
pub fn traverse_with_span(ast: &impl HasTokens, mut f: impl FnMut(enso_text::Range<usize>, &Ast)) {
|
||||
traverse_with_offset(ast, move |offset, ast| {
|
||||
f(enso_text::Range::new(offset, offset + ast.char_count()), ast)
|
||||
})
|
||||
@ -1293,7 +1295,7 @@ pub struct WithLength<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
#[serde(flatten)]
|
||||
pub wrapped: T,
|
||||
pub length: Chars,
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
impl<T> HasRepr for WithLength<T>
|
||||
@ -1307,7 +1309,7 @@ where T: HasRepr
|
||||
self.deref().len()
|
||||
}
|
||||
|
||||
fn char_count(&self) -> Chars {
|
||||
fn char_count(&self) -> usize {
|
||||
self.length
|
||||
}
|
||||
}
|
||||
@ -1761,7 +1763,7 @@ mod tests {
|
||||
fn ast_length() {
|
||||
let ast = Ast::prefix(Ast::var("XĄ"), Ast::var("YY"));
|
||||
assert_eq!(ast.len(), 6.bytes());
|
||||
assert_eq!(ast.char_count(), 5.chars());
|
||||
assert_eq!(ast.char_count(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1773,7 +1775,7 @@ mod tests {
|
||||
#[test]
|
||||
fn ast_id_map() {
|
||||
let span = |ix: usize, length: usize| {
|
||||
enso_text::Range::<Bytes>::new(ix.into(), (ix + length).into())
|
||||
enso_text::Range::<Byte>::new(ix.into(), (ix + length).into())
|
||||
};
|
||||
let uid = default();
|
||||
let ids = vec![(span(0, 2), uid), (span(3, 2), uid), (span(0, 5), uid)];
|
||||
@ -1790,7 +1792,7 @@ mod tests {
|
||||
let v = Var { name: ident.clone() };
|
||||
let ast = Ast::from(v);
|
||||
assert!(ast.wrapped.id.is_some());
|
||||
assert_eq!(ast.wrapped.wrapped.length, ident.chars().count().into());
|
||||
assert_eq!(ast.wrapped.wrapped.length, ident.chars().count());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1822,7 +1824,7 @@ mod tests {
|
||||
let expected_uuid = Id::parse_str(uuid_str).ok();
|
||||
assert_eq!(ast.id, expected_uuid);
|
||||
|
||||
let expected_length = 3.chars();
|
||||
let expected_length = 3;
|
||||
assert_eq!(ast.length, expected_length);
|
||||
|
||||
let expected_var = Var { name: var_name.into() };
|
||||
@ -1897,15 +1899,15 @@ mod tests {
|
||||
#[test]
|
||||
fn utf8_lengths() {
|
||||
let var = Ast::var("価");
|
||||
assert_eq!(var.char_count(), 1.chars());
|
||||
assert_eq!(var.char_count(), 1);
|
||||
assert_eq!(var.len(), 3.bytes());
|
||||
|
||||
let idmap = var.id_map();
|
||||
assert_eq!(idmap.vec[0].0, enso_text::Range::new(0.bytes(), 3.bytes()));
|
||||
assert_eq!(idmap.vec[0].0, enso_text::Range::new(0.byte(), 3.byte()));
|
||||
assert_eq!(idmap.vec[0].1, var.id.unwrap());
|
||||
|
||||
let builder_with_char = Token::Chr('壱');
|
||||
assert_eq!(builder_with_char.char_count(), 1.chars());
|
||||
assert_eq!(builder_with_char.char_count(), 1);
|
||||
assert_eq!(builder_with_char.len(), 3.bytes());
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! A module containing structures and traits used in parser API.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::unit::*;
|
||||
|
||||
@ -43,11 +44,11 @@ pub struct SourceFile {
|
||||
/// The whole content of file.
|
||||
pub content: String,
|
||||
/// The range in bytes of module's "Code" section.
|
||||
pub code: Range<Bytes>,
|
||||
pub code: Range<Byte>,
|
||||
/// The range in bytes of module's "Id Map" section.
|
||||
pub id_map: Range<Bytes>,
|
||||
pub id_map: Range<Byte>,
|
||||
/// The range in bytes of module's "Metadata" section.
|
||||
pub metadata: Range<Bytes>,
|
||||
pub metadata: Range<Byte>,
|
||||
}
|
||||
|
||||
impl Display for SourceFile {
|
||||
@ -64,24 +65,24 @@ impl SourceFile {
|
||||
/// the whole contents is treated as the code.
|
||||
pub fn new(content: String) -> Self {
|
||||
pub const METADATA_LINES: usize = 3;
|
||||
let nl_offsets = content.char_indices().filter_map(|(ix, c)| (c == '\n').as_some(ix));
|
||||
let nl_offsets_bytes = nl_offsets.map(Bytes::from);
|
||||
let nl_offsets_from_end = nl_offsets_bytes.rev().take(METADATA_LINES).collect_vec();
|
||||
match nl_offsets_from_end.as_slice() {
|
||||
[last, before_last, two_before_last] => {
|
||||
let nl_indices = content.char_indices().filter_map(|(ix, c)| (c == '\n').as_some(ix));
|
||||
let nl_indices_bytes = nl_indices.map(Byte::from);
|
||||
let nl_indices_from_end = nl_indices_bytes.rev().take(METADATA_LINES).collect_vec();
|
||||
match nl_indices_from_end.as_slice() {
|
||||
&[last, before_last, two_before_last] => {
|
||||
// Last line should be metadata. Line before should be id map. Line before is the
|
||||
// metadata tag.
|
||||
// We check that tag matches and that trailing lines looks like JSON list/object
|
||||
// respectively.
|
||||
let code_length = *two_before_last + 1.bytes() - Bytes::from(NEWLINES_BEFORE_TAG);
|
||||
let code_range = 0.bytes()..code_length;
|
||||
let tag_range = two_before_last + 1.bytes()..*before_last;
|
||||
let id_map_range = before_last + 1.bytes()..*last;
|
||||
let metadata_range = last + 1.bytes()..Bytes::from(content.len());
|
||||
let tag = &content[tag_range.start.as_usize()..tag_range.end.as_usize()];
|
||||
let idmap = &content[id_map_range.start.as_usize()..id_map_range.end.as_usize()];
|
||||
let metadata =
|
||||
&content[metadata_range.start.as_usize()..metadata_range.end.as_usize()];
|
||||
let code_length =
|
||||
two_before_last + 1.byte_diff() - ByteDiff::from(NEWLINES_BEFORE_TAG);
|
||||
let code_range = 0.byte()..(0.byte() + code_length);
|
||||
let tag_range = two_before_last + 1.byte_diff()..before_last;
|
||||
let id_map_range = before_last + 1.byte_diff()..last;
|
||||
let metadata_range = last + 1.byte_diff()..Byte::from(content.len());
|
||||
let tag = &content[tag_range.start.value..tag_range.end.value];
|
||||
let idmap = &content[id_map_range.start.value..id_map_range.end.value];
|
||||
let metadata = &content[metadata_range.start.value..metadata_range.end.value];
|
||||
let tag_matching = tag == METADATA_TAG;
|
||||
let idmap_matching = Self::looks_like_idmap(idmap);
|
||||
let metadata_matching = Self::looks_like_metadata(metadata);
|
||||
@ -102,9 +103,9 @@ impl SourceFile {
|
||||
|
||||
/// Create a description of source file consisting only of code, with no metadata.
|
||||
fn new_without_metadata(content: String) -> Self {
|
||||
let length = Bytes::from(content.len());
|
||||
let length = Byte::from(content.len());
|
||||
Self {
|
||||
code: (0.bytes()..length).into(),
|
||||
code: (0.byte()..length).into(),
|
||||
id_map: (length..length).into(),
|
||||
metadata: (length..length).into(),
|
||||
content,
|
||||
@ -136,9 +137,9 @@ impl SourceFile {
|
||||
self.slice(&self.metadata)
|
||||
}
|
||||
|
||||
fn slice(&self, range: &Range<Bytes>) -> &str {
|
||||
let start = range.start.as_usize();
|
||||
let end = range.end.as_usize();
|
||||
fn slice(&self, range: &Range<Byte>) -> &str {
|
||||
let start = range.start.value;
|
||||
let end = range.end.value;
|
||||
&self.content[start..end]
|
||||
}
|
||||
}
|
||||
@ -188,7 +189,7 @@ fn to_json_single_line(val: &impl Serialize) -> std::result::Result<String, serd
|
||||
impl<M: Metadata> ParsedSourceFile<M> {
|
||||
/// Serialize to the SourceFile structure,
|
||||
pub fn serialize(&self) -> std::result::Result<SourceFile, serde_json::Error> {
|
||||
let code = self.ast.repr();
|
||||
let code = self.ast.repr().into();
|
||||
let before_tag = "\n".repeat(NEWLINES_BEFORE_TAG);
|
||||
let before_idmap = "\n";
|
||||
let json_id_map = JsonIdMap::from_id_map(&self.ast.id_map(), &code);
|
||||
@ -196,18 +197,20 @@ impl<M: Metadata> ParsedSourceFile<M> {
|
||||
let before_metadata = "\n";
|
||||
let metadata = to_json_single_line(&self.metadata)?;
|
||||
|
||||
let id_map_start = code.len() + before_tag.len() + METADATA_TAG.len() + before_idmap.len();
|
||||
let id_map_start_bytes = Bytes::from(id_map_start);
|
||||
let id_map_start =
|
||||
code.len().value + before_tag.len() + METADATA_TAG.len() + before_idmap.len();
|
||||
let id_map_start_bytes = Byte::from(id_map_start);
|
||||
let metadata_start = id_map_start + id_map.len() + before_metadata.len();
|
||||
let metadata_start_bytes = Bytes::from(metadata_start);
|
||||
let metadata_start_bytes = Byte::from(metadata_start);
|
||||
Ok(SourceFile {
|
||||
content: iformat!(
|
||||
"{code}{before_tag}{METADATA_TAG}{before_idmap}{id_map}\
|
||||
{before_metadata}{metadata}"
|
||||
),
|
||||
code: (0.bytes()..Bytes::from(code.len())).into(),
|
||||
id_map: (id_map_start_bytes..id_map_start_bytes + Bytes::from(id_map.len())).into(),
|
||||
metadata: (metadata_start_bytes..metadata_start_bytes + Bytes::from(metadata.len()))
|
||||
code: (0.byte()..code.len().to_byte()).into(),
|
||||
id_map: (id_map_start_bytes..id_map_start_bytes + ByteDiff::from(id_map.len()))
|
||||
.into(),
|
||||
metadata: (metadata_start_bytes..metadata_start_bytes + ByteDiff::from(metadata.len()))
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
@ -262,8 +265,6 @@ where T: Fail {
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
struct Metadata {
|
||||
foo: usize,
|
||||
@ -277,7 +278,7 @@ mod test {
|
||||
let node = ast::Ast::infix_var("2", "+", "2");
|
||||
let infix = ast::Ast::infix(main, "=", node);
|
||||
let ast: ast::known::Module = ast::Ast::one_line_module(infix).try_into().unwrap();
|
||||
let repr = ast.repr();
|
||||
let repr = ast.repr().into();
|
||||
let metadata = Metadata { foo: 321 };
|
||||
let source = ParsedSourceFile { ast, metadata };
|
||||
let serialized = source.serialize().unwrap();
|
||||
|
@ -68,7 +68,7 @@ impl Client {
|
||||
/// Parses Enso code with JS-based parser.
|
||||
pub fn parse(&self, program: String, ids: IdMap) -> api::Result<Ast> {
|
||||
let ast = || {
|
||||
let ids = JsonIdMap::from_id_map(&ids, &program);
|
||||
let ids = JsonIdMap::from_id_map(&ids, &program.clone().into());
|
||||
let json_ids = serde_json::to_string(&ids)?;
|
||||
let json_ast = parse(program, json_ids)?;
|
||||
let ast = from_json_str_without_recursion_limit(&json_ast)?;
|
||||
|
@ -9,6 +9,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use enso_prelude::*;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Utilities for writing tests using parser. Should not be used in production parts.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::unit::*;
|
||||
|
||||
use crate::Parser;
|
||||
|
||||
@ -41,7 +42,7 @@ impl ParserTestExts for Parser {
|
||||
let program = program.into();
|
||||
DEBUG!("parsing " program);
|
||||
let ast = self.parse(program.clone(), default()).unwrap();
|
||||
assert_eq!(ast.shape().len().as_usize(), program.len());
|
||||
assert_eq!(ast.shape().len(), program.len().bytes());
|
||||
validate_spans(&ast);
|
||||
assert_eq!(ast.repr(), program, "{:?}", ast);
|
||||
ast
|
||||
|
@ -240,7 +240,7 @@ impl Client {
|
||||
|
||||
/// Sends a request to parser service to parse Enso code.
|
||||
pub fn parse(&mut self, program: String, ids: IdMap) -> api::Result<Ast> {
|
||||
let ids = JsonIdMap::from_id_map(&ids, &program);
|
||||
let ids = JsonIdMap::from_id_map(&ids, &program.as_str().into());
|
||||
let request = Request::ParseRequest { program, ids };
|
||||
let response = self.rpc_call::<serde_json::Value>(request)?;
|
||||
match response {
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use ast::crumbs::PatternMatchCrumb::*;
|
||||
use ast::crumbs::*;
|
||||
|
@ -89,7 +89,7 @@ impl<T: Payload> SpanTreeGenerator<T> for String {
|
||||
/// An utility to generate children with increasing offsets.
|
||||
#[derive(Debug, Default)]
|
||||
struct ChildGenerator<T> {
|
||||
current_offset: Bytes,
|
||||
current_offset: ByteDiff,
|
||||
children: Vec<node::Child<T>>,
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ impl<T: Payload> ChildGenerator<T> {
|
||||
/// Add spacing to current generator state. It will be taken into account for the next generated
|
||||
/// children's offsets
|
||||
fn spacing(&mut self, size: usize) {
|
||||
self.current_offset += Bytes::from(size);
|
||||
self.current_offset += (size as i32).byte_diff();
|
||||
}
|
||||
|
||||
fn generate_ast_node(
|
||||
@ -226,7 +226,7 @@ fn generate_node_for_ast<T: Payload>(
|
||||
.unwrap()
|
||||
.generate_node(kind, context),
|
||||
_ => {
|
||||
let size = ast.len();
|
||||
let size = (ast.len().value as i32).byte_diff();
|
||||
let ast_id = ast.id;
|
||||
let children = default();
|
||||
let name = ast::identifier::name(ast);
|
||||
|
@ -15,6 +15,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! A module with SpanTree structure definition.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::unit::*;
|
||||
|
||||
use crate::iter::LeafIterator;
|
||||
@ -36,7 +37,7 @@ pub trait Payload = Default + Clone;
|
||||
#[allow(missing_docs)]
|
||||
pub struct Node<T> {
|
||||
pub kind: Kind,
|
||||
pub size: Bytes,
|
||||
pub size: ByteDiff,
|
||||
pub children: Vec<Child<T>>,
|
||||
pub ast_id: Option<ast::Id>,
|
||||
pub payload: T,
|
||||
@ -132,7 +133,7 @@ impl<T> Node<T> {
|
||||
self.kind = k.into();
|
||||
self
|
||||
}
|
||||
pub fn with_size(mut self, size: Bytes) -> Self {
|
||||
pub fn with_size(mut self, size: ByteDiff) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
@ -182,7 +183,7 @@ pub struct Child<T = ()> {
|
||||
/// A child node.
|
||||
pub node: Node<T>,
|
||||
/// An offset counted from the parent node starting index to the start of this node's span.
|
||||
pub offset: Bytes,
|
||||
pub offset: ByteDiff,
|
||||
/// AST crumbs which lead from parent to child associated AST node.
|
||||
pub ast_crumbs: ast::Crumbs,
|
||||
}
|
||||
@ -263,7 +264,7 @@ impl<T: Payload> ChildBuilder<T> {
|
||||
let builder = ChildBuilder::new(new_child);
|
||||
let child = f(builder).child;
|
||||
let offset_diff = child.offset - offset;
|
||||
node.size += child.size + offset_diff;
|
||||
node.size = node.size + child.size + offset_diff;
|
||||
node.children.push(child);
|
||||
}
|
||||
|
||||
@ -285,7 +286,7 @@ impl<T: Payload> ChildBuilder<T> {
|
||||
}
|
||||
|
||||
/// Offset setter.
|
||||
pub fn offset(mut self, offset: Bytes) -> Self {
|
||||
pub fn offset(mut self, offset: ByteDiff) -> Self {
|
||||
self.offset = offset;
|
||||
self
|
||||
}
|
||||
@ -303,7 +304,7 @@ impl<T: Payload> ChildBuilder<T> {
|
||||
}
|
||||
|
||||
/// Size setter.
|
||||
pub fn size(mut self, size: Bytes) -> Self {
|
||||
pub fn size(mut self, size: ByteDiff) -> Self {
|
||||
self.node.size = size;
|
||||
self
|
||||
}
|
||||
@ -429,7 +430,7 @@ pub struct Ref<'a, T = ()> {
|
||||
/// The node's ref.
|
||||
pub node: &'a Node<T>,
|
||||
/// Span begin's offset counted from the root expression.
|
||||
pub span_offset: Bytes,
|
||||
pub span_offset: Byte,
|
||||
/// Crumbs specifying this node position related to root.
|
||||
pub crumbs: Crumbs,
|
||||
/// Ast crumbs locating associated AST node, related to the root's AST node.
|
||||
@ -455,7 +456,7 @@ impl<'a, T: Payload> Ref<'a, T> {
|
||||
}
|
||||
|
||||
/// Get span of current node.
|
||||
pub fn span(&self) -> text::Range<Bytes> {
|
||||
pub fn span(&self) -> text::Range<Byte> {
|
||||
let start = self.span_offset;
|
||||
let end = self.span_offset + self.node.size;
|
||||
(start..end).into()
|
||||
@ -559,7 +560,7 @@ impl<'a, T: Payload> Ref<'a, T> {
|
||||
|
||||
/// Get the node which exactly matches the given Span. If there many such node's, it pick first
|
||||
/// found by DFS.
|
||||
pub fn find_by_span(self, span: &text::Range<Bytes>) -> Option<Ref<'a, T>> {
|
||||
pub fn find_by_span(self, span: &text::Range<Byte>) -> Option<Ref<'a, T>> {
|
||||
if self.span() == *span {
|
||||
Some(self)
|
||||
} else {
|
||||
@ -648,9 +649,9 @@ pub struct RefMut<'a, T = ()> {
|
||||
/// The node's ref.
|
||||
node: &'a mut Node<T>,
|
||||
/// An offset counted from the parent node start to the start of this node's span.
|
||||
pub offset: Bytes,
|
||||
pub offset: ByteDiff,
|
||||
/// Span begin's offset counted from the root expression.
|
||||
pub span_offset: Bytes,
|
||||
pub span_offset: Byte,
|
||||
/// Crumbs specifying this node position related to root.
|
||||
pub crumbs: Crumbs,
|
||||
/// Ast crumbs locating associated AST node, related to the root's AST node.
|
||||
@ -678,7 +679,7 @@ impl<'a, T: Payload> RefMut<'a, T> {
|
||||
}
|
||||
|
||||
/// Get span of current node.
|
||||
pub fn span(&self) -> text::Range<Bytes> {
|
||||
pub fn span(&self) -> text::Range<Byte> {
|
||||
text::Range::new(self.span_offset, self.span_offset + self.size)
|
||||
}
|
||||
|
||||
@ -686,7 +687,7 @@ impl<'a, T: Payload> RefMut<'a, T> {
|
||||
fn child_from_ref(
|
||||
index: usize,
|
||||
child: &'a mut Child<T>,
|
||||
mut span_begin: Bytes,
|
||||
mut span_begin: Byte,
|
||||
crumbs: Crumbs,
|
||||
mut ast_crumbs: ast::Crumbs,
|
||||
) -> RefMut<'a, T> {
|
||||
@ -850,7 +851,7 @@ mod test {
|
||||
use crate::SpanTree;
|
||||
|
||||
use ast::crumbs;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
#[test]
|
||||
fn node_lookup() {
|
||||
@ -873,11 +874,11 @@ mod test {
|
||||
let grand_child2 = child2.clone().get_descendant(&vec![1]).unwrap();
|
||||
|
||||
// Span begin.
|
||||
assert_eq!(root.span_offset, 0.bytes());
|
||||
assert_eq!(child1.span_offset, 0.bytes());
|
||||
assert_eq!(child2.span_offset, 2.bytes());
|
||||
assert_eq!(grand_child1.span_offset, 2.bytes());
|
||||
assert_eq!(grand_child2.span_offset, 5.bytes());
|
||||
assert_eq!(root.span_offset, 0.byte());
|
||||
assert_eq!(child1.span_offset, 0.byte());
|
||||
assert_eq!(child2.span_offset, 2.byte());
|
||||
assert_eq!(grand_child1.span_offset, 2.byte());
|
||||
assert_eq!(grand_child2.span_offset, 5.byte());
|
||||
|
||||
// Length
|
||||
assert_eq!(root.node.size.value, 7);
|
||||
|
@ -766,7 +766,7 @@ impl Handle {
|
||||
let ast_so_far = self.module.ast();
|
||||
let definition = self.definition()?;
|
||||
let new_definition = f(definition.item)?;
|
||||
info!(self.logger, "Applying graph changes onto definition");
|
||||
info!("Applying graph changes onto definition");
|
||||
let new_ast = new_definition.ast.into();
|
||||
let new_module = ast_so_far.set_traversing(&definition.crumbs, new_ast)?;
|
||||
self.module.update_ast(new_module)
|
||||
@ -795,7 +795,7 @@ impl Handle {
|
||||
|
||||
/// Adds a new node to the graph and returns information about created node.
|
||||
pub fn add_node(&self, node: NewNodeInfo) -> FallibleResult<ast::Id> {
|
||||
info!(self.logger, "Adding node with expression `{node.expression}`");
|
||||
info!("Adding node with expression `{}`", node.expression);
|
||||
let expression_ast = self.parse_node_expression(&node.expression)?;
|
||||
let main_line = MainLine::from_ast(&expression_ast).ok_or(FailedToCreateNode)?;
|
||||
let documentation = node
|
||||
@ -827,7 +827,7 @@ impl Handle {
|
||||
|
||||
/// Removes the node with given Id.
|
||||
pub fn remove_node(&self, id: ast::Id) -> FallibleResult {
|
||||
info!(self.logger, "Removing node {id}");
|
||||
info!("Removing node {id}");
|
||||
self.update_definition_ast(|definition| {
|
||||
let mut graph = GraphInfo::from_definition(definition);
|
||||
graph.remove_node(id)?;
|
||||
@ -842,7 +842,7 @@ impl Handle {
|
||||
/// Sets the given's node expression.
|
||||
#[profile(Debug)]
|
||||
pub fn set_expression(&self, id: ast::Id, expression_text: impl Str) -> FallibleResult {
|
||||
info!(self.logger, "Setting node {id} expression to `{expression_text.as_ref()}`");
|
||||
info!("Setting node {id} expression to `{}`", expression_text.as_ref());
|
||||
let new_expression_ast = self.parse_node_expression(expression_text)?;
|
||||
self.set_expression_ast(id, new_expression_ast)
|
||||
}
|
||||
@ -850,7 +850,7 @@ impl Handle {
|
||||
/// Sets the given's node expression.
|
||||
#[profile(Debug)]
|
||||
pub fn set_expression_ast(&self, id: ast::Id, expression: Ast) -> FallibleResult {
|
||||
info!(self.logger, "Setting node {id} expression to `{expression.repr()}`");
|
||||
info!("Setting node {id} expression to `{}`", expression.repr());
|
||||
self.update_definition_ast(|definition| {
|
||||
let mut graph = GraphInfo::from_definition(definition);
|
||||
graph.edit_node(id, expression)?;
|
||||
@ -888,7 +888,7 @@ impl Handle {
|
||||
use double_representation::refactorings::collapse::collapse;
|
||||
use double_representation::refactorings::collapse::Collapsed;
|
||||
let nodes = nodes.into_iter().map(|id| self.node(id)).collect::<Result<Vec<_>, _>>()?;
|
||||
info!(self.logger, "Collapsing {nodes:?}.");
|
||||
info!("Collapsing {nodes:?}.");
|
||||
let collapsed_positions = nodes
|
||||
.iter()
|
||||
.filter_map(|node| node.metadata.as_ref().and_then(|metadata| metadata.position));
|
||||
@ -921,7 +921,7 @@ impl Handle {
|
||||
let mut graph = GraphInfo::from_definition(definition);
|
||||
graph.update_node(id, |node| {
|
||||
let new_node = f(node);
|
||||
info!(self.logger, "Setting node {id} line to `{new_node.repr()}`");
|
||||
info!("Setting node {id} line to `{}`", new_node.repr());
|
||||
Some(new_node)
|
||||
})?;
|
||||
Ok(graph.source)
|
||||
@ -992,7 +992,7 @@ pub mod tests {
|
||||
use double_representation::identifier::NormalizedName;
|
||||
use double_representation::project;
|
||||
use engine_protocol::language_server::MethodPointer;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::index::*;
|
||||
use parser::Parser;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
@ -1066,7 +1066,7 @@ pub mod tests {
|
||||
pub fn suggestion_db(&self) -> Rc<model::SuggestionDatabase> {
|
||||
use model::suggestion_database::SuggestionDatabase;
|
||||
let entries = self.suggestions.iter();
|
||||
Rc::new(SuggestionDatabase::new_from_entries(Logger::new("Test"), entries))
|
||||
Rc::new(SuggestionDatabase::new_from_entries(entries))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1115,7 +1115,7 @@ pub mod tests {
|
||||
fn graph_controller_notification_relay() {
|
||||
Fixture::set_up().run(|graph| async move {
|
||||
let mut sub = graph.subscribe();
|
||||
let change = TextChange { range: (12.bytes()..12.bytes()).into(), text: "2".into() };
|
||||
let change = TextChange { range: (12.byte()..12.byte()).into(), text: "2".into() };
|
||||
graph.module.apply_code_change(change, &graph.parser, default()).unwrap();
|
||||
assert_eq!(Some(Notification::Invalidate), sub.next().await);
|
||||
});
|
||||
|
@ -224,14 +224,14 @@ impl Handle {
|
||||
///
|
||||
/// Fails if method graph cannot be created (see `graph_for_method` documentation).
|
||||
pub async fn enter_method_pointer(&self, local_call: &LocalCall) -> FallibleResult {
|
||||
debug!(self.logger, "Entering node {local_call.call}.");
|
||||
debug!("Entering node {}.", local_call.call);
|
||||
let method_ptr = &local_call.definition;
|
||||
let graph = controller::Graph::new_method(&self.logger, &self.project, method_ptr);
|
||||
let graph = graph.await?;
|
||||
self.execution_ctx.push(local_call.clone()).await?;
|
||||
debug!(self.logger, "Replacing graph with {graph:?}.");
|
||||
debug!("Replacing graph with {graph:?}.");
|
||||
self.graph.replace(graph);
|
||||
debug!(self.logger, "Sending graph invalidation signal.");
|
||||
debug!("Sending graph invalidation signal.");
|
||||
self.notifier.publish(Notification::EnteredNode(local_call.clone())).await;
|
||||
|
||||
Ok(())
|
||||
|
@ -90,7 +90,6 @@ impl Handle {
|
||||
let my_code = self.code();
|
||||
if code != my_code {
|
||||
error!(
|
||||
self.logger,
|
||||
"The module controller ast was not synchronized with text editor \
|
||||
content!\n >>> Module: {my_code}\n >>> Editor: {code}"
|
||||
);
|
||||
@ -186,7 +185,7 @@ impl Handle {
|
||||
let logger = Logger::new("Mocked Module Controller");
|
||||
let ast = parser.parse(code.to_string(), id_map)?.try_into()?;
|
||||
let metadata = default();
|
||||
let model = Rc::new(model::module::Plain::new(&logger, path, ast, metadata, repository));
|
||||
let model = Rc::new(model::module::Plain::new(path, ast, metadata, repository));
|
||||
Ok(Handle { model, language_server, parser, logger })
|
||||
}
|
||||
|
||||
@ -212,7 +211,7 @@ mod test {
|
||||
use ast;
|
||||
use ast::Ast;
|
||||
use ast::BlockLine;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::index::*;
|
||||
use parser::Parser;
|
||||
use uuid::Uuid;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
@ -229,16 +228,16 @@ mod test {
|
||||
let uuid3 = Uuid::new_v4();
|
||||
let uuid4 = Uuid::new_v4();
|
||||
let id_map = ast::IdMap::new(vec![
|
||||
((0.bytes()..1.bytes()).into(), uuid1),
|
||||
((1.bytes()..2.bytes()).into(), uuid2),
|
||||
((2.bytes()..3.bytes()).into(), uuid3),
|
||||
((0.bytes()..3.bytes()).into(), uuid4),
|
||||
((0.byte()..1.byte()).into(), uuid1),
|
||||
((1.byte()..2.byte()).into(), uuid2),
|
||||
((2.byte()..3.byte()).into(), uuid3),
|
||||
((0.byte()..3.byte()).into(), uuid4),
|
||||
]);
|
||||
let controller =
|
||||
Handle::new_mock(location, code, id_map, ls, parser, default()).unwrap();
|
||||
|
||||
// Change code from "2+2" to "22+2"
|
||||
let change = enso_text::Change::inserted(0.bytes(), "2".to_string());
|
||||
let change = enso_text::Change::inserted(0.byte(), "2".to_string());
|
||||
controller.apply_code_change(change).unwrap();
|
||||
let expected_ast = Ast::new_no_id(ast::Module {
|
||||
lines: vec![BlockLine {
|
||||
|
@ -22,7 +22,9 @@ use double_representation::node::NodeInfo;
|
||||
use double_representation::project;
|
||||
use double_representation::tp;
|
||||
use engine_protocol::language_server;
|
||||
use enso_text::Byte;
|
||||
use enso_text::Location;
|
||||
use enso_text::Rope;
|
||||
use flo_stream::Subscriber;
|
||||
use parser::Parser;
|
||||
|
||||
@ -505,7 +507,7 @@ pub struct Searcher {
|
||||
language_server: Rc<language_server::Connection>,
|
||||
ide: controller::Ide,
|
||||
this_arg: Rc<Option<ThisNode>>,
|
||||
position_in_code: Immutable<Location>,
|
||||
position_in_code: Immutable<Location<Byte>>,
|
||||
project: model::Project,
|
||||
/// A component list builder with favorites prepopulated with
|
||||
/// [`controller::ExecutedGraph::component_groups`]. Stored to reduce the number of
|
||||
@ -558,8 +560,8 @@ impl Searcher {
|
||||
let module_ast = graph.graph().module.ast();
|
||||
let def_id = graph.graph().id;
|
||||
let def_span = double_representation::module::definition_span(&module_ast, &def_id)?;
|
||||
let module_repr: enso_text::Text = module_ast.repr().into();
|
||||
let position = module_repr.location_of_byte_offset_snapped(def_span.end);
|
||||
let module_repr: Rope = module_ast.repr().into();
|
||||
let position = module_repr.offset_to_location_snapped(def_span.end);
|
||||
let this_arg = Rc::new(match mode {
|
||||
Mode::NewNode { source_node: Some(node), .. } => ThisNode::new(node, &graph.graph()),
|
||||
_ => None,
|
||||
@ -689,13 +691,13 @@ impl Searcher {
|
||||
self.invalidate_fragments_added_by_picking();
|
||||
let expression_changed = old_expr != new_expr;
|
||||
if expression_changed {
|
||||
debug!(self.logger, "Reloading list.");
|
||||
debug!("Reloading list.");
|
||||
self.reload_list();
|
||||
} else {
|
||||
let data = self.data.borrow();
|
||||
data.components.update_filtering(&data.input.pattern);
|
||||
if let Actions::Loaded { list } = &data.actions {
|
||||
debug!(self.logger, "Update filtering.");
|
||||
debug!("Update filtering.");
|
||||
list.update_filtering(&data.input.pattern);
|
||||
executor::global::spawn(self.notifier.publish(Notification::NewActionList));
|
||||
}
|
||||
@ -847,7 +849,6 @@ impl Searcher {
|
||||
match self.ide.manage_projects() {
|
||||
Ok(_) => {
|
||||
let ide = self.ide.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
// We checked that manage_projects returns Some just a moment ago, so
|
||||
// unwrapping is safe.
|
||||
@ -859,7 +860,7 @@ impl Searcher {
|
||||
manage_projects.open_project(*id),
|
||||
};
|
||||
if let Err(err) = result.await {
|
||||
error!(logger, "Error when creating new project: {err}");
|
||||
error!("Error when creating new project: {err}");
|
||||
}
|
||||
});
|
||||
Ok(None)
|
||||
@ -1098,7 +1099,6 @@ impl Searcher {
|
||||
#[profile(Debug)]
|
||||
fn this_arg_type_for_next_completion(&self) -> impl Future<Output = Option<String>> {
|
||||
let next_id = self.data.borrow().input.next_completion_id();
|
||||
let logger = self.logger.clone_ref();
|
||||
let graph = self.graph.clone_ref();
|
||||
let this = self.this_arg.clone_ref();
|
||||
async move {
|
||||
@ -1108,7 +1108,7 @@ impl Searcher {
|
||||
}
|
||||
let ThisNode { id, .. } = this.deref().as_ref()?;
|
||||
let opt_type = graph.expression_type(*id).await.map(Into::into);
|
||||
opt_type.map_none(move || error!(logger, "Failed to obtain type for this node."))
|
||||
opt_type.map_none(move || error!("Failed to obtain type for this node."))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1141,7 +1141,7 @@ impl Searcher {
|
||||
) {
|
||||
let ls = self.language_server.clone_ref();
|
||||
let graph = self.graph.graph();
|
||||
let position = self.position_in_code.deref().into();
|
||||
let position = self.my_utf16_location().span.into();
|
||||
let this = self.clone_ref();
|
||||
let return_types = return_types.into_iter().collect_vec();
|
||||
let return_types_for_engine = if return_types.is_empty() {
|
||||
@ -1151,9 +1151,9 @@ impl Searcher {
|
||||
};
|
||||
executor::global::spawn(async move {
|
||||
let this_type = this_type.await;
|
||||
info!(this.logger, "Requesting new suggestion list. Type of `self` is {this_type:?}.");
|
||||
info!("Requesting new suggestion list. Type of `self` is {this_type:?}.");
|
||||
let requests = return_types_for_engine.into_iter().map(|return_type| {
|
||||
info!(this.logger, "Requesting suggestions for returnType {return_type:?}.");
|
||||
info!("Requesting suggestions for returnType {return_type:?}.");
|
||||
let file = graph.module.path().file_path();
|
||||
ls.completion(file, &position, &this_type, &return_type, &tags)
|
||||
});
|
||||
@ -1161,7 +1161,7 @@ impl Searcher {
|
||||
futures::future::join_all(requests).await.into_iter().collect();
|
||||
match responses {
|
||||
Ok(responses) => {
|
||||
info!(this.logger, "Received suggestions from Language Server.");
|
||||
info!("Received suggestions from Language Server.");
|
||||
let list = this.make_action_list(responses.iter());
|
||||
let mut data = this.data.borrow_mut();
|
||||
data.actions = Actions::Loaded { list: Rc::new(list) };
|
||||
@ -1171,7 +1171,7 @@ impl Searcher {
|
||||
}
|
||||
Err(err) => {
|
||||
let msg = "Request for completions to the Language Server returned error";
|
||||
error!(this.logger, "{msg}: {err}");
|
||||
error!("{msg}: {err}");
|
||||
let mut data = this.data.borrow_mut();
|
||||
data.actions = Actions::Error(Rc::new(err.into()));
|
||||
data.components =
|
||||
@ -1218,7 +1218,6 @@ impl Searcher {
|
||||
.map(|entry| Action::Suggestion(action::Suggestion::FromDatabase(entry)))
|
||||
.handle_err(|e| {
|
||||
error!(
|
||||
self.logger,
|
||||
"Response provided a suggestion ID that cannot be \
|
||||
resolved: {e}."
|
||||
)
|
||||
@ -1243,6 +1242,25 @@ impl Searcher {
|
||||
builder.build()
|
||||
}
|
||||
|
||||
/// Convert a location within a current module (i.e. module being edited) to a location indexed
|
||||
/// by UTF-16 code units. This enables Language Server protocol compatibility.
|
||||
fn location_to_utf16(
|
||||
&self,
|
||||
location: Location<Byte>,
|
||||
) -> suggestion_database::entry::ModuleSpan {
|
||||
let module: Rope = self.graph.graph().module.ast().repr().into();
|
||||
suggestion_database::entry::ModuleSpan {
|
||||
module: self.module_qualified_name(),
|
||||
span: module.utf16_code_unit_location_of_location(location),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a position of the searcher in the code to an Engine-compatible UTF-16 location.
|
||||
fn my_utf16_location(&self) -> suggestion_database::entry::ModuleSpan {
|
||||
let location = self.position_in_code.deref().into();
|
||||
self.location_to_utf16(location)
|
||||
}
|
||||
|
||||
fn possible_function_calls(&self) -> Vec<action::Suggestion> {
|
||||
let opt_result = || {
|
||||
let call_ast = self.data.borrow().input.expression.as_ref()?.func.clone_ref();
|
||||
@ -1253,9 +1271,8 @@ impl Searcher {
|
||||
Some(entry.into_iter().map(action::Suggestion::FromDatabase).collect())
|
||||
} else {
|
||||
let name = &call.function_name;
|
||||
let module = self.module_qualified_name();
|
||||
let location = *self.position_in_code;
|
||||
let entries = self.database.lookup_by_name_and_location(name, &module, location);
|
||||
let location = self.my_utf16_location();
|
||||
let entries = self.database.lookup_at(name, &location);
|
||||
Some(entries.into_iter().map(action::Suggestion::FromDatabase).collect())
|
||||
}
|
||||
};
|
||||
@ -1265,11 +1282,10 @@ impl Searcher {
|
||||
/// For the simple function call checks if the function is called on the module (if it can be
|
||||
/// easily determined) and returns the module's qualified name if it is.
|
||||
fn module_whose_method_is_called(&self, call: &SimpleFunctionCall) -> Option<QualifiedName> {
|
||||
let position = *self.position_in_code;
|
||||
let location = self.my_utf16_location();
|
||||
let this_name = ast::identifier::name(call.this_argument.as_ref()?)?;
|
||||
let module_name = self.module_qualified_name();
|
||||
let matching_locals =
|
||||
self.database.lookup_locals_by_name_and_location(this_name, &module_name, position);
|
||||
let matching_locals = self.database.lookup_locals_at(this_name, &location);
|
||||
let module_name = location.module;
|
||||
let not_local_name = matching_locals.is_empty();
|
||||
not_local_name.and_option_from(|| {
|
||||
if this_name == module_name.name().deref() {
|
||||
@ -1634,12 +1650,12 @@ pub mod test {
|
||||
|
||||
impl MockData {
|
||||
fn change_main_body(&mut self, lines: &[&str]) {
|
||||
let code: enso_text::Text = dbg!(crate::test::mock::main_from_lines(lines)).into();
|
||||
let location = code.location_of_text_end();
|
||||
let code: Rope = crate::test::mock::main_from_lines(lines).into();
|
||||
let location = code.last_line_end_location();
|
||||
// TODO [mwu] Not nice that we ended up with duplicated mock data for code.
|
||||
self.graph.module.code = (&code).into();
|
||||
self.graph.graph.code = code.into();
|
||||
self.code_location = location.into();
|
||||
self.graph.graph.code = (&code).into();
|
||||
self.code_location = code.utf16_code_unit_location_of_location(location).into();
|
||||
}
|
||||
|
||||
fn expect_completion(
|
||||
@ -1684,8 +1700,10 @@ pub mod test {
|
||||
let mut client = language_server::MockClient::default();
|
||||
client.require_all_calls();
|
||||
client_setup(&mut data, &mut client);
|
||||
let end_of_code = enso_text::Text::from(&data.graph.module.code).location_of_text_end();
|
||||
let code_range = enso_text::Location::default()..=end_of_code;
|
||||
let code = enso_text::Rope::from(&data.graph.module.code);
|
||||
let start_of_code = enso_text::Location::default();
|
||||
let end_of_code = code.location_of_text_end_utf16_code_unit();
|
||||
let code_range = start_of_code..=end_of_code;
|
||||
let scope = Scope::InModule { range: code_range };
|
||||
let graph = data.graph.controller();
|
||||
let node = &graph.graph().nodes().unwrap()[0];
|
||||
@ -1694,7 +1712,7 @@ pub mod test {
|
||||
let this = data.selected_node.and_option(this);
|
||||
let logger = Logger::new("Searcher"); // new_empty
|
||||
let module_name = crate::test::mock::data::module_qualified_name();
|
||||
let database = suggestion_database_with_mock_entries(&logger, module_name, scope);
|
||||
let database = suggestion_database_with_mock_entries(module_name, scope);
|
||||
let mut ide = controller::ide::MockAPI::new();
|
||||
let mut project = model::project::MockAPI::new();
|
||||
let project_qname = project_qualified_name();
|
||||
@ -1724,7 +1742,7 @@ pub mod test {
|
||||
mode: Immutable(Mode::NewNode { node_id: searcher_target, source_node: None }),
|
||||
language_server: language_server::Connection::new_mock_rc(client),
|
||||
this_arg: Rc::new(this),
|
||||
position_in_code: Immutable(end_of_code),
|
||||
position_in_code: Immutable(code.last_line_end_location()),
|
||||
project: project.clone_ref(),
|
||||
list_builder_with_favorites: Rc::new(list_builder_with_favs),
|
||||
node_edit_guard: node_metadata_guard,
|
||||
@ -1756,11 +1774,10 @@ pub mod test {
|
||||
}
|
||||
|
||||
fn suggestion_database_with_mock_entries(
|
||||
logger: &Logger,
|
||||
module_name: QualifiedName,
|
||||
scope: Scope,
|
||||
) -> Rc<SuggestionDatabase> {
|
||||
let database = Rc::new(SuggestionDatabase::new_empty(logger));
|
||||
let database = Rc::new(SuggestionDatabase::new_empty());
|
||||
let entry1 = model::suggestion_database::Entry {
|
||||
name: "testFunction1".to_string(),
|
||||
kind: Kind::Function,
|
||||
|
@ -338,7 +338,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mock_suggestion_db(logger: impl AnyLogger) -> model::SuggestionDatabase {
|
||||
pub fn mock_suggestion_db() -> model::SuggestionDatabase {
|
||||
let top_module_1 = mock_module("test.Test.TopModule1");
|
||||
let top_module_2 = mock_module("test.Test.TopModule2");
|
||||
let sub_module_1 = mock_module("test.Test.TopModule1.SubModule1");
|
||||
@ -364,7 +364,7 @@ pub(crate) mod tests {
|
||||
fun6,
|
||||
];
|
||||
|
||||
let suggestion_db = model::SuggestionDatabase::new_empty(logger);
|
||||
let suggestion_db = model::SuggestionDatabase::new_empty();
|
||||
for (id, entry) in all_entries.into_iter().enumerate() {
|
||||
suggestion_db.put_entry(id, entry)
|
||||
}
|
||||
@ -404,13 +404,12 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn filtering_component_list() {
|
||||
let logger = Logger::new("test::update_list_after_filtering_pattern_change");
|
||||
let top_module = mock_module("test.Test.TopModule");
|
||||
let sub_module = mock_module("test.Test.TopModule.SubModule");
|
||||
let fun1 = mock_function(&top_module.module, "fun1");
|
||||
let funx2 = mock_function(&sub_module.module, "funx1");
|
||||
let all_entries = [&top_module, &sub_module, &fun1, &funx2];
|
||||
let suggestion_db = model::SuggestionDatabase::new_empty(logger);
|
||||
let suggestion_db = model::SuggestionDatabase::new_empty();
|
||||
for (id, entry) in all_entries.into_iter().enumerate() {
|
||||
suggestion_db.put_entry(id, entry.clone())
|
||||
}
|
||||
@ -459,8 +458,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn component_list_modules_tree() {
|
||||
// Create a components list with sample data.
|
||||
let logger = Logger::new("test::component_list_modules_tree");
|
||||
let suggestion_db = mock_suggestion_db(logger);
|
||||
let suggestion_db = mock_suggestion_db();
|
||||
let mut builder = builder::List::new().with_local_scope_module_id(0);
|
||||
builder.extend_list_and_allow_favorites_with_ids(&suggestion_db, 0..11);
|
||||
let list = builder.build();
|
||||
|
@ -323,8 +323,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn building_component_list() {
|
||||
let logger = Logger::new("tests::module_groups_in_component_list");
|
||||
let suggestion_db = mock_suggestion_db(logger);
|
||||
let suggestion_db = mock_suggestion_db();
|
||||
let mut builder = List::new().with_local_scope_module_id(0);
|
||||
let first_part = (0..3).chain(6..11);
|
||||
let second_part = 3..6;
|
||||
@ -429,8 +428,7 @@ mod tests {
|
||||
/// processed as described in the docs of the [`List::build`] method.
|
||||
#[test]
|
||||
fn building_component_list_with_favorites() {
|
||||
let logger = Logger::new("tests::building_component_list_with_favorites");
|
||||
let db = mock_suggestion_db(logger);
|
||||
let db = mock_suggestion_db();
|
||||
let mut builder = List::new();
|
||||
let qn_of_db_entry_0 = db.lookup(0).unwrap().qualified_name();
|
||||
let qn_of_db_entry_1 = db.lookup(1).unwrap().qualified_name();
|
||||
@ -493,8 +491,7 @@ mod tests {
|
||||
/// inserted into an existing favorites group.
|
||||
#[test]
|
||||
fn building_component_list_with_virtual_component_in_existing_favorites_group() {
|
||||
let logger = Logger::new("tests::virtual_component_in_existing_favorites_group");
|
||||
let db = mock_suggestion_db(logger);
|
||||
let db = mock_suggestion_db();
|
||||
let mut builder = List::new();
|
||||
let qn_of_db_entry_0 = db.lookup(0).unwrap().qualified_name();
|
||||
let project = project::QualifiedName::standard_base_library();
|
||||
@ -521,8 +518,7 @@ mod tests {
|
||||
/// inserted into a new favorites group.
|
||||
#[test]
|
||||
fn building_component_list_with_virtual_component_in_new_favorites_group() {
|
||||
let logger = Logger::new("tests::virtual_component_in_new_favorites_group");
|
||||
let db = mock_suggestion_db(logger);
|
||||
let db = mock_suggestion_db();
|
||||
let mut builder = List::new();
|
||||
let qn_of_db_entry_0 = db.lookup(0).unwrap().qualified_name();
|
||||
let project = project::QualifiedName::standard_base_library();
|
||||
|
@ -333,8 +333,7 @@ mod tests {
|
||||
/// components in the suggestion database.
|
||||
#[test]
|
||||
fn lookup_component_groups_in_suggestion_database() {
|
||||
let logger = Logger::new("tests::lookup_component_groups_in_suggestion_database");
|
||||
let suggestion_db = Rc::new(mock_suggestion_db(logger));
|
||||
let suggestion_db = Rc::new(mock_suggestion_db());
|
||||
|
||||
// Prepare a mock group containing fully qualified component names in non-alphabetical
|
||||
// order. Some of the names correspond to entries present in the suggestion database,
|
||||
@ -377,8 +376,7 @@ mod tests {
|
||||
// only names not found in the suggestion database.
|
||||
#[test]
|
||||
fn constructing_component_group_from_names_not_found_in_db() {
|
||||
let logger = Logger::new("tests::constructing_component_group_from_names_not_found_in_db");
|
||||
let suggestion_db = Rc::new(mock_suggestion_db(logger));
|
||||
let suggestion_db = Rc::new(mock_suggestion_db());
|
||||
let ec_group = execution_context::ComponentGroup {
|
||||
project: project::QualifiedName::standard_base_library(),
|
||||
name: "Input".into(),
|
||||
|
@ -176,7 +176,7 @@ mod test {
|
||||
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
|
||||
use enso_text::traits::*;
|
||||
use enso_text::index::*;
|
||||
use parser::Parser;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
@ -206,7 +206,7 @@ mod test {
|
||||
};
|
||||
let mut sub = controller.subscribe();
|
||||
|
||||
let change = enso_text::Change::inserted(8.bytes(), "2".to_string());
|
||||
let change = enso_text::Change::inserted(8.byte(), "2".to_string());
|
||||
module.apply_code_change(change).unwrap();
|
||||
assert_eq!(Some(Notification::Invalidate), sub.next().await);
|
||||
})
|
||||
|
@ -76,7 +76,6 @@ pub struct FileToUpload<DataProvider> {
|
||||
/// API.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FileUploadProcess<DataProvider> {
|
||||
logger: Logger,
|
||||
bin_connection: Rc<binary::Connection>,
|
||||
json_connection: Rc<language_server::Connection>,
|
||||
file: FileToUpload<DataProvider>,
|
||||
@ -97,24 +96,14 @@ pub enum UploadingState {
|
||||
impl<DP: DataProvider> FileUploadProcess<DP> {
|
||||
/// Constructor.
|
||||
pub fn new(
|
||||
parent: impl AnyLogger,
|
||||
file: FileToUpload<DP>,
|
||||
bin_connection: Rc<binary::Connection>,
|
||||
json_connection: Rc<language_server::Connection>,
|
||||
remote_path: Path,
|
||||
) -> Self {
|
||||
let logger = Logger::new_sub(parent, "FileUploadProcess");
|
||||
let bytes_uploaded = 0;
|
||||
let checksum = sha3::Sha3_224::new();
|
||||
Self {
|
||||
logger,
|
||||
bin_connection,
|
||||
json_connection,
|
||||
file,
|
||||
remote_path,
|
||||
bytes_uploaded,
|
||||
checksum,
|
||||
}
|
||||
Self { bin_connection, json_connection, file, remote_path, bytes_uploaded, checksum }
|
||||
}
|
||||
|
||||
/// Upload next chunk. Returns information if all data has been uploaded.
|
||||
@ -128,9 +117,11 @@ impl<DP: DataProvider> FileUploadProcess<DP> {
|
||||
match self.file.data.next_chunk().await {
|
||||
Ok(Some(data)) => {
|
||||
debug!(
|
||||
self.logger,
|
||||
"Received chunk of {self.file.name} of size {data.len()} \
|
||||
uploading to {self.remote_path:?}: {data:?}"
|
||||
"Received chunk of {} of size {} uploading to {:?}: {:?}",
|
||||
self.file.name,
|
||||
data.len(),
|
||||
self.remote_path,
|
||||
data
|
||||
);
|
||||
let offset = self.bytes_uploaded;
|
||||
self.bin_connection.write_bytes(&self.remote_path, offset, false, &data).await?;
|
||||
@ -145,10 +136,9 @@ impl<DP: DataProvider> FileUploadProcess<DP> {
|
||||
}
|
||||
if self.bytes_uploaded != self.file.size {
|
||||
error!(
|
||||
self.logger,
|
||||
"The promised file size ({self.file.size}) and uploaded \
|
||||
data length ({self.bytes_uploaded}) do not match. Leaving as much data as \
|
||||
received."
|
||||
"The promised file size ({}) and uploaded data length ({}) do not match. \
|
||||
Leaving as much data as received.",
|
||||
self.file.size, self.bytes_uploaded
|
||||
);
|
||||
self.bytes_uploaded = self.file.size;
|
||||
}
|
||||
@ -207,7 +197,7 @@ impl NodeFromDroppedFileHandler {
|
||||
let this = self.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
if let Err(err) = this.upload_file(node, file).await {
|
||||
error!(this.logger, "Error while uploading file: {err}");
|
||||
error!("Error while uploading file: {err}");
|
||||
this.update_metadata(node, |md| md.error = Some(err.to_string()));
|
||||
}
|
||||
});
|
||||
@ -252,13 +242,8 @@ impl NodeFromDroppedFileHandler {
|
||||
let remote_path = self.data_path().append_im(&remote_name);
|
||||
let bin_connection = self.project.binary_rpc();
|
||||
let json_connection = self.project.json_rpc();
|
||||
let mut process = FileUploadProcess::new(
|
||||
&self.logger,
|
||||
file,
|
||||
bin_connection,
|
||||
json_connection,
|
||||
remote_path,
|
||||
);
|
||||
let mut process =
|
||||
FileUploadProcess::new(file, bin_connection, json_connection, remote_path);
|
||||
|
||||
while process.upload_chunk().await? == UploadingState::NotFinished {
|
||||
self.update_metadata(node, |md| md.bytes_uploaded = process.bytes_uploaded);
|
||||
@ -508,7 +493,7 @@ mod test {
|
||||
}
|
||||
|
||||
impl UploadingFixture {
|
||||
fn new(logger: impl AnyLogger, data: TestData) -> Self {
|
||||
fn new(data: TestData) -> Self {
|
||||
let mut binary_cli = binary::MockClient::new();
|
||||
let json_cli = language_server::MockClient::default();
|
||||
json_cli.require_all_calls();
|
||||
@ -520,7 +505,7 @@ mod test {
|
||||
Self {
|
||||
test: TestWithLocalPoolExecutor::set_up(),
|
||||
chunks: data.chunks.into_iter(),
|
||||
process: FileUploadProcess::new(logger, file, bin_con, json_con, data.path),
|
||||
process: FileUploadProcess::new(file, bin_con, json_con, data.path),
|
||||
provider_sink: Some(provider_sink),
|
||||
}
|
||||
}
|
||||
@ -546,9 +531,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn uploading_file() {
|
||||
let logger = Logger::new("test::uploading_file");
|
||||
let data = TestData::new(vec![vec![1, 2, 3, 4, 5], vec![3, 4, 5, 6, 7, 8]]);
|
||||
let mut test = UploadingFixture::new(logger, data);
|
||||
let mut test = UploadingFixture::new(data);
|
||||
|
||||
assert_eq!(test.next_chunk_result().unwrap(), UploadingState::NotFinished);
|
||||
assert_eq!(test.next_chunk_result().unwrap(), UploadingState::NotFinished);
|
||||
@ -557,10 +541,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn checksum_mismatch_should_cause_an_error() {
|
||||
let logger = Logger::new("test::uploading_file");
|
||||
let mut data = TestData::new(vec![vec![1, 2, 3, 4, 5]]);
|
||||
data.checksum = Sha3_224::new(&[3, 4, 5, 6, 7, 8]);
|
||||
let mut test = UploadingFixture::new(logger, data);
|
||||
let mut test = UploadingFixture::new(data);
|
||||
|
||||
assert_eq!(test.next_chunk_result().unwrap(), UploadingState::NotFinished);
|
||||
assert!(test.next_chunk_result().is_err());
|
||||
|
@ -29,20 +29,14 @@ use futures::task::LocalSpawnExt;
|
||||
|
||||
/// Global spawner container. This structure is kept in the global variable `SPAWNER`. See module
|
||||
/// docs for details.
|
||||
#[derive(Default)]
|
||||
struct GlobalSpawner {
|
||||
logger: Logger,
|
||||
spawner: RefCell<Option<Box<dyn LocalSpawn>>>,
|
||||
}
|
||||
|
||||
impl Default for GlobalSpawner {
|
||||
fn default() -> Self {
|
||||
Self { logger: Logger::new("GlobalSpawner"), spawner: default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalSpawner {
|
||||
fn set_spawner(&self, spawner_to_set: impl LocalSpawn + 'static) {
|
||||
info!(self.logger, "Setting new spawner");
|
||||
info!("Setting new spawner");
|
||||
*self.spawner.borrow_mut() = Some(Box::new(spawner_to_set))
|
||||
}
|
||||
|
||||
@ -51,13 +45,10 @@ impl GlobalSpawner {
|
||||
let mut borrowed = self.spawner.borrow_mut();
|
||||
if let Some(unwrapped) = borrowed.as_mut() {
|
||||
if unwrapped.spawn_local(f).is_err() {
|
||||
error!(
|
||||
self.logger,
|
||||
"Failed to spawn the task. Global executor might have been dropped."
|
||||
);
|
||||
error!("Failed to spawn the task. Global executor might have been dropped.");
|
||||
}
|
||||
} else {
|
||||
error!(self.logger, "Fail to spawn the task. No global executor has been provided.")
|
||||
error!("Fail to spawn the task. No global executor has been provided.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ pub mod test;
|
||||
|
||||
|
||||
/// An alias for a main animation loop.
|
||||
pub type MainLoop = animation::Loop<Box<dyn FnMut(animation::TimeInfo)>>;
|
||||
pub type MainLoop = animation::Loop;
|
||||
|
||||
/// Executor. Uses a single-threaded `LocalPool` underneath, relying on ensogl's
|
||||
/// `animation::DynamicLoop` to do as much progress as possible on every animation frame.
|
||||
|
@ -59,7 +59,7 @@ impl Initializer {
|
||||
/// Initialize all Ide objects and structures (executor, views, controllers, integration etc.)
|
||||
#[profile(Task)]
|
||||
pub async fn start(self) -> Result<Ide, FailedIde> {
|
||||
info!(self.logger, "Starting IDE with the following config: {self.config:?}");
|
||||
info!("Starting IDE with the following config: {:?}", self.config);
|
||||
|
||||
ensogl_text_msdf::initialized().await;
|
||||
let ensogl_app = ensogl::application::Application::new(self.config.dom_parent_id());
|
||||
@ -83,12 +83,12 @@ impl Initializer {
|
||||
match self.initialize_ide_controller().await {
|
||||
Ok(controller) => {
|
||||
let ide = Ide::new(ensogl_app, view.clone_ref(), controller);
|
||||
info!(self.logger, "Setup done.");
|
||||
info!("Setup done.");
|
||||
Ok(ide)
|
||||
}
|
||||
Err(error) => {
|
||||
let message = format!("Failed to initialize application: {error}");
|
||||
error!(self.logger, "{message}");
|
||||
error!("{message}");
|
||||
status_bar.add_event(ide_view::status_bar::event::Label::new(message));
|
||||
Err(FailedIde { view })
|
||||
}
|
||||
@ -186,7 +186,7 @@ impl WithProjectManager {
|
||||
/// Creates a new project and returns its id, so the newly connected project can be opened.
|
||||
pub async fn create_project(&self) -> FallibleResult<Uuid> {
|
||||
use project_manager::MissingComponentAction::Install;
|
||||
info!(self.logger, "Creating a new project named '{self.project_name}'.");
|
||||
info!("Creating a new project named '{}'.", self.project_name);
|
||||
let version = enso_config::ARGS.preferred_engine_version.as_ref().map(ToString::to_string);
|
||||
let name = &self.project_name;
|
||||
let response = self.project_manager.create_project(name, &None, &version, &Install);
|
||||
@ -209,7 +209,7 @@ impl WithProjectManager {
|
||||
if let Ok(project_id) = project {
|
||||
Ok(project_id)
|
||||
} else {
|
||||
info!(self.logger, "Attempting to create {self.project_name}");
|
||||
info!("Attempting to create {}", self.project_name);
|
||||
self.create_project().await
|
||||
}
|
||||
}
|
||||
@ -243,7 +243,7 @@ pub fn register_views(app: &Application) {
|
||||
app.views.register::<ide_view::component_browser::breadcrumbs::Breadcrumbs>();
|
||||
app.views.register::<ide_view::component_browser::component_group::View>();
|
||||
app.views.register::<ide_view::component_browser::component_group::wide::View>();
|
||||
app.views.register::<ensogl_component::text::Area>();
|
||||
app.views.register::<ensogl_component::text::Text>();
|
||||
app.views.register::<ensogl_component::selector::NumberPicker>();
|
||||
app.views.register::<ensogl_component::selector::NumberRangePicker>();
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
@ -49,7 +49,6 @@ pub struct InvalidVisualizationId(VisualizationId);
|
||||
/// controllers.
|
||||
#[derive(Debug)]
|
||||
pub struct ExecutionContext {
|
||||
logger: Logger,
|
||||
/// A name of definition which is a root call of this context.
|
||||
pub entry_point: MethodPointer,
|
||||
/// Local call stack.
|
||||
@ -66,15 +65,13 @@ pub struct ExecutionContext {
|
||||
|
||||
impl ExecutionContext {
|
||||
/// Create new execution context
|
||||
pub fn new(logger: impl Into<Logger>, entry_point: MethodPointer) -> Self {
|
||||
let logger = logger.into();
|
||||
pub fn new(entry_point: MethodPointer) -> Self {
|
||||
let stack = default();
|
||||
let visualizations = default();
|
||||
let computed_value_info_registry = default();
|
||||
let is_ready = default();
|
||||
let component_groups = default();
|
||||
Self {
|
||||
logger,
|
||||
entry_point,
|
||||
stack,
|
||||
visualizations,
|
||||
@ -122,7 +119,7 @@ impl ExecutionContext {
|
||||
let id = visualization.id;
|
||||
let (update_sender, receiver) = futures::channel::mpsc::unbounded();
|
||||
let visualization = AttachedVisualization { visualization, update_sender };
|
||||
info!(self.logger, "Inserting to the registry: {id}.");
|
||||
info!("Inserting to the registry: {id}.");
|
||||
self.visualizations.borrow_mut().insert(id, visualization);
|
||||
receiver
|
||||
}
|
||||
@ -154,7 +151,7 @@ impl ExecutionContext {
|
||||
/// This function shadows the asynchronous version from API trait.
|
||||
pub fn detach_visualization(&self, id: VisualizationId) -> FallibleResult<Visualization> {
|
||||
let err = || InvalidVisualizationId(id);
|
||||
info!(self.logger, "Removing from the registry: {id}.");
|
||||
info!("Removing from the registry: {id}.");
|
||||
let removed = self.visualizations.borrow_mut().remove(&id).ok_or_else(err)?;
|
||||
Ok(removed.visualization)
|
||||
}
|
||||
@ -243,11 +240,10 @@ impl model::execution_context::API for ExecutionContext {
|
||||
// TODO [mwu] Should we consider detaching the visualization if the view has dropped the
|
||||
// channel's receiver? Or we need to provide a way to re-establish the channel.
|
||||
let _ = visualization.update_sender.unbounded_send(data);
|
||||
debug!(self.logger, "Sending update data to the visualization {visualization_id}.");
|
||||
debug!("Sending update data to the visualization {visualization_id}.");
|
||||
Ok(())
|
||||
} else {
|
||||
error!(
|
||||
self.logger,
|
||||
"Failed to dispatch update to visualization {visualization_id}. \
|
||||
Failed to found such visualization."
|
||||
);
|
||||
@ -321,8 +317,7 @@ pub mod test {
|
||||
}
|
||||
|
||||
pub fn create(&self) -> ExecutionContext {
|
||||
let logger = Logger::new("Mocked Execution Context");
|
||||
let mut ec = ExecutionContext::new(logger, self.main_method_pointer());
|
||||
let mut ec = ExecutionContext::new(self.main_method_pointer());
|
||||
ec.component_groups = self.component_groups();
|
||||
ec
|
||||
}
|
||||
|
@ -68,16 +68,15 @@ impl ExecutionContext {
|
||||
language_server: Rc<language_server::Connection>,
|
||||
root_definition: language_server::MethodPointer,
|
||||
) -> impl Future<Output = FallibleResult<Self>> {
|
||||
let logger = Logger::new_sub(&parent, "ExecutionContext");
|
||||
async move {
|
||||
info!(logger, "Creating.");
|
||||
info!("Creating.");
|
||||
let id = language_server.client.create_execution_context().await?.context_id;
|
||||
let logger = Logger::new_sub(&parent, iformat! {"ExecutionContext {id}"});
|
||||
let model = model::execution_context::Plain::new(&logger, root_definition);
|
||||
info!(logger, "Created. Id: {id}.");
|
||||
let model = model::execution_context::Plain::new(root_definition);
|
||||
info!("Created. Id: {id}.");
|
||||
let this = Self { id, model, language_server, logger };
|
||||
this.push_root_frame().await?;
|
||||
info!(this.logger, "Pushed root frame.");
|
||||
info!("Pushed root frame.");
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
@ -104,7 +103,7 @@ impl ExecutionContext {
|
||||
"Failed to parse a component group returned by the Engine. The group will not \
|
||||
appear in the Favorites section of the Component Browser. Error: {err}"
|
||||
);
|
||||
error!(self.logger, "{msg}");
|
||||
error!("{msg}");
|
||||
};
|
||||
match self.language_server.get_component_groups(&self.id).await {
|
||||
Ok(ls_response) => {
|
||||
@ -114,14 +113,14 @@ impl ExecutionContext {
|
||||
.filter_map(|group| group.try_into().inspect_err(log_group_parsing_error).ok())
|
||||
.collect();
|
||||
*self.model.component_groups.borrow_mut() = Rc::new(groups);
|
||||
info!(self.logger, "Loaded component groups.");
|
||||
info!("Loaded component groups.");
|
||||
}
|
||||
Err(err) => {
|
||||
let msg = iformat!(
|
||||
"Failed to load component groups. No groups will appear in the Favorites \
|
||||
section of the Component Browser. Error: {err}"
|
||||
);
|
||||
error!(self.logger, "{msg}");
|
||||
error!("{msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +138,7 @@ impl ExecutionContext {
|
||||
let ast_id = vis.expression_id;
|
||||
let ls = self.language_server.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
info!(logger, "About to detach visualization by id: {vis_id}.");
|
||||
info!("About to detach visualization by id: {vis_id}.");
|
||||
ls.detach_visualisation(&exe_id, &vis_id, &ast_id).await?;
|
||||
if let Err(err) = self.model.detach_visualization(vis_id) {
|
||||
warning!(logger, "Failed to update model after detaching visualization: {err:?}.")
|
||||
@ -152,7 +151,7 @@ impl ExecutionContext {
|
||||
match notification {
|
||||
Notification::Completed =>
|
||||
if !self.model.is_ready.replace(true) {
|
||||
info!(self.logger, "Context {self.id} Became ready");
|
||||
info!("Context {} Became ready", self.id);
|
||||
let this = self.clone();
|
||||
executor::global::spawn(async move {
|
||||
this.load_component_groups().await;
|
||||
@ -287,7 +286,7 @@ impl model::execution_context::API for ExecutionContext {
|
||||
visualization_id: VisualizationId,
|
||||
data: VisualizationUpdateData,
|
||||
) -> FallibleResult {
|
||||
debug!(self.logger, "Dispatching visualization update through the context {self.id()}");
|
||||
debug!("Dispatching visualization update through the context {}", self.id());
|
||||
self.model.dispatch_visualization_update(visualization_id, data)
|
||||
}
|
||||
}
|
||||
@ -296,11 +295,10 @@ impl Drop for ExecutionContext {
|
||||
fn drop(&mut self) {
|
||||
let id = self.id;
|
||||
let ls = self.language_server.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
let result = ls.client.destroy_execution_context(&id).await;
|
||||
if result.is_err() {
|
||||
error!(logger, "Error when destroying Execution Context: {result:?}.");
|
||||
error!("Error when destroying Execution Context: {result:?}.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use ast::constants::LANGUAGE_FILE_EXTENSION;
|
||||
use ast::constants::SOURCE_DIRECTORY;
|
||||
use double_representation::definition::DefinitionInfo;
|
||||
use double_representation::identifier::ReferentName;
|
||||
use double_representation::module::ImportId;
|
||||
use double_representation::project;
|
||||
use engine_protocol::language_server::MethodPointer;
|
||||
use flo_stream::Subscriber;
|
||||
@ -26,7 +27,6 @@ pub mod plain;
|
||||
pub mod synchronized;
|
||||
|
||||
pub use double_representation::module::Id;
|
||||
use double_representation::module::ImportId;
|
||||
pub use double_representation::module::QualifiedName;
|
||||
pub use double_representation::tp::QualifiedName as TypeQualifiedName;
|
||||
|
||||
@ -75,7 +75,7 @@ pub enum ModulePathViolation {
|
||||
// ===============
|
||||
|
||||
/// A specialization of text change used in module's text changes across controllers.
|
||||
pub type TextChange = enso_text::Change<enso_text::unit::Bytes, String>;
|
||||
pub type TextChange = enso_text::Change<enso_text::index::Byte, String>;
|
||||
|
||||
|
||||
|
||||
@ -289,7 +289,7 @@ pub enum NotificationKind {
|
||||
/// The code change description.
|
||||
change: TextChange,
|
||||
/// Information about line:col position of replaced fragment.
|
||||
replaced_location: enso_text::Range<enso_text::Location>,
|
||||
replaced_location: enso_text::Range<enso_text::Location<enso_text::Byte>>,
|
||||
},
|
||||
/// The metadata (e.g. some node's position) has been changed.
|
||||
MetadataChanged,
|
||||
@ -469,7 +469,7 @@ impl Add for Position {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign for Position {
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.vector += rhs.vector
|
||||
}
|
||||
@ -739,9 +739,7 @@ pub mod test {
|
||||
repository: Rc<model::undo_redo::Repository>,
|
||||
) -> Module {
|
||||
let ast = parser.parse_module(self.code.clone(), self.id_map.clone()).unwrap();
|
||||
let logger = Logger::new("MockModule");
|
||||
let module =
|
||||
Plain::new(logger, self.path.clone(), ast, self.metadata.clone(), repository);
|
||||
let module = Plain::new(self.path.clone(), ast, self.metadata.clone(), repository);
|
||||
Rc::new(module)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ use parser::Parser;
|
||||
/// (text and graph).
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
logger: Logger,
|
||||
path: Path,
|
||||
content: RefCell<Content>,
|
||||
notifications: notification::Publisher<Notification>,
|
||||
@ -45,14 +44,12 @@ pub struct Module {
|
||||
impl Module {
|
||||
/// Create state with given content.
|
||||
pub fn new(
|
||||
parent: impl AnyLogger,
|
||||
path: Path,
|
||||
ast: ast::known::Module,
|
||||
metadata: Metadata,
|
||||
repository: Rc<model::undo_redo::Repository>,
|
||||
) -> Self {
|
||||
Module {
|
||||
logger: Logger::new_sub(parent, path.to_string()),
|
||||
content: RefCell::new(ParsedSourceFile { ast, metadata }),
|
||||
notifications: default(),
|
||||
path,
|
||||
@ -68,10 +65,10 @@ impl Module {
|
||||
#[profile(Debug)]
|
||||
fn set_content(&self, new_content: Content, kind: NotificationKind) -> FallibleResult {
|
||||
if new_content == *self.content.borrow() {
|
||||
debug!(self.logger, "Ignoring spurious update.");
|
||||
debug!("Ignoring spurious update.");
|
||||
return Ok(());
|
||||
}
|
||||
trace!(self.logger, "Updating module's content: {kind:?}. New content:\n{new_content}");
|
||||
trace!("Updating module's content: {kind:?}. New content:\n{new_content}");
|
||||
let transaction = self.repository.transaction("Setting module's content");
|
||||
transaction.fill_content(self.id(), self.content.borrow().clone());
|
||||
|
||||
@ -174,9 +171,9 @@ impl model::module::API for Module {
|
||||
parser: &Parser,
|
||||
new_id_map: ast::IdMap,
|
||||
) -> FallibleResult {
|
||||
let mut code: enso_text::Text = self.ast().repr().into();
|
||||
let replaced_start = code.location_of_byte_offset_snapped(change.range.start);
|
||||
let replaced_end = code.location_of_byte_offset_snapped(change.range.end);
|
||||
let mut code: enso_text::Rope = self.ast().repr().into();
|
||||
let replaced_start = code.offset_to_location_snapped(change.range.start);
|
||||
let replaced_end = code.offset_to_location_snapped(change.range.end);
|
||||
let replaced_location = enso_text::Range::new(replaced_start, replaced_end);
|
||||
code.apply_change(change.as_ref());
|
||||
let new_ast = parser.parse(code.into(), new_id_map)?.try_into()?;
|
||||
@ -284,14 +281,14 @@ mod test {
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
use crate::model::module::Position;
|
||||
|
||||
use enso_text::traits::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn applying_code_change() {
|
||||
let _test = TestWithLocalPoolExecutor::set_up();
|
||||
let module = model::module::test::plain_from_code("2 + 2");
|
||||
let change = TextChange {
|
||||
range: enso_text::Range::new(2.bytes(), 5.bytes()),
|
||||
range: enso_text::Range::new(2.byte(), 5.byte()),
|
||||
text: "- abc".to_string(),
|
||||
};
|
||||
module.apply_code_change(change, &Parser::new_or_panic(), default()).unwrap();
|
||||
@ -322,13 +319,13 @@ mod test {
|
||||
|
||||
// Code change
|
||||
let change = TextChange {
|
||||
range: enso_text::Range::new(0.bytes(), 1.bytes()),
|
||||
range: enso_text::Range::new(0.byte(), 1.byte()),
|
||||
text: "foo".to_string(),
|
||||
};
|
||||
module.apply_code_change(change.clone(), &Parser::new_or_panic(), default()).unwrap();
|
||||
let replaced_location = enso_text::Range {
|
||||
start: enso_text::Location { line: 0.line(), column: 0.column() },
|
||||
end: enso_text::Location { line: 0.line(), column: 1.column() },
|
||||
start: enso_text::Location { line: 0.into(), offset: 0.byte() },
|
||||
end: enso_text::Location { line: 0.into(), offset: 1.byte() },
|
||||
};
|
||||
expect_notification(NotificationKind::CodeChanged { change, replaced_location });
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! A Wrapper for module which synchronizes opening/closing and all changes with Language Server.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::index::*;
|
||||
|
||||
use crate::model::module::Content;
|
||||
use crate::model::module::ImportMetadata;
|
||||
@ -20,9 +20,9 @@ use double_representation::module::ImportId;
|
||||
use engine_protocol::language_server;
|
||||
use engine_protocol::language_server::TextEdit;
|
||||
use engine_protocol::types::Sha3_224;
|
||||
use enso_text::text;
|
||||
use enso_text::Location;
|
||||
use enso_text::Range;
|
||||
use enso_text::Text;
|
||||
use flo_stream::Subscriber;
|
||||
use parser::api::SourceFile;
|
||||
use parser::Parser;
|
||||
@ -38,13 +38,17 @@ use parser::Parser;
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct ContentSummary {
|
||||
digest: Sha3_224,
|
||||
end_of_file: Location,
|
||||
/// The Engine's Locations are in java Characters, which are equal to UTF16 Code Units.
|
||||
/// See https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html
|
||||
end_of_file: Location<enso_text::Utf16CodeUnit>,
|
||||
}
|
||||
|
||||
impl ContentSummary {
|
||||
fn new(text: &Text) -> Self {
|
||||
fn new(text: &text::Rope) -> Self {
|
||||
let parts = text.rope.iter_chunks(..).map(|s| s.as_bytes());
|
||||
Self { digest: Sha3_224::from_parts(parts), end_of_file: text.location_of_text_end() }
|
||||
let end_location_bytes = text.last_line_end_location();
|
||||
let end_of_file = text.utf16_code_unit_location_of_location(end_location_bytes);
|
||||
Self { digest: Sha3_224::from_parts(parts), end_of_file }
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,19 +59,20 @@ impl ContentSummary {
|
||||
struct ParsedContentSummary {
|
||||
#[shrinkwrap(main_field)]
|
||||
summary: ContentSummary,
|
||||
source: Text,
|
||||
code: Range<Location>,
|
||||
id_map: Range<Location>,
|
||||
metadata: Range<Location>,
|
||||
source: text::Rope,
|
||||
code: Range<Location<Byte>>,
|
||||
id_map: Range<Location<Byte>>,
|
||||
metadata: Range<Location<Byte>>,
|
||||
}
|
||||
|
||||
impl ParsedContentSummary {
|
||||
/// Get summary from `SourceFile`.
|
||||
fn from_source(source: &SourceFile) -> Self {
|
||||
let content = Text::from(&source.content);
|
||||
let code = source.code.map(|i| content.location_of_byte_offset_snapped(i));
|
||||
let id_map = source.id_map.map(|i| content.location_of_byte_offset_snapped(i));
|
||||
let metadata = source.metadata.map(|i| content.location_of_byte_offset_snapped(i));
|
||||
let content = text::Rope::from(&source.content);
|
||||
let to_location = |i: Byte| content.offset_to_location_snapped(i);
|
||||
let code = source.code.map(to_location);
|
||||
let id_map = source.id_map.map(to_location);
|
||||
let metadata = source.metadata.map(to_location);
|
||||
ParsedContentSummary {
|
||||
summary: ContentSummary::new(&content),
|
||||
source: content,
|
||||
@ -78,23 +83,23 @@ impl ParsedContentSummary {
|
||||
}
|
||||
|
||||
// Get fragment of string with code.
|
||||
pub fn code_slice(&self) -> Text {
|
||||
pub fn code_slice(&self) -> text::Rope {
|
||||
self.slice(&self.code)
|
||||
}
|
||||
|
||||
/// Get fragment of string with id map.
|
||||
pub fn id_map_slice(&self) -> Text {
|
||||
pub fn id_map_slice(&self) -> text::Rope {
|
||||
self.slice(&self.id_map)
|
||||
}
|
||||
|
||||
/// Get fragment of string with metadata.
|
||||
pub fn metadata_slice(&self) -> Text {
|
||||
pub fn metadata_slice(&self) -> text::Rope {
|
||||
self.slice(&self.metadata)
|
||||
}
|
||||
|
||||
fn slice(&self, range: &Range<Location>) -> Text {
|
||||
let start_ix = self.source.byte_offset_of_location_snapped(range.start);
|
||||
let end_ix = self.source.byte_offset_of_location_snapped(range.end);
|
||||
fn slice(&self, range: &Range<Location<Byte>>) -> text::Rope {
|
||||
let start_ix = self.source.location_offset_snapped(range.start);
|
||||
let end_ix = self.source.location_offset_snapped(range.end);
|
||||
self.source.sub(Range::new(start_ix, end_ix))
|
||||
}
|
||||
}
|
||||
@ -135,7 +140,6 @@ impl LanguageServerContent {
|
||||
pub struct Module {
|
||||
model: model::module::Plain,
|
||||
language_server: Rc<language_server::Connection>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
|
||||
@ -153,21 +157,20 @@ impl Module {
|
||||
parser: Parser,
|
||||
repository: Rc<model::undo_redo::Repository>,
|
||||
) -> FallibleResult<Rc<Self>> {
|
||||
let logger = Logger::new(iformat!("Module {path}"));
|
||||
let file_path = path.file_path().clone();
|
||||
info!(logger, "Opening module {file_path}");
|
||||
info!("Opening module {file_path}");
|
||||
let opened = language_server.client.open_text_file(&file_path).await?;
|
||||
let content: Text = (&opened.content).into();
|
||||
info!(logger, "Read content of the module {path}, digest is {opened.current_version:?}");
|
||||
let end_of_file = content.location_of_text_end();
|
||||
let content: text::Rope = (&opened.content).into();
|
||||
info!("Read content of the module {path}, digest is {:?}", opened.current_version);
|
||||
let end_of_file_byte = content.last_line_end_location();
|
||||
let end_of_file = content.utf16_code_unit_location_of_location(end_of_file_byte);
|
||||
// TODO[ao] We should not fail here when metadata are malformed, but discard them and set
|
||||
// default instead.
|
||||
let source = parser.parse_with_metadata(opened.content)?;
|
||||
let digest = opened.current_version;
|
||||
let summary = ContentSummary { digest, end_of_file };
|
||||
let model =
|
||||
model::module::Plain::new(&logger, path, source.ast, source.metadata, repository);
|
||||
let this = Rc::new(Module { model, language_server, logger });
|
||||
let model = model::module::Plain::new(path, source.ast, source.metadata, repository);
|
||||
let this = Rc::new(Module { model, language_server });
|
||||
let content = this.model.serialized_content()?;
|
||||
let first_invalidation = this.full_invalidation(&summary, content);
|
||||
executor::global::spawn(Self::runner(this.clone_ref(), summary, first_invalidation));
|
||||
@ -176,12 +179,11 @@ impl Module {
|
||||
|
||||
/// Create a module mock.
|
||||
pub fn mock(model: model::module::Plain) -> Rc<Self> {
|
||||
let logger = Logger::new(iformat!("Mocked Module {model.path()}"));
|
||||
let client = language_server::MockClient::default();
|
||||
client.expect.close_text_file(|_| Ok(()));
|
||||
// We don't expect any other call, because we don't execute `runner()`.
|
||||
let language_server = language_server::Connection::new_mock_rc(client);
|
||||
Rc::new(Module { model, language_server, logger })
|
||||
Rc::new(Module { model, language_server })
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +301,7 @@ impl Module {
|
||||
let this = weak.upgrade();
|
||||
match (notification, this) {
|
||||
(Some(notification), Some(this)) => {
|
||||
debug!(this.logger, "Processing a notification: {notification:?}");
|
||||
debug!("Processing a notification: {notification:?}");
|
||||
let result = this.handle_notification(&ls_content, notification).await;
|
||||
ls_content = this.new_ls_content_info(ls_content.summary().clone(), result)
|
||||
}
|
||||
@ -320,11 +322,11 @@ impl Module {
|
||||
) -> LanguageServerContent {
|
||||
match new_content {
|
||||
Ok(new_content) => {
|
||||
debug!(self.logger, "Updating the LS content digest to: {new_content.summary:?}");
|
||||
debug!("Updating the LS content digest to: {:?}", new_content.summary);
|
||||
LanguageServerContent::Synchronized(new_content)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.logger, "Error during sending text change to Language Server: {err}");
|
||||
error!("Error during sending text change to Language Server: {err}");
|
||||
LanguageServerContent::Desynchronized(old_content)
|
||||
}
|
||||
}
|
||||
@ -339,7 +341,9 @@ impl Module {
|
||||
) -> FallibleResult<ParsedContentSummary> {
|
||||
let Notification { new_file, kind, profiler } = notification;
|
||||
let _profiler = profiler::start_debug!(profiler, "handle_notification");
|
||||
debug!(self.logger, "Handling notification: {content:?}.");
|
||||
debug!("Handling notification: {content:?}.");
|
||||
let code = enso_text::Rope::from(self.model.serialized_content()?.content);
|
||||
let to_engine_location = |l: Location<Byte>| code.utf16_code_unit_location_of_location(l);
|
||||
match content {
|
||||
LanguageServerContent::Desynchronized(summary) =>
|
||||
profiler::await_!(self.full_invalidation(summary, new_file), _profiler),
|
||||
@ -347,10 +351,12 @@ impl Module {
|
||||
NotificationKind::Invalidate =>
|
||||
profiler::await_!(self.partial_invalidation(summary, new_file), _profiler),
|
||||
NotificationKind::CodeChanged { change, replaced_location } => {
|
||||
let code_change =
|
||||
TextEdit { range: replaced_location.into(), text: change.text };
|
||||
let code_change = TextEdit {
|
||||
range: replaced_location.map(to_engine_location).into(),
|
||||
text: change.text,
|
||||
};
|
||||
let id_map_change = TextEdit {
|
||||
range: summary.id_map.into(),
|
||||
range: summary.id_map.map(to_engine_location).into(),
|
||||
text: new_file.id_map_slice().to_string(),
|
||||
};
|
||||
//id_map goes first, because code change may alter its position.
|
||||
@ -360,7 +366,7 @@ impl Module {
|
||||
}
|
||||
NotificationKind::MetadataChanged => {
|
||||
let edits = vec![TextEdit {
|
||||
range: summary.metadata.into(),
|
||||
range: summary.metadata.map(to_engine_location).into(),
|
||||
text: new_file.metadata_slice().to_string(),
|
||||
}];
|
||||
let notify_ls = self.notify_language_server(&summary.summary, &new_file, edits);
|
||||
@ -378,20 +384,24 @@ impl Module {
|
||||
ls_content: &ContentSummary,
|
||||
new_file: SourceFile,
|
||||
) -> impl Future<Output = FallibleResult<ParsedContentSummary>> + 'static {
|
||||
debug!(self.logger, "Handling full invalidation: {ls_content:?}.");
|
||||
debug!("Handling full invalidation: {ls_content:?}.");
|
||||
let range = Range::new(Location::default(), ls_content.end_of_file);
|
||||
let edits = vec![TextEdit { range: range.into(), text: new_file.content.clone() }];
|
||||
self.notify_language_server(ls_content, &new_file, edits)
|
||||
}
|
||||
|
||||
fn edit_for_snipped(start: &Location, source: Text, target: Text) -> Option<TextEdit> {
|
||||
fn edit_for_snipped(
|
||||
start: &Location<Byte>,
|
||||
source: text::Rope,
|
||||
target: text::Rope,
|
||||
) -> Option<TextEdit> {
|
||||
// This is an implicit assumption that always seems to be true. Otherwise finding the
|
||||
// correct location for the final edit would be more complex.
|
||||
debug_assert_eq!(start.column, 0.column());
|
||||
debug_assert_eq!(start.offset, 0.byte());
|
||||
|
||||
let edit = TextEdit::from_prefix_postfix_differences(&source, &target);
|
||||
(edit.range.start != edit.range.end || !edit.text.is_empty())
|
||||
.as_some_from(|| edit.move_by_lines(start.line.as_usize()))
|
||||
.as_some_from(|| edit.move_by_lines(start.line.value))
|
||||
}
|
||||
|
||||
fn edit_for_code(ls_content: &ParsedContentSummary, new_file: &SourceFile) -> Option<TextEdit> {
|
||||
@ -435,7 +445,7 @@ impl Module {
|
||||
ls_content: &ParsedContentSummary,
|
||||
new_file: SourceFile,
|
||||
) -> impl Future<Output = FallibleResult<ParsedContentSummary>> + 'static {
|
||||
debug!(self.logger, "Handling partial invalidation: {ls_content:?}.");
|
||||
debug!("Handling partial invalidation: {ls_content:?}.");
|
||||
let edits = vec![
|
||||
//id_map and metadata go first, because code change may alter their position.
|
||||
Self::edit_for_idmap(ls_content, &new_file),
|
||||
@ -464,7 +474,7 @@ impl Module {
|
||||
old_version: ls_content.digest.clone(),
|
||||
new_version: Sha3_224::new(new_file.content.as_bytes()),
|
||||
};
|
||||
debug!(self.logger, "Notifying LS with edit: {edit:#?}.");
|
||||
debug!("Notifying LS with edit: {edit:#?}.");
|
||||
let ls_future_reply = self.language_server.client.apply_text_file_edit(&edit);
|
||||
async move {
|
||||
ls_future_reply.await?;
|
||||
@ -477,11 +487,10 @@ impl Drop for Module {
|
||||
fn drop(&mut self) {
|
||||
let file_path = self.path().file_path().clone();
|
||||
let language_server = self.language_server.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
let result = language_server.client.close_text_file(&file_path).await;
|
||||
if let Err(err) = result {
|
||||
error!(logger, "Error when closing module file {file_path}: {err}");
|
||||
error!("Error when closing module file {file_path}: {err}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -517,8 +526,8 @@ pub mod test {
|
||||
use engine_protocol::language_server::MockClient;
|
||||
use engine_protocol::language_server::Position;
|
||||
use engine_protocol::language_server::TextRange;
|
||||
use enso_text::text;
|
||||
use enso_text::Change;
|
||||
use enso_text::Text;
|
||||
use json_rpc::error::RpcError;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
@ -528,22 +537,19 @@ pub mod test {
|
||||
// Ensures that subsequent LS text operations form a consistent series of versions.
|
||||
#[derive(Clone, Debug)]
|
||||
struct LsClientSetup {
|
||||
logger: Logger,
|
||||
path: Path,
|
||||
current_ls_content: Rc<CloneCell<Text>>,
|
||||
current_ls_content: Rc<CloneCell<text::Rope>>,
|
||||
current_ls_version: Rc<CloneCell<Sha3_224>>,
|
||||
}
|
||||
|
||||
impl LsClientSetup {
|
||||
fn new(parent: impl AnyLogger, path: Path, initial_content: impl Into<Text>) -> Self {
|
||||
fn new(path: Path, initial_content: impl Into<text::Rope>) -> Self {
|
||||
let current_ls_content = initial_content.into();
|
||||
let current_ls_version =
|
||||
Sha3_224::from_parts(current_ls_content.iter_chunks(..).map(|ch| ch.as_bytes()));
|
||||
let logger = Logger::new_sub(parent, "LsClientSetup");
|
||||
debug!(logger, "Initial content:\n===\n{current_ls_content}\n===");
|
||||
debug!("Initial content:\n===\n{current_ls_content}\n===");
|
||||
Self {
|
||||
path,
|
||||
logger,
|
||||
current_ls_content: Rc::new(CloneCell::new(current_ls_content)),
|
||||
current_ls_version: Rc::new(CloneCell::new(current_ls_version)),
|
||||
}
|
||||
@ -551,7 +557,7 @@ pub mod test {
|
||||
|
||||
/// Create a setup initialized with the data from the given mock description.
|
||||
fn new_for_mock_data(data: &crate::test::mock::Unified) -> Self {
|
||||
Self::new(&data.logger, data.module_path.clone(), data.get_code())
|
||||
Self::new(data.module_path.clone(), data.get_code())
|
||||
}
|
||||
|
||||
/// Set up general text edit expectation in the mock client.
|
||||
@ -572,18 +578,18 @@ pub mod test {
|
||||
let actual_old = this.current_ls_version.get();
|
||||
let actual_new =
|
||||
Sha3_224::from_parts(new_content.iter_chunks(..).map(|s| s.as_bytes()));
|
||||
debug!(this.logger, "Actual digest: {actual_old} => {actual_new}");
|
||||
debug!(this.logger, "Declared digest: {edits.old_version} => {edits.new_version}");
|
||||
debug!(this.logger, "New content:\n===\n{new_content}\n===");
|
||||
debug!("Actual digest: {actual_old} => {actual_new}");
|
||||
debug!("Declared digest: {} => {}", edits.old_version, edits.new_version);
|
||||
debug!("New content:\n===\n{new_content}\n===");
|
||||
assert_eq!(&edits.path, this.path.file_path());
|
||||
assert_eq!(edits.old_version, actual_old);
|
||||
assert_eq!(edits.new_version, actual_new);
|
||||
if result.is_ok() {
|
||||
this.current_ls_content.set(new_content);
|
||||
this.current_ls_version.set(actual_new);
|
||||
debug!(this.logger, "Accepted!");
|
||||
debug!("Accepted!");
|
||||
} else {
|
||||
debug!(this.logger, "Rejected!");
|
||||
debug!("Rejected!");
|
||||
}
|
||||
result
|
||||
});
|
||||
@ -603,8 +609,10 @@ pub mod test {
|
||||
// TODO [mwu]
|
||||
// Currently this assumes that the whole idmap is replaced at each edit.
|
||||
// This code should be adjusted, if partial metadata updates are implemented.
|
||||
let idmap_range =
|
||||
file_so_far.id_map.map(|x| code_so_far.location_of_byte_offset_snapped(x));
|
||||
let idmap_range = file_so_far.id_map.map(|x| {
|
||||
let location_bytes = code_so_far.offset_to_location_snapped(x);
|
||||
code_so_far.utf16_code_unit_location_of_location(location_bytes)
|
||||
});
|
||||
let idmap_range = TextRange::from(idmap_range);
|
||||
assert_eq!(edit_idmap.range, idmap_range);
|
||||
assert!(SourceFile::looks_like_idmap(&edit_idmap.text));
|
||||
@ -640,21 +648,24 @@ pub mod test {
|
||||
|
||||
fn whole_document_range(&self) -> TextRange {
|
||||
let code_so_far = self.current_ls_content.get();
|
||||
let end_of_file = code_so_far.location_of_text_end();
|
||||
let end_of_file_bytes = code_so_far.last_line_end_location();
|
||||
let end_of_file = code_so_far.utf16_code_unit_location_of_location(end_of_file_bytes);
|
||||
TextRange { start: Position { line: 0, character: 0 }, end: end_of_file.into() }
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_edit(code: impl Into<Text>, edit: &TextEdit) -> Text {
|
||||
fn apply_edit(code: impl Into<text::Rope>, edit: &TextEdit) -> text::Rope {
|
||||
let mut code = code.into();
|
||||
let start_loc = code.byte_offset_of_location_snapped(edit.range.start.into());
|
||||
let end_loc = code.byte_offset_of_location_snapped(edit.range.end.into());
|
||||
let change = Change { range: Range::new(start_loc, end_loc), text: edit.text.clone() };
|
||||
let start = code.location_of_utf16_code_unit_location_snapped(edit.range.start.into());
|
||||
let start_byte = code.location_offset_snapped(start);
|
||||
let end = code.location_of_utf16_code_unit_location_snapped(edit.range.end.into());
|
||||
let end_byte = code.location_offset_snapped(end);
|
||||
let change = Change { range: Range::new(start_byte, end_byte), text: edit.text.clone() };
|
||||
code.apply_change(change);
|
||||
code
|
||||
}
|
||||
|
||||
fn apply_edits(code: impl Into<Text>, file_edit: &FileEdit) -> Text {
|
||||
fn apply_edits(code: impl Into<text::Rope>, file_edit: &FileEdit) -> text::Rope {
|
||||
let initial = code.into();
|
||||
file_edit.edits.iter().fold(initial, |content, edit| apply_edit(&content, edit))
|
||||
}
|
||||
@ -677,7 +688,7 @@ pub mod test {
|
||||
// Parser which is time-consuming to construct.
|
||||
let test = |runner: &mut Runner| {
|
||||
let module_path = data.module_path.clone();
|
||||
let edit_handler = Rc::new(LsClientSetup::new(&data.logger, module_path, initial_code));
|
||||
let edit_handler = Rc::new(LsClientSetup::new(module_path, initial_code));
|
||||
let mut fixture = data.fixture_customize(|data, client, _| {
|
||||
data.expect_opening_module(client);
|
||||
data.expect_closing_module(client);
|
||||
@ -752,10 +763,10 @@ pub mod test {
|
||||
|
||||
#[test]
|
||||
fn handle_insertion_edits_bug180558676() {
|
||||
let source = Text::from("from Standard.Base import all\n\nmain =\n operator1 = 0.up_to 100 . to_vector . map .noise\n operator1.sort\n");
|
||||
let target = Text::from("from Standard.Base import all\nimport Standard.Visualization\n\nmain =\n operator1 = 0.up_to 100 . to_vector . map .noise\n operator1.sort\n");
|
||||
let source = text::Rope::from("from Standard.Base import all\n\nmain =\n operator1 = 0.up_to 100 . to_vector . map .noise\n operator1.sort\n");
|
||||
let target = text::Rope::from("from Standard.Base import all\nimport Standard.Visualization\n\nmain =\n operator1 = 0.up_to 100 . to_vector . map .noise\n operator1.sort\n");
|
||||
let edit = Module::edit_for_snipped(
|
||||
&Location { line: 0.into(), column: 0.into() },
|
||||
&Location { line: 0.into(), offset: 0.into() },
|
||||
source,
|
||||
target,
|
||||
);
|
||||
|
@ -266,7 +266,7 @@ impl Project {
|
||||
) -> FallibleResult<Self> {
|
||||
let wrap = UnsupportedEngineVersion::error_wrapper(&properties);
|
||||
let logger = Logger::new_sub(parent, "Project Controller");
|
||||
info!(logger, "Creating a model of project {properties.name}");
|
||||
info!("Creating a model of project {}", properties.name);
|
||||
let binary_protocol_events = language_server_bin.event_stream();
|
||||
let json_rpc_events = language_server_rpc.events();
|
||||
let embedded_visualizations = default();
|
||||
@ -375,36 +375,30 @@ impl Project {
|
||||
pub fn binary_event_handler(
|
||||
&self,
|
||||
) -> impl Fn(engine_protocol::binary::Event) -> futures::future::Ready<()> {
|
||||
let logger = self.logger.clone_ref();
|
||||
let publisher = self.notifications.clone_ref();
|
||||
let weak_execution_contexts = Rc::downgrade(&self.execution_contexts);
|
||||
move |event| {
|
||||
debug!(logger, "Received an event from the binary protocol: {event:?}");
|
||||
debug!("Received an event from the binary protocol: {event:?}");
|
||||
use engine_protocol::binary::client::Event;
|
||||
use engine_protocol::binary::Notification;
|
||||
match event {
|
||||
Event::Notification(Notification::VisualizationUpdate { context, data }) => {
|
||||
debug!(
|
||||
logger,
|
||||
"Visualization binary data: {String::from_utf8_lossy(data.as_ref())}"
|
||||
);
|
||||
debug!("Visualization binary data: {}", String::from_utf8_lossy(data.as_ref()));
|
||||
let data = VisualizationUpdateData::new(data);
|
||||
if let Some(execution_contexts) = weak_execution_contexts.upgrade() {
|
||||
let result =
|
||||
execution_contexts.dispatch_visualization_update(context, data);
|
||||
if let Err(error) = result {
|
||||
error!(logger, "Failed to handle the visualization update: {error}.");
|
||||
error!("Failed to handle the visualization update: {error}.");
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
logger,
|
||||
"Received a visualization update despite project being \
|
||||
already dropped."
|
||||
"Received a visualization update despite project being already dropped."
|
||||
);
|
||||
}
|
||||
}
|
||||
Event::Closed => {
|
||||
error!(logger, "Lost binary connection with the Language Server!");
|
||||
error!("Lost binary connection with the Language Server!");
|
||||
let which = model::project::BackendConnection::LanguageServerBinary;
|
||||
let notification = model::project::Notification::ConnectionLost(which);
|
||||
publisher.notify(notification);
|
||||
@ -413,7 +407,7 @@ impl Project {
|
||||
// https://github.com/enso-org/ide/issues/145
|
||||
}
|
||||
Event::Error(error) => {
|
||||
error!(logger, "Error emitted by the binary data connection: {error}.");
|
||||
error!("Error emitted by the binary data connection: {error}.");
|
||||
}
|
||||
}
|
||||
futures::future::ready(())
|
||||
@ -427,16 +421,14 @@ impl Project {
|
||||
pub fn execution_update_handler(
|
||||
&self,
|
||||
) -> impl Fn(execution_context::Id, ExecutionUpdate) + Clone {
|
||||
let logger = self.logger.clone_ref();
|
||||
let registry = Rc::downgrade(&self.execution_contexts);
|
||||
move |id, update| {
|
||||
if let Some(registry) = registry.upgrade() {
|
||||
if let Err(error) = registry.handle_update(id, update) {
|
||||
error!(logger, "Failed to handle the execution context update: {error}");
|
||||
error!("Failed to handle the execution context update: {error}");
|
||||
}
|
||||
} else {
|
||||
warning!(
|
||||
logger,
|
||||
warn!(
|
||||
"Received an execution context notification despite execution \
|
||||
context being already dropped."
|
||||
);
|
||||
@ -456,13 +448,12 @@ impl Project {
|
||||
// generalize them, as the underlying RPC handlers and their types are separate.
|
||||
// This generalization should be reconsidered once the old JSON-RPC handler is phased out.
|
||||
// See: https://github.com/enso-org/ide/issues/587
|
||||
let logger = self.logger.clone_ref();
|
||||
let publisher = self.notifications.clone_ref();
|
||||
let weak_suggestion_db = Rc::downgrade(&self.suggestion_db);
|
||||
let weak_content_roots = Rc::downgrade(&self.content_roots);
|
||||
let execution_update_handler = self.execution_update_handler();
|
||||
move |event| {
|
||||
debug!(logger, "Received an event from the json-rpc protocol: {event:?}");
|
||||
debug!("Received an event from the json-rpc protocol: {event:?}");
|
||||
use engine_protocol::language_server::Event;
|
||||
use engine_protocol::language_server::Notification;
|
||||
|
||||
@ -489,9 +480,8 @@ impl Project {
|
||||
}
|
||||
Event::Notification(Notification::ExecutionFailed(update)) => {
|
||||
error!(
|
||||
logger,
|
||||
"Execution failed in context {update.context_id}. Error: \
|
||||
{update.message}."
|
||||
"Execution failed in context {}. Error: {}.",
|
||||
update.context_id, update.message
|
||||
);
|
||||
}
|
||||
Event::Notification(Notification::SuggestionDatabaseUpdates(update)) =>
|
||||
@ -510,14 +500,16 @@ impl Project {
|
||||
}
|
||||
Event::Notification(Notification::VisualisationEvaluationFailed(update)) => {
|
||||
error!(
|
||||
logger,
|
||||
"Visualisation evaluation failed in context {update.context_id} \
|
||||
for visualisation {update.visualisation_id} of expression \
|
||||
{update.expression_id}. Error: {update.message}"
|
||||
"Visualisation evaluation failed in context {} for visualisation {} of \
|
||||
expression {}. Error: {}",
|
||||
update.context_id,
|
||||
update.visualisation_id,
|
||||
update.expression_id,
|
||||
update.message
|
||||
);
|
||||
}
|
||||
Event::Closed => {
|
||||
error!(logger, "Lost JSON-RPC connection with the Language Server!");
|
||||
error!("Lost JSON-RPC connection with the Language Server!");
|
||||
let which = model::project::BackendConnection::LanguageServerJson;
|
||||
let notification = model::project::Notification::ConnectionLost(which);
|
||||
publisher.notify(notification);
|
||||
@ -526,7 +518,7 @@ impl Project {
|
||||
// see https://github.com/enso-org/ide/issues/145
|
||||
}
|
||||
Event::Error(error) => {
|
||||
error!(logger, "Error emitted by the JSON-RPC data connection: {error}.");
|
||||
error!("Error emitted by the JSON-RPC data connection: {error}.");
|
||||
}
|
||||
}
|
||||
futures::future::ready(())
|
||||
@ -603,7 +595,7 @@ impl model::project::API for Project {
|
||||
#[profile(Detail)]
|
||||
fn module(&self, path: module::Path) -> BoxFuture<FallibleResult<model::Module>> {
|
||||
async move {
|
||||
info!(self.logger, "Obtaining module for {path}");
|
||||
info!("Obtaining module for {path}");
|
||||
let model_loader = self.load_module(path.clone());
|
||||
let model: model::Module = self.module_registry.get_or_load(path, model_loader).await?;
|
||||
Ok(model)
|
||||
|
@ -173,9 +173,7 @@ mod test {
|
||||
let ast = ast::Ast::one_line_module(line).try_into().unwrap();
|
||||
let path = ModulePath::from_mock_module_name("Test");
|
||||
let urm = default();
|
||||
let logger = Logger::new("Test");
|
||||
let state =
|
||||
Rc::new(model::module::Plain::new(logger, path.clone(), ast, default(), urm));
|
||||
let state = Rc::new(model::module::Plain::new(path.clone(), ast, default(), urm));
|
||||
let registry = Registry::default();
|
||||
let expected = state.clone_ref();
|
||||
|
||||
@ -196,9 +194,7 @@ mod test {
|
||||
let path1 = ModulePath::from_mock_module_name("Test");
|
||||
let path2 = path1.clone();
|
||||
let urm = default();
|
||||
let logger = Logger::new("Test");
|
||||
let state1 =
|
||||
Rc::new(model::module::Plain::new(logger, path1.clone_ref(), ast, default(), urm));
|
||||
let state1 = Rc::new(model::module::Plain::new(path1.clone_ref(), ast, default(), urm));
|
||||
let state2 = state1.clone_ref();
|
||||
let registry1 = Rc::new(Registry::default());
|
||||
let registry2 = registry1.clone_ref();
|
||||
|
@ -4,13 +4,13 @@ use crate::prelude::*;
|
||||
|
||||
use crate::model::module::MethodId;
|
||||
use crate::model::suggestion_database::entry::Kind;
|
||||
use crate::model::suggestion_database::entry::ModuleSpan;
|
||||
use crate::notification;
|
||||
|
||||
use ast::opr::predefined::ACCESS;
|
||||
use double_representation::module::QualifiedName;
|
||||
use engine_protocol::language_server;
|
||||
use engine_protocol::language_server::SuggestionId;
|
||||
use enso_text::Location;
|
||||
use ensogl::data::HashMapTree;
|
||||
use flo_stream::Subscriber;
|
||||
use language_server::types::SuggestionDatabaseUpdatesEvent;
|
||||
@ -58,7 +58,7 @@ impl QualifiedNameToIdMap {
|
||||
let value = Some(id);
|
||||
let old_value = self.replace_value_and_traverse_back_pruning_empty_subtrees(path, value);
|
||||
if old_value.is_some() {
|
||||
event!(WARN, "An existing suggestion entry id at {path} was overwritten with {id}.");
|
||||
warn!("An existing suggestion entry id at {path} was overwritten with {id}.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ impl QualifiedNameToIdMap {
|
||||
let msg = format!(
|
||||
"Could not remove a suggestion entry id at {path} because it does not exist."
|
||||
);
|
||||
event!(WARN, "{msg}");
|
||||
warn!("{msg}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +137,6 @@ pub enum Notification {
|
||||
/// argument names and types.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SuggestionDatabase {
|
||||
logger: Logger,
|
||||
entries: RefCell<HashMap<entry::Id, Rc<Entry>>>,
|
||||
qualified_name_to_id_map: RefCell<QualifiedNameToIdMap>,
|
||||
examples: RefCell<Vec<Rc<Example>>>,
|
||||
@ -147,22 +146,20 @@ pub struct SuggestionDatabase {
|
||||
|
||||
impl SuggestionDatabase {
|
||||
/// Create a database with no entries.
|
||||
pub fn new_empty(logger: impl AnyLogger) -> Self {
|
||||
let logger = Logger::new_sub(logger, "SuggestionDatabase");
|
||||
pub fn new_empty() -> Self {
|
||||
let entries = default();
|
||||
let qualified_name_to_id_map = default();
|
||||
let examples = default();
|
||||
let version = default();
|
||||
let notifications = default();
|
||||
Self { logger, entries, qualified_name_to_id_map, examples, version, notifications }
|
||||
Self { entries, qualified_name_to_id_map, examples, version, notifications }
|
||||
}
|
||||
|
||||
/// Create a database filled with entries provided by the given iterator.
|
||||
pub fn new_from_entries<'a>(
|
||||
logger: impl AnyLogger,
|
||||
entries: impl IntoIterator<Item = (&'a SuggestionId, &'a Entry)>,
|
||||
) -> Self {
|
||||
let ret = Self::new_empty(logger);
|
||||
let ret = Self::new_empty();
|
||||
let entries = entries.into_iter().map(|(id, entry)| (*id, Rc::new(entry.clone())));
|
||||
ret.entries.borrow_mut().extend(entries);
|
||||
ret
|
||||
@ -178,7 +175,6 @@ impl SuggestionDatabase {
|
||||
|
||||
/// Create a new database model from response received from the Language Server.
|
||||
fn from_ls_response(response: language_server::response::GetSuggestionDatabase) -> Self {
|
||||
let logger = Logger::new("SuggestionDatabase");
|
||||
let mut entries = HashMap::new();
|
||||
let mut qualified_name_to_id_map = QualifiedNameToIdMap::default();
|
||||
for ls_entry in response.entries {
|
||||
@ -189,7 +185,7 @@ impl SuggestionDatabase {
|
||||
entries.insert(id, Rc::new(entry));
|
||||
}
|
||||
Err(err) => {
|
||||
error!(logger, "Discarded invalid entry {id}: {err}");
|
||||
error!("Discarded invalid entry {id}: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -197,12 +193,11 @@ impl SuggestionDatabase {
|
||||
// available modules documentation. (https://github.com/enso-org/ide/issues/1011)
|
||||
let examples = example::EXAMPLES.iter().cloned().map(Rc::new).collect_vec();
|
||||
Self {
|
||||
logger,
|
||||
entries: RefCell::new(entries),
|
||||
entries: RefCell::new(entries),
|
||||
qualified_name_to_id_map: RefCell::new(qualified_name_to_id_map),
|
||||
examples: RefCell::new(examples),
|
||||
version: Cell::new(response.current_version),
|
||||
notifications: default(),
|
||||
examples: RefCell::new(examples),
|
||||
version: Cell::new(response.current_version),
|
||||
notifications: default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +223,7 @@ impl SuggestionDatabase {
|
||||
entries.insert(id, Rc::new(entry));
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.logger, "Discarding update for {id}: {err}")
|
||||
error!("Discarding update for {id}: {err}")
|
||||
}
|
||||
},
|
||||
entry::Update::Remove { id } => {
|
||||
@ -241,7 +236,7 @@ impl SuggestionDatabase {
|
||||
"Received a suggestion database 'Remove' event for a nonexistent \
|
||||
entry id: {id}."
|
||||
);
|
||||
error!(self.logger, "{msg}");
|
||||
error!("{msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,13 +247,10 @@ impl SuggestionDatabase {
|
||||
let errors = entry.apply_modifications(*modification);
|
||||
qn_to_id_map.set_and_warn_if_existed(&entry.qualified_name(), id);
|
||||
for error in errors {
|
||||
error!(
|
||||
self.logger,
|
||||
"Error when applying update for entry {id}: {error:?}"
|
||||
);
|
||||
error!("Error when applying update for entry {id}: {error:?}");
|
||||
}
|
||||
} else {
|
||||
error!(self.logger, "Received Modify event for nonexistent id: {id}");
|
||||
error!("Received Modify event for nonexistent id: {id}");
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -300,39 +292,25 @@ impl SuggestionDatabase {
|
||||
}
|
||||
|
||||
/// Search the database for entries with given name and visible at given location in module.
|
||||
pub fn lookup_by_name_and_location(
|
||||
&self,
|
||||
name: impl Str,
|
||||
module: &QualifiedName,
|
||||
location: Location,
|
||||
) -> Vec<Rc<Entry>> {
|
||||
pub fn lookup_at(&self, name: impl Str, location: &ModuleSpan) -> Vec<Rc<Entry>> {
|
||||
self.entries
|
||||
.borrow()
|
||||
.values()
|
||||
.filter(|entry| {
|
||||
entry.matches_name(name.as_ref()) && entry.is_visible_at(module, location)
|
||||
})
|
||||
.filter(|entry| entry.matches_name(name.as_ref()) && entry.is_visible_at(location))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Search the database for Local or Function entries with given name and visible at given
|
||||
/// location in module.
|
||||
pub fn lookup_locals_by_name_and_location(
|
||||
&self,
|
||||
name: impl Str,
|
||||
module: &QualifiedName,
|
||||
location: Location,
|
||||
) -> Vec<Rc<Entry>> {
|
||||
pub fn lookup_locals_at(&self, name: impl Str, location: &ModuleSpan) -> Vec<Rc<Entry>> {
|
||||
self.entries
|
||||
.borrow()
|
||||
.values()
|
||||
.cloned()
|
||||
.filter(|entry| {
|
||||
let is_local = entry.kind == Kind::Function || entry.kind == Kind::Local;
|
||||
is_local
|
||||
&& entry.matches_name(name.as_ref())
|
||||
&& entry.is_visible_at(module, location)
|
||||
is_local && entry.matches_name(name.as_ref()) && entry.is_visible_at(location)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -453,7 +431,8 @@ mod test {
|
||||
use engine_protocol::language_server::SuggestionEntryScope;
|
||||
use engine_protocol::language_server::SuggestionsDatabaseEntry;
|
||||
use engine_protocol::language_server::SuggestionsDatabaseModification;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::unit::*;
|
||||
use enso_text::Location;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
@ -698,9 +677,9 @@ mod test {
|
||||
assert_eq!(db.lookup(3).unwrap().arguments[2].repr_type, "TestAtom");
|
||||
assert!(db.lookup(3).unwrap().arguments[2].is_suspended);
|
||||
assert_eq!(db.lookup(3).unwrap().arguments[2].default_value, None);
|
||||
let range = Location { line: 1.line(), column: 5.column() }..=Location {
|
||||
line: 3.line(),
|
||||
column: 0.column(),
|
||||
let range = Location { line: 1.into(), offset: Utf16CodeUnit::from(5) }..=Location {
|
||||
line: 3.into(),
|
||||
offset: Utf16CodeUnit::from(0),
|
||||
};
|
||||
assert_eq!(db.lookup(3).unwrap().scope, Scope::InModule { range });
|
||||
assert_eq!(db.version.get(), 6);
|
||||
@ -866,7 +845,7 @@ mod test {
|
||||
// Check that the suggestion database doesn't panic when quering invalid qualified names.
|
||||
#[test]
|
||||
fn lookup_by_fully_qualified_name_with_invalid_names() {
|
||||
let db = SuggestionDatabase::new_empty(Logger::new("SuggestionDatabase"));
|
||||
let db = SuggestionDatabase::new_empty();
|
||||
let _ = db.lookup_by_qualified_name_str("");
|
||||
let _ = db.lookup_by_qualified_name_str(".");
|
||||
let _ = db.lookup_by_qualified_name_str("..");
|
||||
|
@ -138,6 +138,23 @@ impl<'a> IntoIterator for &'a QualifiedName {
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === ModuleSpan ===
|
||||
// ==================
|
||||
|
||||
/// A span in a module identified by qualified name.
|
||||
///
|
||||
/// Span uses UTF-16 code units as units of measurement, so it is compatible with the format used
|
||||
/// internally by the suggestion database entries.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ModuleSpan {
|
||||
pub module: module::QualifiedName,
|
||||
pub span: Location<enso_text::Utf16CodeUnit>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === IconName ===
|
||||
// ================
|
||||
@ -197,8 +214,12 @@ pub enum Scope {
|
||||
Everywhere,
|
||||
/// Local symbol that is visible only in a particular section of the module where it has been
|
||||
/// defined.
|
||||
///
|
||||
/// We are using UTF-16 codepoints because this is what Language Server speaks.
|
||||
/// To convert to bytes (or other system) one would need to know the whole module code which
|
||||
/// is not available to the suggestions database.
|
||||
#[allow(missing_docs)]
|
||||
InModule { range: RangeInclusive<Location> },
|
||||
InModule { range: RangeInclusive<Location<enso_text::Utf16CodeUnit>> },
|
||||
}
|
||||
|
||||
/// Represents code snippet and the imports needed for it to work.
|
||||
@ -325,10 +346,11 @@ impl Entry {
|
||||
}
|
||||
|
||||
/// Checks if entry is visible at given location in a specific module.
|
||||
pub fn is_visible_at(&self, module: &module::QualifiedName, location: Location) -> bool {
|
||||
pub fn is_visible_at(&self, location: &ModuleSpan) -> bool {
|
||||
let ModuleSpan { module, span } = location;
|
||||
match &self.scope {
|
||||
Scope::Everywhere => true,
|
||||
Scope::InModule { range } => self.module == *module && range.contains(&location),
|
||||
Scope::InModule { range } => self.module == *module && range.contains(span),
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,7 +383,7 @@ impl Entry {
|
||||
entry {self:?}. Every entry with the 'Method' kind should have a self \
|
||||
type set, but this entry is missing the self type."
|
||||
);
|
||||
event!(ERROR, "{msg}");
|
||||
error!("{msg}");
|
||||
default()
|
||||
}
|
||||
},
|
||||
@ -704,7 +726,7 @@ where I: IntoIterator<Item = &'a language_server::types::DocSection> {
|
||||
documentation of a component is not a valid, losslessly-convertible snake_case \
|
||||
identifier. The component may be displayed with a different icon than expected."
|
||||
);
|
||||
event!(WARN, "{msg}");
|
||||
warn!("{msg}");
|
||||
}
|
||||
Some(icon_name)
|
||||
}
|
||||
|
@ -107,12 +107,11 @@ impl Transaction {
|
||||
pub fn fill_content(&self, id: model::module::Id, content: model::module::Content) {
|
||||
with(self.frame.borrow_mut(), |mut data| {
|
||||
debug!(
|
||||
self.logger,
|
||||
"Filling transaction '{data.name}' with snapshot of module '{id}':\
|
||||
\n{content}"
|
||||
"Filling transaction '{}' with snapshot of module '{id}':\n{content}",
|
||||
data.name
|
||||
);
|
||||
if data.snapshots.try_insert(id, content).is_err() {
|
||||
debug!(self.logger, "Skipping this snapshot, as module's state was already saved.")
|
||||
debug!("Skipping this snapshot, as module's state was already saved.")
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -122,7 +121,7 @@ impl Transaction {
|
||||
/// Ignored transaction when dropped is discarded, rather than being put on top of "Redo" stack.
|
||||
/// It does not affect the actions belonging to transaction in any way.
|
||||
pub fn ignore(&self) {
|
||||
debug!(self.logger, "Marking transaction '{self.frame.borrow().name}' as ignored.");
|
||||
debug!("Marking transaction '{}' as ignored.", self.frame.borrow().name);
|
||||
self.ignored.set(true)
|
||||
}
|
||||
}
|
||||
@ -131,14 +130,13 @@ impl Drop for Transaction {
|
||||
fn drop(&mut self) {
|
||||
if let Some(urm) = self.urm.upgrade() {
|
||||
if !self.ignored.get() {
|
||||
info!(self.logger, "Transaction '{self.name()}' will create a new frame.");
|
||||
info!("Transaction '{}' will create a new frame.", self.name());
|
||||
urm.push_to(Stack::Undo, self.frame.borrow().clone());
|
||||
urm.clear(Stack::Redo);
|
||||
} else {
|
||||
info!(
|
||||
self.logger,
|
||||
"Dropping the ignored transaction '{self.name()}' without \
|
||||
pushing a frame to repository."
|
||||
"Dropping the ignored transaction '{}' without pushing a frame to repository.",
|
||||
self.name()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -249,7 +247,7 @@ impl Repository {
|
||||
Err(ongoing_transaction)
|
||||
} else {
|
||||
let name = name.into();
|
||||
debug!(self.logger, "Creating a new transaction `{name}`");
|
||||
debug!("Creating a new transaction `{name}`");
|
||||
let new_transaction = Rc::new(Transaction::new(self, name));
|
||||
self.data.borrow_mut().current_transaction = Some(Rc::downgrade(&new_transaction));
|
||||
Ok(new_transaction)
|
||||
@ -300,13 +298,13 @@ impl Repository {
|
||||
|
||||
/// Push a new frame to the given stack.
|
||||
fn push_to(&self, stack: Stack, frame: Frame) {
|
||||
debug!(self.logger, "Pushing to {stack} stack a new frame: {frame}");
|
||||
debug!("Pushing to {stack} stack a new frame: {frame}");
|
||||
self.borrow_mut(stack).push(frame);
|
||||
}
|
||||
|
||||
/// Clear all frames from the given stack.
|
||||
fn clear(&self, stack: Stack) {
|
||||
debug!(self.logger, "Clearing {stack} stack.");
|
||||
debug!("Clearing {stack} stack.");
|
||||
self.borrow_mut(stack).clear();
|
||||
}
|
||||
|
||||
@ -328,9 +326,8 @@ impl Repository {
|
||||
fn pop(&self, stack: Stack) -> FallibleResult<Frame> {
|
||||
let frame = self.borrow_mut(stack).pop().ok_or(NoFrameToPop(stack))?;
|
||||
debug!(
|
||||
self.logger,
|
||||
"Popping a frame from {stack}. Remaining length: {self.len(stack)}. \
|
||||
Frame: {frame}"
|
||||
"Popping a frame from {stack}. Remaining length: {}. Frame: {frame}",
|
||||
self.len(stack)
|
||||
);
|
||||
Ok(frame)
|
||||
}
|
||||
@ -388,7 +385,7 @@ impl Manager {
|
||||
|
||||
/// Undo last operation.
|
||||
pub fn undo(&self) -> FallibleResult {
|
||||
debug!(self.logger, "Undo requested, stack size is {self.repository.len(Stack::Undo)}.");
|
||||
debug!("Undo requested, stack size is {}.", self.repository.len(Stack::Undo));
|
||||
let frame = self.repository.last(Stack::Undo)?;
|
||||
|
||||
// Before applying undo we create a special transaction. The purpose it two-fold:
|
||||
@ -411,7 +408,7 @@ impl Manager {
|
||||
// supposed to stay on top, as we maintain an open transaction while undoing.
|
||||
if !popped.contains(&frame) {
|
||||
// No reason to stop the world but should catch our eye in logs.
|
||||
error!(self.logger, "Undone frame mismatch!");
|
||||
error!("Undone frame mismatch!");
|
||||
debug_assert!(false, "Undone frame mismatch!");
|
||||
}
|
||||
|
||||
@ -433,7 +430,7 @@ impl Manager {
|
||||
|
||||
/// Restore all modules affected by the [`Frame`] to their stored state.
|
||||
fn reset_to(&self, frame: &Frame) -> FallibleResult {
|
||||
info!(self.logger, "Resetting to initial state on frame {frame}");
|
||||
info!("Resetting to initial state on frame {frame}");
|
||||
|
||||
// First we must have all modules resolved. Only then we can start applying changes.
|
||||
// Otherwise, if one of the modules could not be retrieved, we'd risk ending up with
|
||||
@ -454,7 +451,7 @@ impl Manager {
|
||||
})?;
|
||||
|
||||
for (module, content) in module_and_content {
|
||||
info!(self.logger, "Undoing on module {module.path()}");
|
||||
info!("Undoing on module {}", module.path());
|
||||
// The below should never fail, because it can fail only if serialization to code fails.
|
||||
// And it cannot fail, as it already underwent this procedure successfully in the past
|
||||
// (we are copying an old state, so it must ba a representable state).
|
||||
@ -561,14 +558,13 @@ main =
|
||||
use model::module::Position;
|
||||
|
||||
let mut fixture = crate::test::mock::Unified::new().fixture();
|
||||
let Fixture { executed_graph, graph, project, logger, .. } = &mut fixture;
|
||||
let logger: &Logger = logger;
|
||||
let Fixture { executed_graph, graph, project, .. } = &mut fixture;
|
||||
|
||||
let urm = project.urm();
|
||||
let nodes = executed_graph.graph().nodes().unwrap();
|
||||
let node = &nodes[0];
|
||||
|
||||
debug!(logger, "{node.position():?}");
|
||||
debug!("{:?}", node.position());
|
||||
let pos1 = Position::new(500.0, 250.0);
|
||||
let pos2 = Position::new(300.0, 150.0);
|
||||
|
||||
|
@ -35,7 +35,6 @@ pub use searcher::Searcher;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Model {
|
||||
logger: Logger,
|
||||
current_project: RefCell<Option<Project>>,
|
||||
controller: controller::Ide,
|
||||
view: view::root::View,
|
||||
@ -76,7 +75,7 @@ impl Model {
|
||||
}
|
||||
Err(err) => {
|
||||
let err_msg = format!("Failed to initialize project: {}", err);
|
||||
error!(self.logger, "{err_msg}");
|
||||
error!("{err_msg}");
|
||||
self.controller.status_notifications().publish_event(err_msg);
|
||||
}
|
||||
}
|
||||
@ -87,15 +86,14 @@ impl Model {
|
||||
/// a second one for opening the project.
|
||||
#[profile(Task)]
|
||||
pub fn open_project(&self, project_name: String) {
|
||||
let logger = self.logger.clone_ref();
|
||||
let controller = self.controller.clone_ref();
|
||||
crate::executor::global::spawn(async move {
|
||||
if let Ok(managing_api) = controller.manage_projects() {
|
||||
if let Err(err) = managing_api.open_project_by_name(project_name).await {
|
||||
error!(logger, "Cannot open project by name: {err}.");
|
||||
error!("Cannot open project by name: {err}.");
|
||||
}
|
||||
} else {
|
||||
warning!(logger, "Project opening failed: no ProjectManagingAPI available.");
|
||||
warn!("Project opening failed: no ProjectManagingAPI available.");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -104,23 +102,19 @@ impl Model {
|
||||
/// Engine. It makes a call to Project Manager.
|
||||
#[profile(Task)]
|
||||
fn create_project(&self, template: Option<&str>) {
|
||||
let logger = self.logger.clone_ref();
|
||||
let controller = self.controller.clone_ref();
|
||||
let template = template.map(ToOwned::to_owned);
|
||||
crate::executor::global::spawn(async move {
|
||||
if let Ok(managing_api) = controller.manage_projects() {
|
||||
if let Err(err) = managing_api.create_new_project(template.clone()).await {
|
||||
if let Some(template) = template {
|
||||
error!(
|
||||
logger,
|
||||
"Could not create new project from template {template}: {err}."
|
||||
);
|
||||
error!("Could not create new project from template {template}: {err}.");
|
||||
} else {
|
||||
error!(logger, "Could not create new project: {err}.");
|
||||
error!("Could not create new project: {err}.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warning!(logger, "Project creation failed: no ProjectManagingAPI available.");
|
||||
warn!("Project creation failed: no ProjectManagingAPI available.");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -148,9 +142,8 @@ impl Presenter {
|
||||
/// project will be displayed (if any).
|
||||
#[profile(Task)]
|
||||
pub fn new(controller: controller::Ide, view: ide_view::root::View) -> Self {
|
||||
let logger = Logger::new("Presenter");
|
||||
let current_project = default();
|
||||
let model = Rc::new(Model { logger, controller, view, current_project });
|
||||
let model = Rc::new(Model { controller, view, current_project });
|
||||
|
||||
frp::new_network! { network
|
||||
let welcome_view_frp = &model.view.welcome_screen().frp;
|
||||
@ -179,7 +172,6 @@ impl Presenter {
|
||||
use controller::ide::BackgroundTaskHandle as ControllerHandle;
|
||||
use ide_view::status_bar::process::Id as ViewHandle;
|
||||
|
||||
let logger = self.model.logger.clone_ref();
|
||||
let process_map = SharedHashMap::<ControllerHandle, ViewHandle>::new();
|
||||
let status_bar = self.model.view.status_bar().clone_ref();
|
||||
let status_notifications = self.model.controller.status_notifications().subscribe();
|
||||
@ -198,7 +190,7 @@ impl Presenter {
|
||||
if let Some(view_handle) = process_map.remove(&handle) {
|
||||
status_bar.finish_process(view_handle);
|
||||
} else {
|
||||
warning!(logger, "Controllers finished process not displayed in view");
|
||||
warn!("Controllers finished process not displayed in view");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,7 +220,7 @@ impl Presenter {
|
||||
self.model.view.welcome_screen().frp.set_projects_list(names);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.model.logger, "Unable to get list of projects: {err}.");
|
||||
error!("Unable to get list of projects: {err}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,21 +17,19 @@ use ide_view as view;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Model {
|
||||
logger: Logger,
|
||||
controller: controller::Text,
|
||||
view: view::code_editor::View,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn new(controller: controller::Text, view: view::code_editor::View) -> Self {
|
||||
let logger = Logger::new("presenter::code");
|
||||
Self { logger, controller, view }
|
||||
Self { controller, view }
|
||||
}
|
||||
|
||||
fn apply_change_from_view(&self, change: &enso_text::Change) {
|
||||
let converted = enso_text::Change { range: change.range, text: change.text.to_string() };
|
||||
if let Err(err) = self.controller.apply_text_change(converted) {
|
||||
error!(self.logger, "Error while applying text change: {err}");
|
||||
error!("Error while applying text change: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,17 +38,16 @@ impl Model {
|
||||
match self.controller.read_content().await {
|
||||
Ok(code) => endpoint.emit(ImString::new(code)),
|
||||
Err(err) => {
|
||||
error!(self.logger, "Error while updating code editor: {err}")
|
||||
error!("Error while updating code editor: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_module(&self, content: String) {
|
||||
let logger = self.logger.clone_ref();
|
||||
let controller = self.controller.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
if let Err(err) = controller.store_content(content).await {
|
||||
error!(logger, "Error while saving module: {err}");
|
||||
error!("Error while saving module: {err}");
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -83,9 +80,9 @@ impl Code {
|
||||
desynchronized <- all_with(&code_in_controller, &text_area.content, |controller, view|
|
||||
*controller != view.to_string()
|
||||
);
|
||||
text_area.set_content <+ code_in_controller.gate(&desynchronized).map(|s| s.to_string());
|
||||
text_area.set_content <+ code_in_controller.gate(&desynchronized);
|
||||
|
||||
maybe_change_to_apply <= text_area.changed;
|
||||
maybe_change_to_apply <= text_area.changed.map(|c| (**c).clone());
|
||||
change_to_apply <- maybe_change_to_apply.gate(&desynchronized);
|
||||
eval change_to_apply ((change) model.apply_change_from_view(change));
|
||||
|
||||
|
@ -101,7 +101,7 @@ impl Model {
|
||||
state.clone_ref(),
|
||||
);
|
||||
let execution_stack =
|
||||
CallStack::new(&logger, controller.clone_ref(), view.clone_ref(), state.clone_ref());
|
||||
CallStack::new(controller.clone_ref(), view.clone_ref(), state.clone_ref());
|
||||
Self {
|
||||
logger,
|
||||
project,
|
||||
@ -184,7 +184,7 @@ impl Model {
|
||||
fn nodes_collapsed(&self, collapsed: &[ViewNodeId]) {
|
||||
self.log_action(
|
||||
|| {
|
||||
debug!(self.logger, "Collapsing node.");
|
||||
debug!("Collapsing node.");
|
||||
let ids = collapsed.iter().filter_map(|node| self.state.ast_node_id_of_view(*node));
|
||||
let new_node_id = self.controller.graph().collapse(ids, COLLAPSED_FUNCTION_NAME);
|
||||
// TODO [mwu] https://github.com/enso-org/ide/issues/760
|
||||
@ -199,7 +199,7 @@ impl Model {
|
||||
fn log_action<F>(&self, f: F, action: &str)
|
||||
where F: FnOnce() -> Option<FallibleResult> {
|
||||
if let Some(Err(err)) = f() {
|
||||
error!(self.logger, "Failed to {action} in AST: {err}");
|
||||
error!("Failed to {action} in AST: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ impl Model {
|
||||
let position = model::module::Position { vector: position };
|
||||
let handler = NodeFromDroppedFileHandler::new(&self.logger, project, graph);
|
||||
if let Err(err) = handler.create_node_and_start_uploading(to_upload, position) {
|
||||
error!(self.logger, "Error when creating node from dropped file: {err}");
|
||||
error!("Error when creating node from dropped file: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,7 +481,6 @@ impl Graph {
|
||||
|
||||
#[profile(Detail)]
|
||||
fn init(self, project_view: &view::project::View) -> Self {
|
||||
let logger = &self.model.logger;
|
||||
let network = &self.network;
|
||||
let model = &self.model;
|
||||
let view = &self.model.view.frp;
|
||||
@ -490,10 +489,10 @@ impl Graph {
|
||||
// Position initialization should go before emitting `update_data` event.
|
||||
update_with_gap <- view.default_y_gap_between_nodes.sample(&update_view);
|
||||
eval update_with_gap ((gap) model.initialize_nodes_positions(*gap));
|
||||
update_data <- update_view.map(f_!([logger,model] match ViewUpdate::new(&*model) {
|
||||
update_data <- update_view.map(f_!([model] match ViewUpdate::new(&*model) {
|
||||
Ok(update) => Rc::new(update),
|
||||
Err(err) => {
|
||||
error!(logger,"Failed to update view: {err:?}");
|
||||
error!("Failed to update view: {err:?}");
|
||||
Rc::new(default())
|
||||
}
|
||||
}));
|
||||
|
@ -19,7 +19,6 @@ use ide_view as view;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Model {
|
||||
logger: Logger,
|
||||
controller: controller::ExecutedGraph,
|
||||
view: view::graph_editor::GraphEditor,
|
||||
state: Rc<State>,
|
||||
@ -27,13 +26,11 @@ struct Model {
|
||||
|
||||
impl Model {
|
||||
fn new(
|
||||
parent: impl AnyLogger,
|
||||
controller: controller::ExecutedGraph,
|
||||
view: view::graph_editor::GraphEditor,
|
||||
state: Rc<State>,
|
||||
) -> Self {
|
||||
let logger = parent.sub("presenter::graph::CallStack");
|
||||
Self { logger, controller, view, state }
|
||||
Self { controller, view, state }
|
||||
}
|
||||
|
||||
fn expression_entered(&self, local_call: &view::graph_editor::LocalCall) {
|
||||
@ -45,7 +42,7 @@ impl Model {
|
||||
}
|
||||
|
||||
fn node_entered(&self, node_id: ViewNodeId) {
|
||||
debug!(self.logger, "Requesting entering the node {node_id}.");
|
||||
debug!("Requesting entering the node {node_id}.");
|
||||
analytics::remote_log_event("integration::node_entered");
|
||||
if let Some(call) = self.state.ast_node_id_of_view(node_id) {
|
||||
match self.controller.node_method_pointer(call) {
|
||||
@ -54,31 +51,29 @@ impl Model {
|
||||
let local_call = LocalCall { call, definition };
|
||||
self.enter_expression(local_call);
|
||||
}
|
||||
Err(_) =>
|
||||
info!(self.logger, "Ignoring request to enter non-enterable node {call}."),
|
||||
Err(_) => info!("Ignoring request to enter non-enterable node {call}."),
|
||||
}
|
||||
} else {
|
||||
error!(self.logger, "Cannot enter {node_id:?}: no AST node bound to the view.");
|
||||
error!("Cannot enter {node_id:?}: no AST node bound to the view.");
|
||||
}
|
||||
}
|
||||
|
||||
fn node_exited(&self) {
|
||||
debug!(self.logger, "Requesting exiting the current node.");
|
||||
debug!("Requesting exiting the current node.");
|
||||
analytics::remote_log_event("integration::node_exited");
|
||||
let controller = self.controller.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
let store_stack = self.store_updated_stack_task();
|
||||
executor::global::spawn(async move {
|
||||
info!(logger, "Exiting node.");
|
||||
info!("Exiting node.");
|
||||
match controller.exit_node().await {
|
||||
Ok(()) =>
|
||||
if let Err(err) = store_stack() {
|
||||
// We cannot really do anything when updating metadata fails.
|
||||
// Can happen in improbable case of serialization failure.
|
||||
error!(logger, "Failed to store an updated call stack: {err}");
|
||||
error!("Failed to store an updated call stack: {err}");
|
||||
},
|
||||
Err(err) => {
|
||||
error!(logger, "Exiting node failed: {err}");
|
||||
error!("Exiting node failed: {err}");
|
||||
|
||||
let event = "integration::exiting_node_failed";
|
||||
let field = "error";
|
||||
@ -91,19 +86,18 @@ impl Model {
|
||||
|
||||
fn enter_expression(&self, local_call: LocalCall) {
|
||||
let controller = self.controller.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
let store_stack = self.store_updated_stack_task();
|
||||
executor::global::spawn(async move {
|
||||
info!(logger, "Entering expression {local_call:?}.");
|
||||
info!("Entering expression {local_call:?}.");
|
||||
match controller.enter_method_pointer(&local_call).await {
|
||||
Ok(()) =>
|
||||
if let Err(err) = store_stack() {
|
||||
// We cannot really do anything when updating metadata fails.
|
||||
// Can happen in improbable case of serialization failure.
|
||||
error!(logger, "Failed to store an updated call stack: {err}");
|
||||
error!("Failed to store an updated call stack: {err}");
|
||||
},
|
||||
Err(err) => {
|
||||
error!(logger, "Entering node failed: {err}.");
|
||||
error!("Entering node failed: {err}.");
|
||||
let event = "integration::entering_node_failed";
|
||||
let field = "error";
|
||||
let data = analytics::AnonymousData(|| err.to_string());
|
||||
@ -149,13 +143,12 @@ pub struct CallStack {
|
||||
impl CallStack {
|
||||
/// Constructor. The returned presenter works right away.
|
||||
pub fn new(
|
||||
parent: impl AnyLogger,
|
||||
controller: controller::ExecutedGraph,
|
||||
view: view::graph_editor::GraphEditor,
|
||||
state: Rc<State>,
|
||||
) -> Self {
|
||||
let network = frp::Network::new("presenter::graph::CallStack");
|
||||
let model = Rc::new(Model::new(parent, controller, view, state));
|
||||
let model = Rc::new(Model::new(controller, view, state));
|
||||
let view = &model.view;
|
||||
let breadcrumbs = &view.model.breadcrumbs;
|
||||
|
||||
@ -181,7 +174,7 @@ impl CallStack {
|
||||
let graph_notifications = self.model.controller.subscribe();
|
||||
let weak = Rc::downgrade(&self.model);
|
||||
spawn_stream_handler(weak, graph_notifications, move |notification, model| {
|
||||
info!(model.logger, "Received controller notification {notification:?}");
|
||||
info!("Received controller notification {notification:?}");
|
||||
match notification {
|
||||
Notification::EnteredNode(frame) => model.add_breadcrumb_in_view(frame),
|
||||
Notification::SteppedOutOfNode(_) => model.view.model.breadcrumbs.pop_breadcrumb(),
|
||||
|
@ -86,10 +86,7 @@ impl Model {
|
||||
if let Some(target_id) = self.state.ast_node_id_of_view(node_id) {
|
||||
manager.set_visualization(target_id, metadata);
|
||||
} else {
|
||||
error!(
|
||||
self.logger,
|
||||
"Failed to update visualization: {node_id:?} does not represent any AST code."
|
||||
)
|
||||
error!("Failed to update visualization: {node_id:?} does not represent any AST code.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +107,7 @@ impl Model {
|
||||
Err(err) => {
|
||||
// TODO [mwu]: We should consider having the visualization also accept error
|
||||
// input.
|
||||
error!(self.logger, "Failed to deserialize visualization update: {err}");
|
||||
error!("Failed to deserialize visualization update: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,7 +131,6 @@ impl Model {
|
||||
#[profile(Detail)]
|
||||
fn load_visualizations(&self) {
|
||||
self.graph_view.reset_visualization_registry();
|
||||
let logger = self.logger.clone_ref();
|
||||
let controller = self.controller.clone_ref();
|
||||
let graph_editor = self.graph_view.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
@ -146,11 +142,11 @@ impl Model {
|
||||
graph_editor.frp.register_visualization.emit(Some(visualization));
|
||||
}
|
||||
Err(err) => {
|
||||
error!(logger, "Error while loading visualization {identifier}: {err:?}");
|
||||
error!("Error while loading visualization {identifier}: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
info!(logger, "Visualizations Initialized.");
|
||||
info!("Visualizations Initialized.");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -181,8 +177,8 @@ impl Visualization {
|
||||
let network = frp::Network::new("presenter::graph::Visualization");
|
||||
|
||||
let controller = project.visualization().clone_ref();
|
||||
let (manager, notifications) = Manager::new(&logger, graph.clone_ref());
|
||||
let (error_manager, error_notifications) = Manager::new(&logger, graph.clone_ref());
|
||||
let (manager, notifications) = Manager::new(graph.clone_ref());
|
||||
let (error_manager, error_notifications) = Manager::new(graph.clone_ref());
|
||||
let model = Rc::new(Model {
|
||||
logger,
|
||||
controller,
|
||||
@ -231,18 +227,17 @@ impl Visualization {
|
||||
) -> Self {
|
||||
let weak = Rc::downgrade(&self.model);
|
||||
spawn_stream_handler(weak, notifier, move |notification, model| {
|
||||
let logger = &model.logger;
|
||||
info!(logger, "Received update for visualization: {notification:?}");
|
||||
info!("Received update for visualization: {notification:?}");
|
||||
match notification {
|
||||
manager::Notification::ValueUpdate { target, data, .. } => {
|
||||
model.handle_value_update(&update_endpoint, target, data);
|
||||
}
|
||||
manager::Notification::FailedToAttach { visualization, error } => {
|
||||
error!(logger, "Visualization {visualization.id} failed to attach: {error}.");
|
||||
error!("Visualization {} failed to attach: {error}.", visualization.id);
|
||||
model.handle_controller_failure(&failure_endpoint, visualization.expression_id);
|
||||
}
|
||||
manager::Notification::FailedToDetach { visualization, error } => {
|
||||
error!(logger, "Visualization {visualization.id} failed to detach: {error}.");
|
||||
error!("Visualization {} failed to detach: {error}.", visualization.id);
|
||||
// Here we cannot really do much. Failing to detach might mean that
|
||||
// visualization was already detached, that we detached it
|
||||
// but failed to observe this (e.g. due to a connectivity
|
||||
@ -251,13 +246,13 @@ impl Visualization {
|
||||
// it rather than likely break visualizations on the node altogether.
|
||||
let forgotten = manager.forget_visualization(visualization.expression_id);
|
||||
if let Some(forgotten) = forgotten {
|
||||
error!(logger, "The visualization will be forgotten: {forgotten:?}")
|
||||
error!("The visualization will be forgotten: {forgotten:?}")
|
||||
}
|
||||
}
|
||||
manager::Notification::FailedToModify { desired, error } => {
|
||||
error!(
|
||||
logger,
|
||||
"Visualization {desired.id} failed to be modified: {error}. Will hide it in GUI."
|
||||
"Visualization {} failed to be modified: {error}. Will hide it in GUI.",
|
||||
desired.id
|
||||
);
|
||||
// Actually it would likely have more sense if we had just restored the previous
|
||||
// visualization, as its LS state should be preserved. However, we already
|
||||
@ -287,10 +282,7 @@ impl Visualization {
|
||||
model.error_manager.retain_visualizations(&nodes_set);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
model.logger,
|
||||
"Cannot update visualization after graph change: {err}"
|
||||
);
|
||||
error!("Cannot update visualization after graph change: {err}");
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
|
@ -204,7 +204,6 @@ impl Description {
|
||||
/// As this type wraps asynchronous operations, it should be stored using `Rc` pointer.
|
||||
#[derive(Debug)]
|
||||
pub struct Manager {
|
||||
logger: Logger,
|
||||
visualizations: SharedHashMap<ast::Id, Description>,
|
||||
executed_graph: ExecutedGraph,
|
||||
notification_sender: futures::channel::mpsc::UnboundedSender<Notification>,
|
||||
@ -215,13 +214,9 @@ impl Manager {
|
||||
///
|
||||
/// Return a handle to the Manager and the receiver for notifications.
|
||||
/// Note that receiver cannot be re-retrieved or changed in the future.
|
||||
pub fn new(
|
||||
logger: impl AnyLogger,
|
||||
executed_graph: ExecutedGraph,
|
||||
) -> (Rc<Self>, UnboundedReceiver<Notification>) {
|
||||
let logger = logger.sub("visualization::Manager");
|
||||
pub fn new(executed_graph: ExecutedGraph) -> (Rc<Self>, UnboundedReceiver<Notification>) {
|
||||
let (notification_sender, notification_receiver) = futures::channel::mpsc::unbounded();
|
||||
let ret = Self { logger, visualizations: default(), executed_graph, notification_sender };
|
||||
let ret = Self { visualizations: default(), executed_graph, notification_sender };
|
||||
(Rc::new(ret), notification_receiver)
|
||||
}
|
||||
|
||||
@ -300,7 +295,7 @@ impl Manager {
|
||||
}
|
||||
|
||||
fn write_new_desired(self: &Rc<Self>, target: ast::Id, new_desired: Option<Desired>) {
|
||||
debug!(self.logger, "Requested to set visualization {target}: {new_desired:?}");
|
||||
debug!("Requested to set visualization {target}: {new_desired:?}");
|
||||
let mut current = match self.visualizations.get_cloned(&target) {
|
||||
None => {
|
||||
if new_desired.is_none() {
|
||||
@ -319,7 +314,6 @@ impl Manager {
|
||||
self.synchronize(target);
|
||||
} else {
|
||||
debug!(
|
||||
self.logger,
|
||||
"Visualization for {target} was already in the desired state: \
|
||||
{new_desired:?}"
|
||||
);
|
||||
@ -368,7 +362,7 @@ impl Manager {
|
||||
let desired_vis_id = description.desired.as_ref().map(|v| v.visualization_id);
|
||||
let new_visualization = description.desired.and_then(|desired| {
|
||||
this.prepare_visualization(desired.clone()).handle_err(|error| {
|
||||
error!(this.logger, "Failed to prepare visualization {desired:?}: {error}")
|
||||
error!("Failed to prepare visualization {desired:?}: {error}")
|
||||
})
|
||||
});
|
||||
match (status, new_visualization) {
|
||||
@ -396,10 +390,7 @@ impl Manager {
|
||||
target: ast::Id,
|
||||
new_visualization: Visualization,
|
||||
) {
|
||||
info!(
|
||||
self.logger,
|
||||
"Will attach visualization {new_visualization.id} to expression {target}"
|
||||
);
|
||||
info!("Will attach visualization {} to expression {target}", new_visualization.id);
|
||||
let status = Status::BeingAttached(new_visualization.clone());
|
||||
self.update_status(target, status);
|
||||
let notifier = self.notification_sender.clone();
|
||||
@ -435,7 +426,7 @@ impl Manager {
|
||||
|
||||
#[profile(Detail)]
|
||||
async fn detach_visualization(self: Rc<Self>, target: ast::Id, so_far: Visualization) {
|
||||
info!(self.logger, "Will detach from {target}: {so_far:?}");
|
||||
info!("Will detach from {target}: {so_far:?}");
|
||||
let status = Status::BeingDetached(so_far.clone());
|
||||
self.update_status(target, status);
|
||||
let detaching_result = self.executed_graph.detach_visualization(so_far.id);
|
||||
@ -467,10 +458,7 @@ impl Manager {
|
||||
so_far: Visualization,
|
||||
new_visualization: Visualization,
|
||||
) {
|
||||
info!(
|
||||
self.logger,
|
||||
"Will modify visualization on {target} from {so_far:?} to {new_visualization:?}"
|
||||
);
|
||||
info!("Will modify visualization on {target} from {so_far:?} to {new_visualization:?}");
|
||||
let status =
|
||||
Status::BeingModified { from: so_far.clone(), to: new_visualization.clone() };
|
||||
self.update_status(target, status);
|
||||
@ -615,8 +603,7 @@ mod tests {
|
||||
inner.project.clone_ref(),
|
||||
execution_context,
|
||||
);
|
||||
let logger: Logger = inner.logger.sub("manager");
|
||||
let (manager, notifier) = Manager::new(logger, executed_graph.clone_ref());
|
||||
let (manager, notifier) = Manager::new(executed_graph.clone_ref());
|
||||
Self { inner, is_ready, manager, notifier, requests }
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ impl Model {
|
||||
*self.searcher.borrow_mut() = Some(searcher);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.logger, "Error while creating searcher integration: {err}");
|
||||
error!("Error while creating searcher integration: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,11 +145,10 @@ impl Model {
|
||||
if self.controller.model.name() != name.as_ref() {
|
||||
let project = self.controller.model.clone_ref();
|
||||
let breadcrumbs = self.view.graph().model.breadcrumbs.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
let name = name.into();
|
||||
executor::global::spawn(async move {
|
||||
if let Err(e) = project.rename_project(name).await {
|
||||
error!(logger, "The project couldn't be renamed: {e}");
|
||||
error!("The project couldn't be renamed: {e}");
|
||||
breadcrumbs.cancel_project_name_editing.emit(());
|
||||
}
|
||||
});
|
||||
@ -157,16 +156,16 @@ impl Model {
|
||||
}
|
||||
|
||||
fn undo(&self) {
|
||||
debug!(self.logger, "Undo triggered in UI.");
|
||||
debug!("Undo triggered in UI.");
|
||||
if let Err(e) = self.controller.model.urm().undo() {
|
||||
error!(self.logger, "Undo failed: {e}");
|
||||
error!("Undo failed: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn redo(&self) {
|
||||
debug!(self.logger, "Redo triggered in UI.");
|
||||
debug!("Redo triggered in UI.");
|
||||
if let Err(e) = self.controller.model.urm().redo() {
|
||||
error!(self.logger, "Redo failed: {e}");
|
||||
error!("Redo failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,7 +269,7 @@ impl Project {
|
||||
let notifications = self.model.controller.model.subscribe();
|
||||
let weak = Rc::downgrade(&self.model);
|
||||
spawn_stream_handler(weak, notifications, |notification, model| {
|
||||
info!(model.logger, "Processing notification {notification:?}");
|
||||
info!("Processing notification {notification:?}");
|
||||
let message = match notification {
|
||||
model::project::Notification::ConnectionLost(_) =>
|
||||
crate::BACKEND_DISCONNECTED_MESSAGE,
|
||||
|
@ -139,7 +139,7 @@ impl Model {
|
||||
}
|
||||
|
||||
fn create_providers(&self) -> provider::Any {
|
||||
provider::create_providers_from_controller(&self.logger, &self.controller)
|
||||
provider::create_providers_from_controller(&self.controller)
|
||||
}
|
||||
|
||||
fn suggestion_for_entry_id(&self, id: list_panel::EntryId) -> FallibleResult<Suggestion> {
|
||||
|
@ -26,7 +26,7 @@ pub type Any = (
|
||||
);
|
||||
|
||||
/// Create providers from the current controller's action list.
|
||||
pub fn create_providers_from_controller(logger: &Logger, controller: &controller::Searcher) -> Any {
|
||||
pub fn create_providers_from_controller(controller: &controller::Searcher) -> Any {
|
||||
use controller::searcher::Actions;
|
||||
match controller.actions() {
|
||||
Actions::Loading => as_any(Rc::new(list_view::entry::EmptyProvider)),
|
||||
@ -37,7 +37,7 @@ pub fn create_providers_from_controller(logger: &Logger, controller: &controller
|
||||
as_any(Rc::new(provider))
|
||||
}
|
||||
Actions::Error(err) => {
|
||||
error!(logger, "Error while obtaining searcher action list: {err}");
|
||||
error!("Error while obtaining searcher action list: {err}");
|
||||
as_any(Rc::new(list_view::entry::EmptyProvider))
|
||||
}
|
||||
}
|
||||
@ -106,8 +106,8 @@ impl list_view::entry::ModelProvider<GlyphHighlightedLabel> for Action {
|
||||
if let Some(char) = char_iter.next() {
|
||||
let (char_idx, (byte_id, char)) = char;
|
||||
if char_idx == *idx {
|
||||
let start = enso_text::unit::Bytes(byte_id as i32);
|
||||
let end = enso_text::unit::Bytes((byte_id + char.len_utf8()) as i32);
|
||||
let start = enso_text::index::Byte(byte_id);
|
||||
let end = enso_text::index::Byte(byte_id + char.len_utf8());
|
||||
break Some(enso_text::Range::new(start, end));
|
||||
}
|
||||
} else {
|
||||
@ -207,7 +207,7 @@ impl list_view::entry::ModelProvider<component_group_view::Entry> for Component
|
||||
|
||||
// === Component Provider helpers ===
|
||||
|
||||
fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Range<text::Bytes>> {
|
||||
fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Range<text::Byte>> {
|
||||
if let MatchInfo::Matches { subsequence } = match_info {
|
||||
let mut char_iter = label.char_indices().enumerate();
|
||||
subsequence
|
||||
@ -217,8 +217,8 @@ fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Ra
|
||||
if let Some(char) = char_iter.next() {
|
||||
let (char_idx, (byte_id, char)) = char;
|
||||
if char_idx == *idx {
|
||||
let start = enso_text::unit::Bytes(byte_id as i32);
|
||||
let end = enso_text::unit::Bytes((byte_id + char.len_utf8()) as i32);
|
||||
let start = enso_text::index::Byte(byte_id);
|
||||
let end = enso_text::index::Byte(byte_id + char.len_utf8());
|
||||
break Some(enso_text::Range::new(start, end));
|
||||
}
|
||||
} else {
|
||||
|
@ -191,9 +191,7 @@ pub mod mock {
|
||||
let path = self.module_path.clone();
|
||||
let metadata = self.metadata.clone();
|
||||
let repository = urm.repository.clone_ref();
|
||||
let logger = &self.logger;
|
||||
let module =
|
||||
Rc::new(model::module::Plain::new(logger, path, ast, metadata, repository));
|
||||
let module = Rc::new(model::module::Plain::new(path, ast, metadata, repository));
|
||||
urm.module_opened(module.clone());
|
||||
module
|
||||
}
|
||||
@ -230,8 +228,7 @@ pub mod mock {
|
||||
}
|
||||
|
||||
pub fn execution_context(&self) -> Rc<model::execution_context::Plain> {
|
||||
let logger = Logger::new_sub(&self.logger, "Mocked Execution Context");
|
||||
Rc::new(model::execution_context::Plain::new(logger, self.method_pointer()))
|
||||
Rc::new(model::execution_context::Plain::new(self.method_pointer()))
|
||||
}
|
||||
|
||||
pub fn project(
|
||||
@ -282,7 +279,7 @@ pub mod mock {
|
||||
let urm = self.undo_redo_manager();
|
||||
let module = self.module(urm.clone());
|
||||
let suggestion_db =
|
||||
Rc::new(model::SuggestionDatabase::new_from_entries(&logger, &self.suggestions));
|
||||
Rc::new(model::SuggestionDatabase::new_from_entries(&self.suggestions));
|
||||
let graph = self.graph(&logger, module.clone_ref(), suggestion_db.clone_ref());
|
||||
let execution = self.execution_context();
|
||||
let project = self.project(
|
||||
|
@ -235,7 +235,7 @@ impl Model {
|
||||
}
|
||||
|
||||
let url = self.socket.url();
|
||||
info!(self.logger, "Reconnecting WS to {url}.");
|
||||
info!("Reconnecting WS to {url}.");
|
||||
|
||||
let new_ws = web_sys::WebSocket::new(&url)?;
|
||||
|
||||
@ -252,12 +252,9 @@ impl Model {
|
||||
|
||||
impl Drop for Model {
|
||||
fn drop(&mut self) {
|
||||
info!(self.logger, "Dropping WS model.");
|
||||
info!("Dropping WS model.");
|
||||
if let Err(e) = self.close("Rust Value has been dropped.") {
|
||||
error!(
|
||||
self.logger,
|
||||
"Error when closing socket due to being dropped: {e.print_to_string()}"
|
||||
)
|
||||
error!("Error when closing socket due to being dropped: {}", e.print_to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,11 +296,10 @@ impl WebSocket {
|
||||
/// Generate a callback to be invoked when socket needs reconnecting.
|
||||
fn reconnect_trigger(&self) -> impl FnMut(web_sys::CloseEvent) {
|
||||
let model = Rc::downgrade(&self.model);
|
||||
let logger = self.logger.clone();
|
||||
move |_| {
|
||||
if let Some(model) = model.upgrade() {
|
||||
if let Err(e) = model.borrow_mut().reconnect() {
|
||||
error!(logger, "Failed to reconnect: {e.print_to_string()}");
|
||||
error!("Failed to reconnect: {}", e.print_to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -331,7 +327,7 @@ impl WebSocket {
|
||||
Some(Ok(())) => {
|
||||
self.model.borrow_mut().clear_callbacks();
|
||||
self.model.borrow_mut().on_close_internal.set_callback(self.reconnect_trigger());
|
||||
info!(self.logger, "Connection opened.");
|
||||
info!("Connection opened.");
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ConnectingError::FailedToConnect),
|
||||
@ -398,14 +394,14 @@ impl WebSocket {
|
||||
|
||||
impl Transport for WebSocket {
|
||||
fn send_text(&mut self, message: &str) -> Result<(), Error> {
|
||||
info!(self.logger, "Sending text message of length {message.len()}.");
|
||||
debug!(self.logger, "Message contents: {message}");
|
||||
info!("Sending text message of length {}.", message.len());
|
||||
debug!("Message contents: {message}");
|
||||
self.send_with_open_socket(|ws| ws.send_with_str(message))
|
||||
}
|
||||
|
||||
fn send_binary(&mut self, message: &[u8]) -> Result<(), Error> {
|
||||
info!(self.logger, "Sending binary message of length {message.len()}.");
|
||||
debug!(self.logger, || format!("Message contents: {:x?}", message));
|
||||
info!("Sending binary message of length {}.", message.len());
|
||||
debug!("Message contents: {:x?}", message);
|
||||
// TODO [mwu]
|
||||
// Here we workaround issue from wasm-bindgen 0.2.58:
|
||||
// https://github.com/rustwasm/wasm-bindgen/issues/2014
|
||||
@ -418,35 +414,32 @@ impl Transport for WebSocket {
|
||||
}
|
||||
|
||||
fn set_event_transmitter(&mut self, transmitter: mpsc::UnboundedSender<TransportEvent>) {
|
||||
info!(self.logger, "Setting event transmitter.");
|
||||
info!("Setting event transmitter.");
|
||||
let transmitter_copy = transmitter.clone();
|
||||
let logger_copy = self.logger.clone_ref();
|
||||
self.set_on_message(move |e| {
|
||||
let data = e.data();
|
||||
if let Some(text) = data.as_string() {
|
||||
debug!(logger_copy, "Received a text message: {text}");
|
||||
debug!("Received a text message: {text}");
|
||||
channel::emit(&transmitter_copy, TransportEvent::TextMessage(text));
|
||||
} else if let Ok(array_buffer) = data.dyn_into::<js_sys::ArrayBuffer>() {
|
||||
let array = js_sys::Uint8Array::new(&array_buffer);
|
||||
let binary_data = array.to_vec();
|
||||
debug!(logger_copy, || format!("Received a binary message: {:x?}", binary_data));
|
||||
debug!("Received a binary message: {:x?}", binary_data);
|
||||
let event = TransportEvent::BinaryMessage(binary_data);
|
||||
channel::emit(&transmitter_copy, event);
|
||||
} else {
|
||||
info!(logger_copy, "Received other kind of message: {e.data().print_to_string()}.");
|
||||
info!("Received other kind of message: {}.", e.data().print_to_string());
|
||||
}
|
||||
});
|
||||
|
||||
let transmitter_copy = transmitter.clone();
|
||||
let logger_copy = self.logger.clone_ref();
|
||||
self.set_on_close(move |_e| {
|
||||
info!(logger_copy, "Connection has been closed.");
|
||||
info!("Connection has been closed.");
|
||||
channel::emit(&transmitter_copy, TransportEvent::Closed);
|
||||
});
|
||||
|
||||
let logger_copy = self.logger.clone_ref();
|
||||
self.set_on_open(move |_e| {
|
||||
info!(logger_copy, "Connection has been opened.");
|
||||
info!("Connection has been opened.");
|
||||
channel::emit(&transmitter, TransportEvent::Opened);
|
||||
});
|
||||
}
|
||||
@ -469,16 +462,16 @@ mod tests {
|
||||
async fn websocket_tests() {
|
||||
executor::web::test::setup_and_forget();
|
||||
let logger = DefaultTraceLogger::new("Test");
|
||||
info!(logger, "Started");
|
||||
info!("Started");
|
||||
|
||||
// Create WebSocket
|
||||
let ws = WebSocket::new_opened(&logger, "ws://localhost:30445").await;
|
||||
let mut ws = ws.expect("Couldn't connect to WebSocket server.");
|
||||
info!(logger, "WebSocket opened: {ws:?}");
|
||||
info!("WebSocket opened: {ws:?}");
|
||||
|
||||
// Log events
|
||||
let handler = ws.establish_event_stream().for_each(f!([logger](event) {
|
||||
info!(logger,"Socket emitted event: {event:?}");
|
||||
let handler = ws.establish_event_stream().for_each(f!([](event) {
|
||||
info!("Socket emitted event: {event:?}");
|
||||
futures::future::ready(())
|
||||
}));
|
||||
|
||||
@ -487,6 +480,6 @@ mod tests {
|
||||
|
||||
// Close socket after some delay.
|
||||
web::sleep(Duration::from_secs(20)).await;
|
||||
info!(logger, "Finished");
|
||||
info!("Finished");
|
||||
}
|
||||
}
|
||||
|
@ -305,9 +305,8 @@ async fn file_events() {
|
||||
/// * using project picker to open (or create) a project
|
||||
/// * establishing a binary protocol connection with Language Server
|
||||
async fn setup_ide() -> controller::Ide {
|
||||
let logger = Logger::new("Test");
|
||||
let config = enso_gui::config::Startup::default();
|
||||
info!(logger, "Setting up the project.");
|
||||
info!("Setting up the project.");
|
||||
let initializer = enso_gui::Initializer::new(config);
|
||||
let error_msg = "Couldn't open project.";
|
||||
initializer.initialize_ide_controller().await.expect(error_msg)
|
||||
@ -317,18 +316,17 @@ async fn setup_ide() -> controller::Ide {
|
||||
#[allow(dead_code)]
|
||||
/// This integration test covers writing and reading a file using the binary protocol
|
||||
async fn file_operations_test() {
|
||||
let logger = Logger::new("Test");
|
||||
let _guard = enso_gui::initializer::setup_global_executor();
|
||||
let ide = setup_ide().await;
|
||||
let project = ide.current_project().expect("IDE is configured without an open project.");
|
||||
info!(logger, "Got project: {project:?}");
|
||||
info!("Got project: {project:?}");
|
||||
// Edit file using the text protocol
|
||||
let path = Path::new(project.json_rpc().project_root().id(), &["test_file.txt"]);
|
||||
let contents = "Hello, 世界!".to_string();
|
||||
let written = project.json_rpc().write_file(&path, &contents).await.unwrap();
|
||||
info!(logger, "Written: {written:?}");
|
||||
info!("Written: {written:?}");
|
||||
let read_back = project.json_rpc().read_file(&path).await.unwrap();
|
||||
info!(logger, "Read back: {read_back:?}");
|
||||
info!("Read back: {read_back:?}");
|
||||
assert_eq!(contents, read_back.contents);
|
||||
|
||||
// Edit file using the binary protocol.
|
||||
@ -346,10 +344,9 @@ async fn file_operations_test() {
|
||||
|
||||
/// The future that tests attaching visualization and routing its updates.
|
||||
async fn binary_visualization_updates_test_hlp() {
|
||||
let logger = Logger::new("Test");
|
||||
let ide = setup_ide().await;
|
||||
let project = ide.current_project().expect("IDE is configured without an open project.");
|
||||
info!(logger, "Got project: {project:?}");
|
||||
info!("Got project: {project:?}");
|
||||
|
||||
use controller::project::MAIN_DEFINITION_NAME;
|
||||
use ensogl::system::web::sleep;
|
||||
@ -359,7 +356,7 @@ async fn binary_visualization_updates_test_hlp() {
|
||||
let method = module_path.method_pointer(project.qualified_name(), MAIN_DEFINITION_NAME);
|
||||
let module_qualified_name = project.qualified_module_name(&module_path);
|
||||
let module = project.module(module_path).await.unwrap();
|
||||
info!(logger, "Got module: {module:?}");
|
||||
info!("Got module: {module:?}");
|
||||
let graph_executed = controller::ExecutedGraph::new(&logger, project, method).await.unwrap();
|
||||
|
||||
let the_node = graph_executed.graph().nodes().unwrap()[0].info.clone();
|
||||
@ -368,9 +365,9 @@ async fn binary_visualization_updates_test_hlp() {
|
||||
// We must yield control for a moment, so the text edit is applied.
|
||||
sleep(Duration::from_millis(1)).await;
|
||||
|
||||
info!(logger, "Main graph: {graph_executed:?}");
|
||||
info!(logger, "The code is: {module.ast().repr():?}");
|
||||
info!(logger, "Main node: {the_node:?} with {the_node.expression().repr()}");
|
||||
info!("Main graph: {graph_executed:?}");
|
||||
info!("The code is: {:?}", module.ast().repr());
|
||||
info!("Main node: {the_node:?} with {}", the_node.expression().repr());
|
||||
|
||||
let method_pointer = QualifiedMethodPointer::module_method(
|
||||
module_qualified_name,
|
||||
@ -378,7 +375,7 @@ async fn binary_visualization_updates_test_hlp() {
|
||||
);
|
||||
let visualization = Visualization::new(the_node.id(), method_pointer, vec![]);
|
||||
let stream = graph_executed.attach_visualization(visualization.clone()).await.unwrap();
|
||||
info!(logger, "Attached the visualization {visualization.id}");
|
||||
info!("Attached the visualization {}", visualization.id);
|
||||
let mut stream = stream.boxed_local();
|
||||
let first_event = stream.next().await.unwrap(); // await update
|
||||
assert_eq!(first_event.as_ref(), "30".as_bytes());
|
||||
|
@ -118,7 +118,7 @@ enum State {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EntryData {
|
||||
display_object: display::object::Instance,
|
||||
text: text::Area,
|
||||
text: text::Text,
|
||||
separator: separator::View,
|
||||
ellipsis: ellipsis::View,
|
||||
state: Rc<Cell<State>>,
|
||||
@ -126,14 +126,13 @@ pub struct EntryData {
|
||||
|
||||
impl EntryData {
|
||||
fn new(app: &Application, text_layer: Option<&Layer>) -> Self {
|
||||
let logger = Logger::new("breadcrumbs::Entry");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let text = app.new_view::<ensogl_text::Area>();
|
||||
let display_object = display::object::Instance::new();
|
||||
let text = app.new_view::<ensogl_text::Text>();
|
||||
if let Some(layer) = text_layer {
|
||||
text.add_to_scene_layer(layer);
|
||||
}
|
||||
let ellipsis = ellipsis::View::new(&logger);
|
||||
let separator = separator::View::new(&logger);
|
||||
let ellipsis = ellipsis::View::new();
|
||||
let separator = separator::View::new();
|
||||
let state = default();
|
||||
display_object.add_child(&ellipsis);
|
||||
Self { display_object, state, text, ellipsis, separator }
|
||||
@ -182,13 +181,13 @@ impl EntryData {
|
||||
|
||||
fn update_layout(&self, contour: Contour, text_size: text::Size, text_offset: f32) {
|
||||
let size = contour.size;
|
||||
self.text.set_position_xy(Vector2(text_offset - size.x / 2.0, text_size.raw / 2.0));
|
||||
self.text.set_position_xy(Vector2(text_offset - size.x / 2.0, text_size.value / 2.0));
|
||||
self.separator.size.set(size);
|
||||
self.ellipsis.size.set(size);
|
||||
}
|
||||
|
||||
fn set_default_color(&self, color: color::Rgba) {
|
||||
self.text.set_default_color(color);
|
||||
self.text.set_property_default(color);
|
||||
self.ellipsis.alpha.set(color.alpha);
|
||||
self.separator.color.set(color.into());
|
||||
}
|
||||
@ -198,7 +197,7 @@ impl EntryData {
|
||||
}
|
||||
|
||||
fn set_default_text_size(&self, size: text::Size) {
|
||||
self.text.set_default_text_size(size);
|
||||
self.text.set_property_default(size);
|
||||
}
|
||||
|
||||
fn is_state_change(&self, model: &Model) -> bool {
|
||||
|
@ -27,6 +27,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
@ -157,8 +158,8 @@ pub struct Model {
|
||||
impl Model {
|
||||
/// Constructor.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let display_object = display::object::Instance::new(&app.logger);
|
||||
let mask = mask::View::new(&app.logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let mask = mask::View::new();
|
||||
display_object.add_child(&mask);
|
||||
let grid = GridView::new(app);
|
||||
grid.reset_entries(1, 0);
|
||||
@ -213,7 +214,7 @@ impl Model {
|
||||
text_padding_left: *text_padding,
|
||||
text_size: text::Size::from(*text_size),
|
||||
hover_color:*hover_color,
|
||||
font_name: ImString::new(font),
|
||||
font_name: font.clone(),
|
||||
selected_color: *selected_color,
|
||||
highlight_corners_radius,
|
||||
greyed_out_color: *greyed_out_color,
|
||||
|
@ -174,7 +174,7 @@ impl View {
|
||||
let mut icon = icon.borrow_mut();
|
||||
if !icon.id.contains(&model.icon) {
|
||||
icon.id = Some(model.icon);
|
||||
let shape = model.icon.create_shape(&self.logger, Vector2(icon::SIZE, icon::SIZE));
|
||||
let shape = model.icon.create_shape(Vector2(icon::SIZE, icon::SIZE));
|
||||
shape.strong_color.set(strong_color.into());
|
||||
shape.weak_color.set(weak_color.into());
|
||||
shape.set_position_x(icon::SIZE / 2.0);
|
||||
@ -197,7 +197,7 @@ impl list_view::Entry for View {
|
||||
Params { colors, selection_layer }: &Params,
|
||||
) -> Self {
|
||||
let logger = Logger::new("component_group::Entry");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let icon: Rc<RefCell<CurrentIcon>> = default();
|
||||
let selected_icon: Rc<RefCell<CurrentIcon>> = default();
|
||||
let label = GlyphHighlightedLabel::new(app, style_prefix, &());
|
||||
@ -209,7 +209,7 @@ impl list_view::Entry for View {
|
||||
selected_label.set_label_layer(&layer);
|
||||
display_object.add_child(&selected_label);
|
||||
} else {
|
||||
error!(logger, "Selection layer is dropped.");
|
||||
error!("Selection layer is dropped.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,8 +231,8 @@ impl list_view::Entry for View {
|
||||
label.set_max_width(*width);
|
||||
selected_label.set_max_width(*width);
|
||||
});
|
||||
label.inner.label.set_default_color <+ all(&colors.entry_text, &init)._0();
|
||||
selected_label.inner.label.set_default_color <+ all(&colors.selected.entry_text,&init)._0();
|
||||
label.inner.label.set_property_default <+ all(&colors.entry_text, &init)._0().ref_into_some();
|
||||
selected_label.inner.label.set_property_default <+ all(&colors.selected.entry_text,&init)._0().ref_into_some();
|
||||
eval colors.icon_strong ((&c) icon.borrow().set_strong_color(c));
|
||||
eval colors.selected.icon_strong((&c) selected_icon.borrow().set_strong_color(c));
|
||||
eval colors.icon_weak ((&c) icon.borrow().set_weak_color(c));
|
||||
@ -275,7 +275,7 @@ impl list_view::Entry for View {
|
||||
self.colors.selected.icon_weak.value(),
|
||||
);
|
||||
} else {
|
||||
error!(self.logger, "Cannot add icon shape to a dropped scene layer.");
|
||||
error!("Cannot add icon shape to a dropped scene layer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,11 +44,10 @@
|
||||
///
|
||||
/// fn main () {
|
||||
/// let app = ensogl::application::Application::new("root");
|
||||
/// let logger = Logger::new("icon");
|
||||
/// let icon1 = Id::Icon1.create_shape(&logger, Vector2(10.0, 10.0));
|
||||
/// let icon1 = Id::Icon1.create_shape(Vector2(10.0, 10.0));
|
||||
/// let icon2_id: Id = "Icon2".parse().unwrap();
|
||||
/// assert_eq!(icon2_id, Id::Icon2);
|
||||
/// let icon2 = icon2_id.create_shape(&logger, Vector2(11.0, 11.0));
|
||||
/// let icon2 = icon2_id.create_shape(Vector2(11.0, 11.0));
|
||||
/// app.display.default_scene.add_child(&icon1);
|
||||
/// app.display.default_scene.add_child(&icon2);
|
||||
///
|
||||
@ -81,10 +80,10 @@ macro_rules! define_icons {
|
||||
|
||||
impl Id {
|
||||
/// Create icon's shape with given size.
|
||||
pub fn create_shape(&self, logger: impl AnyLogger, size: Vector2) -> $crate::icon::Any {
|
||||
pub fn create_shape(&self, size: Vector2) -> $crate::icon::Any {
|
||||
match self {$(
|
||||
Self::$variant => {
|
||||
let view = $name::View::new(logger);
|
||||
let view = $name::View::new();
|
||||
view.size.set(size);
|
||||
let strong_color = view.strong_color.clone_ref();
|
||||
let weak_color = view.weak_color.clone_ref();
|
||||
|
@ -41,7 +41,7 @@ impl list_view::Entry for Entry {
|
||||
|
||||
fn new(app: &Application, _style_prefix: &Path, params: &Self::Params) -> Self {
|
||||
let logger = app.logger.sub("NavigatorIcon");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let icon: Rc<RefCell<Option<icon::Any>>> = default();
|
||||
let icon_id = default();
|
||||
let network = frp::Network::new("searcher_list_panel::navigator::Icon");
|
||||
@ -60,7 +60,7 @@ impl list_view::Entry for Entry {
|
||||
fn update(&self, model: &Self::Model) {
|
||||
if !self.icon_id.get().contains(model) {
|
||||
let size = Vector2(icon::SIZE, icon::SIZE);
|
||||
let icon = model.create_shape(&self.logger, size);
|
||||
let icon = model.create_shape(size);
|
||||
icon.strong_color.set(self.params.strong_color.value().into());
|
||||
icon.weak_color.set(self.params.weak_color.value().into());
|
||||
self.display_object.add_child(&icon);
|
||||
|
@ -47,6 +47,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
@ -411,8 +412,13 @@ ensogl::define_endpoints_2! {
|
||||
}
|
||||
|
||||
impl component::Frp<Model> for Frp {
|
||||
fn init(api: &Self::Private, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
||||
let network = &api.network;
|
||||
fn init(
|
||||
network: &frp::Network,
|
||||
api: &Self::Private,
|
||||
app: &Application,
|
||||
model: &Model,
|
||||
style: &StyleWatchFrp,
|
||||
) {
|
||||
let mouse_position = app.display.default_scene.mouse.frp.position.clone_ref();
|
||||
let input = &api.input;
|
||||
let out = &api.output;
|
||||
@ -461,16 +467,16 @@ impl component::Frp<Model> for Frp {
|
||||
model.header.set_font <+ header_text_font;
|
||||
model.selected_header.set_font <+ header_text_font;
|
||||
header_text_size <- all(&header_text_size, &init)._0();
|
||||
model.header.set_default_text_size <+ header_text_size.map(|v| text::Size(*v));
|
||||
model.selected_header.set_default_text_size <+ header_text_size.map(|v| text::Size(*v));
|
||||
model.header.set_property_default <+ header_text_size.map(|v| text::Size(*v)).ref_into_some();
|
||||
model.selected_header.set_property_default <+ header_text_size.map(|v| text::Size(*v)).ref_into_some();
|
||||
_set_header <- input.set_header.map2(&size_and_header_geometry, f!(
|
||||
(text, (size, hdr_geom, _)) {
|
||||
model.header_text.replace(text.clone());
|
||||
model.update_header_width(*size, *hdr_geom);
|
||||
})
|
||||
);
|
||||
model.header.set_default_color <+ colors.header_text;
|
||||
model.selected_header.set_default_color <+ all(&colors.selected.header_text,&init)._0();
|
||||
model.header.set_property_default <+ colors.header_text.ref_into_some();
|
||||
model.selected_header.set_property_default <+ all(&colors.selected.header_text,&init)._0().ref_into_some();
|
||||
eval colors.background((c) model.background.color.set(c.into()));
|
||||
eval colors.background((c) model.header_background.color.set(c.into()));
|
||||
eval colors.selected.background((c) model.selection_background.color.set(c.into()));
|
||||
@ -639,12 +645,12 @@ impl LayersInner {
|
||||
pub struct Model {
|
||||
display_object: display::object::Instance,
|
||||
entries: list_view::ListView<Entry>,
|
||||
header: text::Area,
|
||||
header: text::Text,
|
||||
header_background: header_background::View,
|
||||
header_text: Rc<RefCell<String>>,
|
||||
header_overlay: header_overlay::View,
|
||||
background: background::View,
|
||||
selected_header: text::Area,
|
||||
selected_header: text::Text,
|
||||
selection_header_background: selection_header_background::View,
|
||||
selection_background: background::View,
|
||||
}
|
||||
@ -660,16 +666,16 @@ impl component::Model for Model {
|
||||
"ComponentGroup"
|
||||
}
|
||||
|
||||
fn new(app: &Application, logger: &Logger) -> Self {
|
||||
fn new(app: &Application) -> Self {
|
||||
let header_text = default();
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let header_overlay = header_overlay::View::new(&logger);
|
||||
let background = background::View::new(&logger);
|
||||
let selection_background = background::View::new(&logger);
|
||||
let header_background = header_background::View::new(&logger);
|
||||
let selection_header_background = selection_header_background::View::new(&logger);
|
||||
let header = text::Area::new(app);
|
||||
let selected_header = text::Area::new(app);
|
||||
let display_object = display::object::Instance::new();
|
||||
let header_overlay = header_overlay::View::new();
|
||||
let background = background::View::new();
|
||||
let selection_background = background::View::new();
|
||||
let header_background = header_background::View::new();
|
||||
let selection_header_background = selection_header_background::View::new();
|
||||
let header = text::Text::new(app);
|
||||
let selected_header = text::Text::new(app);
|
||||
let entries = app.new_view::<list_view::ListView<Entry>>();
|
||||
entries.set_style_prefix(entry::STYLE_PATH);
|
||||
entries.set_background_color(HOVER_COLOR);
|
||||
@ -785,9 +791,8 @@ impl Model {
|
||||
let header_padding_left = header_geometry.padding_left;
|
||||
let header_padding_right = header_geometry.padding_right;
|
||||
let max_text_width = size.x - header_padding_left - header_padding_right;
|
||||
let header_text = self.header_text.borrow().clone();
|
||||
self.header.set_content_truncated(header_text.clone(), max_text_width);
|
||||
self.selected_header.set_content_truncated(header_text, max_text_width);
|
||||
self.header.set_view_width(max_text_width);
|
||||
self.selected_header.set_view_width(max_text_width);
|
||||
}
|
||||
|
||||
fn selection_position(
|
||||
|
@ -113,7 +113,7 @@ pub struct Model {
|
||||
pub kind: Kind,
|
||||
pub color: color::Rgba,
|
||||
pub caption: ImString,
|
||||
pub highlighted: Rc<Vec<text::Range<text::Bytes>>>,
|
||||
pub highlighted: Rc<Vec<text::Range<text::Byte>>>,
|
||||
pub icon: Option<icon::Id>,
|
||||
pub group_id: GroupId,
|
||||
}
|
||||
@ -179,9 +179,7 @@ struct CurrentIcon {
|
||||
impl Default for CurrentIcon {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
display_object: display::object::Instance::new(Logger::new(
|
||||
"component_browser_entry_icon",
|
||||
)),
|
||||
display_object: display::object::Instance::new(),
|
||||
strong_color: default(),
|
||||
weak_color: default(),
|
||||
shape: default(),
|
||||
@ -195,10 +193,7 @@ impl CurrentIcon {
|
||||
if self.id != new_icon {
|
||||
self.id = new_icon;
|
||||
if let Some(icon_id) = new_icon {
|
||||
let shape = icon_id.create_shape(
|
||||
Logger::new("ComponentBrowserEntry"),
|
||||
Vector2(icon::SIZE, icon::SIZE),
|
||||
);
|
||||
let shape = icon_id.create_shape(Vector2(icon::SIZE, icon::SIZE));
|
||||
tracing::debug!("Creating new icon {icon_id:?}.");
|
||||
shape.strong_color.set(self.strong_color.into());
|
||||
shape.weak_color.set(self.weak_color.into());
|
||||
@ -238,7 +233,7 @@ impl display::Object for CurrentIcon {
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Data {
|
||||
display_object: display::object::Instance,
|
||||
label: text::Area,
|
||||
label: text::Text,
|
||||
background: background::View,
|
||||
icon: Rc<RefCell<CurrentIcon>>,
|
||||
style: StyleWatchFrp,
|
||||
@ -246,14 +241,15 @@ pub struct Data {
|
||||
|
||||
impl Data {
|
||||
fn new(app: &Application, text_layer: Option<&Layer>) -> Self {
|
||||
let display_object = display::object::Instance::new(Logger::new("ComponentGroupEntry"));
|
||||
let label = app.new_view::<text::Area>();
|
||||
let background = background::View::new(Logger::new("ComponentGroupEntry"));
|
||||
let display_object = display::object::Instance::new();
|
||||
let label = app.new_view::<text::Text>();
|
||||
let background = background::View::new();
|
||||
let icon = CurrentIcon::default();
|
||||
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
display_object.add_child(&background);
|
||||
display_object.add_child(&icon);
|
||||
display_object.add_child(&label);
|
||||
label.set_long_text_truncation_mode(true);
|
||||
if let Some(layer) = text_layer {
|
||||
label.add_to_scene_layer(layer);
|
||||
}
|
||||
@ -280,7 +276,7 @@ impl Data {
|
||||
let left = -entry_size.x / 2.0 + style.padding;
|
||||
self.icon.borrow().set_position_x(left + style.icon_size / 2.0);
|
||||
let text_x = Self::text_x_position(kind, style);
|
||||
self.label.set_position_xy(Vector2(text_x, style.text_size.raw / 2.0));
|
||||
self.label.set_position_xy(Vector2(text_x, style.text_size.value / 2.0));
|
||||
}
|
||||
|
||||
fn contour(kind: Kind, style: &Style, entry_size: Vector2) -> Contour {
|
||||
@ -401,7 +397,7 @@ impl grid_view::Entry for View {
|
||||
});
|
||||
let colors = Colors::from_main_color(network, &data.style, &color, &style, &is_dimmed);
|
||||
eval colors.background ((c) data.background.color.set(c.into()));
|
||||
data.label.set_default_color <+ colors.text;
|
||||
data.label.set_property_default <+ colors.text.ref_into_some();
|
||||
eval colors.icon_strong ((c) data.icon.borrow_mut().set_strong_color(*c));
|
||||
eval colors.icon_weak ((c) data.icon.borrow_mut().set_weak_color(*c));
|
||||
out.hover_highlight_color <+ colors.hover_highlight;
|
||||
@ -416,9 +412,10 @@ impl grid_view::Entry for View {
|
||||
// === Icon and Text ===
|
||||
|
||||
max_text_width <- kind_and_style.map(|(kind, style)| Data::max_text_width(*kind, style));
|
||||
caption <- input.set_model.map(|m| m.caption.to_string());
|
||||
caption <- input.set_model.map(|m| m.caption.clone_ref());
|
||||
icon <- input.set_model.map(|m| m.icon);
|
||||
data.label.set_content_truncated <+ all(caption, max_text_width);
|
||||
data.label.set_content <+ caption;
|
||||
data.label.set_view_width <+ max_text_width.some();
|
||||
content_changed <- data.label.content.constant(());
|
||||
style_changed <- style.constant(());
|
||||
highlight_range <= all_with3(
|
||||
@ -427,12 +424,12 @@ impl grid_view::Entry for View {
|
||||
&style_changed,
|
||||
|m, (), ()| m.highlighted.deref().clone()
|
||||
);
|
||||
data.label.set_sdf_bold <+ highlight_range.map2(&style, |range, s| {
|
||||
(*range, text::style::SdfBold::new(s.highlight_bold))
|
||||
data.label.set_property <+ highlight_range.map2(&style, |range, s| {
|
||||
(range.into(), Some(text::SdfWeight::new(s.highlight_bold).into()))
|
||||
});
|
||||
data.label.set_default_text_size <+ style.map(|s| s.text_size);
|
||||
data.label.set_property_default <+ style.map(|s| s.text_size).ref_into_some();
|
||||
eval icon ((&icon) data.icon.borrow_mut().update(icon));
|
||||
data.label.set_font <+ style.map(|s| s.font.to_string()).on_change();
|
||||
data.label.set_font <+ style.map(|s| s.font.clone_ref()).on_change();
|
||||
}
|
||||
Self { frp, data }
|
||||
}
|
||||
|
@ -134,12 +134,12 @@ ensogl::define_endpoints_2! {
|
||||
|
||||
impl<const COLUMNS: usize> component::Frp<Model<COLUMNS>> for Frp {
|
||||
fn init(
|
||||
network: &frp::Network,
|
||||
api: &Self::Private,
|
||||
_app: &Application,
|
||||
model: &Model<COLUMNS>,
|
||||
style: &StyleWatchFrp,
|
||||
) {
|
||||
let network = &api.network;
|
||||
let input = &api.input;
|
||||
let out = &api.output;
|
||||
let colors = Colors::from_main_color(network, style, &input.set_color, &input.set_dimmed);
|
||||
@ -378,11 +378,11 @@ impl<const COLUMNS: usize> component::Model for Model<COLUMNS> {
|
||||
"WideComponentGroupView"
|
||||
}
|
||||
|
||||
fn new(app: &Application, logger: &Logger) -> Self {
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let background = background::View::new(&logger);
|
||||
fn new(app: &Application) -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let background = background::View::new();
|
||||
display_object.add_child(&background);
|
||||
let selection_background = background::View::new(&logger);
|
||||
let selection_background = background::View::new();
|
||||
display_object.add_child(&selection_background);
|
||||
let columns: Vec<_> = (0..COLUMNS).map(|i| Column::new(app, ColumnId::new(i))).collect();
|
||||
let columns = Rc::new(columns);
|
||||
|
@ -69,9 +69,8 @@ pub struct Model {
|
||||
|
||||
impl Model {
|
||||
fn new(app: &Application) -> Self {
|
||||
let logger = Logger::new("ColumnGrid");
|
||||
let app = app.clone_ref();
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
Self { app, display_object, content: default(), size: default(), layers: default() }
|
||||
}
|
||||
|
||||
@ -207,7 +206,7 @@ impl component::Model for Model {
|
||||
"ColumnGrid"
|
||||
}
|
||||
|
||||
fn new(app: &Application, _logger: &DefaultWarningLogger) -> Self {
|
||||
fn new(app: &Application) -> Self {
|
||||
Self::new(app)
|
||||
}
|
||||
}
|
||||
@ -295,12 +294,12 @@ fn get_layout(
|
||||
|
||||
impl component::Frp<Model> for Frp {
|
||||
fn init(
|
||||
network: &frp::Network,
|
||||
frp_api: &<Self as API>::Private,
|
||||
_app: &Application,
|
||||
model: &Model,
|
||||
style: &StyleWatchFrp,
|
||||
) {
|
||||
let network = &frp_api.network;
|
||||
let input = &frp_api.input;
|
||||
let (layout_update, init) = get_layout(network, style);
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#![recursion_limit = "512"]
|
||||
// === Features ===
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(cell_update)]
|
||||
@ -31,6 +30,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
#![allow(clippy::precedence)]
|
||||
@ -184,7 +184,7 @@ struct Style {
|
||||
section_heading_size: f32,
|
||||
section_heading_offset: f32,
|
||||
section_heading_text_offset: f32,
|
||||
section_heading_font: String,
|
||||
section_heading_font: ImString,
|
||||
section_heading_color: color::Rgba,
|
||||
section_divider_color: color::Rgba,
|
||||
|
||||
@ -431,13 +431,13 @@ impl Model {
|
||||
fn new(app: &Application) -> Self {
|
||||
let logger = Logger::new("ComponentBrowserPanel");
|
||||
let app = app.clone_ref();
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let navigator = default();
|
||||
let groups_wrapper = component_group::set::Wrapper::new();
|
||||
|
||||
let background = background::View::new(&logger);
|
||||
let background = background::View::new();
|
||||
display_object.add_child(&background);
|
||||
let navigator_shadow = navigator_shadow::View::new(&logger);
|
||||
let navigator_shadow = navigator_shadow::View::new();
|
||||
display_object.add_child(&navigator_shadow);
|
||||
|
||||
let favourites_section = Self::init_column_section(&app);
|
||||
@ -465,7 +465,7 @@ impl Model {
|
||||
display_object.add_child(&breadcrumbs);
|
||||
breadcrumbs.show_ellipsis(true);
|
||||
|
||||
let selection = selection_box::View::new(&app.logger);
|
||||
let selection = selection_box::View::new();
|
||||
scroll_area.add_child(&selection);
|
||||
layers.selection_mask.add_exclusive(&selection);
|
||||
|
||||
@ -702,7 +702,7 @@ impl component::Model for Model {
|
||||
"ComponentBrowserPanel"
|
||||
}
|
||||
|
||||
fn new(app: &Application, _logger: &DefaultWarningLogger) -> Self {
|
||||
fn new(app: &Application) -> Self {
|
||||
Self::new(app)
|
||||
}
|
||||
}
|
||||
@ -717,7 +717,7 @@ impl component::Model for Model {
|
||||
/// provides some utility functions for shape and layout handling.
|
||||
#[derive(Clone, Debug)]
|
||||
struct LabeledSection<T> {
|
||||
pub label: text::Area,
|
||||
pub label: text::Text,
|
||||
pub divider: hline::View,
|
||||
pub content: T,
|
||||
}
|
||||
@ -737,16 +737,15 @@ type ColumnSection = LabeledSection<column_grid::ColumnGrid>;
|
||||
|
||||
impl<T: CloneRef> LabeledSection<T> {
|
||||
pub fn new(content: T, app: &Application) -> Self {
|
||||
let logger = Logger::new("LabeledSection");
|
||||
let label = text::Area::new(app);
|
||||
let divider = hline::View::new(logger);
|
||||
let label = text::Text::new(app);
|
||||
let divider = hline::View::new();
|
||||
Self { label, divider, content }
|
||||
}
|
||||
|
||||
fn set_style(&self, style: &Style) {
|
||||
self.divider.size.set(Vector2(INFINITE, style.section_divider_height));
|
||||
self.label.set_default_color(style.section_heading_color);
|
||||
self.label.set_default_text_size(text::Size(style.section_heading_size));
|
||||
self.label.set_property_default(style.section_heading_color);
|
||||
self.label.set_property_default(text::Size(style.section_heading_size));
|
||||
self.label.set_font(style.section_heading_font.clone());
|
||||
self.label.set_position_x(style.content_padding);
|
||||
}
|
||||
@ -963,12 +962,12 @@ define_endpoints_2! {
|
||||
|
||||
impl component::Frp<Model> for Frp {
|
||||
fn init(
|
||||
network: &frp::Network,
|
||||
frp_api: &<Self as API>::Private,
|
||||
app: &Application,
|
||||
model: &Model,
|
||||
style: &StyleWatchFrp,
|
||||
) {
|
||||
let network = &frp_api.network;
|
||||
let header_height = style.get_number(component_group_theme::header::height);
|
||||
let layout_frp = Style::from_theme(network, style);
|
||||
let scene = &app.display.default_scene;
|
||||
|
@ -92,7 +92,7 @@ const BOTTOM_BUTTONS: [icon::Id; 3] = [icon::Id::SubModules, icon::Id::LocalScop
|
||||
|
||||
impl Navigator {
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let display_object = display::object::Instance::new(&app.logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let top_buttons = app.new_view::<list_view::ListView<icon::Entry>>();
|
||||
let bottom_buttons = app.new_view::<list_view::ListView<icon::Entry>>();
|
||||
top_buttons.set_style_prefix(list_panel_theme::navigator_list_view::HERE.str);
|
||||
|
@ -7,6 +7,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
|
@ -3,6 +3,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
@ -16,7 +17,7 @@ use ensogl_core::display::shape::*;
|
||||
use ensogl_core::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use enso_text::Bytes;
|
||||
use enso_text::Byte;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
@ -90,7 +91,7 @@ struct MockEntries {
|
||||
impl MockEntries {
|
||||
fn new(count: usize) -> Rc<Self> {
|
||||
const HIGHLIGHTED_ENTRY_NAME: &str = "convert";
|
||||
const HIGHLIGHTED_RANGE: Range<Bytes> = Bytes(0)..Bytes(3);
|
||||
const HIGHLIGHTED_RANGE: Range<Byte> = Byte(0)..Byte(3);
|
||||
Rc::new(Self {
|
||||
entries: PREPARED_ITEMS
|
||||
.iter()
|
||||
@ -284,7 +285,7 @@ fn init(app: &Application) {
|
||||
// FIXME(#182193824): This is a workaround for a bug. See the docs of the
|
||||
// [`transparent_circle`].
|
||||
{
|
||||
let transparent_circle = transparent_circle::View::new(&app.logger);
|
||||
let transparent_circle = transparent_circle::View::new();
|
||||
transparent_circle.size.set(Vector2(150.0, 150.0));
|
||||
transparent_circle.set_position_xy(Vector2(200.0, -150.0));
|
||||
scroll_area.content().add_child(&transparent_circle);
|
||||
|
@ -3,7 +3,6 @@
|
||||
#![recursion_limit = "512"]
|
||||
// === Features ===
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(cell_update)]
|
||||
@ -22,6 +21,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
#![allow(clippy::precedence)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use ensogl::system::web::traits::*;
|
||||
use ide_view_component_group::prelude::*;
|
||||
@ -45,7 +46,6 @@ mod frame {
|
||||
#[wasm_bindgen]
|
||||
#[allow(dead_code)]
|
||||
pub fn entry_point_searcher_icons() {
|
||||
let logger = Logger::new("Icons example");
|
||||
let app = Application::new("root");
|
||||
ensogl_hardcoded_theme::builtin::dark::register(&app);
|
||||
ensogl_hardcoded_theme::builtin::light::register(&app);
|
||||
@ -79,7 +79,7 @@ pub fn entry_point_searcher_icons() {
|
||||
|
||||
let mut x = -300.0;
|
||||
icon::Id::for_each(|id| {
|
||||
let shape = id.create_shape(&logger, Vector2(icon::SIZE, icon::SIZE));
|
||||
let shape = id.create_shape(Vector2(icon::SIZE, icon::SIZE));
|
||||
shape.strong_color.set(color::Rgba(0.243, 0.541, 0.160, 1.0).into());
|
||||
shape.weak_color.set(color::Rgba(0.655, 0.788, 0.624, 1.0).into());
|
||||
shape.set_position_x(x);
|
||||
|
@ -5,6 +5,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
@ -107,7 +108,7 @@ fn init(app: &Application) {
|
||||
|
||||
app.views.register::<root::View>();
|
||||
app.views.register::<project::View>();
|
||||
app.views.register::<text::Area>();
|
||||
app.views.register::<text::Text>();
|
||||
app.views.register::<GraphEditor>();
|
||||
let root_view = app.new_view::<root::View>();
|
||||
let project_view = root_view.project();
|
||||
|
@ -22,6 +22,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
#![allow(clippy::precedence)]
|
||||
@ -128,7 +129,7 @@ impl EntryModelProvider {
|
||||
}
|
||||
};
|
||||
let highlighted = if row == 4 {
|
||||
vec![text::Range::new(text::Bytes(2), text::Bytes(4))]
|
||||
vec![text::Range::new(text::Byte(2), text::Byte(4))]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
|
@ -3,6 +3,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
|
@ -66,7 +66,7 @@ impl BubbleChartModel {
|
||||
|
||||
// Avoid re-creating views, if we have already created some before.
|
||||
let mut views = self.views.borrow_mut();
|
||||
views.resize_with(data_inner.len(), || shape::View::new(&self.logger));
|
||||
views.resize_with(data_inner.len(), shape::View::new);
|
||||
|
||||
// TODO[mm] this is somewhat inefficient, as the canvas for each bubble is too large.
|
||||
// But this ensures that we can get a cropped view area and avoids an issue with the data
|
||||
@ -106,7 +106,7 @@ impl BubbleChart {
|
||||
|
||||
pub fn new(scene: &Scene) -> Self {
|
||||
let logger = Logger::new("bubble");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let views = Rc::new(RefCell::new(vec![]));
|
||||
let network = frp::Network::new("bubble_chart");
|
||||
let frp = visualization::instance::Frp::new(&network);
|
||||
|
@ -194,15 +194,15 @@ impl BreadcrumbsModel {
|
||||
let scene = &app.display.default_scene;
|
||||
let project_name = app.new_view();
|
||||
let logger = Logger::new("Breadcrumbs");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let root = display::object::Instance::new(&logger);
|
||||
let breadcrumbs_container = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let root = display::object::Instance::new();
|
||||
let breadcrumbs_container = display::object::Instance::new();
|
||||
let scene = scene.clone_ref();
|
||||
let breadcrumbs = default();
|
||||
let frp_inputs = frp.input.clone_ref();
|
||||
let current_index = default();
|
||||
let camera = scene.camera().clone_ref();
|
||||
let background = background::View::new(&logger);
|
||||
let background = background::View::new();
|
||||
let gap_width = default();
|
||||
|
||||
scene.layers.panel.add_exclusive(&background);
|
||||
@ -288,7 +288,7 @@ impl BreadcrumbsModel {
|
||||
/// where `popped_count` is the number of breadcrumbs in the right side of `index` that needs to
|
||||
/// be popped or a list of `LocalCall`s identifying the breadcrumbs we need to push.
|
||||
fn select_breadcrumb(&self, index: usize) -> (usize, Vec<Option<LocalCall>>) {
|
||||
debug!(self.logger, "Selecting breadcrumb #{index}.");
|
||||
debug!("Selecting breadcrumb #{index}.");
|
||||
let current_index = self.current_index.get();
|
||||
match index.cmp(¤t_index) {
|
||||
Ordering::Less => (current_index - index, default()),
|
||||
@ -310,7 +310,7 @@ impl BreadcrumbsModel {
|
||||
if info.is_some() {
|
||||
local_calls.push(info);
|
||||
} else {
|
||||
error!(self.logger, "LocalCall info is not present.");
|
||||
error!("LocalCall info is not present.");
|
||||
self.remove_breadcrumbs_history_beginning_from(index);
|
||||
break;
|
||||
}
|
||||
@ -336,9 +336,9 @@ impl BreadcrumbsModel {
|
||||
.contains_if(|breadcrumb| breadcrumb.info.expression_id == *expression_id);
|
||||
|
||||
if breadcrumb_exists {
|
||||
debug!(self.logger, "Entering an existing {method_pointer.name} breadcrumb.");
|
||||
debug!("Entering an existing {} breadcrumb.", method_pointer.name);
|
||||
} else {
|
||||
debug!(self.logger, "Creating a new {method_pointer.name} breadcrumb.");
|
||||
debug!("Creating a new {} breadcrumb.", method_pointer.name);
|
||||
self.remove_breadcrumbs_history_beginning_from(self.current_index.get());
|
||||
let breadcrumb = Breadcrumb::new(&self.app, method_pointer, expression_id);
|
||||
let network = &breadcrumb.frp.network;
|
||||
@ -351,7 +351,7 @@ impl BreadcrumbsModel {
|
||||
);
|
||||
}
|
||||
|
||||
debug!(self.logger, "Pushing {breadcrumb.info.method_pointer.name} breadcrumb.");
|
||||
debug!("Pushing {} breadcrumb.", breadcrumb.info.method_pointer.name);
|
||||
breadcrumb.set_position_x(self.breadcrumbs_container_width().round());
|
||||
self.breadcrumbs_container.add_child(&breadcrumb);
|
||||
self.breadcrumbs.borrow_mut().push(breadcrumb);
|
||||
@ -413,9 +413,9 @@ impl BreadcrumbsModel {
|
||||
/// Pops a breadcrumb and returns the index of the previously selected breadcrumb, and the
|
||||
/// index of the newly selected one in the form of (old,new).
|
||||
fn pop_breadcrumb(&self) -> Option<(usize, usize)> {
|
||||
debug!(self.logger, "Popping {self.current_index.get()}");
|
||||
debug!("Popping {}", self.current_index.get());
|
||||
(self.current_index.get() > 0).as_option().map(|_| {
|
||||
debug!(self.logger, "Popping breadcrumb view.");
|
||||
debug!("Popping breadcrumb view.");
|
||||
let old_index = self.current_index.get();
|
||||
let new_index = old_index - 1;
|
||||
self.current_index.set(new_index);
|
||||
@ -426,7 +426,7 @@ impl BreadcrumbsModel {
|
||||
|
||||
fn remove_breadcrumbs_history_beginning_from(&self, index: usize) {
|
||||
for breadcrumb in self.breadcrumbs.borrow_mut().split_off(index) {
|
||||
debug!(self.logger, "Removing {breadcrumb.info.method_pointer.name}.");
|
||||
debug!("Removing {}.", breadcrumb.info.method_pointer.name);
|
||||
breadcrumb.unset_parent();
|
||||
}
|
||||
self.update_layout();
|
||||
|
@ -259,12 +259,11 @@ pub struct BreadcrumbInfo {
|
||||
/// Breadcrumbs model.
|
||||
#[derive(Debug, Clone, CloneRef)]
|
||||
pub struct BreadcrumbModel {
|
||||
logger: Logger,
|
||||
display_object: display::object::Instance,
|
||||
view: background::View,
|
||||
separator: separator::View,
|
||||
icon: icon::View,
|
||||
label: text::Area,
|
||||
label: text::Text,
|
||||
animations: Animations,
|
||||
style: StyleWatch,
|
||||
/// Breadcrumb information such as name and expression id.
|
||||
@ -283,13 +282,11 @@ impl BreadcrumbModel {
|
||||
expression_id: &ast::Id,
|
||||
) -> Self {
|
||||
let scene = &app.display.default_scene;
|
||||
let logger = Logger::new("Breadcrumbs");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let view_logger = Logger::new_sub(&logger, "view_logger");
|
||||
let view = background::View::new(&view_logger);
|
||||
let icon = icon::View::new(&view_logger);
|
||||
let separator = separator::View::new(&view_logger);
|
||||
let label = app.new_view::<text::Area>();
|
||||
let display_object = display::object::Instance::new();
|
||||
let view = background::View::new();
|
||||
let icon = icon::View::new();
|
||||
let separator = separator::View::new();
|
||||
let label = app.new_view::<text::Text>();
|
||||
let expression_id = *expression_id;
|
||||
let method_pointer = method_pointer.clone();
|
||||
let info = Rc::new(BreadcrumbInfo { method_pointer, expression_id });
|
||||
@ -323,7 +320,6 @@ impl BreadcrumbModel {
|
||||
// system (#795)
|
||||
let style = StyleWatch::new(&scene.style_sheet);
|
||||
Self {
|
||||
logger,
|
||||
display_object,
|
||||
view,
|
||||
separator,
|
||||
@ -350,9 +346,9 @@ impl BreadcrumbModel {
|
||||
|
||||
let color = if self.is_selected() { full_color } else { transparent_color };
|
||||
|
||||
self.label.set_default_color.emit(color);
|
||||
self.label.set_default_text_size(text::style::Size::from(TEXT_SIZE));
|
||||
self.label.single_line(true);
|
||||
self.label.set_property_default(color);
|
||||
self.label.set_property_default(text::formatting::Size::from(TEXT_SIZE));
|
||||
self.label.set_single_line_mode(true);
|
||||
self.label.set_position_x(ICON_RADIUS + ICON_RIGHT_MARGIN);
|
||||
self.label.set_position_y(TEXT_SIZE / 2.0);
|
||||
self.label.set_content(&self.info.method_pointer.name);
|
||||
@ -403,7 +399,7 @@ impl BreadcrumbModel {
|
||||
|
||||
fn set_color(&self, value: Vector4<f32>) {
|
||||
let color = color::Rgba::from(value);
|
||||
self.label.set_color_all(color);
|
||||
self.label.set_property(.., color);
|
||||
self.icon.red.set(color.red);
|
||||
self.icon.green.set(color.green);
|
||||
self.icon.blue.set(color.blue);
|
||||
|
@ -9,7 +9,6 @@ use crate::component::breadcrumbs::TEXT_SIZE;
|
||||
use crate::component::breadcrumbs::VERTICAL_MARGIN;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl::application;
|
||||
use ensogl::application::shortcut;
|
||||
use ensogl::application::Application;
|
||||
use ensogl::data::color;
|
||||
@ -18,9 +17,8 @@ use ensogl::display::object::ObjectOps;
|
||||
use ensogl::gui::cursor;
|
||||
use ensogl::DEPRECATED_Animation;
|
||||
use ensogl_component::text;
|
||||
use ensogl_component::text::style::Size as TextSize;
|
||||
use ensogl_component::text::formatting::Size as TextSize;
|
||||
use ensogl_hardcoded_theme as theme;
|
||||
use logger::DefaultWarningLogger as Logger;
|
||||
|
||||
|
||||
|
||||
@ -122,11 +120,10 @@ impl Animations {
|
||||
#[allow(missing_docs)]
|
||||
struct ProjectNameModel {
|
||||
app: Application,
|
||||
logger: Logger,
|
||||
display_object: display::object::Instance,
|
||||
view: background::View,
|
||||
style: StyleWatch,
|
||||
text_field: text::Area,
|
||||
text_field: text::Text,
|
||||
project_name: Rc<RefCell<String>>,
|
||||
}
|
||||
|
||||
@ -135,29 +132,27 @@ impl ProjectNameModel {
|
||||
fn new(app: &Application) -> Self {
|
||||
let app = app.clone_ref();
|
||||
let scene = &app.display.default_scene;
|
||||
let logger = Logger::new("ProjectName");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||
// system (#795)
|
||||
let style = StyleWatch::new(&scene.style_sheet);
|
||||
let base_color = style.get_color(theme::graph_editor::breadcrumbs::transparent);
|
||||
let text_size: TextSize = TEXT_SIZE.into();
|
||||
let text_field = app.new_view::<text::Area>();
|
||||
text_field.set_default_color.emit(base_color);
|
||||
text_field.set_default_text_size(text_size);
|
||||
text_field.single_line(true);
|
||||
let text_field = app.new_view::<text::Text>();
|
||||
text_field.set_property_default(base_color);
|
||||
text_field.set_property_default(text_size);
|
||||
text_field.set_single_line_mode(true);
|
||||
|
||||
text_field.remove_from_scene_layer(&scene.layers.main);
|
||||
text_field.add_to_scene_layer(&scene.layers.panel_text);
|
||||
text_field.hover();
|
||||
|
||||
let view_logger = Logger::new_sub(&logger, "view_logger");
|
||||
let view = background::View::new(&view_logger);
|
||||
let view = background::View::new();
|
||||
|
||||
scene.layers.panel.add_exclusive(&view);
|
||||
|
||||
let project_name = default();
|
||||
Self { app, logger, display_object, view, style, text_field, project_name }.init()
|
||||
Self { app, display_object, view, style, text_field, project_name }.init()
|
||||
}
|
||||
|
||||
/// Compute the width of the ProjectName view.
|
||||
@ -190,7 +185,7 @@ impl ProjectNameModel {
|
||||
|
||||
/// Revert the text field content to the last committed project name.
|
||||
fn reset_name(&self) {
|
||||
debug!(self.logger, "Resetting project name.");
|
||||
debug!("Resetting project name.");
|
||||
self.update_text_field_content(self.project_name.borrow().as_str());
|
||||
}
|
||||
|
||||
@ -201,7 +196,7 @@ impl ProjectNameModel {
|
||||
}
|
||||
|
||||
fn set_color(&self, value: color::Rgba) {
|
||||
self.text_field.set_default_color(value);
|
||||
self.text_field.set_property_default(value);
|
||||
}
|
||||
|
||||
fn set_position(&self, value: Vector3<f32>) {
|
||||
@ -211,7 +206,7 @@ impl ProjectNameModel {
|
||||
/// Change the text field content and commit the given name.
|
||||
fn rename(&self, name: impl Str) {
|
||||
let name = name.into();
|
||||
debug!(self.logger, "Renaming: '{name}'.");
|
||||
debug!("Renaming: '{name}'.");
|
||||
self.update_text_field_content(&name);
|
||||
self.commit(name);
|
||||
}
|
||||
@ -219,7 +214,7 @@ impl ProjectNameModel {
|
||||
/// Confirm the given name as the current project name.
|
||||
fn commit<T: Into<String>>(&self, name: T) {
|
||||
let name = name.into();
|
||||
debug!(self.logger, "Committing name: '{name}'.");
|
||||
debug!("Committing name: '{name}'.");
|
||||
*self.project_name.borrow_mut() = name;
|
||||
}
|
||||
}
|
||||
@ -341,14 +336,14 @@ impl ProjectName {
|
||||
on_mouse_over_and_editable <- all(frp.output.is_hovered,editable).map(|(a,b)| *a && *b);
|
||||
mouse_over_while_editing <- on_mouse_over_and_editable.gate(&on_mouse_over_and_editable);
|
||||
frp.output.source.pointer_style <+ mouse_over_while_editing.map(|_|
|
||||
cursor::Style::new_text_cursor()
|
||||
cursor::Style::cursor()
|
||||
);
|
||||
no_mouse_or_edit <- on_mouse_over_and_editable.gate_not(&on_mouse_over_and_editable);
|
||||
frp.output.source.pointer_style <+ no_mouse_or_edit.map(|_|
|
||||
cursor::Style::default()
|
||||
);
|
||||
frp.output.source.pointer_style <+ frp.input.start_editing.gate(&frp.output.is_hovered).map(|_|
|
||||
cursor::Style::new_text_cursor()
|
||||
cursor::Style::cursor()
|
||||
);
|
||||
}
|
||||
|
||||
@ -372,7 +367,7 @@ impl Deref for ProjectName {
|
||||
}
|
||||
}
|
||||
|
||||
impl application::command::FrpNetworkProvider for ProjectName {
|
||||
impl FrpNetworkProvider for ProjectName {
|
||||
fn network(&self) -> &frp::Network {
|
||||
&self.frp.network
|
||||
}
|
||||
|
@ -762,8 +762,8 @@ macro_rules! define_components {
|
||||
/// Constructor.
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn new(logger:Logger) -> Self {
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
$(let $field = <$field_type>::new(Logger::new_sub(&logger,stringify!($field)));)*
|
||||
let display_object = display::object::Instance::new();
|
||||
$(let $field = <$field_type>::new();)*
|
||||
$(display_object.add_child(&$field);)*
|
||||
let mut shape_view_events:Vec<PointerTarget> = Vec::default();
|
||||
$(shape_view_events.push($field.events.clone_ref());)*
|
||||
@ -1283,10 +1283,10 @@ impl EdgeModelData {
|
||||
#[profile(Debug)]
|
||||
pub fn new(scene: &Scene, network: &frp::Network) -> Self {
|
||||
let logger = Logger::new("edge");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let front = Front::new(Logger::new_sub(&logger, "front"));
|
||||
let back = Back::new(Logger::new_sub(&logger, "back"));
|
||||
let joint = joint::View::new(Logger::new_sub(&logger, "joint"));
|
||||
let joint = joint::View::new();
|
||||
|
||||
display_object.add_child(&front);
|
||||
display_object.add_child(&back);
|
||||
|
@ -25,7 +25,6 @@ use ensogl::gui;
|
||||
use ensogl::Animation;
|
||||
use ensogl_component::shadow;
|
||||
use ensogl_component::text;
|
||||
use ensogl_component::text::Text;
|
||||
use ensogl_hardcoded_theme as theme;
|
||||
use ensogl_hardcoded_theme;
|
||||
use std::f32::EPSILON;
|
||||
@ -95,7 +94,7 @@ const UNRESOLVED_SYMBOL_TYPE: &str = "Builtins.Main.Unresolved_Symbol";
|
||||
///
|
||||
/// This is just a plain string, as this is what text area expects and node just redirects this
|
||||
/// value,
|
||||
pub type Comment = String;
|
||||
pub type Comment = ImString;
|
||||
|
||||
|
||||
|
||||
@ -324,7 +323,7 @@ ensogl::define_endpoints_2! {
|
||||
/// Press event. Emitted when user clicks on non-active part of the node, like its
|
||||
/// background. In edit mode, the whole node area is considered non-active.
|
||||
background_press (),
|
||||
expression (Text),
|
||||
expression (enso_text::Rope),
|
||||
comment (Comment),
|
||||
skip (bool),
|
||||
freeze (bool),
|
||||
@ -447,7 +446,7 @@ pub struct NodeModel {
|
||||
pub action_bar: action_bar::ActionBar,
|
||||
pub vcs_indicator: vcs::StatusIndicator,
|
||||
pub style: StyleWatchFrp,
|
||||
pub comment: text::Area,
|
||||
pub comment: text::Text,
|
||||
}
|
||||
|
||||
impl NodeModel {
|
||||
@ -481,17 +480,13 @@ impl NodeModel {
|
||||
let scene = &app.display.default_scene;
|
||||
let logger = Logger::new("node");
|
||||
|
||||
let main_logger = Logger::new_sub(&logger, "main_area");
|
||||
let drag_logger = Logger::new_sub(&logger, "drag_area");
|
||||
let error_indicator_logger = Logger::new_sub(&logger, "error_indicator");
|
||||
|
||||
let error_indicator = error_shape::View::new(&error_indicator_logger);
|
||||
let error_indicator = error_shape::View::new();
|
||||
let profiling_label = ProfilingLabel::new(app);
|
||||
let backdrop = backdrop::View::new(&main_logger);
|
||||
let background = background::View::new(&main_logger);
|
||||
let drag_area = drag_area::View::new(&drag_logger);
|
||||
let backdrop = backdrop::View::new();
|
||||
let background = background::View::new();
|
||||
let drag_area = drag_area::View::new();
|
||||
let vcs_indicator = vcs::StatusIndicator::new(app);
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
|
||||
display_object.add_child(&profiling_label);
|
||||
display_object.add_child(&drag_area);
|
||||
@ -499,7 +494,7 @@ impl NodeModel {
|
||||
display_object.add_child(&background);
|
||||
display_object.add_child(&vcs_indicator);
|
||||
|
||||
let input = input::Area::new(&logger, app);
|
||||
let input = input::Area::new(app);
|
||||
let visualization = visualization::Container::new(&logger, app, registry);
|
||||
|
||||
display_object.add_child(&visualization);
|
||||
@ -509,16 +504,16 @@ impl NodeModel {
|
||||
let (x, y) = ERROR_VISUALIZATION_SIZE;
|
||||
error_visualization.set_size.emit(Vector2(x, y));
|
||||
|
||||
let action_bar = action_bar::ActionBar::new(&logger, app);
|
||||
let action_bar = action_bar::ActionBar::new(app);
|
||||
display_object.add_child(&action_bar);
|
||||
scene.layers.above_nodes.add_exclusive(&action_bar);
|
||||
|
||||
let output = output::Area::new(&logger, app);
|
||||
let output = output::Area::new(app);
|
||||
display_object.add_child(&output);
|
||||
|
||||
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
|
||||
let comment = text::Area::new(app);
|
||||
let comment = text::Text::new(app);
|
||||
display_object.add_child(&comment);
|
||||
|
||||
let app = app.clone_ref();
|
||||
@ -688,7 +683,7 @@ impl Node {
|
||||
#[profile(Debug)]
|
||||
pub fn new(app: &Application, registry: visualization::Registry) -> Self {
|
||||
let frp = Frp::new();
|
||||
let network = &frp.private.network;
|
||||
let network = frp.network();
|
||||
let out = &frp.private.output;
|
||||
let input = &frp.private.input;
|
||||
let model = Rc::new(NodeModel::new(app, registry));
|
||||
@ -712,11 +707,11 @@ impl Node {
|
||||
// ths user hovers the drag area. The input port manager merges this information with
|
||||
// port hover events and outputs the final hover event for any part inside of the node.
|
||||
|
||||
let drag_area = &model.drag_area.events;
|
||||
drag_area_hover <- bool(&drag_area.mouse_out,&drag_area.mouse_over);
|
||||
let drag_area = &model.drag_area.events;
|
||||
drag_area_hover <- bool(&drag_area.mouse_out,&drag_area.mouse_over);
|
||||
model.input.set_hover <+ drag_area_hover;
|
||||
model.output.set_hover <+ model.input.body_hover;
|
||||
out.hover <+ model.output.body_hover;
|
||||
out.hover <+ model.output.body_hover;
|
||||
|
||||
|
||||
// === Background Press ===
|
||||
@ -750,7 +745,6 @@ impl Node {
|
||||
// === Comment ===
|
||||
|
||||
let comment_base_color = style_frp.get_color(theme::graph_editor::node::text);
|
||||
// comment_color.target <+ all_with(
|
||||
comment_color <- all_with(
|
||||
&comment_base_color, &model.output.expression_label_visibility,
|
||||
|&base_color,&expression_visible| {
|
||||
@ -761,14 +755,14 @@ impl Node {
|
||||
});
|
||||
color
|
||||
});
|
||||
eval comment_color ((value) model.comment.set_color_all(color::Rgba::from(value)));
|
||||
eval comment_color ((value) model.comment.set_property(.., color::Rgba::from(value)));
|
||||
|
||||
eval model.comment.width ([model](width)
|
||||
model.comment.set_position_x(-*width - COMMENT_MARGIN));
|
||||
eval model.comment.height ([model](height)
|
||||
model.comment.set_position_y(*height / 2.0));
|
||||
model.comment.set_content <+ input.set_comment;
|
||||
out.comment <+ model.comment.content.map(|text| text.to_string());
|
||||
out.comment <+ model.comment.content.map(|text| text.to_im_string());
|
||||
|
||||
|
||||
// === Size ===
|
||||
@ -782,15 +776,15 @@ impl Node {
|
||||
let visualization_button_state = action_bar.action_visibility.clone_ref();
|
||||
out.skip <+ action_bar.action_skip;
|
||||
out.freeze <+ action_bar.action_freeze;
|
||||
show_action_bar <- out.hover && input.show_quick_action_bar_on_hover;
|
||||
show_action_bar <- out.hover && input.show_quick_action_bar_on_hover;
|
||||
eval show_action_bar ((t) action_bar.set_visibility(t));
|
||||
eval input.show_quick_action_bar_on_hover((value) action_bar.show_on_hover(value));
|
||||
|
||||
|
||||
// === View Mode ===
|
||||
|
||||
model.input.set_view_mode <+ input.set_view_mode;
|
||||
model.output.set_view_mode <+ input.set_view_mode;
|
||||
model.input.set_view_mode <+ input.set_view_mode;
|
||||
model.output.set_view_mode <+ input.set_view_mode;
|
||||
model.profiling_label.set_view_mode <+ input.set_view_mode;
|
||||
model.vcs_indicator.set_visibility <+ input.set_view_mode.map(|&mode| {
|
||||
!matches!(mode,view::Mode::Profiling {..})
|
||||
|
@ -86,12 +86,11 @@ struct Icons {
|
||||
}
|
||||
|
||||
impl Icons {
|
||||
fn new(logger: impl AnyLogger) -> Self {
|
||||
let logger = Logger::new_sub(logger, "Icons");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let freeze = ToggleButton::new(&logger);
|
||||
let visibility = ToggleButton::new(&logger);
|
||||
let skip = ToggleButton::new(&logger);
|
||||
fn new() -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let freeze = ToggleButton::new();
|
||||
let visibility = ToggleButton::new();
|
||||
let skip = ToggleButton::new();
|
||||
display_object.add_child(&visibility);
|
||||
// Note: Disabled for https://github.com/enso-org/ide/issues/1397
|
||||
// Should be re-enabled when https://github.com/enso-org/ide/issues/862 as been implemented.
|
||||
@ -135,12 +134,11 @@ struct Model {
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||
fn new(app: &Application) -> Self {
|
||||
let scene = &app.display.default_scene;
|
||||
let logger = Logger::new_sub(logger, "ActionBar");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let hover_area = hover_area::View::new(&logger);
|
||||
let icons = Icons::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let hover_area = hover_area::View::new();
|
||||
let icons = Icons::new();
|
||||
let shapes = compound::events::MouseEvents::default();
|
||||
let size = default();
|
||||
let styles = StyleWatch::new(&scene.style_sheet);
|
||||
@ -256,8 +254,8 @@ impl Deref for ActionBar {
|
||||
|
||||
impl ActionBar {
|
||||
/// Constructor.
|
||||
pub fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||
let model = Rc::new(Model::new(logger, app));
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let model = Rc::new(Model::new(app));
|
||||
let frp = Frp::new();
|
||||
ActionBar { frp, model }.init_frp()
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl Container {
|
||||
pub fn new(scene: &Scene) -> Self {
|
||||
let scene = scene.clone_ref();
|
||||
let logger = Logger::new("error::Container");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
let background_dom = Self::create_background_dom(&scene);
|
||||
let visualization = error_visualization::Error::new(&scene);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Definition of the node input port component.
|
||||
|
||||
use crate::prelude::*;
|
||||
use enso_text::traits::*;
|
||||
use enso_text::index::*;
|
||||
use enso_text::unit::*;
|
||||
use ensogl::display::shape::*;
|
||||
use ensogl::display::traits::*;
|
||||
@ -15,7 +15,7 @@ use crate::Type;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_frp;
|
||||
use enso_text::text::Text;
|
||||
use enso_text::text::Rope;
|
||||
use ensogl::application::Application;
|
||||
use ensogl::data::color;
|
||||
use ensogl::display;
|
||||
@ -108,13 +108,13 @@ impl Debug for Expression {
|
||||
/// Helper struct used for `Expression` conversions.
|
||||
#[derive(Debug, Default)]
|
||||
struct ExprConversion {
|
||||
prev_tok_local_index: Bytes,
|
||||
prev_tok_local_index: Byte,
|
||||
/// Index of the last traverse parent node in the `SpanTree`.
|
||||
last_parent_tok_index: Bytes,
|
||||
last_parent_tok_index: Byte,
|
||||
}
|
||||
|
||||
impl ExprConversion {
|
||||
fn new(last_parent_tok_index: Bytes) -> Self {
|
||||
fn new(last_parent_tok_index: Byte) -> Self {
|
||||
let prev_tok_local_index = default();
|
||||
Self { prev_tok_local_index, last_parent_tok_index }
|
||||
}
|
||||
@ -126,17 +126,17 @@ impl From<node::Expression> for Expression {
|
||||
#[profile(Debug)]
|
||||
fn from(t: node::Expression) -> Self {
|
||||
// The length difference between `code` and `viz_code` so far.
|
||||
let mut shift = 0.bytes();
|
||||
let mut shift = 0.byte();
|
||||
let mut span_tree = t.input_span_tree.map(|_| port::Model::default());
|
||||
let mut viz_code = String::new();
|
||||
let code = t.code;
|
||||
span_tree.root_ref_mut().dfs_with_layer_data(ExprConversion::default(), |node, info| {
|
||||
let is_expected_arg = node.is_expected_argument();
|
||||
let span = node.span();
|
||||
let mut size = span.size();
|
||||
let mut size = Byte::try_from(span.size()).unwrap(); // FIXME: hande errors
|
||||
let mut index = span.start;
|
||||
let offset_from_prev_tok = node.offset - info.prev_tok_local_index;
|
||||
info.prev_tok_local_index = node.offset + size;
|
||||
let offset_from_prev_tok = node.offset - info.prev_tok_local_index.to_diff();
|
||||
info.prev_tok_local_index = size + node.offset;
|
||||
viz_code += &" ".repeat(offset_from_prev_tok.as_usize());
|
||||
if node.children.is_empty() {
|
||||
viz_code += &code.as_str()[enso_text::Range::new(index, index + size)];
|
||||
@ -145,16 +145,16 @@ impl From<node::Expression> for Expression {
|
||||
if is_expected_arg {
|
||||
if let Some(name) = node.name() {
|
||||
size = name.len().into();
|
||||
index += 1.bytes();
|
||||
shift += 1.bytes() + size;
|
||||
index += 1.byte();
|
||||
shift += 1.byte() + size;
|
||||
viz_code += " ";
|
||||
viz_code += name;
|
||||
}
|
||||
}
|
||||
let port = node.payload_mut();
|
||||
port.local_index = index - info.last_parent_tok_index;
|
||||
port.index = index;
|
||||
port.length = size;
|
||||
port.index = index.into();
|
||||
port.length = size.into();
|
||||
ExprConversion::new(index)
|
||||
});
|
||||
Self { viz_code, code, span_tree }
|
||||
@ -205,7 +205,7 @@ ensogl::define_endpoints! {
|
||||
Output {
|
||||
pointer_style (cursor::Style),
|
||||
width (f32),
|
||||
expression (Text),
|
||||
expression (Rope),
|
||||
editing (bool),
|
||||
ports_visible (bool),
|
||||
body_hover (bool),
|
||||
@ -220,12 +220,11 @@ ensogl::define_endpoints! {
|
||||
/// Internal model of the port area.
|
||||
#[derive(Debug)]
|
||||
pub struct Model {
|
||||
logger: Logger,
|
||||
app: Application,
|
||||
display_object: display::object::Instance,
|
||||
ports: display::object::Instance,
|
||||
header: display::object::Instance,
|
||||
label: text::Area,
|
||||
label: text::Text,
|
||||
expression: RefCell<Expression>,
|
||||
id_crumbs_map: RefCell<HashMap<ast::Id, Crumbs>>,
|
||||
styles: StyleWatch,
|
||||
@ -235,13 +234,12 @@ pub struct Model {
|
||||
impl Model {
|
||||
/// Constructor.
|
||||
#[profile(Debug)]
|
||||
pub fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||
let logger = Logger::new_sub(&logger, "input_ports");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let ports = display::object::Instance::new(&Logger::new_sub(&logger, "ports"));
|
||||
let header = display::object::Instance::new(&Logger::new_sub(&logger, "header"));
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let ports = display::object::Instance::new();
|
||||
let header = display::object::Instance::new();
|
||||
let app = app.clone_ref();
|
||||
let label = app.new_view::<text::Area>();
|
||||
let label = app.new_view::<text::Text>();
|
||||
let id_crumbs_map = default();
|
||||
let expression = default();
|
||||
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||
@ -250,7 +248,6 @@ impl Model {
|
||||
display_object.add_child(&ports);
|
||||
ports.add_child(&header);
|
||||
Self {
|
||||
logger,
|
||||
app,
|
||||
display_object,
|
||||
ports,
|
||||
@ -273,11 +270,11 @@ impl Model {
|
||||
self.label.add_to_scene_layer(&scene.layers.label);
|
||||
|
||||
let text_color = self.styles.get_color(theme::graph_editor::node::text);
|
||||
self.label.single_line(true);
|
||||
self.label.set_single_line_mode(true);
|
||||
self.label.disable_command("cursor_move_up");
|
||||
self.label.disable_command("cursor_move_down");
|
||||
self.label.set_default_color(text_color);
|
||||
self.label.set_default_text_size(text::Size(TEXT_SIZE));
|
||||
self.label.set_property_default(text_color);
|
||||
self.label.set_property_default(text::Size(TEXT_SIZE));
|
||||
self.label.remove_all_cursors();
|
||||
|
||||
let origin = Vector2(TEXT_OFFSET, 0.0);
|
||||
@ -356,8 +353,8 @@ impl Deref for Area {
|
||||
impl Area {
|
||||
/// Constructor.
|
||||
#[profile(Debug)]
|
||||
pub fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||
let model = Rc::new(Model::new(logger, app));
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let model = Rc::new(Model::new(app));
|
||||
let frp = Frp::new();
|
||||
let network = &frp.network;
|
||||
let selection_color = Animation::new(network);
|
||||
@ -446,13 +443,13 @@ impl Area {
|
||||
selection_color_rgba <- profiled.switch(&std_selection_color,&profiled_selection_color);
|
||||
|
||||
selection_color.target <+ selection_color_rgba.map(|c| color::Lcha::from(c));
|
||||
model.label.set_selection_color <+ selection_color.value.map(|&c| color::Rgb::from(c));
|
||||
model.label.set_selection_color <+ selection_color.value.map(|c| color::Lch::from(c));
|
||||
|
||||
init_colors <- source::<()>();
|
||||
std_base_color <- all(std_base_color,init_colors)._0();
|
||||
profiled_base_color <- all(profiled_base_color,init_colors)._0();
|
||||
base_color <- profiled.switch(&std_base_color,&profiled_base_color);
|
||||
eval base_color ((color) model.label.set_default_color(color));
|
||||
eval base_color ((color) model.label.set_property_default(color));
|
||||
init_colors.emit(());
|
||||
}
|
||||
|
||||
@ -464,11 +461,11 @@ impl Area {
|
||||
let expr = self.model.expression.borrow();
|
||||
expr.root_ref().get_descendant(crumbs).ok().map(|node| {
|
||||
let unit = GLYPH_WIDTH;
|
||||
let range_before = enso_text::Range::new(0.bytes(), node.payload.index);
|
||||
let char_offset: Chars = expr.viz_code[range_before].chars().count().into();
|
||||
let char_count: Chars = expr.viz_code[node.payload.range()].chars().count().into();
|
||||
let width = unit * (i32::from(char_count) as f32);
|
||||
let x = width / 2.0 + unit * (i32::from(char_offset) as f32);
|
||||
let range_before = enso_text::Range::new(ByteDiff(0), node.payload.index);
|
||||
let char_offset = expr.viz_code[range_before].chars().count();
|
||||
let char_count = expr.viz_code[node.payload.range()].chars().count();
|
||||
let width = unit * (char_count as f32);
|
||||
let x = width / 2.0 + unit * (char_offset as f32);
|
||||
Vector2::new(TEXT_OFFSET + x, 0.0)
|
||||
})
|
||||
}
|
||||
@ -485,7 +482,7 @@ impl Area {
|
||||
}
|
||||
|
||||
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
|
||||
pub fn label(&self) -> &text::Area {
|
||||
pub fn label(&self) -> &text::Text {
|
||||
&self.model.label
|
||||
}
|
||||
|
||||
@ -516,7 +513,7 @@ struct PortLayerBuilder {
|
||||
/// The number of chars the expression should be shifted. For example, consider
|
||||
/// `(foo bar)`, where expression `foo bar` does not get its own port, and thus a 1 char
|
||||
/// shift should be applied when considering its children.
|
||||
shift: Chars,
|
||||
shift: usize,
|
||||
/// The depth at which the current expression is, where root is at depth 0.
|
||||
depth: usize,
|
||||
}
|
||||
@ -528,7 +525,7 @@ impl PortLayerBuilder {
|
||||
parent: impl display::Object,
|
||||
parent_frp: Option<port::FrpEndpoints>,
|
||||
parent_parensed: bool,
|
||||
shift: Chars,
|
||||
shift: usize,
|
||||
depth: usize,
|
||||
) -> Self {
|
||||
let parent = parent.display_object().clone_ref();
|
||||
@ -546,7 +543,7 @@ impl PortLayerBuilder {
|
||||
parent: display::object::Instance,
|
||||
new_parent_frp: Option<port::FrpEndpoints>,
|
||||
parent_parensed: bool,
|
||||
shift: Chars,
|
||||
shift: usize,
|
||||
) -> Self {
|
||||
let depth = self.depth + 1;
|
||||
let parent_frp = new_parent_frp.or_else(|| self.parent_frp.clone());
|
||||
@ -601,7 +598,7 @@ impl Area {
|
||||
let range_before_start = node.payload.index - node.payload.local_index;
|
||||
let range_before_end = node.payload.index;
|
||||
let range_before = enso_text::Range::new(range_before_start, range_before_end);
|
||||
let local_char_offset: Chars = code[range_before].chars().count().into();
|
||||
let local_char_offset = code[range_before].chars().count();
|
||||
|
||||
let new_parent = if not_a_port {
|
||||
builder.parent.clone_ref()
|
||||
@ -609,17 +606,16 @@ impl Area {
|
||||
let port = &mut node;
|
||||
|
||||
let index = local_char_offset + builder.shift;
|
||||
let size: Chars = code[port.payload.range()].chars().count().into();
|
||||
let size = code[port.payload.range()].chars().count();
|
||||
let unit = GLYPH_WIDTH;
|
||||
let width = unit * i32::from(size) as f32;
|
||||
let width = unit * size as f32;
|
||||
let width_padded = width + 2.0 * PORT_PADDING_X;
|
||||
let height = 18.0;
|
||||
let padded_size = Vector2(width_padded, height);
|
||||
let size = Vector2(width, height);
|
||||
let logger = &self.model.logger;
|
||||
let port_shape = port.payload_mut().init_shape(logger, size, node::HEIGHT);
|
||||
let port_shape = port.payload_mut().init_shape(size, node::HEIGHT);
|
||||
|
||||
port_shape.mod_position(|t| t.x = unit * i32::from(index) as f32);
|
||||
port_shape.mod_position(|t| t.x = unit * index as f32);
|
||||
if DEBUG {
|
||||
port_shape.mod_position(|t| t.y = DEBUG_PORT_OFFSET)
|
||||
}
|
||||
@ -725,7 +721,7 @@ impl Area {
|
||||
}
|
||||
}
|
||||
let new_parent_frp = Some(node.frp.output.clone_ref());
|
||||
let new_shift = if !not_a_port { 0.chars() } else { builder.shift + local_char_offset };
|
||||
let new_shift = if !not_a_port { 0 } else { builder.shift + local_char_offset };
|
||||
builder.nested(new_parent, new_parent_frp, is_parensed, new_shift)
|
||||
});
|
||||
*self.model.id_crumbs_map.borrow_mut() = id_crumbs_map;
|
||||
@ -826,7 +822,8 @@ impl Area {
|
||||
set_color <- all_with(&label_color,&self.set_edit_mode,|&color, _| color);
|
||||
eval set_color ([label](color) {
|
||||
let range = enso_text::Range::new(index, index + length);
|
||||
label.set_color_bytes(range,color::Rgba::from(color));
|
||||
let range = enso_text::Range::<Byte>::try_from(range).unwrap(); // FIXME: handle errors
|
||||
label.set_property(range,color::Rgba::from(color));
|
||||
});
|
||||
}
|
||||
|
||||
@ -905,7 +902,7 @@ impl Area {
|
||||
self.init_port_frp_on_new_expression(&mut new_expression);
|
||||
self.init_new_expression(new_expression);
|
||||
if self.frp.editing.value() {
|
||||
self.model.label.set_cursor_at_end();
|
||||
self.model.label.set_cursor_at_text_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +96,10 @@ pub struct Shape {
|
||||
impl Shape {
|
||||
/// Constructor.
|
||||
#[profile(Debug)]
|
||||
pub fn new(logger: &Logger, size: Vector2, hover_height: f32) -> Self {
|
||||
let root = display::object::Instance::new(logger);
|
||||
let hover = hover::View::new(logger);
|
||||
let viz = viz::View::new(logger);
|
||||
pub fn new(size: Vector2, hover_height: f32) -> Self {
|
||||
let root = display::object::Instance::new();
|
||||
let hover = hover::View::new();
|
||||
let viz = viz::View::new();
|
||||
|
||||
let width_padded = size.x + 2.0 * PADDING_X;
|
||||
hover.size.set(Vector2::new(width_padded, hover_height));
|
||||
@ -152,9 +152,9 @@ pub struct Model {
|
||||
pub frp: Frp,
|
||||
pub shape: Option<Shape>,
|
||||
pub name: Option<String>,
|
||||
pub index: Bytes,
|
||||
pub local_index: Bytes,
|
||||
pub length: Bytes,
|
||||
pub index: ByteDiff,
|
||||
pub local_index: ByteDiff,
|
||||
pub length: ByteDiff,
|
||||
pub highlight_color: color::Lcha, // TODO needed? and other fields?
|
||||
}
|
||||
|
||||
@ -176,21 +176,14 @@ impl Model {
|
||||
/// will be skipped, as there is no point in making them ports. The skip algorithm is
|
||||
/// implemented as part of the port are initialization.
|
||||
#[profile(Debug)]
|
||||
pub fn init_shape(
|
||||
&mut self,
|
||||
logger: impl AnyLogger,
|
||||
size: Vector2,
|
||||
hover_height: f32,
|
||||
) -> Shape {
|
||||
let logger_name = format!("port({},{})", self.index, self.length);
|
||||
let logger = Logger::new_sub(logger, logger_name);
|
||||
let shape = Shape::new(&logger, size, hover_height);
|
||||
pub fn init_shape(&mut self, size: Vector2, hover_height: f32) -> Shape {
|
||||
let shape = Shape::new(size, hover_height);
|
||||
self.shape = Some(shape);
|
||||
self.shape.as_ref().unwrap().clone_ref()
|
||||
}
|
||||
|
||||
/// The range of this port.
|
||||
pub fn range(&self) -> enso_text::Range<Bytes> {
|
||||
pub fn range(&self) -> enso_text::Range<ByteDiff> {
|
||||
let start = self.index;
|
||||
let end = self.index + self.length;
|
||||
enso_text::Range::new(start, end)
|
||||
|
@ -108,7 +108,7 @@ impl From<node::Expression> for Expression {
|
||||
span_tree.root_ref_mut().dfs_with_layer_data((), |node, ()| {
|
||||
let span = node.span();
|
||||
let port = node.payload_mut();
|
||||
port.index = span.start;
|
||||
port.index = span.start.into();
|
||||
port.length = span.size();
|
||||
});
|
||||
Expression { code, span_tree, whole_expr_type, whole_expr_id }
|
||||
@ -154,11 +154,10 @@ ensogl::define_endpoints! {
|
||||
/// Internal model of the port area.
|
||||
#[derive(Debug)]
|
||||
pub struct Model {
|
||||
logger: Logger,
|
||||
app: Application,
|
||||
display_object: display::object::Instance,
|
||||
ports: display::object::Instance,
|
||||
label: text::Area,
|
||||
label: text::Text,
|
||||
expression: RefCell<Expression>,
|
||||
id_crumbs_map: RefCell<HashMap<ast::Id, Crumbs>>,
|
||||
port_count: Cell<usize>,
|
||||
@ -170,12 +169,11 @@ pub struct Model {
|
||||
impl Model {
|
||||
/// Constructor.
|
||||
#[profile(Debug)]
|
||||
pub fn new(logger: impl AnyLogger, app: &Application, frp: &Frp) -> Self {
|
||||
let logger = Logger::new_sub(&logger, "output_ports");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let ports = display::object::Instance::new(&Logger::new_sub(&logger, "ports"));
|
||||
pub fn new(app: &Application, frp: &Frp) -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let ports = display::object::Instance::new();
|
||||
let app = app.clone_ref();
|
||||
let label = app.new_view::<text::Area>();
|
||||
let label = app.new_view::<text::Text>();
|
||||
let id_crumbs_map = default();
|
||||
let expression = default();
|
||||
let port_count = default();
|
||||
@ -185,7 +183,6 @@ impl Model {
|
||||
display_object.add_child(&label);
|
||||
display_object.add_child(&ports);
|
||||
Self {
|
||||
logger,
|
||||
app,
|
||||
display_object,
|
||||
ports,
|
||||
@ -209,11 +206,11 @@ impl Model {
|
||||
self.label.add_to_scene_layer(&scene.layers.label);
|
||||
|
||||
let text_color = self.styles.get_color(theme::graph_editor::node::text);
|
||||
self.label.single_line(true);
|
||||
self.label.set_single_line_mode(true);
|
||||
self.label.disable_command("cursor_move_up");
|
||||
self.label.disable_command("cursor_move_down");
|
||||
self.label.set_default_color(text_color);
|
||||
self.label.set_default_text_size(text::Size(input::area::TEXT_SIZE));
|
||||
self.label.set_property_default(text_color);
|
||||
self.label.set_property_default(text::Size(input::area::TEXT_SIZE));
|
||||
self.label.remove_all_cursors();
|
||||
|
||||
self.label.mod_position(|t| t.y = input::area::TEXT_SIZE / 2.0);
|
||||
@ -367,9 +364,8 @@ impl Model {
|
||||
if is_a_port {
|
||||
let port = &mut node;
|
||||
let crumbs = port.crumbs.clone_ref();
|
||||
let logger = &self.logger;
|
||||
let (port_shape,port_frp) = port.payload_mut()
|
||||
.init_shape(logger,&self.app,&self.styles,&self.styles_frp,port_index
|
||||
.init_shape(&self.app,&self.styles,&self.styles_frp,port_index
|
||||
,port_count);
|
||||
let port_network = &port_frp.network;
|
||||
|
||||
@ -466,9 +462,9 @@ impl Deref for Area {
|
||||
|
||||
impl Area {
|
||||
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
|
||||
pub fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let frp = Frp::new();
|
||||
let model = Rc::new(Model::new(logger, app, &frp));
|
||||
let model = Rc::new(Model::new(app, &frp));
|
||||
let network = &frp.network;
|
||||
let label_color = color::Animation::new(network);
|
||||
|
||||
@ -512,7 +508,7 @@ impl Area {
|
||||
label_color.target_alpha <+ label_alpha_tgt;
|
||||
label_color_on_change <- label_color.value.sample(&frp.set_expression);
|
||||
new_label_color <- any(&label_color.value,&label_color_on_change);
|
||||
eval new_label_color ((color) model.label.set_color_all(color::Rgba::from(color)));
|
||||
eval new_label_color ((color) model.label.set_property(.., color::Rgba::from(color)));
|
||||
|
||||
|
||||
// === View Mode ===
|
||||
|
@ -383,11 +383,11 @@ macro_rules! fn_multi_only {
|
||||
|
||||
impl PortShapeView {
|
||||
#[profile(Debug)]
|
||||
fn new(number_of_ports: usize, logger: &Logger) -> Self {
|
||||
fn new(number_of_ports: usize) -> Self {
|
||||
if number_of_ports <= 1 {
|
||||
Self::Single(SinglePortView::new(&logger))
|
||||
Self::Single(SinglePortView::new())
|
||||
} else {
|
||||
Self::Multi(MultiPortView::new(&logger))
|
||||
Self::Multi(MultiPortView::new())
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,10 +452,10 @@ ensogl::define_endpoints! {
|
||||
pub struct Model {
|
||||
pub frp: Option<Frp>,
|
||||
pub shape: Option<PortShapeView>,
|
||||
pub type_label: Option<text::Area>,
|
||||
pub type_label: Option<text::Text>,
|
||||
pub display_object: Option<display::object::Instance>,
|
||||
pub index: Bytes,
|
||||
pub length: Bytes,
|
||||
pub index: ByteDiff,
|
||||
pub length: ByteDiff,
|
||||
port_count: usize,
|
||||
port_index: usize,
|
||||
}
|
||||
@ -464,16 +464,13 @@ impl Model {
|
||||
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
|
||||
pub fn init_shape(
|
||||
&mut self,
|
||||
logger: impl AnyLogger,
|
||||
app: &Application,
|
||||
styles: &StyleWatch,
|
||||
styles_frp: &StyleWatchFrp,
|
||||
port_index: usize,
|
||||
port_count: usize,
|
||||
) -> (display::object::Instance, Frp) {
|
||||
let logger_name = format!("port({},{})", self.index, self.length);
|
||||
let logger = Logger::new_sub(logger, logger_name);
|
||||
let shape = PortShapeView::new(port_count, &logger);
|
||||
let shape = PortShapeView::new(port_count);
|
||||
|
||||
let is_first = port_index == 0;
|
||||
let is_last = port_index == port_count.saturating_sub(1);
|
||||
@ -485,13 +482,13 @@ impl Model {
|
||||
shape.set_padding_right(padding_right);
|
||||
self.shape = Some(shape.clone());
|
||||
|
||||
let type_label = app.new_view::<text::Area>();
|
||||
let type_label = app.new_view::<text::Text>();
|
||||
let offset_y =
|
||||
styles.get_number(ensogl_hardcoded_theme::graph_editor::node::type_label::offset_y);
|
||||
type_label.set_position_y(offset_y);
|
||||
self.type_label = Some(type_label.clone());
|
||||
|
||||
let display_object = display::object::Instance::new(logger);
|
||||
let display_object = display::object::Instance::new();
|
||||
display_object.add_child(&shape);
|
||||
display_object.add_child(&type_label);
|
||||
self.display_object = Some(display_object.clone());
|
||||
@ -506,7 +503,7 @@ impl Model {
|
||||
fn init_frp(
|
||||
&mut self,
|
||||
shape: &PortShapeView,
|
||||
type_label: &text::Area,
|
||||
type_label: &text::Text,
|
||||
styles: &StyleWatch,
|
||||
styles_frp: &StyleWatchFrp,
|
||||
) {
|
||||
@ -577,7 +574,7 @@ impl Model {
|
||||
showing_full_type <- bool(&full_type_timer.on_reset,&full_type_timer.on_end);
|
||||
type_description <- all_with(&frp.tp,&showing_full_type,|tp,&show_full_tp| {
|
||||
tp.map_ref(|tp| {
|
||||
if show_full_tp { tp.to_string() } else { tp.abbreviate().to_string() }
|
||||
if show_full_tp { tp.to_im_string() } else { tp.abbreviate().to_im_string() }
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -588,16 +585,16 @@ impl Model {
|
||||
|
||||
// === Type Label ===
|
||||
|
||||
type_label_visibility <- frp.on_hover.and(&frp.set_type_label_visibility);
|
||||
on_type_label_visible <- type_label_visibility.on_true();
|
||||
type_label_visibility <- frp.on_hover.and(&frp.set_type_label_visibility);
|
||||
on_type_label_visible <- type_label_visibility.on_true();
|
||||
type_label_opacity.target <+ on_type_label_visible.constant(PORT_OPACITY_HOVERED);
|
||||
type_label_opacity.target <+ type_label_visibility.on_false().constant(0.0);
|
||||
|
||||
type_label_color <- all_with(&color.value,&type_label_opacity.value,
|
||||
|color,&opacity| color.opaque.with_alpha(opacity).into());
|
||||
type_label.set_color_all <+ type_label_color;
|
||||
type_label.set_default_color <+ type_label_color;
|
||||
type_label.set_content <+ type_description.map(|s| s.clone().unwrap_or_default());
|
||||
type_label_color <- all_with(&color.value,&type_label_opacity.value,
|
||||
|color,&opacity| color.opaque.with_alpha(opacity));
|
||||
type_label.set_property <+ type_label_color.ref_into_some().map(|t| ((..).into(),*t));
|
||||
type_label.set_property_default <+ type_label_color.ref_into_some();
|
||||
type_label.set_content <+ type_description.map(|s| s.clone().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +606,7 @@ impl Model {
|
||||
frp.source.tooltip <+ all_with(&type_description,&frp.on_hover,|text,&hovering| {
|
||||
if hovering {
|
||||
if let Some(text) = text.clone() {
|
||||
tooltip::Style::set_label(text).with_placement(TOOLTIP_LOCATION)
|
||||
tooltip::Style::set_label(text.into()).with_placement(TOOLTIP_LOCATION)
|
||||
} else {
|
||||
tooltip::Style::unset_label()
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ ensogl::define_endpoints! {
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct ProfilingLabel {
|
||||
root: display::object::Instance,
|
||||
label: text::Area,
|
||||
label: text::Text,
|
||||
frp: Frp,
|
||||
styles: StyleWatchFrp,
|
||||
}
|
||||
@ -197,9 +197,9 @@ impl ProfilingLabel {
|
||||
/// Constructs a `ProfilingLabel` for the given application.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let scene = &app.display.default_scene;
|
||||
let root = display::object::Instance::new(Logger::new("ProfilingIndicator"));
|
||||
let root = display::object::Instance::new();
|
||||
|
||||
let label = text::Area::new(app);
|
||||
let label = text::Text::new(app);
|
||||
root.add_child(&label);
|
||||
label.set_position_y(crate::component::node::input::area::TEXT_SIZE / 2.0);
|
||||
label.remove_from_scene_layer(&scene.layers.main);
|
||||
@ -230,7 +230,7 @@ impl ProfilingLabel {
|
||||
(&frp.set_status,&frp.set_min_global_duration,&frp.set_max_global_duration,&theme,
|
||||
|&status,&min,&max,&theme| status.display_color(min,max,theme)
|
||||
);
|
||||
label.set_default_color <+ color.value.map(|c| c.into());
|
||||
label.set_property_default <+ color.value.ref_into_some();
|
||||
|
||||
|
||||
// === Position ===
|
||||
@ -241,7 +241,7 @@ impl ProfilingLabel {
|
||||
|
||||
// === Content ===
|
||||
|
||||
label.set_content <+ frp.set_status.map(|status| status.to_string());
|
||||
label.set_content <+ frp.set_status.map(|status| status.to_im_string());
|
||||
}
|
||||
|
||||
ProfilingLabel { root, label, frp, styles }
|
||||
|
@ -88,9 +88,9 @@ struct StatusIndicatorModel {
|
||||
}
|
||||
|
||||
impl StatusIndicatorModel {
|
||||
fn new(logger: &Logger) -> Self {
|
||||
let shape = status_indicator_shape::View::new(logger);
|
||||
let root = display::object::Instance::new(&logger);
|
||||
fn new() -> Self {
|
||||
let shape = status_indicator_shape::View::new();
|
||||
let root = display::object::Instance::new();
|
||||
root.add_child(&shape);
|
||||
StatusIndicatorModel { shape, root }
|
||||
}
|
||||
@ -145,8 +145,7 @@ pub struct StatusIndicator {
|
||||
impl StatusIndicator {
|
||||
/// Constructor.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let logger = Logger::new("status_indicator");
|
||||
let model = Rc::new(StatusIndicatorModel::new(&logger));
|
||||
let model = Rc::new(StatusIndicatorModel::new());
|
||||
let frp = Frp::new();
|
||||
Self { model, frp }.init_frp(app)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user