From 61546a7ade55a8ae2c2350d7fe0582366144faf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Dani=C5=82o?= Date: Tue, 4 Oct 2022 04:51:27 +0200 Subject: [PATCH] Wip/wdanilo/widgets 182746060 (#3678) --- Cargo.lock | 70 +- app/gui/analytics/src/lib.rs | 1 + app/gui/config/src/lib.rs | 1 + .../double-representation/src/lib.rs | 1 + .../double-representation/src/module.rs | 4 +- .../src/refactorings/collapse.rs | 7 +- .../double-representation/src/text.rs | 74 +- app/gui/controller/engine-model/src/lib.rs | 1 + app/gui/controller/engine-protocol/Cargo.toml | 1 + .../engine-protocol/src/binary/client.rs | 17 +- .../src/common/ongoing_calls.rs | 19 +- .../controller/engine-protocol/src/handler.rs | 16 +- .../src/language_server/types.rs | 47 +- app/gui/controller/engine-protocol/src/lib.rs | 1 + app/gui/controller/src/lib.rs | 1 + .../src/bin/api_events_to_profile.rs | 1 + .../src/bin/message_beanpoles.rs | 1 + app/gui/enso-profiler-enso-data/src/lib.rs | 1 + app/gui/language/ast/impl/src/crumbs.rs | 9 +- app/gui/language/ast/impl/src/id_map.rs | 31 +- app/gui/language/ast/impl/src/lib.rs | 102 +- app/gui/language/ast/macros/src/lib.rs | 1 + app/gui/language/parser/src/api.rs | 65 +- app/gui/language/parser/src/jsclient.rs | 2 +- app/gui/language/parser/src/lib.rs | 1 + app/gui/language/parser/src/main.rs | 1 + app/gui/language/parser/src/test_utils.rs | 3 +- app/gui/language/parser/src/wsclient.rs | 2 +- app/gui/language/span-tree/example/src/lib.rs | 1 + app/gui/language/span-tree/src/generate.rs | 6 +- app/gui/language/span-tree/src/lib.rs | 1 + app/gui/language/span-tree/src/node.rs | 39 +- app/gui/src/controller/graph.rs | 20 +- app/gui/src/controller/graph/executed.rs | 6 +- app/gui/src/controller/module.rs | 15 +- app/gui/src/controller/searcher.rs | 81 +- app/gui/src/controller/searcher/component.rs | 10 +- .../controller/searcher/component/builder.rs | 12 +- .../controller/searcher/component/group.rs | 6 +- app/gui/src/controller/text.rs | 4 +- app/gui/src/controller/upload.rs | 49 +- app/gui/src/executor/global.rs | 17 +- app/gui/src/executor/web.rs | 2 +- app/gui/src/ide/initializer.rs | 12 +- app/gui/src/lib.rs | 1 + app/gui/src/model/execution_context/plain.rs | 15 +- .../model/execution_context/synchronized.rs | 24 +- app/gui/src/model/module.rs | 12 +- app/gui/src/model/module/plain.rs | 23 +- app/gui/src/model/module/synchronized.rs | 153 +- app/gui/src/model/project/synchronized.rs | 50 +- app/gui/src/model/registry.rs | 8 +- app/gui/src/model/suggestion_database.rs | 71 +- .../src/model/suggestion_database/entry.rs | 32 +- app/gui/src/model/undo_redo.rs | 40 +- app/gui/src/presenter.rs | 26 +- app/gui/src/presenter/code.rs | 15 +- app/gui/src/presenter/graph.rs | 13 +- app/gui/src/presenter/graph/call_stack.rs | 33 +- app/gui/src/presenter/graph/visualization.rs | 34 +- .../presenter/graph/visualization/manager.rs | 29 +- app/gui/src/presenter/project.rs | 15 +- app/gui/src/presenter/searcher.rs | 2 +- app/gui/src/presenter/searcher/provider.rs | 14 +- app/gui/src/test.rs | 9 +- app/gui/src/transport/web.rs | 47 +- app/gui/tests/language_server.rs | 23 +- .../breadcrumbs/src/entry.rs | 17 +- .../component-browser/breadcrumbs/src/lib.rs | 7 +- .../component-group/src/entry.rs | 12 +- .../component-group/src/icon/define_macro.rs | 9 +- .../component-group/src/icon/entry.rs | 4 +- .../component-group/src/lib.rs | 45 +- .../component-group/src/new_entry.rs | 37 +- .../component-group/src/wide.rs | 10 +- .../searcher-list-panel/src/column_grid.rs | 7 +- .../searcher-list-panel/src/lib.rs | 27 +- .../searcher-list-panel/src/navigator.rs | 2 +- app/gui/view/component-browser/src/lib.rs | 1 + .../debug_scene/component-group/src/lib.rs | 7 +- .../component-list-panel-view/src/lib.rs | 2 +- app/gui/view/debug_scene/icons/src/lib.rs | 4 +- app/gui/view/debug_scene/interface/src/lib.rs | 3 +- .../new-component-list-panel-view/src/lib.rs | 3 +- app/gui/view/debug_scene/src/lib.rs | 1 + .../view/debug_scene/visualization/src/lib.rs | 1 + .../visualization/native/bubble_chart.rs | 4 +- .../graph-editor/src/component/breadcrumbs.rs | 24 +- .../src/component/breadcrumbs/breadcrumb.rs | 24 +- .../src/component/breadcrumbs/project_name.rs | 37 +- .../view/graph-editor/src/component/edge.rs | 8 +- .../view/graph-editor/src/component/node.rs | 48 +- .../src/component/node/action_bar.rs | 24 +- .../graph-editor/src/component/node/error.rs | 2 +- .../src/component/node/input/area.rs | 93 +- .../src/component/node/input/port.rs | 27 +- .../src/component/node/output/area.rs | 30 +- .../src/component/node/output/port.rs | 41 +- .../src/component/node/profiling.rs | 10 +- .../graph-editor/src/component/node/vcs.rs | 9 +- .../graph-editor/src/component/profiling.rs | 2 +- .../src/component/visualization/container.rs | 15 +- .../visualization/container/action_bar.rs | 20 +- .../visualization/container/fullscreen.rs | 2 +- .../foreign/java_script/instance.rs | 5 +- app/gui/view/graph-editor/src/lib.rs | 3 +- app/gui/view/src/code_editor.rs | 12 +- app/gui/view/src/component_browser.rs | 10 +- app/gui/view/src/debug_mode_popup.rs | 2 +- app/gui/view/src/documentation.rs | 12 +- app/gui/view/src/lib.rs | 1 + app/gui/view/src/open_dialog.rs | 2 +- app/gui/view/src/open_dialog/project_list.rs | 12 +- app/gui/view/src/project.rs | 16 +- app/gui/view/src/root.rs | 4 +- app/gui/view/src/searcher.rs | 4 +- app/gui/view/src/status_bar.rs | 14 +- app/gui/view/src/window_control_buttons.rs | 6 +- app/gui/view/welcome-screen/src/lib.rs | 3 +- build-config.yaml | 2 +- build/build-utils/src/lib.rs | 1 + build/enso-formatter/src/main.rs | 20 + build/rust-scripts/src/bin/test_all.rs | 1 + build/src/main.rs | 1 + integration-test/src/lib.rs | 1 + integration-test/tests/engine.rs | 3 +- lib/rust/automata/src/lib.rs | 1 + lib/rust/callback/src/lib.rs | 1 + lib/rust/code-builder/src/lib.rs | 1 + lib/rust/config-reader/src/lib.rs | 1 + lib/rust/data-structures/src/lib.rs | 1 + lib/rust/debug-api/src/lib.rs | 1 + .../ensogl/app/theme/derive/src/from_theme.rs | 2 +- lib/rust/ensogl/app/theme/derive/src/lib.rs | 2 +- .../ensogl/app/theme/hardcoded/src/lib.rs | 1 + lib/rust/ensogl/component/button/src/lib.rs | 8 +- .../component/drop-down-menu/src/lib.rs | 14 +- .../ensogl/component/drop-manager/src/lib.rs | 23 +- .../ensogl/component/file-browser/src/lib.rs | 3 +- .../ensogl/component/flame-graph/src/block.rs | 18 +- .../ensogl/component/flame-graph/src/lib.rs | 8 +- .../ensogl/component/flame-graph/src/mark.rs | 18 +- .../component/grid-view/src/entry/visible.rs | 2 +- .../ensogl/component/grid-view/src/lib.rs | 6 +- .../component/grid-view/src/selectable.rs | 6 +- .../src/selectable/highlight/layer.rs | 2 +- .../ensogl/component/grid-view/src/simple.rs | 25 +- .../ensogl/component/gui/src/component.rs | 9 +- lib/rust/ensogl/component/gui/src/lib.rs | 1 + lib/rust/ensogl/component/label/src/lib.rs | 16 +- .../ensogl/component/list-view/src/entry.rs | 31 +- .../component/list-view/src/entry/list.rs | 31 +- .../ensogl/component/list-view/src/lib.rs | 13 +- .../ensogl/component/scroll-area/src/lib.rs | 9 +- .../ensogl/component/scrollbar/src/lib.rs | 3 +- .../component/selector/src/decimal_aligned.rs | 18 +- lib/rust/ensogl/component/selector/src/lib.rs | 5 +- .../ensogl/component/selector/src/model.rs | 31 +- .../sequence-diagram/src/labeled_line.rs | 13 +- .../component/sequence-diagram/src/lib.rs | 14 +- lib/rust/ensogl/component/shadow/src/lib.rs | 1 + lib/rust/ensogl/component/src/lib.rs | 1 + lib/rust/ensogl/component/text/Cargo.toml | 1 + lib/rust/ensogl/component/text/src/buffer.rs | 1170 +++++++++- .../component/text/src/buffer/formatting.rs | 603 +++++ .../ensogl/component/text/src/buffer/index.rs | 56 + .../text/src/buffer/{view => }/movement.rs | 133 +- .../ensogl/component/text/src/buffer/rope.rs | 10 + .../text/src/buffer/rope/formatted.rs | 84 + .../text/src/buffer/{view => rope}/word.rs | 17 +- .../text/src/buffer/{view => }/selection.rs | 102 +- .../ensogl/component/text/src/buffer/style.rs | 305 --- .../ensogl/component/text/src/buffer/view.rs | 615 ----- .../ensogl/component/text/src/component.rs | 5 +- .../component/text/src/component/area.rs | 1195 ---------- .../component/text/src/component/line.rs | 385 +++ .../component/text/src/component/selection.rs | 320 ++- .../component/text/src/component/text.rs | 2054 +++++++++++++++++ lib/rust/ensogl/component/text/src/font.rs | 218 +- .../text/src/font/embedded/src/lib.rs | 1 + .../component/text/src/font/family/src/lib.rs | 75 +- .../component/text/src/font/glsl/glyph.glsl | 2 +- .../text/src/font/glsl/glyph_mac.glsl | 2 +- .../ensogl/component/text/src/font/glyph.rs | 425 +++- .../component/text/src/font/msdf/Cargo.toml | 1 + .../text/src/font/msdf/src/binding.rs | 80 +- .../text/src/font/msdf/src/emscripten_data.rs | 2 +- .../component/text/src/font/msdf/src/lib.rs | 19 +- .../ensogl/component/text/src/font/pen.rs | 129 -- lib/rust/ensogl/component/text/src/lib.rs | 27 +- .../ensogl/component/toggle-button/src/lib.rs | 22 +- lib/rust/ensogl/component/tooltip/src/lib.rs | 4 +- lib/rust/ensogl/core/Cargo.toml | 1 - lib/rust/ensogl/core/src/animation/easing.rs | 25 +- .../core/src/animation/frp/animation.rs | 26 +- lib/rust/ensogl/core/src/animation/loops.rs | 211 +- .../core/src/animation/physics/inertia.rs | 30 +- lib/rust/ensogl/core/src/application/args.rs | 2 +- lib/rust/ensogl/core/src/application/frp.rs | 93 +- .../ensogl/core/src/data/color/animation.rs | 3 + lib/rust/ensogl/core/src/data/color/data.rs | 6 + lib/rust/ensogl/core/src/data/dirty.rs | 17 +- lib/rust/ensogl/core/src/debug/monitor.rs | 313 +-- lib/rust/ensogl/core/src/debug/stats.rs | 12 +- .../core/src/display/camera/camera2d.rs | 23 +- lib/rust/ensogl/core/src/display/object.rs | 2 +- .../ensogl/core/src/display/object/class.rs | 199 +- lib/rust/ensogl/core/src/display/scene.rs | 34 +- .../ensogl/core/src/display/scene/layer.rs | 12 +- .../display/shape/primitive/style_watch.rs | 6 +- .../src/display/shape/primitive/system.rs | 19 +- .../ensogl/core/src/display/style/data.rs | 29 +- .../ensogl/core/src/display/style/theme.rs | 4 +- .../ensogl/core/src/display/symbol/dom.rs | 3 +- .../ensogl/core/src/display/symbol/gpu.rs | 22 +- .../symbol/gpu/geometry/compound/sprite.rs | 3 +- .../symbol/gpu/geometry/primitive/mesh.rs | 7 +- .../core/src/display/symbol/gpu/registry.rs | 7 +- .../core/src/display/symbol/gpu/shader.rs | 5 +- lib/rust/ensogl/core/src/display/world.rs | 61 +- lib/rust/ensogl/core/src/gui/component.rs | 18 +- lib/rust/ensogl/core/src/gui/cursor.rs | 8 +- lib/rust/ensogl/core/src/lib.rs | 4 + .../core/src/system/gpu/data/attribute.rs | 16 +- .../ensogl/core/src/system/gpu/data/buffer.rs | 12 +- .../core/src/system/gpu/shader/compiler.rs | 14 +- lib/rust/ensogl/example/Cargo.toml | 1 - lib/rust/ensogl/example/animation/src/lib.rs | 7 +- .../example/complex-shape-system/src/lib.rs | 7 +- .../example/custom-shape-system/src/lib.rs | 4 +- .../ensogl/example/dom-symbols/src/lib.rs | 1 + .../ensogl/example/drop-manager/src/lib.rs | 3 + .../ensogl/example/easing-animator/src/lib.rs | 6 +- .../ensogl/example/glyph-system/Cargo.toml | 19 - .../ensogl/example/glyph-system/src/lib.rs | 80 - lib/rust/ensogl/example/grid-view/src/lib.rs | 1 + lib/rust/ensogl/example/list-view/src/lib.rs | 11 +- .../ensogl/example/mouse-events/src/lib.rs | 7 +- .../example/profiling-run-graph/src/lib.rs | 1 + .../render-profile-flamegraph/src/lib.rs | 1 + .../ensogl/example/scroll-area/src/lib.rs | 7 +- .../ensogl/example/shape-system/src/lib.rs | 1 + lib/rust/ensogl/example/slider/src/lib.rs | 1 + .../sprite-system-benchmark/src/lib.rs | 7 +- .../ensogl/example/sprite-system/src/lib.rs | 1 + lib/rust/ensogl/example/src/lib.rs | 2 +- lib/rust/ensogl/example/text-area/src/lib.rs | 284 ++- lib/rust/ensogl/src/lib.rs | 1 + lib/rust/frp/src/lib.rs | 3 + lib/rust/frp/src/macros.rs | 1 + lib/rust/frp/src/network.rs | 5 + lib/rust/frp/src/nodes.rs | 77 +- lib/rust/fuzzly/src/lib.rs | 1 + lib/rust/generics/src/lib.rs | 1 + lib/rust/json-rpc/src/lib.rs | 1 + .../launcher-shims/src/bin/launcher_000.rs | 1 + .../launcher-shims/src/bin/launcher_001.rs | 1 + .../launcher-shims/src/bin/launcher_002.rs | 1 + .../launcher-shims/src/bin/launcher_003.rs | 1 + .../launcher-shims/src/bin/launcher_004.rs | 1 + lib/rust/launcher-shims/src/lib.rs | 1 + lib/rust/logger/src/lib.rs | 9 +- lib/rust/macro-utils/src/lib.rs | 1 + lib/rust/metamodel/lexpr/src/lib.rs | 1 + lib/rust/metamodel/src/lib.rs | 1 + lib/rust/optics/src/lib.rs | 1 + .../generate-java/src/bin/graph-java.rs | 1 + .../generate-java/src/bin/graph-meta.rs | 1 + .../generate-java/src/bin/graph-rust.rs | 1 + .../generate-java/src/bin/java-tests.rs | 1 + lib/rust/parser/generate-java/src/lib.rs | 1 + lib/rust/parser/generate-java/src/main.rs | 1 + lib/rust/parser/jni/src/lib.rs | 1 + lib/rust/parser/src/lexer.rs | 2 +- lib/rust/parser/src/lib.rs | 1 + lib/rust/parser/src/macros/resolver.rs | 27 +- lib/rust/parser/src/main.rs | 1 + lib/rust/parser/src/source/code.rs | 6 +- lib/rust/parser/src/source/span.rs | 4 +- .../parser/src/syntax/tree/visitor/src/lib.rs | 1 + lib/rust/prelude/Cargo.toml | 1 + lib/rust/prelude/src/bool.rs | 27 + lib/rust/prelude/src/data.rs | 4 +- lib/rust/prelude/src/data/non_empty_vec.rs | 176 +- lib/rust/prelude/src/data/semigroup.rs | 2 +- lib/rust/prelude/src/data/vec_indexed_by.rs | 415 ++++ lib/rust/prelude/src/lib.rs | 50 +- lib/rust/prelude/src/macros.rs | 4 +- lib/rust/prelude/src/not_same.rs | 18 + lib/rust/prelude/src/range.rs | 181 ++ lib/rust/prelude/src/string.rs | 50 + lib/rust/prelude/src/vec.rs | 17 + lib/rust/profiler/data/src/bin/devtools.rs | 1 + lib/rust/profiler/data/src/bin/intervals.rs | 1 + .../profiler/data/src/bin/measurements.rs | 1 + lib/rust/profiler/data/src/bin/processes.rs | 1 + lib/rust/profiler/data/src/lib.rs | 1 + lib/rust/profiler/demo-data/src/lib.rs | 1 + lib/rust/profiler/flame-graph/src/lib.rs | 1 + lib/rust/profiler/macros/src/lib.rs | 1 + lib/rust/profiler/src/lib.rs | 1 + lib/rust/reflect/macros/src/lib.rs | 1 + lib/rust/reflect/src/lib.rs | 1 + lib/rust/shapely/macros/src/lib.rs | 1 + lib/rust/shapely/src/lib.rs | 1 + lib/rust/shortcuts/example/src/lib.rs | 1 + lib/rust/shortcuts/src/lib.rs | 409 +--- lib/rust/text/src/index.rs | 381 +++ lib/rust/text/src/lib.rs | 15 +- lib/rust/text/src/range.rs | 146 +- lib/rust/text/src/rope.rs | 3 +- lib/rust/text/src/spans.rs | 314 ++- lib/rust/text/src/text.rs | 1171 ++++++---- lib/rust/text/src/unit.rs | 372 ++- lib/rust/types/src/lib.rs | 1 + lib/rust/types/src/unit.rs | 74 +- lib/rust/web/src/binding/mock.rs | 16 + lib/rust/web/src/binding/wasm.rs | 1 + lib/rust/web/src/lib.rs | 19 +- package.json | 3 + tools/language-server/logstat/src/main.rs | 1 + tools/language-server/wstest/src/main.rs | 1 + 322 files changed, 10518 insertions(+), 5832 deletions(-) create mode 100644 lib/rust/ensogl/component/text/src/buffer/formatting.rs create mode 100644 lib/rust/ensogl/component/text/src/buffer/index.rs rename lib/rust/ensogl/component/text/src/buffer/{view => }/movement.rs (57%) create mode 100644 lib/rust/ensogl/component/text/src/buffer/rope.rs create mode 100644 lib/rust/ensogl/component/text/src/buffer/rope/formatted.rs rename lib/rust/ensogl/component/text/src/buffer/{view => rope}/word.rs (93%) rename lib/rust/ensogl/component/text/src/buffer/{view => }/selection.rs (83%) delete mode 100644 lib/rust/ensogl/component/text/src/buffer/style.rs delete mode 100644 lib/rust/ensogl/component/text/src/buffer/view.rs delete mode 100644 lib/rust/ensogl/component/text/src/component/area.rs create mode 100644 lib/rust/ensogl/component/text/src/component/line.rs create mode 100644 lib/rust/ensogl/component/text/src/component/text.rs delete mode 100644 lib/rust/ensogl/component/text/src/font/pen.rs delete mode 100644 lib/rust/ensogl/example/glyph-system/Cargo.toml delete mode 100644 lib/rust/ensogl/example/glyph-system/src/lib.rs create mode 100644 lib/rust/prelude/src/bool.rs create mode 100644 lib/rust/prelude/src/data/vec_indexed_by.rs create mode 100644 lib/rust/prelude/src/not_same.rs create mode 100644 lib/rust/prelude/src/range.rs create mode 100644 lib/rust/text/src/index.rs diff --git a/Cargo.lock b/Cargo.lock index 0bb6afe26219..68f45676787a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/app/gui/analytics/src/lib.rs b/app/gui/analytics/src/lib.rs index 4eed94038b86..226dccb73bc6 100644 --- a/app/gui/analytics/src/lib.rs +++ b/app/gui/analytics/src/lib.rs @@ -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)] diff --git a/app/gui/config/src/lib.rs b/app/gui/config/src/lib.rs index dcbbf8383226..63b8e3b53939 100644 --- a/app/gui/config/src/lib.rs +++ b/app/gui/config/src/lib.rs @@ -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)] diff --git a/app/gui/controller/double-representation/src/lib.rs b/app/gui/controller/double-representation/src/lib.rs index 5d28e486f5a2..330bb9f4537c 100644 --- a/app/gui/controller/double-representation/src/lib.rs +++ b/app/gui/controller/double-representation/src/lib.rs @@ -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)] diff --git a/app/gui/controller/double-representation/src/module.rs b/app/gui/controller/double-representation/src/module.rs index d4bd6224173c..c3afb7fd3101 100644 --- a/app/gui/controller/double-representation/src/module.rs +++ b/app/gui/controller/double-representation/src/module.rs @@ -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> { +) -> FallibleResult> { let location = locate(ast, id)?; ast.range_of_descendant_at(&location.crumbs) } diff --git a/app/gui/controller/double-representation/src/refactorings/collapse.rs b/app/gui/controller/double-representation/src/refactorings/collapse.rs index 8d733d2f6439..1e67dcb4333a 100644 --- a/app/gui/controller/double-representation/src/refactorings/collapse.rs +++ b/app/gui/controller/double-representation/src/refactorings/collapse.rs @@ -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); }; diff --git a/app/gui/controller/double-representation/src/text.rs b/app/gui/controller/double-representation/src/text.rs index 530f34699fc3..c587c38dd808 100644 --- a/app/gui/controller/double-representation/src/text.rs +++ b/app/gui/controller/double-representation/src/text.rs @@ -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, + change: &enso_text::text::Change, 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, ast::Id> = default(); + let mut preferred: HashMap, 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 = (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 = (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) -> Chars { +fn spaces_size(itr: impl Iterator) -> 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, + pub change: enso_text::Change, } 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"); } diff --git a/app/gui/controller/engine-model/src/lib.rs b/app/gui/controller/engine-model/src/lib.rs index e89d5907059c..13a6caaec829 100644 --- a/app/gui/controller/engine-model/src/lib.rs +++ b/app/gui/controller/engine-model/src/lib.rs @@ -1,6 +1,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] diff --git a/app/gui/controller/engine-protocol/Cargo.toml b/app/gui/controller/engine-protocol/Cargo.toml index 2b4f3e494f45..de763da5b80b 100644 --- a/app/gui/controller/engine-protocol/Cargo.toml +++ b/app/gui/controller/engine-protocol/Cargo.toml @@ -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" } diff --git a/app/gui/controller/engine-protocol/src/binary/client.rs b/app/gui/controller/engine-protocol/src/binary/client.rs index 1854a147e0ac..2a41b3ea9a63 100644 --- a/app/gui/controller/engine-protocol/src/binary/client.rs +++ b/app/gui/controller/engine-protocol/src/binary/client.rs @@ -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 + '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 { - 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 { - 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>> { - 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> { - 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 { diff --git a/app/gui/controller/engine-protocol/src/common/ongoing_calls.rs b/app/gui/controller/engine-protocol/src/common/ongoing_calls.rs index 41c38ca10cef..1d35edfabf5e 100644 --- a/app/gui/controller/engine-protocol/src/common/ongoing_calls.rs +++ b/app/gui/controller/engine-protocol/src/common/ongoing_calls.rs @@ -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 where Id: Hash + Eq { - logger: Logger, ongoing_calls: HashMap>, } @@ -23,11 +23,8 @@ impl OngoingCalls where Id: Copy + Debug + Display + Hash + Eq + Send + Sync + 'static { /// Creates a new, empty ongoing request storage. - pub fn new(parent: impl AnyLogger) -> OngoingCalls { - OngoingCalls { - logger: Logger::new_sub(parent, "ongoing_calls"), - ongoing_calls: HashMap::new(), - } + pub fn new() -> OngoingCalls { + 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> { 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) { - 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() } diff --git a/app/gui/controller/engine-protocol/src/handler.rs b/app/gui/controller/engine-protocol/src/handler.rs index 7bc03663554d..7fefdc6139c4 100644 --- a/app/gui/controller/engine-protocol/src/handler.rs +++ b/app/gui/controller/engine-protocol/src/handler.rs @@ -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 + 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, { - 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. diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs index e7c98cf41c55..ab8c7d2d9704 100644 --- a/app/gui/controller/engine-protocol/src/language_server/types.rs +++ b/app/gui/controller/engine-protocol/src/language_server/types.rs @@ -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 for Position { |location| +impls! { From + &From > 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 for enso_text::Location { |position| +impls! { From + &From for enso_text::Location { |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 > for TextRange { |range| +impls! { From + &From >> for TextRange { |range| TextRange { start : range.start.into(), end : range.end.into(), } }} -impls! { From + &From for enso_text::Range { |range| +impls! { From + &From for enso_text::Range> { |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, - target: impl Into, + source: impl Into, + target: impl Into, ) -> 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 > for SuggestionEntryScope { |range| +impls! { From + &From >> for SuggestionEntryScope { |range| SuggestionEntryScope { start : range.start().into(), end : range.end().into(), } }} -impls! { From + &From for RangeInclusive { |this| +impls! { From + &From for RangeInclusive> { |this| this.start.into()..=this.end.into() }} diff --git a/app/gui/controller/engine-protocol/src/lib.rs b/app/gui/controller/engine-protocol/src/lib.rs index 31401063cee0..c08d39d65075 100644 --- a/app/gui/controller/engine-protocol/src/lib.rs +++ b/app/gui/controller/engine-protocol/src/lib.rs @@ -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)] diff --git a/app/gui/controller/src/lib.rs b/app/gui/controller/src/lib.rs index e89d5907059c..13a6caaec829 100644 --- a/app/gui/controller/src/lib.rs +++ b/app/gui/controller/src/lib.rs @@ -1,6 +1,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] diff --git a/app/gui/enso-profiler-enso-data/src/bin/api_events_to_profile.rs b/app/gui/enso-profiler-enso-data/src/bin/api_events_to_profile.rs index e59e5cae5d72..39ed731afd6d 100644 --- a/app/gui/enso-profiler-enso-data/src/bin/api_events_to_profile.rs +++ b/app/gui/enso-profiler-enso-data/src/bin/api_events_to_profile.rs @@ -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)] diff --git a/app/gui/enso-profiler-enso-data/src/bin/message_beanpoles.rs b/app/gui/enso-profiler-enso-data/src/bin/message_beanpoles.rs index 63c363c2cd1e..ed9f1009f630 100644 --- a/app/gui/enso-profiler-enso-data/src/bin/message_beanpoles.rs +++ b/app/gui/enso-profiler-enso-data/src/bin/message_beanpoles.rs @@ -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)] diff --git a/app/gui/enso-profiler-enso-data/src/lib.rs b/app/gui/enso-profiler-enso-data/src/lib.rs index 5a1e4a097101..6f9fced00085 100644 --- a/app/gui/enso-profiler-enso-data/src/lib.rs +++ b/app/gui/enso-profiler-enso-data/src/lib.rs @@ -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)] diff --git a/app/gui/language/ast/impl/src/crumbs.rs b/app/gui/language/ast/impl/src/crumbs.rs index beb1e8356a0c..3c6fd15eca0a 100644 --- a/app/gui/language/ast/impl/src/crumbs.rs +++ b/app/gui/language/ast/impl/src/crumbs.rs @@ -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> { - let mut position = 0.bytes(); + fn range_of_descendant_at(&self, crumbs: &[Crumb]) -> FallibleResult> { + 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"); } } diff --git a/app/gui/language/ast/impl/src/id_map.rs b/app/gui/language/ast/impl/src/id_map.rs index 8079d1e8d83e..5c9ed4e7ed41 100644 --- a/app/gui/language/ast/impl/src/id_map.rs +++ b/app/gui/language/ast/impl/src/id_map.rs @@ -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, Id)>, + pub vec: Vec<(enso_text::Range, Id)>, } impl IdMap { /// Create a new instance. - pub fn new(vec: Vec<(enso_text::Range, Id)>) -> IdMap { + pub fn new(vec: Vec<(enso_text::Range, Id)>) -> IdMap { IdMap { vec } } /// Assigns Span to given ID. - pub fn insert(&mut self, span: impl Into>, id: Id) { + pub fn insert(&mut self, span: impl Into>, id: Id) { self.vec.push((span.into(), id)); } /// Generate random Uuid for span. - pub fn generate(&mut self, span: impl Into>) { + pub fn generate(&mut self, span: impl Into>) { 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::()).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() } diff --git a/app/gui/language/ast/impl/src/lib.rs b/app/gui/language/ast/impl/src/lib.rs index 090b79800f1b..30013d5b1ece 100644 --- a/app/gui/language/ast/impl/src/lib.rs +++ b/app/gui/language/ast/impl/src/lib.rs @@ -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 = Vec; @@ -283,7 +285,7 @@ impl Ast { } /// Just wraps shape, id and len into Ast node. - fn from_ast_id_len(shape: Shape, id: Option, char_count: Chars) -> Ast { + fn from_ast_id_len(shape: Shape, id: Option, 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 { + pub fn child_offset(&self, child: &Ast) -> FallibleResult { 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> { + pub fn span_of_child_at(&self, crumb: &Crumb) -> FallibleResult> { 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 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 { - offset: Chars, + offset: usize, callback: F, } impl TraverserWithOffset { pub fn new(callback: F) -> TraverserWithOffset { - let offset = 0.chars(); + let offset = 0; TraverserWithOffset { offset, callback } } } impl TokenConsumer for TraverserWithOffset -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, &Ast)) { +pub fn traverse_with_span(ast: &impl HasTokens, mut f: impl FnMut(enso_text::Range, &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 { #[shrinkwrap(main_field)] #[serde(flatten)] pub wrapped: T, - pub length: Chars, + pub length: usize, } impl HasRepr for WithLength @@ -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::::new(ix.into(), (ix + length).into()) + enso_text::Range::::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()); } } diff --git a/app/gui/language/ast/macros/src/lib.rs b/app/gui/language/ast/macros/src/lib.rs index 1611b5dc0643..f6ef5b66a799 100644 --- a/app/gui/language/ast/macros/src/lib.rs +++ b/app/gui/language/ast/macros/src/lib.rs @@ -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)] diff --git a/app/gui/language/parser/src/api.rs b/app/gui/language/parser/src/api.rs index fbb4f99bb2f0..424c10cb51a4 100644 --- a/app/gui/language/parser/src/api.rs +++ b/app/gui/language/parser/src/api.rs @@ -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, + pub code: Range, /// The range in bytes of module's "Id Map" section. - pub id_map: Range, + pub id_map: Range, /// The range in bytes of module's "Metadata" section. - pub metadata: Range, + pub metadata: Range, } 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) -> &str { - let start = range.start.as_usize(); - let end = range.end.as_usize(); + fn slice(&self, range: &Range) -> &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 ParsedSourceFile { /// Serialize to the SourceFile structure, pub fn serialize(&self) -> std::result::Result { - 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 ParsedSourceFile { 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(); diff --git a/app/gui/language/parser/src/jsclient.rs b/app/gui/language/parser/src/jsclient.rs index bacbaa332bf8..efae77304bea 100644 --- a/app/gui/language/parser/src/jsclient.rs +++ b/app/gui/language/parser/src/jsclient.rs @@ -68,7 +68,7 @@ impl Client { /// Parses Enso code with JS-based parser. pub fn parse(&self, program: String, ids: IdMap) -> api::Result { 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)?; diff --git a/app/gui/language/parser/src/lib.rs b/app/gui/language/parser/src/lib.rs index 9ef7f5ec0323..9c6fbd1bdf9a 100644 --- a/app/gui/language/parser/src/lib.rs +++ b/app/gui/language/parser/src/lib.rs @@ -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)] diff --git a/app/gui/language/parser/src/main.rs b/app/gui/language/parser/src/main.rs index 4092612d0f5a..defe8a85c8b1 100644 --- a/app/gui/language/parser/src/main.rs +++ b/app/gui/language/parser/src/main.rs @@ -1,6 +1,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] use enso_prelude::*; diff --git a/app/gui/language/parser/src/test_utils.rs b/app/gui/language/parser/src/test_utils.rs index 9afd624c0fc4..c9d98782b8f4 100644 --- a/app/gui/language/parser/src/test_utils.rs +++ b/app/gui/language/parser/src/test_utils.rs @@ -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 diff --git a/app/gui/language/parser/src/wsclient.rs b/app/gui/language/parser/src/wsclient.rs index 0f45b9482b19..6a27392cecba 100644 --- a/app/gui/language/parser/src/wsclient.rs +++ b/app/gui/language/parser/src/wsclient.rs @@ -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 { - 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::(request)?; match response { diff --git a/app/gui/language/span-tree/example/src/lib.rs b/app/gui/language/span-tree/example/src/lib.rs index 139653b27cfa..facc80a38a04 100644 --- a/app/gui/language/span-tree/example/src/lib.rs +++ b/app/gui/language/span-tree/example/src/lib.rs @@ -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::*; diff --git a/app/gui/language/span-tree/src/generate.rs b/app/gui/language/span-tree/src/generate.rs index cfe06b16f6a9..5728e0f5309a 100644 --- a/app/gui/language/span-tree/src/generate.rs +++ b/app/gui/language/span-tree/src/generate.rs @@ -89,7 +89,7 @@ impl SpanTreeGenerator for String { /// An utility to generate children with increasing offsets. #[derive(Debug, Default)] struct ChildGenerator { - current_offset: Bytes, + current_offset: ByteDiff, children: Vec>, } @@ -97,7 +97,7 @@ impl ChildGenerator { /// 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( .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); diff --git a/app/gui/language/span-tree/src/lib.rs b/app/gui/language/span-tree/src/lib.rs index 23f59ae36856..e0eba1d319c0 100644 --- a/app/gui/language/span-tree/src/lib.rs +++ b/app/gui/language/span-tree/src/lib.rs @@ -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)] diff --git a/app/gui/language/span-tree/src/node.rs b/app/gui/language/span-tree/src/node.rs index 6754b7699e0c..5d1ff474854e 100644 --- a/app/gui/language/span-tree/src/node.rs +++ b/app/gui/language/span-tree/src/node.rs @@ -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 { pub kind: Kind, - pub size: Bytes, + pub size: ByteDiff, pub children: Vec>, pub ast_id: Option, pub payload: T, @@ -132,7 +133,7 @@ impl Node { 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 { /// A child node. pub node: Node, /// 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 ChildBuilder { 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 ChildBuilder { } /// 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 ChildBuilder { } /// 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, /// 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 { + pub fn span(&self) -> text::Range { 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) -> Option> { + pub fn find_by_span(self, span: &text::Range) -> Option> { if self.span() == *span { Some(self) } else { @@ -648,9 +649,9 @@ pub struct RefMut<'a, T = ()> { /// The node's ref. node: &'a mut Node, /// 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 { + pub fn span(&self) -> text::Range { 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, - 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); diff --git a/app/gui/src/controller/graph.rs b/app/gui/src/controller/graph.rs index a97ec38f219e..edd7914ee50a 100644 --- a/app/gui/src/controller/graph.rs +++ b/app/gui/src/controller/graph.rs @@ -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 { - 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::, _>>()?; - 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 { 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); }); diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 22e9da357f18..0b9e53b70fdc 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -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(()) diff --git a/app/gui/src/controller/module.rs b/app/gui/src/controller/module.rs index c68ff97debd3..89b4c373127f 100644 --- a/app/gui/src/controller/module.rs +++ b/app/gui/src/controller/module.rs @@ -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 { diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs index db9050d9d09a..60fa9c56839c 100644 --- a/app/gui/src/controller/searcher.rs +++ b/app/gui/src/controller/searcher.rs @@ -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, ide: controller::Ide, this_arg: Rc>, - position_in_code: Immutable, + position_in_code: Immutable>, 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> { 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, + ) -> 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 { 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 { - 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 { - 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, diff --git a/app/gui/src/controller/searcher/component.rs b/app/gui/src/controller/searcher/component.rs index 9c8b3431d801..9f9a5f72a8da 100644 --- a/app/gui/src/controller/searcher/component.rs +++ b/app/gui/src/controller/searcher/component.rs @@ -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(); diff --git a/app/gui/src/controller/searcher/component/builder.rs b/app/gui/src/controller/searcher/component/builder.rs index 1fdae3bc049e..3747d94554bc 100644 --- a/app/gui/src/controller/searcher/component/builder.rs +++ b/app/gui/src/controller/searcher/component/builder.rs @@ -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(); diff --git a/app/gui/src/controller/searcher/component/group.rs b/app/gui/src/controller/searcher/component/group.rs index e7e5b67fdc3f..4896a6f3bf59 100644 --- a/app/gui/src/controller/searcher/component/group.rs +++ b/app/gui/src/controller/searcher/component/group.rs @@ -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(), diff --git a/app/gui/src/controller/text.rs b/app/gui/src/controller/text.rs index 1318447222e1..14a53ccf6468 100644 --- a/app/gui/src/controller/text.rs +++ b/app/gui/src/controller/text.rs @@ -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); }) diff --git a/app/gui/src/controller/upload.rs b/app/gui/src/controller/upload.rs index 08dc391c06ff..0debb6792d26 100644 --- a/app/gui/src/controller/upload.rs +++ b/app/gui/src/controller/upload.rs @@ -76,7 +76,6 @@ pub struct FileToUpload { /// API. #[derive(Clone, Debug)] pub struct FileUploadProcess { - logger: Logger, bin_connection: Rc, json_connection: Rc, file: FileToUpload, @@ -97,24 +96,14 @@ pub enum UploadingState { impl FileUploadProcess { /// Constructor. pub fn new( - parent: impl AnyLogger, file: FileToUpload, bin_connection: Rc, json_connection: Rc, 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 FileUploadProcess { 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 FileUploadProcess { } 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()); diff --git a/app/gui/src/executor/global.rs b/app/gui/src/executor/global.rs index 1c865766fccd..5e860d932423 100644 --- a/app/gui/src/executor/global.rs +++ b/app/gui/src/executor/global.rs @@ -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>>, } -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.") } } } diff --git a/app/gui/src/executor/web.rs b/app/gui/src/executor/web.rs index 6dd8f4db72cf..13cee03e28c7 100644 --- a/app/gui/src/executor/web.rs +++ b/app/gui/src/executor/web.rs @@ -21,7 +21,7 @@ pub mod test; /// An alias for a main animation loop. -pub type MainLoop = animation::Loop>; +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. diff --git a/app/gui/src/ide/initializer.rs b/app/gui/src/ide/initializer.rs index 38725019ea76..e824888d3804 100644 --- a/app/gui/src/ide/initializer.rs +++ b/app/gui/src/ide/initializer.rs @@ -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 { - 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 { 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::(); app.views.register::(); app.views.register::(); - app.views.register::(); + app.views.register::(); app.views.register::(); app.views.register::(); diff --git a/app/gui/src/lib.rs b/app/gui/src/lib.rs index 1bbe1370ec01..188bf93b359c 100644 --- a/app/gui/src/lib.rs +++ b/app/gui/src/lib.rs @@ -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)] diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index 9f20e11b6dd3..9c810077dcd8 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -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, 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 { 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 } diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index 26e31451a5d2..978489a4f776 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -68,16 +68,15 @@ impl ExecutionContext { language_server: Rc, root_definition: language_server::MethodPointer, ) -> impl Future> { - 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:?}."); } }); } diff --git a/app/gui/src/model/module.rs b/app/gui/src/model/module.rs index fb6e455a8177..3e5399d16649 100644 --- a/app/gui/src/model/module.rs +++ b/app/gui/src/model/module.rs @@ -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; +pub type TextChange = enso_text::Change; @@ -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, + replaced_location: enso_text::Range>, }, /// 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, ) -> 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) } } diff --git a/app/gui/src/model/module/plain.rs b/app/gui/src/model/module/plain.rs index 0c0fd2c458d9..513ca5b3139b 100644 --- a/app/gui/src/model/module/plain.rs +++ b/app/gui/src/model/module/plain.rs @@ -35,7 +35,6 @@ use parser::Parser; /// (text and graph). #[derive(Debug)] pub struct Module { - logger: Logger, path: Path, content: RefCell, notifications: notification::Publisher, @@ -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, ) -> 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 }); diff --git a/app/gui/src/model/module/synchronized.rs b/app/gui/src/model/module/synchronized.rs index 447f78ef5faf..7278f4cd6332 100644 --- a/app/gui/src/model/module/synchronized.rs +++ b/app/gui/src/model/module/synchronized.rs @@ -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, } 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, - id_map: Range, - metadata: Range, + source: text::Rope, + code: Range>, + id_map: Range>, + metadata: Range>, } 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) -> 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>) -> 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, - logger: Logger, } @@ -153,21 +157,20 @@ impl Module { parser: Parser, repository: Rc, ) -> FallibleResult> { - 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 { - 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 { 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| 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> + '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 { + fn edit_for_snipped( + start: &Location, + source: text::Rope, + target: text::Rope, + ) -> Option { // 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 { @@ -435,7 +445,7 @@ impl Module { ls_content: &ParsedContentSummary, new_file: SourceFile, ) -> impl Future> + '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>, + current_ls_content: Rc>, current_ls_version: Rc>, } impl LsClientSetup { - fn new(parent: impl AnyLogger, path: Path, initial_content: impl Into) -> Self { + fn new(path: Path, initial_content: impl Into) -> 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, edit: &TextEdit) -> Text { + fn apply_edit(code: impl Into, 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, file_edit: &FileEdit) -> Text { + fn apply_edits(code: impl Into, 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, ); diff --git a/app/gui/src/model/project/synchronized.rs b/app/gui/src/model/project/synchronized.rs index 4c8c7c001323..9698fe4d870b 100644 --- a/app/gui/src/model/project/synchronized.rs +++ b/app/gui/src/model/project/synchronized.rs @@ -266,7 +266,7 @@ impl Project { ) -> FallibleResult { 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> { 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) diff --git a/app/gui/src/model/registry.rs b/app/gui/src/model/registry.rs index 0fca9d37c5e5..fcdc207fb61c 100644 --- a/app/gui/src/model/registry.rs +++ b/app/gui/src/model/registry.rs @@ -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(); diff --git a/app/gui/src/model/suggestion_database.rs b/app/gui/src/model/suggestion_database.rs index 367e65a89d4c..b449b471e1f4 100644 --- a/app/gui/src/model/suggestion_database.rs +++ b/app/gui/src/model/suggestion_database.rs @@ -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>>, qualified_name_to_id_map: RefCell, examples: RefCell>>, @@ -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, ) -> 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> { + pub fn lookup_at(&self, name: impl Str, location: &ModuleSpan) -> Vec> { 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> { + pub fn lookup_locals_at(&self, name: impl Str, location: &ModuleSpan) -> Vec> { 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(".."); diff --git a/app/gui/src/model/suggestion_database/entry.rs b/app/gui/src/model/suggestion_database/entry.rs index 3a583ab23b3f..0498ec3b1429 100644 --- a/app/gui/src/model/suggestion_database/entry.rs +++ b/app/gui/src/model/suggestion_database/entry.rs @@ -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, +} + + + // ================ // === 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 }, + InModule { range: RangeInclusive> }, } /// 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 { 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) } diff --git a/app/gui/src/model/undo_redo.rs b/app/gui/src/model/undo_redo.rs index 69cca87aa53b..00d51ea6947d 100644 --- a/app/gui/src/model/undo_redo.rs +++ b/app/gui/src/model/undo_redo.rs @@ -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 { 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); diff --git a/app/gui/src/presenter.rs b/app/gui/src/presenter.rs index c8e9d20f24dd..c44132fc9829 100644 --- a/app/gui/src/presenter.rs +++ b/app/gui/src/presenter.rs @@ -35,7 +35,6 @@ pub use searcher::Searcher; #[derive(Debug)] struct Model { - logger: Logger, current_project: RefCell>, 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::::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}."); } } } diff --git a/app/gui/src/presenter/code.rs b/app/gui/src/presenter/code.rs index 3f5b8d264369..94b926ffcc8d 100644 --- a/app/gui/src/presenter/code.rs +++ b/app/gui/src/presenter/code.rs @@ -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)); diff --git a/app/gui/src/presenter/graph.rs b/app/gui/src/presenter/graph.rs index 5d35be02f5b0..e249d01e32f2 100644 --- a/app/gui/src/presenter/graph.rs +++ b/app/gui/src/presenter/graph.rs @@ -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(&self, f: F, action: &str) where F: FnOnce() -> Option { 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()) } })); diff --git a/app/gui/src/presenter/graph/call_stack.rs b/app/gui/src/presenter/graph/call_stack.rs index 4cc4daeb5871..5fee913e6eef 100644 --- a/app/gui/src/presenter/graph/call_stack.rs +++ b/app/gui/src/presenter/graph/call_stack.rs @@ -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, @@ -27,13 +26,11 @@ struct Model { impl Model { fn new( - parent: impl AnyLogger, controller: controller::ExecutedGraph, view: view::graph_editor::GraphEditor, state: Rc, ) -> 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, ) -> 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(), diff --git a/app/gui/src/presenter/graph/visualization.rs b/app/gui/src/presenter/graph/visualization.rs index 9bbcdbb38081..ca45b7f3d3c2 100644 --- a/app/gui/src/presenter/graph/visualization.rs +++ b/app/gui/src/presenter/graph/visualization.rs @@ -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}"); } }, _ => {} diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 247f9aff2c05..1d4923c11bc4 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -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, executed_graph: ExecutedGraph, notification_sender: futures::channel::mpsc::UnboundedSender, @@ -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, UnboundedReceiver) { - let logger = logger.sub("visualization::Manager"); + pub fn new(executed_graph: ExecutedGraph) -> (Rc, UnboundedReceiver) { 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, target: ast::Id, new_desired: Option) { - 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, 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 } } } diff --git a/app/gui/src/presenter/project.rs b/app/gui/src/presenter/project.rs index 3181b84ac2ed..fa7b6204dc22 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -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, diff --git a/app/gui/src/presenter/searcher.rs b/app/gui/src/presenter/searcher.rs index c8ca8fea5b5b..15a2dd1b7efc 100644 --- a/app/gui/src/presenter/searcher.rs +++ b/app/gui/src/presenter/searcher.rs @@ -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 { diff --git a/app/gui/src/presenter/searcher/provider.rs b/app/gui/src/presenter/searcher/provider.rs index f2185dc12bdb..150719aec5b0 100644 --- a/app/gui/src/presenter/searcher/provider.rs +++ b/app/gui/src/presenter/searcher/provider.rs @@ -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 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 for Component // === Component Provider helpers === -fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec> { +fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec> { 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 Rc { - 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( diff --git a/app/gui/src/transport/web.rs b/app/gui/src/transport/web.rs index 2bac3752ed39..8b2106f55f92 100644 --- a/app/gui/src/transport/web.rs +++ b/app/gui/src/transport/web.rs @@ -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) { - 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::() { 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"); } } diff --git a/app/gui/tests/language_server.rs b/app/gui/tests/language_server.rs index 9a755ee22fa8..4c74120dd06c 100644 --- a/app/gui/tests/language_server.rs +++ b/app/gui/tests/language_server.rs @@ -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()); diff --git a/app/gui/view/component-browser/breadcrumbs/src/entry.rs b/app/gui/view/component-browser/breadcrumbs/src/entry.rs index 971c1c0f438f..0ab69252c7da 100644 --- a/app/gui/view/component-browser/breadcrumbs/src/entry.rs +++ b/app/gui/view/component-browser/breadcrumbs/src/entry.rs @@ -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>, @@ -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::(); + let display_object = display::object::Instance::new(); + let text = app.new_view::(); 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 { diff --git a/app/gui/view/component-browser/breadcrumbs/src/lib.rs b/app/gui/view/component-browser/breadcrumbs/src/lib.rs index fe336e98f53f..e6b9c52e1df9 100644 --- a/app/gui/view/component-browser/breadcrumbs/src/lib.rs +++ b/app/gui/view/component-browser/breadcrumbs/src/lib.rs @@ -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, diff --git a/app/gui/view/component-browser/component-group/src/entry.rs b/app/gui/view/component-browser/component-group/src/entry.rs index aa7d18708a65..f000f1db230f 100644 --- a/app/gui/view/component-browser/component-group/src/entry.rs +++ b/app/gui/view/component-browser/component-group/src/entry.rs @@ -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> = default(); let selected_icon: Rc> = 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."); } } } diff --git a/app/gui/view/component-browser/component-group/src/icon/define_macro.rs b/app/gui/view/component-browser/component-group/src/icon/define_macro.rs index a7ddc52a28e5..7903986761a8 100644 --- a/app/gui/view/component-browser/component-group/src/icon/define_macro.rs +++ b/app/gui/view/component-browser/component-group/src/icon/define_macro.rs @@ -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(); diff --git a/app/gui/view/component-browser/component-group/src/icon/entry.rs b/app/gui/view/component-browser/component-group/src/icon/entry.rs index 7d45142934db..10067cbbedec 100644 --- a/app/gui/view/component-browser/component-group/src/icon/entry.rs +++ b/app/gui/view/component-browser/component-group/src/icon/entry.rs @@ -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>> = 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); diff --git a/app/gui/view/component-browser/component-group/src/lib.rs b/app/gui/view/component-browser/component-group/src/lib.rs index ce5c5d59aced..d67bf89d2da8 100644 --- a/app/gui/view/component-browser/component-group/src/lib.rs +++ b/app/gui/view/component-browser/component-group/src/lib.rs @@ -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 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 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, - header: text::Area, + header: text::Text, header_background: header_background::View, header_text: Rc>, 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::>(); 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( diff --git a/app/gui/view/component-browser/component-group/src/new_entry.rs b/app/gui/view/component-browser/component-group/src/new_entry.rs index 26e22110493c..234bfec9fee0 100644 --- a/app/gui/view/component-browser/component-group/src/new_entry.rs +++ b/app/gui/view/component-browser/component-group/src/new_entry.rs @@ -113,7 +113,7 @@ pub struct Model { pub kind: Kind, pub color: color::Rgba, pub caption: ImString, - pub highlighted: Rc>>, + pub highlighted: Rc>>, pub icon: Option, 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>, 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::(); - let background = background::View::new(Logger::new("ComponentGroupEntry")); + let display_object = display::object::Instance::new(); + let label = app.new_view::(); + 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 } } diff --git a/app/gui/view/component-browser/component-group/src/wide.rs b/app/gui/view/component-browser/component-group/src/wide.rs index 567e4a32cd14..a4988015b3b9 100644 --- a/app/gui/view/component-browser/component-group/src/wide.rs +++ b/app/gui/view/component-browser/component-group/src/wide.rs @@ -134,12 +134,12 @@ ensogl::define_endpoints_2! { impl component::Frp> for Frp { fn init( + network: &frp::Network, api: &Self::Private, _app: &Application, model: &Model, 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 component::Model for Model { "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); diff --git a/app/gui/view/component-browser/searcher-list-panel/src/column_grid.rs b/app/gui/view/component-browser/searcher-list-panel/src/column_grid.rs index 38f874f78c7c..c4849c228200 100644 --- a/app/gui/view/component-browser/searcher-list-panel/src/column_grid.rs +++ b/app/gui/view/component-browser/searcher-list-panel/src/column_grid.rs @@ -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 for Frp { fn init( + network: &frp::Network, frp_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); diff --git a/app/gui/view/component-browser/searcher-list-panel/src/lib.rs b/app/gui/view/component-browser/searcher-list-panel/src/lib.rs index dc2fc0798893..6224528cc65b 100644 --- a/app/gui/view/component-browser/searcher-list-panel/src/lib.rs +++ b/app/gui/view/component-browser/searcher-list-panel/src/lib.rs @@ -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 { - pub label: text::Area, + pub label: text::Text, pub divider: hline::View, pub content: T, } @@ -737,16 +737,15 @@ type ColumnSection = LabeledSection; impl LabeledSection { 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 for Frp { fn init( + network: &frp::Network, frp_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; diff --git a/app/gui/view/component-browser/searcher-list-panel/src/navigator.rs b/app/gui/view/component-browser/searcher-list-panel/src/navigator.rs index 5ecaa3b262c2..5cd7bbe1c802 100644 --- a/app/gui/view/component-browser/searcher-list-panel/src/navigator.rs +++ b/app/gui/view/component-browser/searcher-list-panel/src/navigator.rs @@ -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::>(); let bottom_buttons = app.new_view::>(); top_buttons.set_style_prefix(list_panel_theme::navigator_list_view::HERE.str); diff --git a/app/gui/view/component-browser/src/lib.rs b/app/gui/view/component-browser/src/lib.rs index 3de78d763819..58ab3515a7b0 100644 --- a/app/gui/view/component-browser/src/lib.rs +++ b/app/gui/view/component-browser/src/lib.rs @@ -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)] diff --git a/app/gui/view/debug_scene/component-group/src/lib.rs b/app/gui/view/debug_scene/component-group/src/lib.rs index 09a1e7e479df..7adb18448468 100644 --- a/app/gui/view/debug_scene/component-group/src/lib.rs +++ b/app/gui/view/debug_scene/component-group/src/lib.rs @@ -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 { const HIGHLIGHTED_ENTRY_NAME: &str = "convert"; - const HIGHLIGHTED_RANGE: Range = Bytes(0)..Bytes(3); + const HIGHLIGHTED_RANGE: Range = 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); diff --git a/app/gui/view/debug_scene/component-list-panel-view/src/lib.rs b/app/gui/view/debug_scene/component-list-panel-view/src/lib.rs index 5ca4ef5a493f..8260361780fc 100644 --- a/app/gui/view/debug_scene/component-list-panel-view/src/lib.rs +++ b/app/gui/view/debug_scene/component-list-panel-view/src/lib.rs @@ -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)] diff --git a/app/gui/view/debug_scene/icons/src/lib.rs b/app/gui/view/debug_scene/icons/src/lib.rs index b55e01bf569f..10dbe7db1d10 100644 --- a/app/gui/view/debug_scene/icons/src/lib.rs +++ b/app/gui/view/debug_scene/icons/src/lib.rs @@ -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); diff --git a/app/gui/view/debug_scene/interface/src/lib.rs b/app/gui/view/debug_scene/interface/src/lib.rs index 2dfb05313993..6fdd3e17e459 100644 --- a/app/gui/view/debug_scene/interface/src/lib.rs +++ b/app/gui/view/debug_scene/interface/src/lib.rs @@ -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::(); app.views.register::(); - app.views.register::(); + app.views.register::(); app.views.register::(); let root_view = app.new_view::(); let project_view = root_view.project(); diff --git a/app/gui/view/debug_scene/new-component-list-panel-view/src/lib.rs b/app/gui/view/debug_scene/new-component-list-panel-view/src/lib.rs index 3931d9573a4e..c133ec9fcc14 100644 --- a/app/gui/view/debug_scene/new-component-list-panel-view/src/lib.rs +++ b/app/gui/view/debug_scene/new-component-list-panel-view/src/lib.rs @@ -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![] }; diff --git a/app/gui/view/debug_scene/src/lib.rs b/app/gui/view/debug_scene/src/lib.rs index b8aadeb174f0..5a53536cf0a0 100644 --- a/app/gui/view/debug_scene/src/lib.rs +++ b/app/gui/view/debug_scene/src/lib.rs @@ -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)] diff --git a/app/gui/view/debug_scene/visualization/src/lib.rs b/app/gui/view/debug_scene/visualization/src/lib.rs index 7d8e87589c1d..96825cbc9155 100644 --- a/app/gui/view/debug_scene/visualization/src/lib.rs +++ b/app/gui/view/debug_scene/visualization/src/lib.rs @@ -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)] diff --git a/app/gui/view/graph-editor/src/builtin/visualization/native/bubble_chart.rs b/app/gui/view/graph-editor/src/builtin/visualization/native/bubble_chart.rs index 57c57e6c4bdd..b55d3b9f418a 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/native/bubble_chart.rs +++ b/app/gui/view/graph-editor/src/builtin/visualization/native/bubble_chart.rs @@ -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); diff --git a/app/gui/view/graph-editor/src/component/breadcrumbs.rs b/app/gui/view/graph-editor/src/component/breadcrumbs.rs index 05ddc985a99f..9a028f4db8a6 100644 --- a/app/gui/view/graph-editor/src/component/breadcrumbs.rs +++ b/app/gui/view/graph-editor/src/component/breadcrumbs.rs @@ -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>) { - 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(); diff --git a/app/gui/view/graph-editor/src/component/breadcrumbs/breadcrumb.rs b/app/gui/view/graph-editor/src/component/breadcrumbs/breadcrumb.rs index 558e98530adb..c26a13ab6b66 100644 --- a/app/gui/view/graph-editor/src/component/breadcrumbs/breadcrumb.rs +++ b/app/gui/view/graph-editor/src/component/breadcrumbs/breadcrumb.rs @@ -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::(); + 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::(); 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) { 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); diff --git a/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs b/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs index 8f5648189ff0..4bfc4d0d7d44 100644 --- a/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs +++ b/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs @@ -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>, } @@ -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_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_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) { @@ -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>(&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 } diff --git a/app/gui/view/graph-editor/src/component/edge.rs b/app/gui/view/graph-editor/src/component/edge.rs index 5a016912281a..7dd8d1f4cc78 100644 --- a/app/gui/view/graph-editor/src/component/edge.rs +++ b/app/gui/view/graph-editor/src/component/edge.rs @@ -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 = 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); diff --git a/app/gui/view/graph-editor/src/component/node.rs b/app/gui/view/graph-editor/src/component/node.rs index a3f967f80ba9..fe1f4279525f 100644 --- a/app/gui/view/graph-editor/src/component/node.rs +++ b/app/gui/view/graph-editor/src/component/node.rs @@ -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 {..}) diff --git a/app/gui/view/graph-editor/src/component/node/action_bar.rs b/app/gui/view/graph-editor/src/component/node/action_bar.rs index 7e846c752d26..0d7b904fad8b 100644 --- a/app/gui/view/graph-editor/src/component/node/action_bar.rs +++ b/app/gui/view/graph-editor/src/component/node/action_bar.rs @@ -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() } diff --git a/app/gui/view/graph-editor/src/component/node/error.rs b/app/gui/view/graph-editor/src/component/node/error.rs index 097cad2ae788..a3e28557257b 100644 --- a/app/gui/view/graph-editor/src/component/node/error.rs +++ b/app/gui/view/graph-editor/src/component/node/error.rs @@ -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); diff --git a/app/gui/view/graph-editor/src/component/node/input/area.rs b/app/gui/view/graph-editor/src/component/node/input/area.rs index 001925beb10d..150e3df69794 100644 --- a/app/gui/view/graph-editor/src/component/node/input/area.rs +++ b/app/gui/view/graph-editor/src/component/node/input/area.rs @@ -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 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 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, id_crumbs_map: RefCell>, 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::(); + let label = app.new_view::(); 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, 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, 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::::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(); } } } diff --git a/app/gui/view/graph-editor/src/component/node/input/port.rs b/app/gui/view/graph-editor/src/component/node/input/port.rs index 27afed55ce98..0e00d9d03804 100644 --- a/app/gui/view/graph-editor/src/component/node/input/port.rs +++ b/app/gui/view/graph-editor/src/component/node/input/port.rs @@ -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, pub name: Option, - 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 { + pub fn range(&self) -> enso_text::Range { let start = self.index; let end = self.index + self.length; enso_text::Range::new(start, end) diff --git a/app/gui/view/graph-editor/src/component/node/output/area.rs b/app/gui/view/graph-editor/src/component/node/output/area.rs index ad48b4deec7d..cb583df2c466 100644 --- a/app/gui/view/graph-editor/src/component/node/output/area.rs +++ b/app/gui/view/graph-editor/src/component/node/output/area.rs @@ -108,7 +108,7 @@ impl From 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, id_crumbs_map: RefCell>, port_count: Cell, @@ -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::(); + let label = app.new_view::(); 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 === diff --git a/app/gui/view/graph-editor/src/component/node/output/port.rs b/app/gui/view/graph-editor/src/component/node/output/port.rs index f227d44a1e3d..681daee14a5d 100644 --- a/app/gui/view/graph-editor/src/component/node/output/port.rs +++ b/app/gui/view/graph-editor/src/component/node/output/port.rs @@ -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, pub shape: Option, - pub type_label: Option, + pub type_label: Option, pub display_object: Option, - 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::(); + let type_label = app.new_view::(); 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() } diff --git a/app/gui/view/graph-editor/src/component/node/profiling.rs b/app/gui/view/graph-editor/src/component/node/profiling.rs index fad9468e8a81..875daf6a0dc6 100644 --- a/app/gui/view/graph-editor/src/component/node/profiling.rs +++ b/app/gui/view/graph-editor/src/component/node/profiling.rs @@ -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 } diff --git a/app/gui/view/graph-editor/src/component/node/vcs.rs b/app/gui/view/graph-editor/src/component/node/vcs.rs index 1d33542d5125..2936643870aa 100644 --- a/app/gui/view/graph-editor/src/component/node/vcs.rs +++ b/app/gui/view/graph-editor/src/component/node/vcs.rs @@ -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) } diff --git a/app/gui/view/graph-editor/src/component/profiling.rs b/app/gui/view/graph-editor/src/component/profiling.rs index e441e335f82c..371fbefb1774 100644 --- a/app/gui/view/graph-editor/src/component/profiling.rs +++ b/app/gui/view/graph-editor/src/component/profiling.rs @@ -148,7 +148,7 @@ impl Button { let frp = Frp::new(); let network = &frp.network; - let button = ToggleButton::::new(Logger::new("profiling::Button")); + let button = ToggleButton::::new(); scene.layers.panel.add_exclusive(&button); button.set_visibility(true); button.set_size(Vector2(32.0, 32.0)); diff --git a/app/gui/view/graph-editor/src/component/visualization/container.rs b/app/gui/view/graph-editor/src/component/visualization/container.rs index 73b0424e69a5..4546fe56cbbe 100644 --- a/app/gui/view/graph-editor/src/component/visualization/container.rs +++ b/app/gui/view/graph-editor/src/component/visualization/container.rs @@ -169,11 +169,10 @@ pub struct View { impl View { /// Constructor. - pub fn new(logger: &Logger, scene: Scene) -> Self { - let logger = Logger::new_sub(logger, "view"); - let display_object = display::object::Instance::new(&logger); - let background = background::View::new(&logger); - let overlay = overlay::View::new(&logger); + pub fn new(scene: Scene) -> Self { + let display_object = display::object::Instance::new(); + let background = background::View::new(); + let overlay = overlay::View::new(); display_object.add_child(&background); display_object.add_child(&overlay); @@ -265,11 +264,11 @@ impl ContainerModel { pub fn new(logger: &Logger, app: &Application, registry: visualization::Registry) -> Self { let scene = &app.display.default_scene; let logger = Logger::new_sub(logger, "visualization_container"); - let display_object = display::object::Instance::new(&logger); - let drag_root = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); + let drag_root = display::object::Instance::new(); let visualization = default(); let vis_frp_connection = default(); - let view = View::new(&logger, scene.clone_ref()); + let view = View::new(scene.clone_ref()); let fullscreen_view = fullscreen::Panel::new(&logger, scene); let scene = scene.clone_ref(); let is_fullscreen = default(); diff --git a/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs b/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs index 983d9c7bdfac..2de96cd2ea53 100644 --- a/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs +++ b/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs @@ -165,12 +165,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 icon_root = display::object::Instance::new(&logger); - let reset_position_icon = pin_icon::View::new(&logger); - let drag_icon = four_arrow_icon::View::new(&logger); + fn new() -> Self { + let display_object = display::object::Instance::new(); + let icon_root = display::object::Instance::new(); + let reset_position_icon = pin_icon::View::new(); + let drag_icon = four_arrow_icon::View::new(); let size = default(); display_object.add_child(&icon_root); @@ -262,13 +261,12 @@ struct Model { impl Model { fn new(app: &Application, vis_registry: visualization::Registry) -> Self { - let logger = Logger::new("ActionBarModel"); - let background = background::View::new(&logger); - let hover_area = hover_area::View::new(&logger); + let background = background::View::new(); + let hover_area = hover_area::View::new(); let visualization_chooser = VisualizationChooser::new(app, vis_registry); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let size = default(); - let icons = Icons::new(logger); + let icons = Icons::new(); let shapes = compound::events::MouseEvents::default(); app.display.default_scene.layers.below_main.add_exclusive(&hover_area); diff --git a/app/gui/view/graph-editor/src/component/visualization/container/fullscreen.rs b/app/gui/view/graph-editor/src/component/visualization/container/fullscreen.rs index d8a6f4ecf3b7..dbfc1efc0ef0 100644 --- a/app/gui/view/graph-editor/src/component/visualization/container/fullscreen.rs +++ b/app/gui/view/graph-editor/src/component/visualization/container/fullscreen.rs @@ -62,7 +62,7 @@ impl Panel { /// Constructor. pub fn new(logger: &Logger, scene: &Scene) -> Self { let logger = Logger::new_sub(logger, "fullscreen_view"); - 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) diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs index f5c28b38dc80..b88a8cf567f3 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs @@ -303,10 +303,9 @@ impl Instance { let callback = Box::new(callback); self.model.preprocessor_change.borrow_mut().replace(callback); if let Err(err) = self.model.update_preprocessor() { - let logger = self.model.logger.clone(); error!( - logger, - "Failed to trigger initial preprocessor update from JS: {err.print_to_string()}" + "Failed to trigger initial preprocessor update from JS: {}", + err.print_to_string() ); } self diff --git a/app/gui/view/graph-editor/src/lib.rs b/app/gui/view/graph-editor/src/lib.rs index 703807a75430..65ff42c891c1 100644 --- a/app/gui/view/graph-editor/src/lib.rs +++ b/app/gui/view/graph-editor/src/lib.rs @@ -15,6 +15,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] #![allow(incomplete_features)] // To be removed, see: https://github.com/enso-org/ide/issues/1559 #![warn(missing_copy_implementations)] #![warn(missing_debug_implementations)] @@ -1693,7 +1694,7 @@ impl GraphEditorModel { let network = frp.network(); let scene = &app.display.default_scene; let logger = Logger::new("GraphEditor"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let nodes = Nodes::new(&logger); let edges = Edges::new(&logger); let vis_registry = visualization::Registry::with_default_visualizations(); diff --git a/app/gui/view/src/code_editor.rs b/app/gui/view/src/code_editor.rs index 4b5046d62528..17a0829db731 100644 --- a/app/gui/view/src/code_editor.rs +++ b/app/gui/view/src/code_editor.rs @@ -52,7 +52,7 @@ ensogl::define_endpoints! { /// The View of IDE Code Editor. #[derive(Clone, CloneRef, Debug)] pub struct View { - model: text::Area, + model: text::Text, styles: StyleWatchFrp, frp: Frp, } @@ -71,7 +71,7 @@ impl View { let styles = StyleWatchFrp::new(&app.display.default_scene.style_sheet); let frp = Frp::new(); let network = &frp.network; - let model = app.new_view::(); + let model = app.new_view::(); let height_fraction = DEPRECATED_Animation::::new(network); model.set_position_x(PADDING_LEFT); @@ -111,16 +111,16 @@ impl View { eval position ((pos) model.set_position_xy(*pos)); let color = styles.get_color(ensogl_hardcoded_theme::code::syntax::base); - eval color ((color) model.set_default_color(color)); + eval color ((color) model.set_property_default(color)); } init.emit(()); - model.set_default_color(color.value()); + model.set_property_default(color.value()); Self { model, styles, frp } } /// Return the Text Area component inside this editor. - pub fn text_area(&self) -> &text::Area { + pub fn text_area(&self) -> &text::Text { &self.model } } @@ -131,7 +131,7 @@ impl display::Object for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/app/gui/view/src/component_browser.rs b/app/gui/view/src/component_browser.rs index 61fbf01943d2..185f52f9c88c 100644 --- a/app/gui/view/src/component_browser.rs +++ b/app/gui/view/src/component_browser.rs @@ -29,7 +29,6 @@ pub use ide_view_component_browser::*; #[allow(missing_docs)] #[derive(Clone, CloneRef, Debug)] pub struct Model { - logger: Logger, display_object: display::object::Instance, pub list: list_panel::ComponentBrowserPanel, pub documentation: documentation::View, @@ -40,15 +39,14 @@ impl component::Model for Model { "ComponentBrowser" } - fn new(app: &Application, logger: &Logger) -> Self { - let logger = logger.sub("ComponentBrowser"); - let display_object = display::object::Instance::new(&logger); + fn new(app: &Application) -> Self { + let display_object = display::object::Instance::new(); let list = app.new_view::(); let documentation = documentation::View::new(&app.display.default_scene); app.display.default_scene.layers.node_searcher.add_exclusive(&display_object); display_object.add_child(&list); display_object.add_child(&documentation); - Self { logger, display_object, list, documentation } + Self { display_object, list, documentation } } } @@ -88,12 +86,12 @@ ensogl::define_endpoints_2! { impl component::Frp for Frp { fn init( + network: &frp::Network, frp_api: &::Private, _app: &Application, model: &Model, style: &StyleWatchFrp, ) { - let network = &frp_api.network; let input = &frp_api.input; let out = &frp_api.output; let list_panel = &model.list.output; diff --git a/app/gui/view/src/debug_mode_popup.rs b/app/gui/view/src/debug_mode_popup.rs index 796084cb5f02..d684280a1b23 100644 --- a/app/gui/view/src/debug_mode_popup.rs +++ b/app/gui/view/src/debug_mode_popup.rs @@ -112,7 +112,7 @@ impl Model { /// Constructor. pub fn new(app: &Application) -> Self { let logger = Logger::new("DebugModePopup"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let label = PopupLabel::new(app); label.set_delay(LABEL_VISIBILITY_DELAY_MS); display_object.add_child(&label); diff --git a/app/gui/view/src/documentation.rs b/app/gui/view/src/documentation.rs index a33269f38cbd..c5e6d9ab98d4 100644 --- a/app/gui/view/src/documentation.rs +++ b/app/gui/view/src/documentation.rs @@ -73,14 +73,14 @@ impl Model { /// Constructor. fn new(scene: &Scene) -> Self { let logger = Logger::new("DocumentationView"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let outer_div = web::document.create_div_or_panic(); let outer_dom = DomSymbol::new(&outer_div); let inner_div = web::document.create_div_or_panic(); let inner_dom = DomSymbol::new(&inner_div); let size = Rc::new(Cell::new(Vector2(VIEW_WIDTH - PADDING, VIEW_HEIGHT - PADDING - PADDING_TOP))); - let overlay = overlay::View::new(&logger); + let overlay = overlay::View::new(); // FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape // system (#795) @@ -163,7 +163,7 @@ impl Model { match copy_button.add_event_listener_with_callback("click", callback) { Ok(_) => Some(closure), Err(e) => { - error!(&self.logger, "Unable to add event listener to copy button: {e:?}"); + error!("Unable to add event listener to copy button: {e:?}"); None } } @@ -174,10 +174,7 @@ impl Model { let ok_closures = closures.into_iter().filter_map(|t| t.ok()).collect_vec(); let err_indices = errors.into_iter().filter_map(|t| t.err()).collect_vec(); if !err_indices.is_empty() { - error!( - &self.logger, - "Failed to attach listeners to copy buttons with indices: {err_indices:?}." - ) + error!("Failed to attach listeners to copy buttons with indices: {err_indices:?}.") } self.code_copy_closures.set(ok_closures) } @@ -190,7 +187,6 @@ impl Model { Ok(string) => string, Err(err) => { error!( - self.logger, "Error during documentation vis-data serialization: \ {err:?}" ); diff --git a/app/gui/view/src/lib.rs b/app/gui/view/src/lib.rs index b58a77d012f2..f99eacdcce34 100644 --- a/app/gui/view/src/lib.rs +++ b/app/gui/view/src/lib.rs @@ -16,6 +16,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] #![allow(incomplete_features)] // To be removed, see: https://github.com/enso-org/ide/issues/1559 #![warn(missing_copy_implementations)] #![warn(missing_debug_implementations)] diff --git a/app/gui/view/src/open_dialog.rs b/app/gui/view/src/open_dialog.rs index 732f296d41c5..7150ad788bac 100644 --- a/app/gui/view/src/open_dialog.rs +++ b/app/gui/view/src/open_dialog.rs @@ -49,7 +49,7 @@ impl OpenDialog { // Once FileBrowser will be implemented as component, it should be instantiated this way: //let file_browser = app.new_view::(); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); display_object.add_child(&project_list); display_object.add_child(&file_browser); diff --git a/app/gui/view/src/open_dialog/project_list.rs b/app/gui/view/src/open_dialog/project_list.rs index a825ff03f5e8..72aa646a857d 100644 --- a/app/gui/view/src/open_dialog/project_list.rs +++ b/app/gui/view/src/open_dialog/project_list.rs @@ -69,7 +69,7 @@ pub struct ProjectList { network: frp::Network, display_object: display::object::Instance, background: background::View, //TODO[ao] use Card instead. - caption: text::Area, + caption: text::Text, list: list_view::ListView, style_watch: StyleWatchFrp, } @@ -87,9 +87,9 @@ impl ProjectList { pub fn new(app: &Application) -> Self { let logger = Logger::new("ProjectList"); let network = frp::Network::new("ProjectList"); - let display_object = display::object::Instance::new(&logger); - let background = background::View::new(&logger); - let caption = app.new_view::(); + let display_object = display::object::Instance::new(); + let background = background::View::new(); + let caption = app.new_view::(); let list = app.new_view::>(); display_object.add_child(&background); display_object.add_child(&caption); @@ -132,8 +132,8 @@ impl ProjectList { eval list_size ((size) list.resize(*size)); eval list_y ((y) list.set_position_y(*y)); eval caption_xy ((xy) caption.set_position_xy(*xy)); - eval color ((color) caption.set_default_color(color)); - eval label_size ((size) caption.set_default_text_size(text::Size(*size))); + eval color ((color) caption.set_property_default(color)); + eval label_size ((size) caption.set_property_default(text::Size(*size))); }; init.emit(()); diff --git a/app/gui/view/src/project.rs b/app/gui/view/src/project.rs index 3976e98de08b..a082026de44f 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -260,7 +260,7 @@ struct Model { code_editor: code_editor::View, fullscreen_vis: Rc>>, prompt_background: prompt_background::View, - prompt: ensogl_text::Area, + prompt: ensogl_text::Text, open_dialog: Rc, debug_mode_popup: debug_mode_popup::View, } @@ -269,14 +269,14 @@ impl Model { fn new(app: &Application) -> Self { let logger = Logger::new("project::View"); let scene = &app.display.default_scene; - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let searcher = SearcherVariant::new(app); let graph_editor = app.new_view::(); searcher.set_navigator(graph_editor.model.navigator.clone_ref()); let code_editor = app.new_view::(); let fullscreen_vis = default(); - let prompt_background = prompt_background::View::new(&logger); - let prompt = ensogl_text::Area::new(app); + let prompt_background = prompt_background::View::new(); + let prompt = ensogl_text::Text::new(app); let debug_mode_popup = debug_mode_popup::View::new(app); let window_control_buttons = ARGS.is_in_cloud.unwrap_or_default().as_some_from(|| { let window_control_buttons = app.new_view::(); @@ -344,7 +344,7 @@ impl Model { if let Some(node) = self.graph_editor.nodes().get_cloned_ref(&node_id) { node.position().xy() } else { - error!(self.logger, "Trying to show searcher under nonexisting node"); + error!("Trying to show searcher under nonexisting node"); default() } } @@ -730,8 +730,8 @@ impl View { let mut color = *color; color.alpha *= weight; model.prompt_background.color_rgba.set(bg_color.into()); - model.prompt.set_color_all(color); - model.prompt.set_default_text_size(ensogl_text::Size(*size)); + model.prompt.set_property(.., color); + model.prompt.set_property_default(ensogl_text::Size(*size)); }) ); _eval <- all_with3(&model.prompt.width,&prompt_size,&prompt_bg_padding, @@ -800,7 +800,7 @@ impl display::Object for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/app/gui/view/src/root.rs b/app/gui/view/src/root.rs index 0c88667febe7..089c638e1eb8 100644 --- a/app/gui/view/src/root.rs +++ b/app/gui/view/src/root.rs @@ -45,7 +45,7 @@ impl Model { pub fn new(app: &Application) -> Self { let app = app.clone_ref(); let logger = Logger::new("RootView"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let state = Rc::new(CloneCell::new(State::WelcomeScreen)); let status_bar = crate::status_bar::View::new(&app); display_object.add_child(&status_bar); @@ -161,7 +161,7 @@ impl display::Object for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/app/gui/view/src/searcher.rs b/app/gui/view/src/searcher.rs index c233da211631..600d5d2dc1ee 100644 --- a/app/gui/view/src/searcher.rs +++ b/app/gui/view/src/searcher.rs @@ -120,7 +120,7 @@ impl Model { let scene = &app.display.default_scene; let app = app.clone_ref(); let logger = Logger::new("SearcherView"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let list = app.new_view::>(); list.focus(); let documentation = documentation::View::new(scene); @@ -288,7 +288,7 @@ impl display::Object for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/app/gui/view/src/status_bar.rs b/app/gui/view/src/status_bar.rs index c2b6b2e1a104..0aa4167a2e80 100644 --- a/app/gui/view/src/status_bar.rs +++ b/app/gui/view/src/status_bar.rs @@ -151,7 +151,7 @@ struct Model { display_object: display::object::Instance, root: display::object::Instance, background: background::View, - label: text::Area, + label: text::Text, events: Rc>>, processes: Rc>>, next_process_id: Rc>, @@ -162,10 +162,10 @@ impl Model { fn new(app: &Application) -> Self { let scene = &app.display.default_scene; let logger = Logger::new("StatusBar"); - let display_object = display::object::Instance::new(&logger); - let root = display::object::Instance::new(&logger); - let background = background::View::new(&logger); - let label = text::Area::new(app); + let display_object = display::object::Instance::new(); + let root = display::object::Instance::new(); + let background = background::View::new(); + let label = text::Text::new(app); let events = default(); let processes = default(); let next_process_id = Rc::new(RefCell::new(process::Id(1))); @@ -178,8 +178,8 @@ impl Model { let text_color_path = theme::application::status_bar::text; let style = StyleWatch::new(&app.display.default_scene.style_sheet); let text_color = style.get_color(text_color_path); - label.frp.set_color_all.emit(text_color); - label.frp.set_default_color.emit(text_color); + label.frp.set_property(.., text_color); + label.frp.set_property_default(text_color); Self { logger, diff --git a/app/gui/view/src/window_control_buttons.rs b/app/gui/view/src/window_control_buttons.rs index 2307125c4447..f62923daeac3 100644 --- a/app/gui/view/src/window_control_buttons.rs +++ b/app/gui/view/src/window_control_buttons.rs @@ -150,7 +150,7 @@ impl Model { pub fn new(app: &Application) -> Self { let app = app.clone_ref(); let logger = Logger::new("TopButtons"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); ensogl::shapes_order_dependencies! { app.display.default_scene => { @@ -164,7 +164,7 @@ impl Model { let fullscreen = fullscreen::View::new(&app); display_object.add_child(&fullscreen); - let shape = shape::View::new(&logger); + let shape = shape::View::new(); display_object.add_child(&shape); Self { app, logger, display_object, shape, close, fullscreen } @@ -284,7 +284,7 @@ impl Deref for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/app/gui/view/welcome-screen/src/lib.rs b/app/gui/view/welcome-screen/src/lib.rs index dd481dbffb60..5e9c782d7214 100644 --- a/app/gui/view/welcome-screen/src/lib.rs +++ b/app/gui/view/welcome-screen/src/lib.rs @@ -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_docs)] @@ -127,7 +128,7 @@ impl Model { pub fn new(app: &Application) -> Self { let application = app.clone_ref(); let logger = Logger::new("WelcomeScreen"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let side_menu = SideMenu::new(&logger); let template_cards = TemplateCards::new(&logger); diff --git a/build-config.yaml b/build-config.yaml index eb61e6f400d7..4c26b461dc96 100644 --- a/build-config.yaml +++ b/build-config.yaml @@ -1,6 +1,6 @@ # Options intended to be common for all developers. -wasm-size-limit: 14.80 MiB +wasm-size-limit: 15.25 MiB required-versions: cargo-watch: ^8.1.1 diff --git a/build/build-utils/src/lib.rs b/build/build-utils/src/lib.rs index f4119e1bf9b5..2cd5759b5b0f 100644 --- a/build/build-utils/src/lib.rs +++ b/build/build-utils/src/lib.rs @@ -6,6 +6,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)] diff --git a/build/enso-formatter/src/main.rs b/build/enso-formatter/src/main.rs index b26f3504f325..b6448ebec836 100644 --- a/build/enso-formatter/src/main.rs +++ b/build/enso-formatter/src/main.rs @@ -14,6 +14,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] // === Non-Standard Linter Configuration === #![deny(keyword_idents)] #![deny(macro_use_extern_crate)] @@ -91,6 +92,24 @@ const STD_LINTER_ATTRIBS: &[&str] = &[ // "warn(variant_size_differences)", // Rustc lints that emit a warning by default: // "deny(unconditional_recursion)", + // This is allowed because in some cases, it allows way nicer formatting. For example, the + // code: ``` + // fn test(x: usize) -> usize { + // if x > 1 { + // 0 + // } else { + // 1 + // } + // ``` + // is automatically formatted as a multi-line expression. However, it is shorter when using a + // local variable and it cannot be configured in rustfmt. + // ``` + // fn test(x: usize) -> usize { + // let out = if x > 1 { 0 } else { 1 }; + // out + // } + // ``` + "allow(clippy::let_and_return)", ]; @@ -599,6 +618,7 @@ pub struct Struct1 {} // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] // === Non-Standard Linter Configuration === #![warn(missing_copy_implementations)] diff --git a/build/rust-scripts/src/bin/test_all.rs b/build/rust-scripts/src/bin/test_all.rs index df8a69d7b1b2..6d399587538d 100644 --- a/build/rust-scripts/src/bin/test_all.rs +++ b/build/rust-scripts/src/bin/test_all.rs @@ -3,6 +3,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] use std::path::Path; use std::path::PathBuf; diff --git a/build/src/main.rs b/build/src/main.rs index ce817f2488b9..21a899b42297 100644 --- a/build/src/main.rs +++ b/build/src/main.rs @@ -1,6 +1,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] use enso_build::prelude::*; diff --git a/integration-test/src/lib.rs b/integration-test/src/lib.rs index cef66b9a663a..731515f93c37 100644 --- a/integration-test/src/lib.rs +++ b/integration-test/src/lib.rs @@ -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_docs)] #![warn(trivial_casts)] diff --git a/integration-test/tests/engine.rs b/integration-test/tests/engine.rs index 85fbe174a70a..82ca250036d3 100644 --- a/integration-test/tests/engine.rs +++ b/integration-test/tests/engine.rs @@ -28,9 +28,8 @@ struct TestOnNewProjectControllersOnly { impl TestOnNewProjectControllersOnly { async fn set_up() -> Self { let executor = setup_global_executor(); - 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."; let ide = initializer.initialize_ide_controller().await.expect(error_msg); diff --git a/lib/rust/automata/src/lib.rs b/lib/rust/automata/src/lib.rs index 730095ef5b64..166a88a6b92b 100644 --- a/lib/rust/automata/src/lib.rs +++ b/lib/rust/automata/src/lib.rs @@ -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)] diff --git a/lib/rust/callback/src/lib.rs b/lib/rust/callback/src/lib.rs index 6f72ad911371..649770537f97 100644 --- a/lib/rust/callback/src/lib.rs +++ b/lib/rust/callback/src/lib.rs @@ -8,6 +8,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)] diff --git a/lib/rust/code-builder/src/lib.rs b/lib/rust/code-builder/src/lib.rs index cf10742ed01d..3e1bf466df3d 100644 --- a/lib/rust/code-builder/src/lib.rs +++ b/lib/rust/code-builder/src/lib.rs @@ -7,6 +7,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] #![allow(incomplete_features)] // To be removed, see: https://github.com/enso-org/ide/issues/1559 #![allow(missing_docs)] #![warn(unsafe_code)] diff --git a/lib/rust/config-reader/src/lib.rs b/lib/rust/config-reader/src/lib.rs index 63cb5ba24011..581d8beadd21 100644 --- a/lib/rust/config-reader/src/lib.rs +++ b/lib/rust/config-reader/src/lib.rs @@ -60,6 +60,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] use inflector::*; diff --git a/lib/rust/data-structures/src/lib.rs b/lib/rust/data-structures/src/lib.rs index 911a819671f3..cda804a3a118 100644 --- a/lib/rust/data-structures/src/lib.rs +++ b/lib/rust/data-structures/src/lib.rs @@ -7,6 +7,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)] diff --git a/lib/rust/debug-api/src/lib.rs b/lib/rust/debug-api/src/lib.rs index ee6719fbeb8f..543001080ef7 100644 --- a/lib/rust/debug-api/src/lib.rs +++ b/lib/rust/debug-api/src/lib.rs @@ -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_docs)] #![warn(trivial_casts)] diff --git a/lib/rust/ensogl/app/theme/derive/src/from_theme.rs b/lib/rust/ensogl/app/theme/derive/src/from_theme.rs index 9a1da4baa225..4b9021740783 100644 --- a/lib/rust/ensogl/app/theme/derive/src/from_theme.rs +++ b/lib/rust/ensogl/app/theme/derive/src/from_theme.rs @@ -37,7 +37,7 @@ impl ThemeTypes { fn from_ty(ty: &Type) -> Self { match ty { Type::Path(type_path) - if type_path.clone().into_token_stream().to_string() == "String" => + if type_path.clone().into_token_stream().to_string().contains("ImString") => Self::String, Type::Path(type_path) if type_path.clone().into_token_stream().to_string() == "f32" => Self::Number, diff --git a/lib/rust/ensogl/app/theme/derive/src/lib.rs b/lib/rust/ensogl/app/theme/derive/src/lib.rs index 421fb1f1c265..fd215f764096 100644 --- a/lib/rust/ensogl/app/theme/derive/src/lib.rs +++ b/lib/rust/ensogl/app/theme/derive/src/lib.rs @@ -25,7 +25,6 @@ #![recursion_limit = "512"] // === Features === #![allow(incomplete_features)] -#![feature(negative_impls)] #![feature(associated_type_defaults)] #![feature(bool_to_option)] #![feature(cell_update)] @@ -44,6 +43,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)] diff --git a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs index 2111af92d845..fa37ae82ff01 100644 --- a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs +++ b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs @@ -4,6 +4,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)] diff --git a/lib/rust/ensogl/component/button/src/lib.rs b/lib/rust/ensogl/component/button/src/lib.rs index f004249e1e67..17b780246e8b 100644 --- a/lib/rust/ensogl/component/button/src/lib.rs +++ b/lib/rust/ensogl/component/button/src/lib.rs @@ -72,6 +72,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)] @@ -85,7 +86,6 @@ use crate::prelude::*; use ensogl_core::display::shape::*; use enso_frp as frp; -use ensogl_core::application; use ensogl_core::application::Application; use ensogl_core::data::color; use ensogl_core::data::color::Rgba; @@ -212,8 +212,8 @@ impl Model { pub fn new(app: &Application) -> Self { let app = app.clone_ref(); let logger = Logger::new(Shape::debug_name()); - let display_object = display::object::Instance::new(&logger); - let shape = ShapeView::new(&logger); + let display_object = display::object::Instance::new(); + let shape = ShapeView::new(); display_object.add_child(&shape); Self { app, logger, display_object, shape } } @@ -380,7 +380,7 @@ impl Deref for View { } } -impl application::command::FrpNetworkProvider for View { +impl FrpNetworkProvider for View { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs index 14dcc3ca9770..5ad700651414 100644 --- a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs +++ b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs @@ -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)] @@ -122,7 +123,7 @@ struct Model { icon: arrow::View, icon_overlay: chooser_hover_area::View, - label: text::Area, + label: text::Text, selection_menu: list_view::ListView, // `SingleMaskedProvider` allows us to hide the selected element. @@ -131,12 +132,11 @@ struct Model { impl Model { fn new(app: &Application) -> Self { - let logger = Logger::new("drop_down_menu"); - let display_object = display::object::Instance::new(&logger); - let icon = arrow::View::new(&logger); - let icon_overlay = chooser_hover_area::View::new(&logger); + let display_object = display::object::Instance::new(); + let icon = arrow::View::new(); + let icon_overlay = chooser_hover_area::View::new(); let selection_menu = list_view::ListView::new(app); - let label = app.new_view::(); + let label = app.new_view::(); let content = default(); Self { display_object, icon, icon_overlay, label, selection_menu, content }.init() @@ -376,7 +376,7 @@ impl DropDownMenu { // shape system (#795) let styles = StyleWatch::new(&app.display.default_scene.style_sheet); let text_color = styles.get_color(theme::widget::list_view::text); - model.label.set_default_color(text_color); + model.label.set_property_default(text_color); self } diff --git a/lib/rust/ensogl/component/drop-manager/src/lib.rs b/lib/rust/ensogl/component/drop-manager/src/lib.rs index c3f7a5f9048d..0b6819679c98 100644 --- a/lib/rust/ensogl/component/drop-manager/src/lib.rs +++ b/lib/rust/ensogl/component/drop-manager/src/lib.rs @@ -4,6 +4,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)] @@ -132,19 +133,17 @@ pub struct Manager { impl Manager { /// Constructor, adding listener to the given target. pub fn new(target: &enso_web::EventTarget) -> Self { - let logger = Logger::new("DropFileManager"); - debug!(logger, "Creating"); + debug!("Creating."); let network = frp::Network::new("DropFileManager"); frp::extend! { network files_received <- source(); } - let drop: DropClosure = - Closure::new(f!([logger,files_received](event:web_sys::DragEvent) { - debug!(logger, "Dropped files."); - event.prevent_default(); - Self::handle_drop_event(&logger,event,&files_received) - })); + let drop: DropClosure = Closure::new(f!([files_received](event:web_sys::DragEvent) { + debug!("Dropped files."); + event.prevent_default(); + Self::handle_drop_event(event,&files_received) + })); // To mark element as a valid drop target, the `dragover` event handler should return // `false`. See // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#define_the_drop_zone @@ -162,18 +161,14 @@ impl Manager { &self.files_received } - fn handle_drop_event( - logger: &Logger, - event: web_sys::DragEvent, - files_received: &frp::Source>, - ) { + fn handle_drop_event(event: web_sys::DragEvent, files_received: &frp::Source>) { let opt_files = event.data_transfer().and_then(|t| t.files()); if let Some(js_files) = opt_files { let js_files_iter = (0..js_files.length()).filter_map(|i| js_files.get(i)); let files_iter = js_files_iter.filter_map(|f| match File::from_js_file(&f) { Ok(file) => Some(file), Err(err) => { - error!(logger, "Error when processing dropped file: {err:?}."); + error!("Error when processing dropped file: {err:?}."); None } }); diff --git a/lib/rust/ensogl/component/file-browser/src/lib.rs b/lib/rust/ensogl/component/file-browser/src/lib.rs index 79eec5bd6e98..ca3f5e450ba2 100644 --- a/lib/rust/ensogl/component/file-browser/src/lib.rs +++ b/lib/rust/ensogl/component/file-browser/src/lib.rs @@ -8,6 +8,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)] @@ -94,7 +95,7 @@ impl FileBrowser { pub fn new() -> Self { let logger = Logger::new("FileBrowser"); let frp = Frp::new(); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); Self { logger, frp, display_object } } } diff --git a/lib/rust/ensogl/component/flame-graph/src/block.rs b/lib/rust/ensogl/component/flame-graph/src/block.rs index 60c0bbb783c6..1cb3435e3e46 100644 --- a/lib/rust/ensogl/component/flame-graph/src/block.rs +++ b/lib/rust/ensogl/component/flame-graph/src/block.rs @@ -64,10 +64,14 @@ ensogl_core::define_endpoints_2! { } impl component::Frp 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 background = &model.background.events; - frp::extend! { network eval api.input.set_content((t) model.set_content(t)); eval api.input.set_size((size) model.set_size(*size)); @@ -92,7 +96,7 @@ impl component::Frp for Frp { pub struct Model { app: Application, background: background::View, - label: Rc>>, + label: Rc>>, display_object: display::object::Instance, } @@ -101,12 +105,12 @@ impl component::Model for Model { "FlameGraphBlock" } - fn new(app: &Application, logger: &Logger) -> Self { + fn new(app: &Application) -> Self { let scene = &app.display.default_scene; - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let label = default(); - let background = background::View::new(&logger); + let background = background::View::new(); display_object.add_child(&background); scene.layers.tooltip.add_exclusive(&background); diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index d3f883463321..93612c5091bc 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -4,6 +4,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)] @@ -179,19 +180,16 @@ fn align_mark(mut mark: profiler_flame_graph::Mark, origin_x: f64) -> profiler_f impl FlameGraph { /// Create an empty graph, pub fn empty(app: &Application) -> Self { - let logger = Logger::new("FlameGraph"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let blocks = default(); let marks = default(); let origin_x = default(); let app = app.clone_ref(); - Self { display_object, blocks, marks, origin_x, app } } /// Create a `FlameGraph` EnsoGL component from the given graph data from the profiler. pub fn from_data(data: profiler_flame_graph::Graph, app: &Application) -> Self { - let logger = Logger::new("FlameGraph"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let activity_blocks = data.activity_blocks.into_iter().filter(|block| block.width() > MIN_INTERVAL_TIME_MS); diff --git a/lib/rust/ensogl/component/flame-graph/src/mark.rs b/lib/rust/ensogl/component/flame-graph/src/mark.rs index 59845f654206..da91bfeb9925 100644 --- a/lib/rust/ensogl/component/flame-graph/src/mark.rs +++ b/lib/rust/ensogl/component/flame-graph/src/mark.rs @@ -68,10 +68,14 @@ ensogl_core::define_endpoints_2! { } impl component::Frp 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 background = &model.background.events; - frp::extend! { network eval api.input.set_content((t) model.set_content(t)); @@ -95,7 +99,7 @@ impl component::Frp for Frp { pub struct Model { app: Application, background: background::View, - label: Rc>>, + label: Rc>>, display_object: display::object::Instance, } @@ -104,12 +108,12 @@ impl component::Model for Model { "FlameGraphMark" } - fn new(app: &Application, logger: &Logger) -> Self { + fn new(app: &Application) -> Self { let scene = &app.display.default_scene; - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let label = default(); - let background = background::View::new(&logger); + let background = background::View::new(); display_object.add_child(&background); scene.layers.tooltip.add_exclusive(&background); diff --git a/lib/rust/ensogl/component/grid-view/src/entry/visible.rs b/lib/rust/ensogl/component/grid-view/src/entry/visible.rs index 6dae12f60209..4391602942ec 100644 --- a/lib/rust/ensogl/component/grid-view/src/entry/visible.rs +++ b/lib/rust/ensogl/component/grid-view/src/entry/visible.rs @@ -88,7 +88,7 @@ where EntryParams: frp::node::Data text_layer: Option<&Layer>, ) -> (VisibleEntry, Option>) { let entry = E::new(&self.app, text_layer); - let overlay = entry::overlay::View::new(Logger::new("EntryOverlay")); + let overlay = entry::overlay::View::new(); entry.add_child(&overlay); let init = if let Some(network) = self.network.upgrade_or_warn() { let entry_frp = entry.frp(); diff --git a/lib/rust/ensogl/component/grid-view/src/lib.rs b/lib/rust/ensogl/component/grid-view/src/lib.rs index 5e36d4dcbd43..7b92749042a3 100644 --- a/lib/rust/ensogl/component/grid-view/src/lib.rs +++ b/lib/rust/ensogl/component/grid-view/src/lib.rs @@ -28,6 +28,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)] @@ -219,8 +220,7 @@ pub struct Model { impl Model { fn new(entry_creation_ctx: entry::visible::CreationCtx) -> Self { - let logger = Logger::new("GridView"); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let visible_entries = default(); let free_entries = default(); let column_widths = ColumnWidths::new(0); @@ -692,7 +692,7 @@ pub(crate) mod tests { let network = frp.network(); let param_set = Rc::new(Cell::new(0)); let model_set = Rc::new(Cell::new(0)); - let display_object = display::object::Instance::new(Logger::new("TestEntry")); + let display_object = display::object::Instance::new(); frp::extend! { network eval frp.input.set_model ((model) model_set.set(**model)); eval frp.input.set_params ((param) param_set.set(*param.param)); diff --git a/lib/rust/ensogl/component/grid-view/src/selectable.rs b/lib/rust/ensogl/component/grid-view/src/selectable.rs index 0c445d8a3b7e..e24302695878 100644 --- a/lib/rust/ensogl/component/grid-view/src/selectable.rs +++ b/lib/rust/ensogl/component/grid-view/src/selectable.rs @@ -163,7 +163,7 @@ where { fn new_wrapping(app: &Application, grid: InnerGridView) -> Self { use frp::io::keyboard::ArrowDirection as Direction; - let highlights = highlight::shape::View::new(Logger::new("highlights")); + let highlights = highlight::shape::View::new(); let header_highlights = Immutable(None); let selection_handler = highlight::SelectionHandler::new_connected(app, &grid); let hover_handler = highlight::HoverHandler::new_connected(app, &grid); @@ -216,7 +216,7 @@ impl> GridViewWithHeaders Self { let mut this = Self::new_wrapping(app, header::GridView::::new(app)); - let header_highlights = highlight::shape::View::new(Logger::new("header_highlights")); + let header_highlights = highlight::shape::View::new(); this.grid.add_child(&header_highlights); this.selection_handler.connect_with_header_shape(&header_highlights); this.hover_handler.connect_with_header_shape(&header_highlights); @@ -331,7 +331,7 @@ mod tests { fn new(_: &Application, _: Option<&Layer>) -> Self { let frp = EntryFrp::::new(); - let display_object = display::object::Instance::new(Logger::new("TestEntry")); + let display_object = display::object::Instance::new(); let network = frp.network(); let input = &frp.private().input; let out = &frp.private().output; diff --git a/lib/rust/ensogl/component/grid-view/src/selectable/highlight/layer.rs b/lib/rust/ensogl/component/grid-view/src/selectable/highlight/layer.rs index 674474789250..3b4fbaa5c3e4 100644 --- a/lib/rust/ensogl/component/grid-view/src/selectable/highlight/layer.rs +++ b/lib/rust/ensogl/component/grid-view/src/selectable/highlight/layer.rs @@ -51,7 +51,7 @@ impl Handler { where InnerGridView: AsRef> + display::Object, { - let shape = shape::View::new(Logger::new("HighlightMask")); + let shape = shape::View::new(); let entries = parent_layer.create_sublayer(); let text = parent_layer.create_sublayer(); let header = default(); diff --git a/lib/rust/ensogl/component/grid-view/src/simple.rs b/lib/rust/ensogl/component/grid-view/src/simple.rs index cac74da05dca..395b480014ac 100644 --- a/lib/rust/ensogl/component/grid-view/src/simple.rs +++ b/lib/rust/ensogl/component/grid-view/src/simple.rs @@ -93,16 +93,16 @@ impl> From for EntryModel { #[derive(Clone, Debug)] pub struct EntryData { display_object: display::object::Instance, - pub label: text::Area, + pub label: text::Text, pub background: entry::shape::View, } impl EntryData { fn new(app: &Application, text_layer: Option<&Layer>) -> Self { - let logger = Logger::new("list_view::entry::Label"); - let display_object = display::object::Instance::new(&logger); - let label = app.new_view::(); - let background = entry::shape::View::new(&logger); + let display_object = display::object::Instance::new(); + let label = app.new_view::(); + label.set_long_text_truncation_mode(true); + let background = entry::shape::View::new(); display_object.add_child(&label); display_object.add_child(&background); if let Some(layer) = text_layer { @@ -114,7 +114,7 @@ impl EntryData { fn update_layout(&self, contour: entry::Contour, text_size: text::Size, text_offset: f32) { self.background.set_contour(contour); let size = contour.size; - self.label.set_position_xy(Vector2(text_offset - size.x / 2.0, text_size.raw / 2.0)); + self.label.set_position_xy(Vector2(text_offset - size.x / 2.0, text_size.value / 2.0)); } } @@ -159,17 +159,18 @@ impl crate::Entry for Entry { eval layout ((&(c, ts, to)) data.update_layout(c, ts, to)); eval bg_color ((color) data.background.color.set(color.into())); disabled <- input.set_model.map(|m| *m.disabled); - data.label.set_default_color <+ all_with3( + data.label.set_property_default <+ all_with3( &text_color, &dis_text_color, &disabled, |c, dc, d| if *d { *dc } else { *c } - ); - data.label.set_font <+ font.map(ToString::to_string); - data.label.set_default_text_size <+ text_size; - content <- input.set_model.map(|m| m.text.to_string()); + ).ref_into_some(); + data.label.set_font <+ font; + data.label.set_property_default <+ text_size.ref_into_some(); + content <- input.set_model.map(|m| m.text.clone_ref()); max_width_px <- input.set_size.map(|size| size.x); - data.label.set_content_truncated <+ all(&content, &max_width_px); + data.label.set_content <+ content; + data.label.set_view_width <+ max_width_px.some(); out.override_column_width <+ input.set_model.filter_map(|m| *m.override_width); out.contour <+ contour; diff --git a/lib/rust/ensogl/component/gui/src/component.rs b/lib/rust/ensogl/component/gui/src/component.rs index 1c51e2b0012b..1934534547fb 100644 --- a/lib/rust/ensogl/component/gui/src/component.rs +++ b/lib/rust/ensogl/component/gui/src/component.rs @@ -32,7 +32,7 @@ pub trait Model { fn label() -> &'static str; /// Constructor. - fn new(app: &Application, logger: &Logger) -> Self; + fn new(app: &Application) -> Self; } @@ -48,7 +48,8 @@ pub trait Frp: Default + API { /// Frp initializer. Should set up the logic for processing inputs and generating outputs /// through the FRP API. fn init( - frp_api: &::Private, + network: &frp::Network, + frp: &::Private, app: &Application, model: &Model, style: &StyleWatchFrp, @@ -83,10 +84,10 @@ where /// Constructor. pub fn new(app: &Application) -> Self { let logger = Logger::new(M::label()); - let model = Rc::new(M::new(app, &logger)); + let model = Rc::new(M::new(app)); let frp = F::default(); let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); - F::init(frp.private(), app, &model, &style); + F::init(frp.network(), frp.private(), app, &model, &style); let display_object = model.display_object().clone_ref(); let widget = Widget::new(app, frp, model, display_object); Self { widget, logger } diff --git a/lib/rust/ensogl/component/gui/src/lib.rs b/lib/rust/ensogl/component/gui/src/lib.rs index 0701a52194ae..d11b37303597 100644 --- a/lib/rust/ensogl/component/gui/src/lib.rs +++ b/lib/rust/ensogl/component/gui/src/lib.rs @@ -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_copy_implementations)] #![warn(missing_debug_implementations)] diff --git a/lib/rust/ensogl/component/label/src/lib.rs b/lib/rust/ensogl/component/label/src/lib.rs index fbaabf34c692..e64cca6ebbd8 100644 --- a/lib/rust/ensogl/component/label/src/lib.rs +++ b/lib/rust/ensogl/component/label/src/lib.rs @@ -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)] @@ -81,7 +82,7 @@ ensogl_core::define_endpoints! { #[derive(Clone, Debug)] struct Model { background: background::View, - label: text::Area, + label: text::Text, display_object: display::object::Instance, style: StyleWatch, } @@ -90,10 +91,9 @@ impl Model { fn new(app: Application) -> Self { let app = app.clone_ref(); let scene = &app.display.default_scene; - let logger = Logger::new("TextLabel"); - let display_object = display::object::Instance::new(&logger); - let label = app.new_view::(); - let background = background::View::new(&logger); + let display_object = display::object::Instance::new(); + let label = app.new_view::(); + let background = background::View::new(); display_object.add_child(&background); display_object.add_child(&label); @@ -147,8 +147,8 @@ impl Model { fn set_opacity(&self, value: f32) { let text_color_path = theme::text; let text_color = self.style.get_color(text_color_path).multiply_alpha(value); - self.label.frp.set_color_all.emit(text_color); - self.label.frp.set_default_color.emit(text_color); + self.label.frp.set_property(.., text_color); + self.label.frp.set_property_default(text_color); let bg_color_path = theme::background; let bg_color = self.style.get_color(bg_color_path).multiply_alpha(value); @@ -178,7 +178,7 @@ impl Label { } /// Set layers for Label's background and text respectively. This is needed because - /// `text::Area` uses its own `add_to_scene_layer` method instead of utilizing more common + /// `text::Text` uses its own `add_to_scene_layer` method instead of utilizing more common /// [`Layer::add_exclusive`]. pub fn set_layers(&self, background_layer: &Layer, text_layer: &Layer) { self.model.set_layers(background_layer, text_layer); diff --git a/lib/rust/ensogl/component/list-view/src/entry.rs b/lib/rust/ensogl/component/list-view/src/entry.rs index 51109f8c07a0..058a87a47a88 100644 --- a/lib/rust/ensogl/component/list-view/src/entry.rs +++ b/lib/rust/ensogl/component/list-view/src/entry.rs @@ -71,9 +71,9 @@ pub trait Entry: CloneRef + Debug + display::Object + 'static { /// Resize the entry's view to fit a new width. fn set_max_width(&self, max_width_px: f32); - /// Set the layer of all [`text::Area`] components inside. The [`text::Area`] component is + /// Set the layer of all [`text::Text`] components inside. The [`text::Text`] component is /// handled in a special way, and is often in different layer than shapes. See TODO comment - /// in [`text::Area::add_to_scene_layer`] method. + /// in [`text::Text::add_to_scene_layer`] method. fn set_label_layer(&self, label_layer: &display::scene::Layer); } @@ -89,8 +89,8 @@ pub trait Entry: CloneRef + Debug + display::Object + 'static { #[derive(Clone, CloneRef, Debug)] pub struct Label { display_object: display::object::Instance, - pub label: text::Area, - text: frp::Source, + pub label: text::Text, + text: frp::Source, max_width_px: frp::Source, /// The `network` is public to allow extending it in components based on a [`Label`]. This /// should only be done for components that are small extensions of a Label, where creating a @@ -103,31 +103,32 @@ pub struct Label { impl Label { /// Constructor. pub fn new(app: &Application, style_prefix: &Path) -> Self { - let logger = Logger::new("list_view::entry::Label"); - let display_object = display::object::Instance::new(logger); - let label = app.new_view::(); + let display_object = display::object::Instance::new(); + let label = app.new_view::(); let network = frp::Network::new("list_view::entry::Label"); let style_watch = StyleWatchFrp::new(&app.display.default_scene.style_sheet); let text_style = style_prefix.sub("text"); let font = style_watch.get_text(text_style.sub("font")); let size = style_watch.get_number(text_style.sub("size")); let color = style_watch.get_color(text_style); + label.set_long_text_truncation_mode(true); display_object.add_child(&label); frp::extend! { network init <- source::<()>(); - text <- source::(); + text <- source::(); max_width_px <- source::(); color <- all(&color,&init)._0(); font <- all(&font,&init)._0(); size <- all(&size,&init)._0(); - label.set_default_color <+ color; + label.set_property_default <+ color.ref_into_some(); label.set_font <+ font; - label.set_default_text_size <+ size.map(|v| text::Size(*v)); + label.set_property_default <+ size.map(|v| text::Size(*v)).ref_into_some(); eval size ((size) label.set_position_y(size/2.0)); - label.set_content_truncated <+ all(&text, &max_width_px); + label.set_content <+ text; + label.set_view_width <+ max_width_px.some(); } init.emit(()); Self { display_object, label, text, max_width_px, network, style_watch } @@ -171,7 +172,7 @@ pub struct GlyphHighlightedLabelModel { /// Displayed text. pub label: String, /// A list of ranges of highlighted bytes. - pub highlighted: Vec>, + pub highlighted: Vec>, } /// The [`Entry`] similar to the [`Label`], but allows highlighting some parts of text. @@ -179,7 +180,7 @@ pub struct GlyphHighlightedLabelModel { #[derive(Clone, CloneRef, Debug)] pub struct GlyphHighlightedLabel { pub inner: Label, - highlight: frp::Source>>, + highlight: frp::Source>>, } impl Entry for GlyphHighlightedLabel { @@ -194,12 +195,12 @@ impl Entry for GlyphHighlightedLabel { let label = &inner.label; frp::extend! { network - highlight <- source::>>(); + highlight <- source::>>(); content_changed <- label.content.constant(()); set_highlight <- all(highlight, highlight_bold, content_changed); eval set_highlight ([label]((highlight, bold, ())) { for range in highlight { - label.set_sdf_bold(range, text::style::SdfBold::new(*bold)); + label.set_property(range, text::formatting::SdfWeight::new(*bold)); } }); } diff --git a/lib/rust/ensogl/component/list-view/src/entry/list.rs b/lib/rust/ensogl/component/list-view/src/entry/list.rs index 0fb345a1386c..b460d18ed6c7 100644 --- a/lib/rust/ensogl/component/list-view/src/entry/list.rs +++ b/lib/rust/ensogl/component/list-view/src/entry/list.rs @@ -84,7 +84,7 @@ impl ListData { let entries = default(); let entries_range = Rc::new(CloneCell::new(default()..default())); let entry_params = default(); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let provider = default(); let label_layer = Rc::new(RefCell::new(app.display.default_scene.layers.label.downgrade())); Self { @@ -169,7 +169,7 @@ impl ListData { ) { range.end = range.end.min(self.provider.get().entry_count()); if range != self.entries_range.get() { - debug!(self.logger, "Update entries for {range:?}"); + debug!("Update entries for {range:?}"); let provider = self.provider.get(); let current_entries: HashSet = with(self.entries.borrow_mut(), |mut entries| { @@ -185,7 +185,7 @@ impl ListData { |e: &DisplayedEntry| e.id.get().map_or(true, |i| !range.contains(&i)); let outdated = entries.iter().filter(|e| is_outdated(e)); for (entry, (id, model)) in outdated.zip(models) { - Self::update_entry(&self.logger, entry, id, &model); + Self::update_entry(entry, id, &model); } }); self.entries_range.set(range); @@ -205,7 +205,7 @@ impl ListData { let new_entry = self.create_new_entry(&style_prefix); if let Some(id) = entry.id.get() { let model = provider.get(id); - Self::update_entry(&self.logger, &new_entry, id, &model); + Self::update_entry(&new_entry, id, &model); } *entry = new_entry; } @@ -241,7 +241,6 @@ impl ListData { let provider = provider.into(); if provider.entry_count() > MAX_SAFE_ENTRIES_COUNT { error!( - self.logger, "ListView entry count exceed {MAX_SAFE_ENTRIES_COUNT} - so big \ number of entries can cause visual glitches, e.g. https://github.com/enso-org/ide/\ issues/757 or https://github.com/enso-org/ide/issues/758" @@ -257,7 +256,7 @@ impl ListData { }; entries.resize_with(range.len(), create_new_entry_with_max_width); for (entry, (id, model)) in entries.iter().zip(models) { - Self::update_entry(&self.logger, entry, id, &model); + Self::update_entry(entry, id, &model); } self.entries_range.set(range); self.provider.set(provider); @@ -267,9 +266,8 @@ impl ListData { let layers = &self.app.display.default_scene.layers; let layer = self.label_layer.borrow().upgrade().unwrap_or_else(|| { error!( - self.logger, - "Cannot set layer {self.label_layer:?} for labels: the layer does \ - not exist in the scene" + "Cannot set layer {:?} for labels: the layer does not exist in the scene", + self.label_layer ); layers.main.clone_ref() }); @@ -280,22 +278,13 @@ impl ListData { entry } - fn update_entry( - logger: &Logger, - entry: &DisplayedEntry, - id: entry::Id, - model: &Option, - ) { - debug!( - logger, - "Setting new model {model:?} for entry {id}; \ - old entry: {entry.id.get():?}." - ); + fn update_entry(entry: &DisplayedEntry, id: entry::Id, model: &Option) { + debug!("Setting new model {:?} for entry {}; old entry: {:?}.", model, id, entry.id.get()); entry.id.set(Some(id)); match model { Some(model) => entry.entry.update(model), None => { - error!(logger, "Model provider didn't return model for id {id}."); + error!("Model provider didn't return model for id {id}."); entry.entry.update(&default()); } }; diff --git a/lib/rust/ensogl/component/list-view/src/lib.rs b/lib/rust/ensogl/component/list-view/src/lib.rs index a32620154e69..eddffac0e20e 100644 --- a/lib/rust/ensogl/component/list-view/src/lib.rs +++ b/lib/rust/ensogl/component/list-view/src/lib.rs @@ -10,6 +10,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)] @@ -200,12 +201,12 @@ impl Model { fn new(app: &Application) -> Self { let app = app.clone_ref(); let logger = Logger::new("SelectionContainer"); - let display_object = display::object::Instance::new(&logger); - let scrolled_area = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); + let scrolled_area = display::object::Instance::new(); let entries = entry::List::new(&logger, &app); - let background = background::View::new(&logger); - let overlay = overlay::View::new(&logger); - let selection = selection::View::new(&logger); + let background = background::View::new(); + let overlay = overlay::View::new(); + let selection = selection::View::new(); display_object.add_child(&background); display_object.add_child(&overlay); display_object.add_child(&scrolled_area); @@ -703,7 +704,7 @@ impl display::Object for ListView { } } -impl application::command::FrpNetworkProvider for ListView { +impl FrpNetworkProvider for ListView { fn network(&self) -> &frp::Network { &self.frp.network } diff --git a/lib/rust/ensogl/component/scroll-area/src/lib.rs b/lib/rust/ensogl/component/scroll-area/src/lib.rs index 5d683032125f..6fb7847c74d5 100644 --- a/lib/rust/ensogl/component/scroll-area/src/lib.rs +++ b/lib/rust/ensogl/component/scroll-area/src/lib.rs @@ -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)] @@ -244,22 +245,22 @@ impl ScrollArea { let scene = &app.display.default_scene; let logger = Logger::new("ScrollArea"); let camera = scene.layers.node_searcher.camera(); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let masked_layer = layer::Masked::new(&logger, &camera); let display_object = display::object::InstanceWithLayer::new(display_object, masked_layer); let content_layer = display_object.layer.masked_layer.create_sublayer(); let ui_layer = display_object.layer.masked_layer.create_sublayer(); - let content = display::object::Instance::new(&logger); + let content = display::object::Instance::new(); display_object.add_child(&content); content_layer.add_exclusive(&content); - let scrollbars = display::object::Instance::new(&logger); + let scrollbars = display::object::Instance::new(); display_object.add_child(&scrollbars); ui_layer.add_exclusive(&scrollbars); - let mask = mask::View::new(&logger); + let mask = mask::View::new(); display_object.add_child(&mask); display_object.layer.mask_layer.add_exclusive(&mask); diff --git a/lib/rust/ensogl/component/scrollbar/src/lib.rs b/lib/rust/ensogl/component/scrollbar/src/lib.rs index 95c5dbc70ff5..d212bdb87882 100644 --- a/lib/rust/ensogl/component/scrollbar/src/lib.rs +++ b/lib/rust/ensogl/component/scrollbar/src/lib.rs @@ -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)] @@ -341,7 +342,7 @@ impl Deref for Scrollbar { } } -impl application::command::FrpNetworkProvider for Scrollbar { +impl FrpNetworkProvider for Scrollbar { fn network(&self) -> &frp::Network { self.frp.network() } diff --git a/lib/rust/ensogl/component/selector/src/decimal_aligned.rs b/lib/rust/ensogl/component/selector/src/decimal_aligned.rs index 3bf2bc36a639..3ccd78c6c5b8 100644 --- a/lib/rust/ensogl/component/selector/src/decimal_aligned.rs +++ b/lib/rust/ensogl/component/selector/src/decimal_aligned.rs @@ -5,7 +5,6 @@ use crate::prelude::*; use enso_frp as frp; -use ensogl_core::application; use ensogl_core::application::Application; use ensogl_core::display; use ensogl_core::display::object::ObjectOps; @@ -44,19 +43,18 @@ pub struct Model { /// base position of the root, depending on the position of the decimal separator. root: display::object::Instance, /// Label containing the text to display. This is the label that will be shown. - label_full: text::Area, + label_full: text::Text, /// This label contains the text to the left of the decimal. This is here, so we can get /// information about the text width of this portion of the label. This label will /// not appear in the UI. - label_left: text::Area, + label_left: text::Text, } impl Model { fn new(app: &Application) -> Self { - let logger = Logger::new("DecimalAlignedLabel"); - let root = display::object::Instance::new(&logger); - let label_full = app.new_view::(); - let label_left = app.new_view::(); + let root = display::object::Instance::new(); + let label_full = app.new_view::(); + let label_left = app.new_view::(); label_full.remove_from_scene_layer(&app.display.default_scene.layers.main); label_full.add_to_scene_layer(&app.display.default_scene.layers.label); @@ -74,12 +72,12 @@ impl Frp { let network = &frp.network; frp::extend! { network - formatted <- frp.set_content.map(|value| format!("{:.2}", value)); + formatted <- frp.set_content.map(|value| format!("{:.2}", value).to_im_string()); // FIXME: the next line is locale dependent as it is meant to split on the decimal // separator, which might be different from "." in some locales. We need a way to get // the current locale dependent decimal separator for this. // See https://github.com/enso-org/ide/issues/1542 for progress on this. - left <- formatted.map(|s| s.split('.').next().map(|s| s.to_string())).unwrap(); + left <- formatted.map(|s| s.split('.').next().map(|s| s.to_im_string())).unwrap(); model.label_left.set_content <+ left; model.label_full.set_content <+ formatted; @@ -130,7 +128,7 @@ impl Deref for FloatLabel { } } -impl application::command::FrpNetworkProvider for FloatLabel { +impl FrpNetworkProvider for FloatLabel { fn network(&self) -> &frp::Network { self.frp.network() } diff --git a/lib/rust/ensogl/component/selector/src/lib.rs b/lib/rust/ensogl/component/selector/src/lib.rs index e51bfe3cd354..e7df0a0b17c9 100644 --- a/lib/rust/ensogl/component/selector/src/lib.rs +++ b/lib/rust/ensogl/component/selector/src/lib.rs @@ -16,6 +16,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 @@ impl Deref for NumberPicker { } } -impl application::command::FrpNetworkProvider for NumberPicker { +impl FrpNetworkProvider for NumberPicker { fn network(&self) -> &enso_frp::Network { self.frp.network() } @@ -177,7 +178,7 @@ impl Deref for NumberRangePicker { } } -impl application::command::FrpNetworkProvider for NumberRangePicker { +impl FrpNetworkProvider for NumberRangePicker { fn network(&self) -> &enso_frp::Network { self.frp.network() } diff --git a/lib/rust/ensogl/component/selector/src/model.rs b/lib/rust/ensogl/component/selector/src/model.rs index 4637bd5ac3dc..68d22ecf337f 100644 --- a/lib/rust/ensogl/component/selector/src/model.rs +++ b/lib/rust/ensogl/component/selector/src/model.rs @@ -52,15 +52,15 @@ pub struct Model { /// that is centered on the decimal label. pub label: FloatLabel, /// A label that is aligned to the left edge of the background - pub label_left: text::Area, + pub label_left: text::Text, /// A label that is aligned to the right edge of the background - pub label_right: text::Area, + pub label_right: text::Text, /// A label that is left aligned on the background. Meant to contain a caption describing the /// value that is selected. For example "Alpha", "Red", or "Size". - pub caption_left: text::Area, + pub caption_left: text::Text, /// A label that is centered on the background. Meant to contain a caption describing the /// range that is selected. For example "Allowed Size", or "Valid Price". - pub caption_center: text::Area, + pub caption_center: text::Text, /// Shape root that all other elements are parented to. Should be used to place the shapes as /// a group. pub root: display::object::Instance, @@ -77,19 +77,18 @@ pub struct Model { #[allow(missing_docs)] impl Model { pub fn new(app: &Application) -> Self { - let logger = Logger::new("selector::common::Model"); - let root = display::object::Instance::new(&logger); + let root = display::object::Instance::new(); let label = FloatLabel::new(app); - let label_left = app.new_view::(); - let label_right = app.new_view::(); - let caption_center = app.new_view::(); - let caption_left = app.new_view::(); - let background = background::View::new(&logger); - let track = track::View::new(&logger); - let track_handle_left = io_rect::View::new(&logger); - let track_handle_right = io_rect::View::new(&logger); - let left_overflow = left_overflow::View::new(&logger); - let right_overflow = right_overflow::View::new(&logger); + let label_left = app.new_view::(); + let label_right = app.new_view::(); + let caption_center = app.new_view::(); + let caption_left = app.new_view::(); + let background = background::View::new(); + let track = track::View::new(); + let track_handle_left = io_rect::View::new(); + let track_handle_right = io_rect::View::new(); + let left_overflow = left_overflow::View::new(); + let right_overflow = right_overflow::View::new(); let background_color = default(); let track_color = default(); let background_left_corner_roundness = default(); diff --git a/lib/rust/ensogl/component/sequence-diagram/src/labeled_line.rs b/lib/rust/ensogl/component/sequence-diagram/src/labeled_line.rs index 92c4d3c1596e..a534fa1d150c 100644 --- a/lib/rust/ensogl/component/sequence-diagram/src/labeled_line.rs +++ b/lib/rust/ensogl/component/sequence-diagram/src/labeled_line.rs @@ -55,8 +55,13 @@ ensogl_core::define_endpoints_2! { } impl component::Frp 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 line = &model.line.events; frp::extend! { network eval api.input.set_size((size) model.set_size(*size)); @@ -88,8 +93,8 @@ impl component::Model for Model { "LabeledLine" } - fn new(_app: &Application, logger: &Logger) -> Self { - let line = shape::arrow::View::new(&logger); + fn new(_app: &Application) -> Self { + let line = shape::arrow::View::new(); Model { line } } } diff --git a/lib/rust/ensogl/component/sequence-diagram/src/lib.rs b/lib/rust/ensogl/component/sequence-diagram/src/lib.rs index c59e54fd62e5..dcd4f18f2991 100644 --- a/lib/rust/ensogl/component/sequence-diagram/src/lib.rs +++ b/lib/rust/ensogl/component/sequence-diagram/src/lib.rs @@ -4,6 +4,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)] @@ -61,8 +62,13 @@ ensogl_core::define_endpoints_2! { } impl component::Frp for Frp { - fn init(api: &Self::Private, _app: &Application, model: &Model, _style: &StyleWatchFrp) { - let network = api.network.clone(); + fn init( + network: &frp::Network, + api: &Self::Private, + _app: &Application, + model: &Model, + _style: &StyleWatchFrp, + ) { frp::extend! { network eval api.input.set_profile ((profile) model.set_profile(profile)); let model = model.clone_ref(); @@ -94,9 +100,9 @@ impl component::Model for Model { "SequenceDiagram" } - fn new(app: &Application, logger: &Logger) -> Self { + fn new(app: &Application) -> Self { let app = app.clone(); - let display_object = display::object::Instance::new(&logger); + let display_object = display::object::Instance::new(); let actor_lines = default(); let message_lines = default(); let origin_x = default(); diff --git a/lib/rust/ensogl/component/shadow/src/lib.rs b/lib/rust/ensogl/component/shadow/src/lib.rs index a7f475cd3cfc..2e162e934bc9 100644 --- a/lib/rust/ensogl/component/shadow/src/lib.rs +++ b/lib/rust/ensogl/component/shadow/src/lib.rs @@ -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)] diff --git a/lib/rust/ensogl/component/src/lib.rs b/lib/rust/ensogl/component/src/lib.rs index 8b7264f19ff3..6c22745ce97d 100644 --- a/lib/rust/ensogl/component/src/lib.rs +++ b/lib/rust/ensogl/component/src/lib.rs @@ -3,6 +3,7 @@ // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] +#![allow(clippy::let_and_return)] // ============== diff --git a/lib/rust/ensogl/component/text/Cargo.toml b/lib/rust/ensogl/component/text/Cargo.toml index 50427686465b..ef20bca592f6 100644 --- a/lib/rust/ensogl/component/text/Cargo.toml +++ b/lib/rust/ensogl/component/text/Cargo.toml @@ -23,6 +23,7 @@ bincode = "2.0.0-rc.1" serde = { version = "1", features = ["rc"] } ordered-float = "3.0.0" ensogl-text-font-family = { path = "src/font/family" } +rustybuzz = "0.5.1" [dev-dependencies] wasm-bindgen-test = { version = "0.3.8" } diff --git a/lib/rust/ensogl/component/text/src/buffer.rs b/lib/rust/ensogl/component/text/src/buffer.rs index 1b4941715614..368b80167a81 100644 --- a/lib/rust/ensogl/component/text/src/buffer.rs +++ b/lib/rust/ensogl/component/text/src/buffer.rs @@ -1,15 +1,29 @@ //! Root of text buffer implementation. The text buffer is a sophisticated model for text styling //! and editing operations. +use crate::index::*; use crate::prelude::*; +use enso_text::unit::*; + +use crate::buffer::formatting::Formatting; +use crate::buffer::rope::formatted::FormattedRope; +use crate::buffer::selection::Selection; + +use enso_frp as frp; +use enso_text::text; +use enso_text::text::BoundsError; +use ensogl_text_font_family::NonVariableFaceHeader; // ============== // === Export === // ============== -pub mod style; -pub mod view; +pub mod formatting; +pub mod index; +pub mod movement; +pub mod rope; +pub mod selection; @@ -18,13 +32,138 @@ pub mod traits { pub use enso_text::traits::*; } -pub use style::*; -pub use view::*; +pub use formatting::*; +pub use movement::*; +pub use enso_text::index::*; pub use enso_text::unit::*; pub use enso_text::Range; -pub use enso_text::Text; -pub use enso_text::TextCell; +pub use enso_text::Rope; +pub use enso_text::RopeCell; + + + +// =============== +// === History === +// =============== + +/// Modifications history. Contains data used by undo / redo mechanism. +#[derive(Debug, Clone, CloneRef, Default)] +pub struct History { + data: Rc>, +} + +/// Internal representation of `History`. +#[derive(Debug, Clone, Default)] +pub struct HistoryData { + undo_stack: Vec<(Rope, Formatting, selection::Group)>, + #[allow(dead_code)] + /// Not yet implemented. + redo_stack: Vec<(Rope, Formatting, selection::Group)>, +} + + + +// ==================== +// === Modification === +// ==================== + +/// The summary of single text modification, usually returned by `modify`-like functions in +/// `BufferModel`. +#[allow(missing_docs)] +#[derive(Clone, Debug, Default)] +pub struct Modification { + pub changes: Vec>, + pub selection_group: selection::Group, + /// Byte offset of this modification. For example, after pressing a backspace with a cursor + /// placed after an ASCII char, this should result in `-1`. + pub byte_offset: ByteDiff, +} + +impl Modification { + fn merge(&mut self, other: Modification) { + self.changes.extend(other.changes); + for selection in other.selection_group { + self.selection_group.merge(selection) + } + self.byte_offset += other.byte_offset; + } +} + + + +// ============== +// === Change === +// ============== + +/// A buffer change. It is a rope change with additional information required for lazy line +/// redrawing. +#[allow(missing_docs)] +#[derive(Clone, Debug, Eq, PartialEq, Deref)] +pub struct Change { + #[deref] + pub change: text::Change, + pub change_range: RangeInclusive, + pub line_diff: LineDiff, + pub selection: Selection, +} + +impl Default for Change { + fn default() -> Self { + let change = default(); + let line_diff = default(); + let change_range = Line(0)..=Line(0); + let selection = default(); + Self { change, change_range, line_diff, selection } + } +} + + + +// =========== +// === FRP === +// =========== + +ensogl_core::define_endpoints! { + Input { + cursors_move (Option), + cursors_select (Option), + set_cursor (Location), + add_cursor (Location), + set_newest_selection_end (Location), + set_oldest_selection_end (Location), + insert (ImString), + paste (Rc>), + remove_all_cursors (), + delete_left (), + delete_right (), + delete_word_left (), + delete_word_right (), + clear_selection (), + keep_first_selection_only (), + keep_last_selection_only (), + keep_first_cursor_only (), + keep_last_cursor_only (), + keep_oldest_selection_only (), + keep_newest_selection_only (), + keep_oldest_cursor_only (), + keep_newest_cursor_only (), + undo (), + redo (), + set_property (Rc>>, Option), + mod_property (Rc>>, Option), + set_property_default (Option), + set_first_view_line (Line), + mod_first_view_line (LineDiff), + } + + Output { + selection_edit_mode (Modification), + selection_non_edit_mode (selection::Group), + text_change (Rc>), + first_view_line (Line), + } +} @@ -32,115 +171,1004 @@ pub use enso_text::TextCell; // === Buffer === // ============== -/// Internally mutable text container with associated styles. -#[derive(Clone, CloneRef, Debug, Default)] +/// Buffer for a region of a buffer. There are several cases where multiple views share the same +/// buffer, including displaying the buffer in separate tabs or displaying multiple users in the +/// same file (keeping a view per user and merging them visually). +#[derive(Debug, Clone, CloneRef, Deref)] +#[allow(missing_docs)] pub struct Buffer { - data: Rc, + #[deref] + model: BufferModel, + pub frp: Frp, } -impl Deref for Buffer { - type Target = BufferData; - fn deref(&self) -> &Self::Target { - &self.data +impl Buffer { + /// Constructor. + pub fn new(model: impl Into) -> Self { + let frp = Frp::new(); + let network = &frp.network; + let input = &frp.input; + let output = &frp.output; + let model = model.into(); + let m = &model; + + frp::extend! { network + mod_on_insert <- input.insert.map(f!((s) m.insert(s))); + mod_on_paste <- input.paste.map(f!((s) m.paste(s))); + mod_on_delete_left <- input.delete_left.map(f_!(m.delete_left())); + mod_on_delete_right <- input.delete_right.map(f_!(m.delete_right())); + mod_on_delete_word_left <- input.delete_word_left.map(f_!(m.delete_word_left())); + mod_on_delete_word_right <- input.delete_word_right.map(f_!(m.delete_word_right())); + mod_on_delete <- any(mod_on_delete_left,mod_on_delete_right, mod_on_delete_word_left, + mod_on_delete_word_right); + any_mod <- any(mod_on_insert, mod_on_paste, mod_on_delete); + changed <- any_mod.map(|m| !m.changes.is_empty()); + output.source.text_change <+ any_mod.gate(&changed).map(|m| Rc::new(m.changes.clone())); + + sel_on_move <- input.cursors_move.map(f!((t) m.moved_selection(*t,false))); + sel_on_mod <- input.cursors_select.map(f!((t) m.moved_selection(*t,true))); + sel_on_clear <- input.clear_selection.constant(default()); + sel_on_keep_last <- input.keep_last_selection_only.map(f_!(m.last_selection())); + sel_on_keep_first <- input.keep_first_selection_only.map(f_!(m.first_selection())); + sel_on_keep_lst_cursor <- input.keep_last_cursor_only.map(f_!(m.last_cursor())); + sel_on_keep_fst_cursor <- input.keep_first_cursor_only.map(f_!(m.first_cursor())); + + sel_on_keep_newest <- input.keep_newest_selection_only.map(f_!(m.newest_selection())); + sel_on_keep_oldest <- input.keep_oldest_selection_only.map(f_!(m.oldest_selection())); + sel_on_keep_newest_cursor <- input.keep_newest_cursor_only.map(f_!(m.newest_cursor())); + sel_on_keep_oldest_cursor <- input.keep_oldest_cursor_only.map(f_!(m.oldest_cursor())); + + sel_on_set_cursor <- input.set_cursor.map(f!((t) m.set_cursor(*t))); + sel_on_add_cursor <- input.add_cursor.map(f!((t) m.add_cursor(*t))); + sel_on_set_newest_end <- input.set_newest_selection_end.map + (f!((t) m.set_newest_selection_end(*t))); + sel_on_set_oldest_end <- input.set_oldest_selection_end.map + (f!((t) m.set_oldest_selection_end(*t))); + + sel_on_remove_all <- input.remove_all_cursors.map(|_| default()); + sel_on_undo <= input.undo.map(f_!(m.undo())); + + eval input.set_property (((range,value)) m.set_property(range,*value)); + eval input.mod_property (((range,value)) m.mod_property(range,*value)); + eval input.set_property_default ((prop) m.set_property_default(*prop)); + + output.source.selection_edit_mode <+ any_mod; + output.source.selection_non_edit_mode <+ sel_on_undo; + output.source.selection_non_edit_mode <+ sel_on_move; + output.source.selection_non_edit_mode <+ sel_on_mod; + output.source.selection_non_edit_mode <+ sel_on_clear; + output.source.selection_non_edit_mode <+ sel_on_keep_last; + output.source.selection_non_edit_mode <+ sel_on_keep_first; + output.source.selection_non_edit_mode <+ sel_on_keep_newest; + output.source.selection_non_edit_mode <+ sel_on_keep_oldest; + output.source.selection_non_edit_mode <+ sel_on_keep_lst_cursor; + output.source.selection_non_edit_mode <+ sel_on_keep_fst_cursor; + output.source.selection_non_edit_mode <+ sel_on_keep_newest_cursor; + output.source.selection_non_edit_mode <+ sel_on_keep_oldest_cursor; + output.source.selection_non_edit_mode <+ sel_on_set_cursor; + output.source.selection_non_edit_mode <+ sel_on_add_cursor; + output.source.selection_non_edit_mode <+ sel_on_set_newest_end; + output.source.selection_non_edit_mode <+ sel_on_set_oldest_end; + output.source.selection_non_edit_mode <+ sel_on_remove_all; + + eval output.source.selection_edit_mode ((t) m.set_selection(&t.selection_group)); + eval output.source.selection_non_edit_mode ((t) m.set_selection(t)); + + // === Buffer Area Management === + + eval input.set_first_view_line ((line) m.set_first_view_line(*line)); + output.source.first_view_line <+ input.set_first_view_line; + + new_first_view_line <- input.mod_first_view_line.map + (f!((diff) m.mod_first_view_line(*diff))); + output.source.first_view_line <+ new_first_view_line; + } + Self { model, frp } } } -impl Buffer { + + +// =================== +// === BufferModel === +// =================== + +/// Buffer of styled text associated with selections and text-view related information (like first +/// or last visible line). +#[derive(Debug, Clone, CloneRef, Deref, Default)] +pub struct BufferModel { + #[deref] + data: Rc, +} + +/// Internal representation of [`BufferModel`]. +#[allow(missing_docs)] +#[derive(Debug, Deref, Default)] +pub struct BufferModelData { + #[deref] + pub rope: FormattedRope, + pub selection: RefCell, + next_selection_id: Cell, + pub history: History, + /// The line that corresponds to `ViewLine(0)`. + first_view_line: Cell, + view_line_count: Cell>, +} + +impl BufferModel { /// Constructor. pub fn new() -> Self { default() } +} + + +// === Location === + +impl BufferModel { + /// The full text range. + pub fn full_range(&self) -> Range { + let start = Byte::from(0); + let end = self.last_line_end_offset(); + Range { start, end } + } + + /// Get the previous column of the provided location. + pub fn prev_column(&self, location: Location) -> Location { + // Column can be bigger than last line column if the cursor moved from longer line to a + // shorter one. We keep the bigger column in the cursor, so if it moves back to the longer + // line, the column selection will be preserved. + let line_last_column = self.line_last_column(location.line); + let current_column = std::cmp::min(location.offset, line_last_column); + if current_column > Column(0) { + location.with_offset(current_column - Column(1)) + } else if location.line > Line(0) { + let location = location.dec_line(); + location.with_offset(self.line_last_column(location.line)) + } else { + location + } + } + + /// Get the next column of the provided location. + pub fn next_column(&self, location: Location) -> Location { + let desired_column = location.offset + Column(1); + if desired_column <= self.line_last_column(location.line) { + location.with_offset(desired_column) + } else if location.line < self.last_line_index() { + location.inc_line().zero_offset() + } else { + location + } + } + + /// Last column of the provided line index. + pub fn line_last_column(&self, line: Line) -> Column { + self.rope.line_last_column(line).unwrap() + } + + /// Last column of the last line. + pub fn last_line_last_column(&self) -> Column { + self.line_last_column(self.last_line_index()) + } + + /// Last location of the last line. + pub fn last_line_last_location(&self) -> Location { + let line = self.last_line_index(); + let offset = self.line_last_column(line); + Location { line, offset } + } + + /// Byte offset of the first line of this buffer view. + pub fn first_view_line_byte_offset(&self) -> Byte { + self.line_offset(self.first_view_line()).unwrap() + } + + /// Byte offset of the last line of this buffer view. + pub fn last_view_line_byte_offset(&self) -> Byte { + self.line_offset(self.last_view_line()).unwrap() + } + + /// Byte offset range of lines visible in this buffer view. + pub fn view_line_byte_offset_range(&self) -> std::ops::Range { + self.first_view_line_byte_offset()..self.last_view_line_byte_offset() + } + + /// Byte offset of the end of this buffer view. Snapped to the closest valid value. + pub fn view_end_byte_offset_snapped(&self) -> Byte { + self.line_end_offset_snapped(self.last_view_line()) + } - /// Creates a new `View` for the buffer. - pub fn new_view(&self) -> View { - View::new(self) + /// Return the offset after the last character of a given view line if the line exists. + pub fn end_offset_of_view_line(&self, line: Line) -> Option { + self.line_end_offset(line + self.first_view_line.get()).ok() + } + + /// The byte range of this buffer view. + pub fn view_byte_range(&self) -> std::ops::Range { + self.first_view_line_byte_offset()..self.view_end_byte_offset_snapped() + } + + /// The byte offset of the given buffer view line index. + pub fn byte_offset_of_view_line_index(&self, view_line: Line) -> Result { + let line = self.first_view_line() + view_line; + self.line_offset(line) + } + + /// Byte range of the given view line. + pub fn byte_range_of_view_line_index_snapped( + &self, + view_line: ViewLine, + ) -> std::ops::Range { + let line = Line::from_in_context_snapped(self, view_line); + self.line_range_snapped(line) + } + + /// End byte offset of the last line. + pub fn last_line_end_offset(&self) -> Byte { + self.rope.text().last_line_end_offset() } } +// === Selection === + +impl BufferModel { + fn first_selection(&self) -> selection::Group { + self.selection.borrow().first().cloned().into() + } + + fn last_selection(&self) -> selection::Group { + self.selection.borrow().last().cloned().into() + } + + fn first_cursor(&self) -> selection::Group { + self.first_selection().snap_selections_to_start() + } + + fn last_cursor(&self) -> selection::Group { + self.last_selection().snap_selections_to_start() + } + + fn newest_selection(&self) -> selection::Group { + self.selection.borrow().newest().cloned().into() + } + + fn oldest_selection(&self) -> selection::Group { + self.selection.borrow().oldest().cloned().into() + } + + fn newest_cursor(&self) -> selection::Group { + self.newest_selection().snap_selections_to_start() + } + + fn oldest_cursor(&self) -> selection::Group { + self.oldest_selection().snap_selections_to_start() + } + + fn new_cursor(&self, location: Location) -> Selection { + let id = self.next_selection_id.get(); + self.next_selection_id.set(selection::Id { value: id.value + 1 }); + Selection::new_cursor(location, id) + } + + /// Returns the last used selection or a new one if no active selection exists. This allows for + /// nice animations when moving cursor between lines after clicking with mouse. + fn set_cursor(&self, location: Location) -> selection::Group { + let last_selection = self.selection.borrow().last().cloned(); + let opt_existing = last_selection.map(|t| t.with_location(location)); + opt_existing.unwrap_or_else(|| self.new_cursor(location)).into() + } + + fn add_cursor(&self, location: Location) -> selection::Group { + let mut selection = self.selection.borrow().clone(); + let selection_group = self.new_cursor(location); + selection.merge(selection_group); + selection + } + + fn set_newest_selection_end(&self, location: Location) -> selection::Group { + let mut group = self.selection.borrow().clone(); + group.newest_mut().for_each(|s| s.end = location); + group + } + + fn set_oldest_selection_end(&self, location: Location) -> selection::Group { + let mut group = self.selection.borrow().clone(); + group.oldest_mut().for_each(|s| s.end = location); + group + } + + /// Current selections expressed in bytes. + pub fn byte_selections(&self) -> Vec> { + let selections = self.selection.borrow().clone(); + selections.iter().map(|s| Selection::::from_in_context_snapped(self, *s)).collect() + } -// ================== -// === BufferData === -// ================== + /// Set the selection to a new value. + pub fn set_selection(&self, selection: &selection::Group) { + *self.selection.borrow_mut() = selection.clone(); + } -/// Internal data of `Buffer`. -#[derive(Debug, Default)] -pub struct BufferData { - pub(crate) text: TextCell, - pub(crate) style: StyleCell, + /// Return all active selections. + pub fn selections(&self) -> selection::Group { + self.selection.borrow().clone() + } + + /// Return all selections as vector of strings. For cursors, the string will be empty. + pub fn selections_contents(&self) -> Vec { + let mut result = Vec::::new(); + for selection in self.byte_selections() { + result.push(self.rope.text.sub(selection.range()).into()) + } + result + } } -impl Deref for BufferData { - type Target = TextCell; - fn deref(&self) -> &Self::Target { - &self.text + +// === Line Shaping === + +impl BufferModel {} + + +// === Modification === + +impl BufferModel { + /// Get content for lines in the given range. + pub fn lines_content(&self, range: RangeInclusive) -> Vec { + let start_line = Line::from_in_context_snapped(self, *range.start()); + let end_line = Line::from_in_context_snapped(self, *range.end()); + let start_byte_offset = self.line_offset(start_line).unwrap(); + let end_byte_offset = self.line_end_offset_snapped(end_line); + let range = start_byte_offset..end_byte_offset; + self.lines_vec(range) + } + + /// Insert new text in the place of current selections / cursors. + fn insert(&self, text: impl Into) -> Modification { + self.modify_selections(iter::repeat(text.into()), None) + } + + /// Paste new text in the place of current selections / cursors. In case of pasting multiple + /// chunks (e.g. after copying multiple selections), the chunks will be pasted into subsequent + /// selections. In case there are more chunks than selections, end chunks will be dropped. In + /// case there is more selections than chunks, end selections will be replaced with empty + /// strings. In case there is only one chunk, it will be pasted to all selections. + fn paste(&self, text: &[String]) -> Modification { + if text.len() == 1 { + self.modify_selections(iter::repeat((&text[0]).into()), None) + } else { + self.modify_selections(text.iter().map(|t| t.into()), None) + } + } + + // TODO: Delete left should first delete the vowel (if any) and do not move cursor. After + // pressing backspace second time, the consonant should be removed. Please read this topic + // to learn more: https://phabricator.wikimedia.org/T53472 + fn delete_left(&self) -> Modification { + self.modify_selections(iter::empty(), Some(Transform::Left)) + } + + fn delete_right(&self) -> Modification { + self.modify_selections(iter::empty(), Some(Transform::Right)) + } + + fn delete_word_left(&self) -> Modification { + self.modify_selections(iter::empty(), Some(Transform::LeftWord)) + } + + fn delete_word_right(&self) -> Modification { + self.modify_selections(iter::empty(), Some(Transform::RightWord)) + } + + /// Generic buffer modify utility. It replaces each selection range with next iterator item. + /// + /// If `transform` is provided, it will modify the selections being a simple cursor before + /// applying modification, what is useful when handling delete operations. + fn modify_selections(&self, mut iter: I, transform: Option) -> Modification + where I: Iterator { + self.commit_history(); + let mut modification = Modification::default(); + for rel_byte_selection in self.byte_selections() { + let text = iter.next().unwrap_or_default(); + let byte_selection = rel_byte_selection.map(|t| t + modification.byte_offset); + let selection = Selection::::from_in_context_snapped(self, byte_selection); + modification.merge(self.modify_selection(selection, text, transform)); + } + modification + } + + /// Generic selection modify utility. It replaces selection range with given text. + /// + /// If `transform` is provided and selection is a simple cursor, it will modify it before + /// applying modification, what is useful when handling delete operations. + /// + /// It returns selection after modification and byte offset of the next selection ranges. + fn modify_selection( + &self, + selection: Selection, + text: Rope, + transform: Option, + ) -> Modification { + let text_byte_size = text.last_byte_index(); + let transformed = match transform { + Some(t) if selection.is_cursor() => self.moved_selection_region(t, selection, true), + _ => selection, + }; + + let byte_selection = Selection::::from_in_context_snapped(self, transformed); + let line_selection = + Selection::::from_in_context_snapped(self, byte_selection); + let line_selection = line_selection.map_shape(|s| s.normalized()); + let range = byte_selection.range(); + self.rope.replace(range, &text); + + let new_byte_cursor_pos = range.start + text_byte_size; + let new_byte_selection = Selection::new_cursor(new_byte_cursor_pos, selection.id); + let local_byte_selection = + Selection::>::from_in_context_snapped(self, new_byte_selection); + + let redraw_start_line = transformed.min().line; + let redraw_end_line = transformed.max().line; + let selected_line_count = redraw_end_line - redraw_start_line + Line(1); + let inserted_line_count = local_byte_selection.end.line - redraw_start_line + Line(1); + let line_diff = inserted_line_count - selected_line_count; + + let loc_selection = + Selection::::from_in_context_snapped(self, new_byte_selection); + let selection_group = selection::Group::from(loc_selection); + let change = text::Change { range, text }; + let change_range = redraw_start_line..=redraw_end_line; + let change = Change { change, change_range, line_diff, selection: line_selection }; + let changes = vec![change]; + let byte_offset = text_byte_size.to_diff() - range.size(); + Modification { changes, selection_group, byte_offset } } } -impl BufferData { - /// Constructor. - pub fn new() -> Self { - default() + +// === Properties === + +impl BufferModel { + fn set_property(&self, ranges: &Vec>, property: Option) { + if let Some(property) = property { + for range in ranges { + let range = self.crop_byte_range(range); + self.formatting.set_property(range, property) + } + } } - /// Text getter. - pub fn text(&self) -> Text { - self.text.get() + fn mod_property(&self, ranges: &Vec>, property: Option) { + if let Some(property) = property { + for range in ranges { + let range = self.crop_byte_range(range); + self.formatting.mod_property(range, property) + } + } + } + + fn set_property_default(&self, property: Option) { + if let Some(property) = property { + self.formatting.set_property_default(property) + } + } + + /// Resolve the provided property by applying a default value if needed. + pub fn resolve_property(&self, property: Property) -> ResolvedProperty { + self.formatting.resolve_property(property) + } +} + + +// === View === + +impl BufferModel { + fn set_first_view_line(&self, line: Line) { + self.first_view_line.set(line); } - /// Text setter. - pub(crate) fn set_text(&self, text: impl Into) { - self.text.set(text); + fn mod_first_view_line(&self, diff: LineDiff) -> Line { + let line = self.first_view_line.get() + diff; + self.set_first_view_line(line); + line } - /// Style getter. - pub fn style(&self) -> Style { - self.style.get() + /// Index of the first line of this buffer view. + pub fn first_view_line(&self) -> Line { + self.first_view_line.get() } - /// Style setter. - pub(crate) fn set_style(&self, style: Style) { - self.style.set(style) + /// Index of the last line of this buffer view. + pub fn last_view_line(&self) -> Line { + let last_view_line = ViewLine(self.first_view_line().value + self.view_line_count() - 1); + Line::from_in_context_snapped(self, last_view_line) } - /// Query style information for the provided range. - pub fn sub_style(&self, range: impl enso_text::RangeBounds) -> Style { - let range = self.crop_byte_range(range); - self.style.sub(range) + /// Number of lines visible in this buffer view. + pub fn view_line_count(&self) -> usize { + self.view_line_count + .get() + .unwrap_or_else(|| self.last_line_index().value + 1 - self.first_view_line.get().value) + } + + /// Last index of visible lines. + pub fn last_view_line_index(&self) -> ViewLine { + ViewLine(self.view_line_count() - 1) + } + + /// Range of visible lines. + pub fn view_line_range(&self) -> RangeInclusive { + ViewLine(0)..=ViewLine::from_in_context_snapped(self, self.last_view_line()) + } + + /// Return all lines of this buffer view. + pub fn view_lines_content(&self) -> Vec { + self.lines_vec(self.view_byte_range()) } } +// === Undo / Redo === -// ============== -// === Setter === -// ============== +impl BufferModel { + fn commit_history(&self) { + let text = self.rope.text(); + let style = self.rope.style(); + let selection = self.selection.borrow().clone(); + self.history.data.borrow_mut().undo_stack.push((text, style, selection)); + } + + fn undo(&self) -> Option { + let item = self.history.data.borrow_mut().undo_stack.pop(); + item.map(|(text, style, selection)| { + self.rope.set_text(text); + self.rope.set_style(style); + selection + }) + } +} + + + +// ================= +// === RangeLike === +// ================= + +/// A range-like description. Any of the enum variants can be used to describe a range in text. +/// There are conversions defined, so you never need to use this type explicitly. +#[allow(missing_docs)] +#[derive(Debug, Clone, Default, From)] +pub enum RangeLike { + #[default] + Selections, + BufferRangeUBytes(Range), + BufferRangeLocationColumn(Range), + RangeBytes(std::ops::Range), + RangeFull(RangeFull), +} + +impl RangeLike { + /// Expand the [`RangeLike`] to vector of text ranges. + pub fn expand(&self, buffer: &BufferModel) -> Vec> { + match self { + RangeLike::Selections => { + let byte_selections = buffer.byte_selections().into_iter(); + byte_selections + .map(|t| { + let start = std::cmp::min(t.start, t.end); + let end = std::cmp::max(t.start, t.end); + Range::new(start, end) + }) + .collect() + } + RangeLike::BufferRangeUBytes(range) => vec![*range], + RangeLike::RangeBytes(range) => vec![range.into()], + RangeLike::RangeFull(_) => vec![buffer.full_range()], + RangeLike::BufferRangeLocationColumn(range) => { + let start = Byte::from_in_context_snapped(buffer, range.start); + let end = Byte::from_in_context_snapped(buffer, range.end); + vec![Range::new(start, end)] + } + } + } +} -/// Generic setter for buffer data and metadata, like colors, font weight, etc. -trait Setter { - /// Replace the range with the provided value. The exact meaning of this function depends on the - /// provided data type. See implementations provided in the `style` module. - fn replace(&self, range: impl enso_text::RangeBounds, data: T); +impl From<&Range> for RangeLike { + fn from(range: &Range) -> Self { + RangeLike::BufferRangeUBytes(*range) + } +} + +impl From<&Range> for RangeLike { + fn from(range: &Range) -> Self { + RangeLike::BufferRangeLocationColumn(*range) + } +} + +impl From<&std::ops::Range> for RangeLike { + fn from(range: &std::ops::Range) -> Self { + RangeLike::RangeBytes(range.clone()) + } +} + +impl From<&RangeFull> for RangeLike { + fn from(range: &RangeFull) -> Self { + RangeLike::RangeFull(*range) + } } -/// Generic setter for default value for metadata like colors, font weight, etc. -trait DefaultSetter { - /// Replace the default value of the metadata. The exact meaning of this function depends on the - /// provided data type. See implementations provided in the `style` module. - fn set_default(&self, data: T); + + +// ==================== +// === LocationLike === +// ==================== + +/// A location-like description. Any of the enum variants can be used to describe a location in +/// text. There are conversions defined, so you never need to use this type explicitly. +#[allow(missing_docs)] +#[derive(Debug, Copy, Clone, From)] +pub enum LocationLike { + LocationColumnLine(Location), + LocationUBytesLine(Location), + LocationColumnViewLine(Location), + LocationUBytesViewLine(Location), +} + +impl Default for LocationLike { + fn default() -> Self { + LocationLike::LocationColumnLine(default()) + } } -impl Setter for Buffer { - fn replace(&self, range: impl enso_text::RangeBounds, text: Text) { - let range = self.crop_byte_range(range); - let size = text.byte_size(); - self.text.replace(range, text); - self.style.set_resize_with_default(range, size); +impl LocationLike { + /// Expand the [`LocationLike`] structure to a known location. + pub fn expand(self, buffer: &BufferModel) -> Location { + match self { + LocationLike::LocationColumnLine(loc) => loc, + LocationLike::LocationUBytesLine(loc) => Location::from_in_context_snapped(buffer, loc), + LocationLike::LocationColumnViewLine(loc) => + Location::from_in_context_snapped(buffer, loc), + LocationLike::LocationUBytesViewLine(loc) => + Location::from_in_context_snapped(buffer, loc), + } } } -impl Setter<&Text> for Buffer { - fn replace(&self, range: impl enso_text::RangeBounds, text: &Text) { - self.replace(range, text.clone()) + +// =================== +// === Conversions === +// =================== + +/// Perform conversion between two values. It is just like the [`From`] trait, but it performs the +/// conversion in a "context". The "context" is an object containing additional information required +/// to perform the conversion. For example, the context can be a text buffer, which is needed to +/// convert byte offset to line-column location. +#[allow(missing_docs)] +pub trait FromInContext { + fn from_in_context(context: Ctx, arg: T) -> Self; +} + +/// Perform conversion between two values. It is just like [`FromInContext`], but the result value +/// is snapped to the closest valid location. For example, when performing conversion between +/// [`Line`] and [`ViewLine`], if the line is not visible, the closest visible line will be +/// returned. +#[allow(missing_docs)] +pub trait FromInContextSnapped { + fn from_in_context_snapped(context: Ctx, arg: T) -> Self; +} + +/// Try performing conversion between two values. It is like the [`FromInContextSnapped`] trait, but +/// can fail. +#[allow(missing_docs)] +pub trait TryFromInContext +where Self: Sized { + type Error; + fn try_from_in_context(context: Ctx, arg: T) -> Result; +} + + +// === Generic Impls === + +impl<'t, T, U> FromInContext<&'t Buffer, U> for T +where T: FromInContext<&'t BufferModel, U> +{ + fn from_in_context(buffer: &'t Buffer, elem: U) -> Self { + T::from_in_context(&buffer.model, elem) + } +} + +impl<'t, T, U> FromInContextSnapped<&'t Buffer, U> for T +where T: FromInContextSnapped<&'t BufferModel, U> +{ + fn from_in_context_snapped(buffer: &'t Buffer, elem: U) -> Self { + T::from_in_context_snapped(&buffer.model, elem) + } +} + +impl<'t, T, U> TryFromInContext<&'t Buffer, U> for T +where T: TryFromInContext<&'t BufferModel, U> +{ + type Error = >::Error; + fn try_from_in_context(buffer: &'t Buffer, elem: U) -> Result { + T::try_from_in_context(&buffer.model, elem) + } +} + + +// === Conversion Redirection === + +/// Redirects the conversion to [`Rope`] counterpart of conversion function. This trait introduces +/// a new metric, the [`ViewLine`] and extends [`Rope`] conversions with it. +macro_rules! redirect_conversion_to_rope { + ([$($ctx:tt)*] [$($from:tt)*] [$($to:tt)*]) => { + impl<$($ctx)*> FromInContextSnapped<&BufferModel, $($from)*> for $($to)* + where for<'t> $($to)*: enso_text::FromInContextSnapped<&'t Rope, $($from)*> + { + fn from_in_context_snapped(buffer: &BufferModel, value: $($from)*) -> Self { + <$($to)* as enso_text::FromInContextSnapped<&Rope, $($from)*>>:: + from_in_context_snapped(&*buffer.rope.text.borrow(),value) + } + } + }; +} + + + +// === Conversions to Line === + +redirect_conversion_to_rope!([T][Location][Line]); +redirect_conversion_to_rope!([][Byte][Line]); + +/// Conversion error between [`ViewLine`] and [`Line`]. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +pub enum ViewLineToLineConversionError { + TooSmall, + TooBig, +} + +impl TryFromInContext<&BufferModel, ViewLine> for Line { + type Error = ViewLineToLineConversionError; + fn try_from_in_context(buffer: &BufferModel, view_line: ViewLine) -> Result { + let line = buffer.first_view_line() + Line(view_line.value); + if line > buffer.last_line_index() { + Err(ViewLineToLineConversionError::TooBig) + } else { + Ok(line) + } + } +} + +impl FromInContextSnapped<&BufferModel, ViewLineToLineConversionError> for Line { + fn from_in_context_snapped(buffer: &BufferModel, err: ViewLineToLineConversionError) -> Self { + match err { + ViewLineToLineConversionError::TooSmall => Line(0), + ViewLineToLineConversionError::TooBig => buffer.last_line_index(), + } + } +} + +impl FromInContextSnapped<&BufferModel, ViewLine> for Line { + fn from_in_context_snapped(buffer: &BufferModel, view_line: ViewLine) -> Self { + Line::try_from_in_context(buffer, view_line) + .unwrap_or_else(|err| Line::from_in_context_snapped(buffer, err)) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Line { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + Line::from_in_context_snapped(buffer, location.line) + } +} + + +// === Conversions to ViewLine === + +/// Conversion error between [`Line`] and [`ViewLine`]. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +pub enum LineToViewLineConversionError { + TooSmall, + TooBig, +} + +impl TryFromInContext<&BufferModel, Line> for ViewLine { + type Error = LineToViewLineConversionError; + fn try_from_in_context(buffer: &BufferModel, line: Line) -> Result { + let line_diff = line - buffer.first_view_line(); + if line_diff.value < 0 { + Err(LineToViewLineConversionError::TooSmall) + } else { + let view_line = ViewLine(line_diff.value as usize); + if view_line > buffer.last_view_line_index() { + Err(LineToViewLineConversionError::TooBig) + } else { + Ok(view_line) + } + } + } +} + +impl FromInContextSnapped<&BufferModel, LineToViewLineConversionError> for ViewLine { + fn from_in_context_snapped(buffer: &BufferModel, err: LineToViewLineConversionError) -> Self { + match err { + LineToViewLineConversionError::TooSmall => ViewLine(0), + LineToViewLineConversionError::TooBig => buffer.last_view_line_index(), + } + } +} + +impl FromInContextSnapped<&BufferModel, Line> for ViewLine { + fn from_in_context_snapped(buffer: &BufferModel, line: Line) -> Self { + ViewLine::try_from_in_context(buffer, line) + .unwrap_or_else(|err| ViewLine::from_in_context_snapped(buffer, err)) + } +} + + +// === Conversions to Byte === + +impl FromInContextSnapped<&BufferModel, Location> for Byte { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + buffer.line_offset(location.line).unwrap() + location.offset + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Byte { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let location = Location::::from_in_context_snapped(buffer, location); + Byte::from_in_context_snapped(buffer, location) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Byte { + fn from_in_context_snapped(context: &BufferModel, location: Location) -> Self { + let location = Location::::from_in_context_snapped(context, location); + Byte::from_in_context_snapped(context, location) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Byte { + fn from_in_context_snapped( + context: &BufferModel, + location: Location, + ) -> Self { + let location = Location::::from_in_context_snapped(context, location); + Byte::from_in_context_snapped(context, location) + } +} + + +// === Conversions to Location === + +redirect_conversion_to_rope!([][Location][Location]); +redirect_conversion_to_rope!([][Byte][Location]); + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped( + context: &BufferModel, + location: Location, + ) -> Self { + let line = Line::from_in_context_snapped(context, location.line); + Location(line, location.offset) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(context: &BufferModel, location: Location) -> Self { + let line = Line::from_in_context_snapped(context, location.line); + Location::from_in_context_snapped(context, Location(line, location.offset)) + } +} + + +// === Conversions to Location === + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let line = ViewLine::from_in_context_snapped(buffer, location.line); + location.with_line(line) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let location = Location::::from_in_context_snapped(buffer, location); + Location::::from_in_context_snapped(buffer, location) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let line = Line::from_in_context_snapped(buffer, location.line); + Location::::from_in_context_snapped(buffer, location.with_line(line)) + } +} + +impl FromInContextSnapped<&BufferModel, Byte> for Location { + fn from_in_context_snapped(buffer: &BufferModel, offset: Byte) -> Self { + let location = Location::::from_in_context_snapped(buffer, offset); + Location::::from_in_context_snapped(buffer, location) + } +} + + +// === Conversions to Location === + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let line = ViewLine::from_in_context_snapped(buffer, location.line); + location.with_line(line) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let location = Location::::from_in_context_snapped(buffer, location); + Location::::from_in_context_snapped(buffer, location) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let line = Line::from_in_context_snapped(buffer, location.line); + Location::::from_in_context_snapped(buffer, location.with_line(line)) + } +} + +impl FromInContextSnapped<&BufferModel, Byte> for Location { + fn from_in_context_snapped(buffer: &BufferModel, offset: Byte) -> Self { + let location = Location::::from_in_context_snapped(buffer, offset); + Location::::from_in_context_snapped(buffer, location) + } +} + + +// === Conversions to Location === + +redirect_conversion_to_rope!([][Location][Location]); +redirect_conversion_to_rope!([][Byte][Location]); + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, offset: Location) -> Self { + let line = Line::from_in_context_snapped(buffer, offset.line); + Location(line, offset.offset) + } +} + +impl FromInContextSnapped<&BufferModel, Location> for Location { + fn from_in_context_snapped(buffer: &BufferModel, location: Location) -> Self { + let line = Line::from_in_context_snapped(buffer, location.line); + Location::from_in_context_snapped(buffer, location.with_line(line)) + } +} + + +// === Conversions of Range ==== + +impl<'t, S, T> FromInContextSnapped<&'t BufferModel, Range> for Range +where T: FromInContextSnapped<&'t BufferModel, S> +{ + fn from_in_context_snapped(context: &'t BufferModel, range: Range) -> Self { + let start = T::from_in_context_snapped(context, range.start); + let end = T::from_in_context_snapped(context, range.end); + Range::new(start, end) + } +} + + +// === Selections === + +impl<'t, T, S> FromInContextSnapped<&'t BufferModel, Selection> for Selection +where + T: Copy, + S: FromInContextSnapped<&'t BufferModel, T>, +{ + fn from_in_context_snapped(buffer: &'t BufferModel, selection: Selection) -> Self { + let start = S::from_in_context_snapped(buffer, selection.start); + let end = S::from_in_context_snapped(buffer, selection.end); + let id = selection.id; + Selection::new(start, end, id) } } diff --git a/lib/rust/ensogl/component/text/src/buffer/formatting.rs b/lib/rust/ensogl/component/text/src/buffer/formatting.rs new file mode 100644 index 000000000000..3dcff9558566 --- /dev/null +++ b/lib/rust/ensogl/component/text/src/buffer/formatting.rs @@ -0,0 +1,603 @@ +//! Text style definition (color, bold, italics, etc.). + +use super::*; + +use crate::buffer::Range; +use crate::font; + +use enso_text::spans::RangedValue; + + +// ============== +// === Export === +// ============== + +pub use crate::data::color; +pub use font::Style; +pub use font::Weight; +pub use font::Width; + + + +// ============= +// === Units === +// ============= + +/// Defines a newtype for a primitive style property, like `Bold`. See usage below to learn more. +macro_rules! def_unit { + ($name:ident($field_type:ty) = $def:expr) => { + /// Formatting property. + #[derive(Clone, Copy, Debug, From, PartialEq, PartialOrd)] + #[allow(missing_docs)] + pub struct $name { + pub value: $field_type, + } + + impl $name { + /// Constructor. + pub const fn new(value: $field_type) -> $name { + $name { value } + } + } + + /// Smart constructor. + #[allow(non_snake_case)] + pub fn $name(value: $field_type) -> $name { + $name { value } + } + + impl Default for $name { + fn default() -> Self { + Self::new($def) + } + } + }; +} + +def_unit!(Size(f32) = 12.0); +def_unit!(SdfWeight(f32) = 0.0); + + + +/// ================== +/// === Properties === +/// ================== + +/// Run the provided macro with formatting properties definition. +#[macro_export] +macro_rules! with_formatting_properties { + ($macro_name:ident) => { + $macro_name! { + size : Size, + color : color::Lcha, + weight : Weight, + width : Width, + style : Style, + sdf_weight : SdfWeight, + } + }; +} + +/// Define formatting properties. See the usage to learn more. +macro_rules! define_property { + ($($field:ident : $field_type:ty),* $(,)?) => {paste! { + /// Unresolved formatting property. Value of [`None`] indicates a default value. + #[allow(missing_docs)] + #[derive(Clone, Copy, Debug, From)] + pub enum Property { + $([<$field:camel>] (Option<$field_type>)),* + } + + impl Property { + /// The property tag accessor. + pub fn tag(self) -> PropertyTag { + self.into() + } + } + + $( + impl From<$field_type> for Property { + fn from(value: $field_type) -> Self { + Property::[<$field:camel>] (Some(value)) + } + } + + impl From<&$field_type> for Property { + fn from(value: &$field_type) -> Self { + Property::[<$field:camel>] (Some(*value)) + } + } + )* + + /// Resolved property. Just like [`Property`] but without the possibility to use default + /// value placeholder. + #[allow(missing_docs)] + #[derive(Clone, Copy, Debug, From)] + pub enum ResolvedProperty { + $([<$field:camel>] ($field_type)),* + } + + impl ResolvedProperty { + /// The property tag accessor. + pub fn tag(self) -> PropertyTag { + self.into() + } + } + + $( + impl From<&$field_type> for ResolvedProperty { + fn from(value: &$field_type) -> Self { + ResolvedProperty::[<$field:camel>] (*value) + } + } + )* + + /// A property name without values. + #[allow(missing_docs)] + #[derive(Clone, Copy, Debug, From)] + pub enum PropertyTag { + $([<$field:camel>]),* + } + + impl From for PropertyTag { + fn from(property: Property) -> Self { + match property { + $(Property::[<$field:camel>](_) => Self::[<$field:camel>]),* + } + } + } + + impl From for PropertyTag { + fn from(property: ResolvedProperty) -> Self { + match property { + $(ResolvedProperty::[<$field:camel>](_) => Self::[<$field:camel>]),* + } + } + } + }}; +} + +with_formatting_properties! { define_property } + +impl From for Property { + fn from(t: color::Rgba) -> Self { + Property::Color(Some(t.into())) + } +} + +impl From<&color::Rgba> for Property { + fn from(t: &color::Rgba) -> Self { + Property::Color(Some(t.into())) + } +} + +impl From for ResolvedProperty { + fn from(t: color::Rgba) -> Self { + ResolvedProperty::Color(t.into()) + } +} + +impl From<&color::Rgba> for ResolvedProperty { + fn from(t: &color::Rgba) -> Self { + ResolvedProperty::Color(t.into()) + } +} + + + +// ================== +// === Formatting === +// ================== + +/// Defines struct containing all styles information. Also defines many utils, like iterator for it. +/// See the usage below to learn more. +macro_rules! define_formatting { + ($($field:ident : $field_type:ty),* $(,)?) => {paste! { + + /// Definition of possible text styles, like `color`, or `bold`. Each style is encoded as + /// [`Spanned`] for some spans in the buffer. + #[derive(Clone,Debug,Default)] + #[allow(missing_docs)] + pub struct Formatting { + $(pub $field : Spanned<$field_type>),* + } + + impl Formatting { + /// Constructor. + pub fn new() -> Self { + Self::default() + } + + /// Return new style narrowed to the given range. + pub fn sub(&self, range:Range) -> Self { + $(let $field = self.$field.sub(range);)* + Self {$($field),*} + } + + /// Replace the provided `range` with the `None` value (default), repeated over `len` + /// bytes. Use with care, as it's very easy to provide incorrect byte size value, which + /// may result in styles being applied to parts of grapheme clusters only. + pub fn set_resize_with_default(&mut self, range:Range, len:Byte) { + $(self.$field.replace_resize(range,len,None);)* + } + + /// Return all span ranges of default values for the given property. + pub fn span_ranges_of_default_values(&self, tag:PropertyTag) -> Vec> { + match tag { + $(PropertyTag::[<$field:camel>] => { + let spans = self.$field.spans.to_vector(); + spans.into_iter().filter(|t| t.value.is_none()).map(|t| t.range) + .collect_vec() + })* + } + } + } + + impl Formatting { + /// Set the value of the given property for the given range. + pub fn set_property(&mut self, range:Range, property: Property) { + let size = Byte::try_from(range.size()).unwrap_or_default(); + match property { + $(Property::[<$field:camel>](t) => self.$field.replace_resize(range, size, t)),* + } + } + + /// Resolve the property. Applies the default value if the property value was [`None`]. + pub fn resolve_property(&self, property: Property) -> ResolvedProperty { + match property { + $(Property::[<$field:camel>](t) => ResolvedProperty::[<$field:camel>] + (t.unwrap_or_default())),* + } + } + + /// Sets a new default value for the given property. + pub fn set_property_default(&mut self, property: ResolvedProperty) { + match property { + $(ResolvedProperty::[<$field:camel>](t) => self.$field.default = t),* + } + } + } + }}; +} + +with_formatting_properties! { define_formatting } + +impl Formatting { + /// Returns list of spans for triples of (width, weight, style). The triple is used to identify + /// a non-variable font family. + pub fn non_variable_font_spans(&self) -> Vec> { + let seq_width = self.width.to_vector(); + let seq_weight = self.weight.to_vector(); + let seq_style = self.style.to_vector(); + RangedValue::zip3_def_seq(&seq_width, &seq_weight, &seq_style, NonVariableFaceHeader::new) + } + + /// Return list of spans for different [`NonVariableFaceHeader`]. The result will be aligned + /// with grapheme cluster boundaries. If the face header changes inside a grapheme cluster, the + /// cluster will be associated with the header it starts with. + pub fn chunks_per_font_face<'a>( + &self, + rope: &'a Rope, + ) -> impl Iterator, NonVariableFaceHeader)> + 'a { + let seq_font_header = self.non_variable_font_spans(); + let iter = gen_iter!(move { + let mut start_byte = Byte(0); + let mut end_byte = Byte(0); + let mut header_iter = seq_font_header.into_iter(); + let mut opt_header = header_iter.next(); + while let Some(header) = opt_header + && let Some(new_end_byte) = rope.next_grapheme_offset(end_byte) { + end_byte = new_end_byte; + if end_byte >= header.range.end { + yield (start_byte..end_byte, header.value); + start_byte = end_byte; + opt_header = header_iter.next(); + } + } + if start_byte != end_byte { + error!("Misaligned bytes found when shaping text. {:?} != {:?}", start_byte, end_byte); + yield (start_byte..end_byte, default()); + } + }); + // We are merging subsequent ranges if they have the same header. The underlying rope + // implementation can return chunks with the same value. For example, after setting a glyph + // to a bold face, and unsetting it, there will be separate chunks emitted. + iter.coalesce(|mut a, b| { + if a.1 == b.1 { + a.0.end = b.0.end; + Ok(a) + } else { + Err((a, b)) + } + }) + } +} + + + +// ================= +// === Iterators === +// ================= + +/// Byte-based iterator for the [`Formatting`]. +#[derive(Debug)] +pub struct FormattingByteIterator { + offset: Byte, + value: StyleIteratorValue, + component: StyleIteratorComponents, +} + +impl FormattingByteIterator { + /// Constructor. + fn new(component: StyleIteratorComponents) -> Self { + let offset = default(); + let value = default(); + Self { offset, value, component } + } + + /// Skip the given amount of bytes. + pub fn skip_bytes(&mut self, bytes: Byte) { + for _ in 0..bytes.value { + self.next(); + } + } +} + +macro_rules! define_iterators { + ($($field:ident : $field_type:ty),* $(,)?) => {paste! { + + /// The formatting for the given byte in the buffer. + #[allow(missing_docs)] + #[derive(Clone,Copy,Debug,Default)] + pub struct FormattingForByte { + $(pub $field : $field_type),* + } + + #[derive(Debug)] + struct StyleIteratorComponents { + $($field : std::vec::IntoIter>),* + } + + #[derive(Debug,Default)] + struct StyleIteratorValue { + $($field : Option>),* + } + + impl Iterator for FormattingByteIterator { + type Item = FormattingForByte; + fn next(&mut self) -> Option { + $( + if self.value.$field.map(|t| self.offset < t.range.end) != Some(true) { + self.value.$field = self.component.$field.next() + } + let $field = self.value.$field?.value; + )* + self.offset += Byte(1); + Some(FormattingForByte {$($field),*}) + } + } + + impl Formatting { + /// Iterate over style values for subsequent bytes of the buffer. + pub fn iter_bytes(&self) -> FormattingByteIterator { + $(let $field = self.$field.to_vector().into_iter();)* + FormattingByteIterator::new(StyleIteratorComponents {$($field),*}) + } + } + }}; +} + +with_formatting_properties! { define_iterators } + + + +// =============== +// === Spanned === +// =============== + +/// Formatting property spanned over text. It also contains a default value to be used for places +/// not covered by the spans. The default value can be changed at runtime (e.g. the default color of +/// a text. +#[derive(Clone, Debug, Default, Deref, DerefMut)] +#[allow(missing_docs)] +pub struct Spanned { + #[deref] + #[deref_mut] + pub spans: enso_text::Spans>, + pub default: T, +} + +impl Spanned { + /// Return new property narrowed to the given range. + pub fn sub(&self, range: Range) -> Self { + let spans = self.spans.sub(range); + let default = self.default; + Self { spans, default } + } + + /// Convert the property to a vector of spans. + pub fn to_vector(&self) -> Vec> { + let spans_iter = self.spans.to_vector().into_iter(); + spans_iter.map(|t| t.map_value(|v| v.unwrap_or(self.default))).collect_vec() + } + + /// Modify the values in the given range, first resolving them. If a value was not set, the + /// default value will be used instead. + pub fn modify_resolved(&mut self, range: Range, f: impl Fn(T) -> T) { + self.spans.modify(range, |t| Some(f(t.unwrap_or(self.default)))) + } +} + + + +// ==================== +// === PropertyDiff === +// ==================== + +/// Run the provided macro with formatting property diffs definition. +#[macro_export] +macro_rules! with_formatting_property_diffs { + ($macro_name:ident) => { + $macro_name! { + Size: f32 = 0.0, + Weight: i32 = 0, + Width: i32 = 0, + SdfWeight: f32 = 0.0, + } + }; +} + +macro_rules! define_property_diffs { + ($($field:ident : $tp:ty = $def:expr),* $(,)?) => {paste! { + + $(def_unit!{[<$field Diff>]($tp) = $def})* + + /// A diff for a property. It is used to modify a property in a given range. A struct is used + /// instead of a closure to better fit the FRP-model. + #[allow(missing_docs)] + #[derive(Clone, Copy, Debug, From)] + pub enum PropertyDiff { + $($field([<$field Diff>])),* + } + + impl From for PropertyTag { + fn from(t: PropertyDiff) -> Self { + match t { + $(PropertyDiff::$field(_) => PropertyTag::$field),* + } + } + } + + impl Formatting { + /// Applies the property diff to the given property. + pub fn mod_property(&mut self, range:Range, property: PropertyDiff) { + match property { + $(PropertyDiff::$field(t) => { + self.[<$field:snake:lower>].modify_resolved(range, |p| p.apply_diff(t)) + })* + } + } + } + }} +} + +with_formatting_property_diffs!(define_property_diffs); + +/// Apply the property diff. +#[allow(missing_docs)] +pub trait PropertyDiffApply { + fn apply_diff(&self, diff: Diff) -> Self; +} + +impl PropertyDiffApply for Size { + fn apply_diff(&self, diff: SizeDiff) -> Self { + let value = self.value + diff.value; + let value = if value < 0.0 { 0.0 } else { value }; + Size { value } + } +} + +impl PropertyDiffApply for Weight { + fn apply_diff(&self, diff: WeightDiff) -> Self { + Weight::from((self.to_number() as i32 + diff.value).max(0) as u16) + } +} + +impl PropertyDiffApply for Width { + fn apply_diff(&self, diff: WidthDiff) -> Self { + match self.to_number() as i32 + diff.value { + 1 => Width::UltraCondensed, + 2 => Width::ExtraCondensed, + 3 => Width::Condensed, + 4 => Width::SemiCondensed, + 5 => Width::Normal, + 6 => Width::SemiExpanded, + 7 => Width::Expanded, + 8 => Width::ExtraExpanded, + 9 => Width::UltraExpanded, + _ => Width::Normal, + } + } +} + +impl PropertyDiffApply for SdfWeight { + fn apply_diff(&self, diff: SdfWeightDiff) -> Self { + SdfWeight(self.value + diff.value) + } +} + + + +// ====================== +// === FormattingCell === +// ====================== + +/// Internally mutable version of `Formatting`. +#[derive(Clone, Debug, Default)] +pub struct FormattingCell { + cell: RefCell, +} + +impl FormattingCell { + /// Constructor. + pub fn new() -> Self { + default() + } + + /// Getter of the current style value. + pub fn get(&self) -> Formatting { + self.cell.borrow().clone() + } + + /// Setter of the style value. + pub fn set(&self, style: Formatting) { + *self.cell.borrow_mut() = style; + } + + /// Return style narrowed to the given range. + pub fn sub(&self, range: Range) -> Formatting { + self.cell.borrow().sub(range) + } + + /// Replace the provided `range` with the `None` value (default), repeated over `len` + /// bytes. Use with care, as it's very easy to provide incorrect byte size value, which + /// may result in styles being applied to parts of grapheme clusters only. + pub fn set_resize_with_default(&self, range: Range, len: Byte) { + self.cell.borrow_mut().set_resize_with_default(range, len) + } + + /// Set the property for the given range. + pub fn set_property(&self, range: Range, property: Property) { + self.cell.borrow_mut().set_property(range, property) + } + + /// Modify the property in the given range. + pub fn mod_property(&self, range: Range, property: PropertyDiff) { + self.cell.borrow_mut().mod_property(range, property) + } + + /// Set property default value in the given range. + pub fn set_property_default(&self, property: ResolvedProperty) { + self.cell.borrow_mut().set_property_default(property) + } + + /// Resolve property by applying default values if needed. + pub fn resolve_property(&self, property: Property) -> ResolvedProperty { + self.cell.borrow().resolve_property(property) + } +} + +macro_rules! define_formatting_cell_getters { + ($($field:ident : $field_type:ty),* $(,)?) => {paste! { + impl FormattingCell { + $( + /// Property getter. + pub fn [<$field:snake:lower>](&self) -> Spanned<$field_type> { + self.cell.borrow().[<$field:snake:lower>].clone() + } + )* + } + }}; +} + +with_formatting_properties! { define_formatting_cell_getters } diff --git a/lib/rust/ensogl/component/text/src/buffer/index.rs b/lib/rust/ensogl/component/text/src/buffer/index.rs new file mode 100644 index 000000000000..60723718924a --- /dev/null +++ b/lib/rust/ensogl/component/text/src/buffer/index.rs @@ -0,0 +1,56 @@ +//! Indexing units definitions. + +use crate::prelude::*; +use enso_text::unit::*; + + + +enso_text::define_line_unit!(ViewLine); + +impl Add for ViewLine { + type Output = ViewLine; + fn add(self, line_diff: LineDiff) -> Self::Output { + if -line_diff.value > self.value as i32 { + error!("Adding of LineDiff to ViewLine resulted in negative value."); + ViewLine(0) + } else { + ViewLine((self.value as i32 + line_diff.value) as usize) + } + } +} + +impl Add for LineDiff { + type Output = ViewLine; + fn add(self, line: ViewLine) -> Self::Output { + line + self + } +} + +impl Add<&LineDiff> for ViewLine { + type Output = ViewLine; + fn add(self, line_diff: &LineDiff) -> Self::Output { + self + *line_diff + } +} + +impl Add for &ViewLine { + type Output = ViewLine; + fn add(self, line_diff: LineDiff) -> Self::Output { + *self + line_diff + } +} + +impl Add<&LineDiff> for &ViewLine { + type Output = ViewLine; + fn add(self, line_diff: &LineDiff) -> Self::Output { + *self + *line_diff + } +} + + +// ==================== +// === ViewLocation === +// ==================== + +/// Alias for [`Location`] with [`ViewLine`] as line index. +pub type ViewLocation = Location; diff --git a/lib/rust/ensogl/component/text/src/buffer/view/movement.rs b/lib/rust/ensogl/component/text/src/buffer/movement.rs similarity index 57% rename from lib/rust/ensogl/component/text/src/buffer/view/movement.rs rename to lib/rust/ensogl/component/text/src/buffer/movement.rs index 26392b35dc72..bab09a3e5c0b 100644 --- a/lib/rust/ensogl/component/text/src/buffer/view/movement.rs +++ b/lib/rust/ensogl/component/text/src/buffer/movement.rs @@ -1,9 +1,10 @@ //! Text cursor transform implementation. -use crate::buffer::view::*; +use crate::buffer::*; -use crate::buffer::view::selection; -use crate::buffer::view::word::WordCursor; +use crate::buffer::rope::word::WordCursor; +use crate::buffer::selection; +use crate::buffer::selection::Selection; @@ -52,7 +53,7 @@ pub enum Transform { // === Transform Handling === // ========================== -impl ViewBuffer { +impl BufferModel { /// Convert selection to cursor location after a vertical movement. fn vertical_motion_selection_to_location( &self, @@ -74,26 +75,21 @@ impl ViewBuffer { fn vertical_motion( &self, selection: Selection, - line_delta: Line, + line_diff: LineDiff, modify: bool, ) -> selection::Shape { - let move_up = line_delta < 0.line(); + let move_up = line_diff < LineDiff(0); let location = self.vertical_motion_selection_to_location(selection, move_up, modify); - let min_line = 0.line(); - let max_line = self.last_line_index(); - let border_step = if move_up { (-1).line() } else { 1.line() }; - let snap_top = location.line < min_line; - let snap_bottom = location.line > max_line; - let next_line = max_line + border_step; - let bottom = location.line + line_delta; - let line = if snap_top { - border_step - } else if snap_bottom { - next_line + let first_line = Line(0); + let last_line = self.last_line_index(); + let desired_line = location.line.to_diff() + line_diff; + let tgt_location = if desired_line < first_line.to_diff() { + Location { line: first_line, offset: Column(0) } + } else if desired_line > last_line.to_diff() { + Location { line: last_line, offset: self.last_line_last_column() } } else { - bottom + location.with_line(desired_line.to_line()) }; - let tgt_location = location.with_line(line); selection::Shape(selection.start, tgt_location) } @@ -102,27 +98,18 @@ impl ViewBuffer { /// If `modify` is `true`, the selections are modified, otherwise the results of individual /// region movements become cursors. Modify is often mapped to the `shift` button in text /// editors. - pub fn moved_selection(&self, transform: Transform, modify: bool) -> selection::Group { - let mut result = selection::Group::new(); - for &selection in self.selection.borrow().iter() { - let new_selection = self.moved_selection_region(transform, selection, modify); - result.merge(new_selection); - } - result - } - - /// Location of the previous grapheme cluster if any. - pub fn prev_grapheme_location(&self, location: Location) -> Option { - let offset = self.byte_offset_of_location_snapped(location); - let prev_offset = self.prev_grapheme_offset(offset); - prev_offset.map(|off| self.offset_to_location(off)) - } - - /// Location of the next grapheme cluster if any. - pub fn next_grapheme_location(&self, location: Location) -> Option { - let offset = self.byte_offset_of_location_snapped(location); - let next_offset = self.next_grapheme_offset(offset); - next_offset.map(|off| self.offset_to_location(off)) + pub fn moved_selection(&self, movement: Option, modify: bool) -> selection::Group { + movement + .map(|transform| { + let mut result = selection::Group::new(); + let selections = self.selection.borrow().clone(); + for &selection in selections.iter() { + let new_selection = self.moved_selection_region(transform, selection, modify); + result.merge(new_selection); + } + result + }) + .unwrap_or_default() } /// Compute the result of movement on one selection region. @@ -139,95 +126,85 @@ impl ViewBuffer { let text = &self.text(); let shape = selection::Shape; let shape: selection::Shape = match transform { - Transform::All => shape(default(), self.offset_to_location(text.byte_size())), - - Transform::Up => self.vertical_motion(selection, (-1).line(), modify), - - Transform::Down => self.vertical_motion(selection, 1.line(), modify), - + Transform::All => shape(default(), self.last_line_last_location()), + Transform::Up => self.vertical_motion(selection, LineDiff(-1), modify), + Transform::Down => self.vertical_motion(selection, LineDiff(1), modify), Transform::StartOfDocument => shape(selection.start, default()), - - Transform::EndOfDocument => - shape(selection.start, self.offset_to_location(text.byte_size())), - + Transform::EndOfDocument => { + let end = Location::from_in_context_snapped(self, text.last_byte_index()); + shape(selection.start, end) + } Transform::Left => { - let def = shape(selection.start, default()); let do_move = selection.is_cursor() || modify; if do_move { - self.prev_grapheme_location(selection.end) - .map(|t| shape(selection.start, t)) - .unwrap_or(def) + shape(selection.start, self.prev_column(selection.end)) } else { shape(selection.start, selection.min()) } } - Transform::Right => { - let def = shape(selection.start, selection.end); let do_move = selection.is_cursor() || modify; if do_move { - self.next_grapheme_location(selection.end) - .map(|t| shape(selection.start, t)) - .unwrap_or(def) + shape(selection.start, self.next_column(selection.end)) } else { shape(selection.start, selection.max()) } } Transform::LeftSelectionBorder => shape(selection.start, selection.min()), - Transform::RightSelectionBorder => shape(selection.start, selection.max()), Transform::LeftOfLine => { - let end = Location(selection.end.line, 0.column()); + let end = Location(selection.end.line, Column(0)); shape(selection.start, end) } Transform::RightOfLine => { let line = selection.end.line; - let text_byte_size = text.byte_size(); + let text_byte_size = text.last_byte_index(); let is_last_line = line == self.last_line_index(); - let next_line_offset_opt = self.byte_offset_of_line_index(line + 1.line()); - let next_line_offset = next_line_offset_opt.unwrap_or_else(|_| text.byte_size()); + let next_line_offset_opt = self.line_offset(line + Line(1)); + let next_line_offset = + next_line_offset_opt.unwrap_or_else(|_| text.last_byte_index()); let offset = if is_last_line { text_byte_size } else { text.prev_grapheme_offset(next_line_offset).unwrap_or(text_byte_size) }; - let end = self.offset_to_location(offset); + let end = Location::from_in_context_snapped(self, offset); shape(selection.start, end) } Transform::LeftWord => { - let end_offset = self.byte_offset_of_location_snapped(selection.end); + let end_offset = Byte::from_in_context_snapped(self, selection.end); let mut word_cursor = WordCursor::new(text, end_offset); - let offset = word_cursor.prev_boundary().unwrap_or_else(|| 0.bytes()); - let end = self.offset_to_location(offset); + let offset = word_cursor.prev_boundary().unwrap_or_else(|| 0.byte()); + let end = Location::from_in_context_snapped(self, offset); shape(selection.start, end) } Transform::RightWord => { - let end_offset = self.byte_offset_of_location_snapped(selection.end); + let end_offset = Byte::from_in_context_snapped(self, selection.end); let mut word_cursor = WordCursor::new(text, end_offset); - let offset = word_cursor.next_boundary().unwrap_or_else(|| text.byte_size()); - let end = self.offset_to_location(offset); + let offset = word_cursor.next_boundary().unwrap_or_else(|| text.last_byte_index()); + let end = Location::from_in_context_snapped(self, offset); shape(selection.start, end) } Transform::Word => { - let end_offset = self.byte_offset_of_location_snapped(selection.end); + let end_offset = Byte::from_in_context_snapped(self, selection.end); let mut word_cursor = WordCursor::new(text, end_offset); let offsets = word_cursor.select_word(); - let start = self.offset_to_location(offsets.0); - let end = self.offset_to_location(offsets.1); + let start = Location::from_in_context_snapped(self, offsets.0); + let end = Location::from_in_context_snapped(self, offsets.1); shape(start, end) } Transform::Line => { - let start_offset = self.byte_offset_of_line_index_snapped(selection.start.line); - let end_offset = self.end_byte_offset_of_line_index_snapped(selection.end.line); - let start = self.offset_to_location(start_offset); - let end = self.offset_to_location(end_offset); + let start_offset = self.line_offset_snapped(selection.start.line); + let end_offset = self.line_end_offset_snapped(selection.end.line); + let start = Location::from_in_context_snapped(self, start_offset); + let end = Location::from_in_context_snapped(self, end_offset); shape(start, end) } }; diff --git a/lib/rust/ensogl/component/text/src/buffer/rope.rs b/lib/rust/ensogl/component/text/src/buffer/rope.rs new file mode 100644 index 000000000000..ae452b6b1645 --- /dev/null +++ b/lib/rust/ensogl/component/text/src/buffer/rope.rs @@ -0,0 +1,10 @@ +//! Rope is a fast string modification structure. This crate contains its representation with +//! associated formatting and handy operations. + + +// ============== +// === Export === +// ============== + +pub mod formatted; +pub mod word; diff --git a/lib/rust/ensogl/component/text/src/buffer/rope/formatted.rs b/lib/rust/ensogl/component/text/src/buffer/rope/formatted.rs new file mode 100644 index 000000000000..400e6e77e792 --- /dev/null +++ b/lib/rust/ensogl/component/text/src/buffer/rope/formatted.rs @@ -0,0 +1,84 @@ +//! A rope (efficient text representation) with formatting information. + +use crate::prelude::*; + +use crate::buffer::formatting::Formatting; +use crate::buffer::formatting::FormattingCell; + +use enso_text::Rope; +use enso_text::RopeCell; + + + +// ===================== +// === FormattedRope === +// ===================== + +/// A rope (efficient text representation) with formatting information. +#[derive(Clone, CloneRef, Debug, Default, Deref)] +pub struct FormattedRope { + data: Rc, +} + +impl FormattedRope { + /// Constructor. + pub fn new() -> Self { + default() + } + + /// Replace the content of the buffer with the provided text. + pub fn replace(&self, range: impl enso_text::RangeBounds, text: impl Into) { + let text = text.into(); + let range = self.crop_byte_range(range); + let size = text.last_byte_index(); + self.text.replace(range, text); + self.formatting.set_resize_with_default(range, size); + } +} + + + +// ========================= +// === FormattedRopeData === +// ========================= + +/// Internal data of `FormattedRope`. +#[derive(Debug, Default, Deref)] +pub struct FormattedRopeData { + #[deref] + pub(crate) text: RopeCell, + pub(crate) formatting: FormattingCell, +} + +impl FormattedRopeData { + /// Constructor. + pub fn new() -> Self { + default() + } + + /// Rope getter. + pub fn text(&self) -> Rope { + self.text.get() + } + + /// Rope setter. + pub fn set_text(&self, text: impl Into) { + self.text.set(text); + } + + /// Formatting getter. + pub fn style(&self) -> Formatting { + self.formatting.get() + } + + /// Formatting setter. + pub fn set_style(&self, style: Formatting) { + self.formatting.set(style) + } + + /// Query style information for the provided range. + pub fn sub_style(&self, range: impl enso_text::RangeBounds) -> Formatting { + let range = self.crop_byte_range(range); + self.formatting.sub(range) + } +} diff --git a/lib/rust/ensogl/component/text/src/buffer/view/word.rs b/lib/rust/ensogl/component/text/src/buffer/rope/word.rs similarity index 93% rename from lib/rust/ensogl/component/text/src/buffer/view/word.rs rename to lib/rust/ensogl/component/text/src/buffer/rope/word.rs index b60d7e1817bb..1d5e190f936b 100644 --- a/lib/rust/ensogl/component/text/src/buffer/view/word.rs +++ b/lib/rust/ensogl/component/text/src/buffer/rope/word.rs @@ -1,7 +1,7 @@ //! Implementation of a cursor allowing word-based traversal. use crate::prelude::*; -use enso_text::unit::*; +use enso_text::index::*; use enso_text::rope; @@ -18,13 +18,13 @@ pub struct WordCursor<'a> { impl<'a> WordCursor<'a> { /// Constructor. - pub fn new(text: &'a rope::Rope, pos: Bytes) -> WordCursor<'a> { - let cursor = rope::Cursor::new(text, pos.value as usize); + pub fn new(text: &'a rope::XiRope, pos: Byte) -> WordCursor<'a> { + let cursor = rope::Cursor::new(text, pos.value); WordCursor { cursor } } /// Get previous boundary, and set the cursor at the boundary found. - pub fn prev_boundary(&mut self) -> Option { + pub fn prev_boundary(&mut self) -> Option { self.prev_codepoint_class().map(|mut cls| { let mut candidate = self.cursor.pos(); while let Some(prev_cls) = self.prev_codepoint_class() { @@ -40,7 +40,7 @@ impl<'a> WordCursor<'a> { } /// Get next boundary, and set the cursor at the boundary found. - pub fn next_boundary(&mut self) -> Option { + pub fn next_boundary(&mut self) -> Option { self.next_codepoint_class().map(|mut cls| { let mut candidate = self.cursor.pos(); while let Some(next_cls) = self.next_codepoint_class() { @@ -57,10 +57,10 @@ impl<'a> WordCursor<'a> { /// Return the selection for the word containing the current cursor. The cursor is moved to the /// end of that selection. - pub fn select_word(&mut self) -> (Bytes, Bytes) { + pub fn select_word(&mut self) -> (Byte, Byte) { let initial = self.cursor.pos(); let init_cls_after = self.next_codepoint_class(); - self.cursor.set(initial); // FIXME ??? + self.cursor.set(initial); let init_cls_before = self.prev_codepoint_class(); let mut start = initial; let init_boundary_opt = init_cls_before.zip_with(init_cls_after, Boundary::new_initial); @@ -210,8 +210,7 @@ enum CharClass { fn char_class(codepoint: char) -> CharClass { if codepoint <= ' ' { - // TODO:deal with \r - if codepoint == '\n' { + if codepoint == '\n' || codepoint == '\r' { return CharClass::Lf; } return CharClass::Space; diff --git a/lib/rust/ensogl/component/text/src/buffer/view/selection.rs b/lib/rust/ensogl/component/text/src/buffer/selection.rs similarity index 83% rename from lib/rust/ensogl/component/text/src/buffer/view/selection.rs rename to lib/rust/ensogl/component/text/src/buffer/selection.rs index 5feb12679ee1..03051e8c2091 100644 --- a/lib/rust/ensogl/component/text/src/buffer/view/selection.rs +++ b/lib/rust/ensogl/component/text/src/buffer/selection.rs @@ -11,17 +11,30 @@ use enso_text::Range; // === Boundary === // ================ -/// Selection boundary data type. In most cases it's either `Location` or `Bytes`. +/// Selection boundary data type. In most cases it's either `Location` or `Byte`. pub trait Boundary = Copy + Ord + Eq; +// ========== +// === Id === +// ========== + +/// Selection ID. +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Display, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id { + pub value: usize, +} + + + // ============= // === Shape === // ============= /// Text selection shape. In case the `start` and `end` offsets are equal, the selection is -/// interpreted as a cursor. Please note that the start of the selection is not always smaller then +/// interpreted as a cursor. Please note that the start of the selection is not always smaller than /// its end. If the selection was dragged from right to left, the start byte offset will be bigger /// than the end. Use the `min` and `max` methods to discover the edges. #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] @@ -33,16 +46,18 @@ pub struct Shape { /// Constructor. #[allow(non_snake_case)] -pub fn Shape(start: T, end: T) -> Shape { +pub fn Shape(start: T, end: T) -> Shape { Shape::new(start, end) } -impl Shape { +impl Shape { /// Constructor. pub fn new(start: T, end: T) -> Self { Self { start, end } } +} +impl Shape { /// Cursor constructor. pub fn new_cursor(start: T) -> Self { let end = start; @@ -54,6 +69,22 @@ impl Shape { (self.min()..self.max()).into() } + /// Normalized version of selection where the start is always smaller than the end. + pub fn normalized(self) -> Self { + if self.start <= self.end { + self + } else { + self.reversed() + } + } + + /// Reversed version of selection. + pub fn reversed(self) -> Self { + let start = self.end; + let end = self.start; + Self { start, end } + } + /// Gets the earliest offset within the selection, ie the minimum of both edges. pub fn min(self) -> T { std::cmp::min(self.start, self.end) @@ -85,8 +116,10 @@ impl Shape { } /// Map both start and end values. - pub fn map(&self, f: impl Fn(T) -> T) -> Self { - self.with_start(f(self.start)).with_end(f(self.end)) + pub fn map(self, f: impl Fn(T) -> S) -> Shape { + let start = f(self.start); + let end = f(self.end); + Shape { start, end } } /// Produce cursor by snapping the end edge to the start one. @@ -115,57 +148,55 @@ impl Shape { // === Selection === // ================= -/// Text selection. It is a text selection `Shape` bundled with an `id` information, which is used -/// by graphical interface to track and animate the movement of the selections. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +/// Text selection. It is a text selection [`Shape`] bundled with an [`Id`] information, which is +/// used by graphical interface to track and animate the movement of the selections. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Deref, DerefMut)] #[allow(missing_docs)] pub struct Selection { + #[deref] + #[deref_mut] pub shape: Shape, - pub id: usize, -} - -impl Deref for Selection { - type Target = Shape; - fn deref(&self) -> &Self::Target { - &self.shape - } -} - -impl DerefMut for Selection { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.shape - } + pub id: Id, } /// Constructor. #[allow(non_snake_case)] -pub fn Selection(start: T, end: T, id: usize) -> Selection { +pub fn Selection(start: T, end: T, id: Id) -> Selection { Selection::new(start, end, id) } -impl Selection { +impl Selection { /// Constructor. - pub fn new(start: T, end: T, id: usize) -> Self { + pub fn new(start: T, end: T, id: Id) -> Self { let shape = Shape::new(start, end); Self { shape, id } } +} +impl Selection { /// Cursor constructor. - pub fn new_cursor(offset: T, id: usize) -> Self { + pub fn new_cursor(offset: T, id: Id) -> Self { let shape = Shape::new_cursor(offset); Self { shape, id } } /// Replace the shape value. - pub fn with_shape(&self, shape: Shape) -> Self { - Self { shape, ..*self } + pub fn with_shape(self, shape: Shape) -> Selection { + let id = self.id; + Selection { shape, id } } /// Map the shape value. - pub fn map_shape(&self, f: impl FnOnce(Shape) -> Shape) -> Self { + pub fn map_shape(self, f: impl FnOnce(Shape) -> Shape) -> Selection { self.with_shape(f(self.shape)) } + /// Map the id value. + pub fn map_id(self, f: impl FnOnce(Id) -> Id) -> Self { + let id = f(self.id); + Self { id, ..self } + } + /// Replace the start value. pub fn with_start(&self, start: T) -> Self { self.map_shape(|s| s.with_start(start)) @@ -176,6 +207,11 @@ impl Selection { self.map_shape(|s| s.with_end(end)) } + /// Replace the location value. + pub fn with_location(self, location: T) -> Self { + self.with_start(location).with_end(location) + } + /// Map the start value. pub fn map_start(&self, f: impl FnOnce(T) -> T) -> Self { self.map_shape(|s| s.map_start(f)) @@ -187,7 +223,7 @@ impl Selection { } /// Map both start and end values. - pub fn map(&self, f: impl Fn(T) -> T) -> Self { + pub fn map(&self, f: impl Fn(T) -> S) -> Selection { self.map_shape(|s| s.map(f)) } @@ -228,8 +264,8 @@ impl Selection { /// A set of zero or more selections. /// -/// The selections are kept in sorted order in order to maintain a good performance in algorithms. -/// It is used in many places, including selection merging process. +/// The selections are kept in sorted order to maintain a good performance in algorithms. It is used +/// in many places, including selection merging process. #[derive(Clone, Debug, Default)] pub struct Group { sorted_selections: Vec, diff --git a/lib/rust/ensogl/component/text/src/buffer/style.rs b/lib/rust/ensogl/component/text/src/buffer/style.rs deleted file mode 100644 index 5da7866ac8c8..000000000000 --- a/lib/rust/ensogl/component/text/src/buffer/style.rs +++ /dev/null @@ -1,305 +0,0 @@ -//! Text style definition (color, bold, italics, etc.). - -use super::*; - -use crate::buffer::Range; -use crate::data::color; - - - -// ============== -// === Macros === -// ============== - -/// Defines a newtype for a primitive style property, like `Bold`. See usage below to learn more. -macro_rules! def_style_property { - ($name:ident($field_type:ty)) => { - /// Style property. - #[derive(Clone, Copy, Debug, From, PartialEq, PartialOrd)] - #[allow(missing_docs)] - pub struct $name { - /// The raw, weakly typed value. - pub raw: $field_type, - } - - impl $name { - /// Constructor. - pub const fn new(raw: $field_type) -> $name { - $name { raw } - } - } - - /// Smart constructor. - #[allow(non_snake_case)] - pub fn $name(raw: $field_type) -> $name { - $name { raw } - } - }; -} - - -/// Defines struct containing all styles information. Also defines many utils, like iterator for it. -/// See the usage below to learn more. -macro_rules! define_styles { - ($($field:ident : $field_type:ty),* $(,)?) => { - - // === StyleValue === - - /// The value of a style at some point in the buffer. - #[derive(Clone,Copy,Debug,Default)] - #[allow(missing_docs)] - pub struct StyleValue { - $(pub $field : $field_type),* - } - - #[derive(Debug)] - struct StyleIteratorComponents { - $($field : std::vec::IntoIter<(Range,$field_type)>),* - } - - - // === Iterator === - - #[derive(Debug,Default)] - struct StyleIteratorValue { - $($field : Option<(Range,$field_type)>),* - } - - impl Iterator for StyleIterator { - type Item = StyleValue; - fn next(&mut self) -> Option { - $( - if self.value.$field.map(|t| self.offset < t.0.end) != Some(true) { - self.value.$field = self.component.$field.next() - } - let $field = self.value.$field?.1; - )* - self.offset += 1.bytes(); - Some(StyleValue {$($field),*}) - } - } - - - // === Style === - - /// Definition of possible text styles, like `color`, or `bold`. Each style is encoded as - /// `Property` for some spans in the buffer. - #[derive(Clone,Debug,Default)] - #[allow(missing_docs)] - pub struct Style { - $(pub $field : Property<$field_type>),* - } - - impl Style { - /// Constructor. - pub fn new() -> Self { - Self::default() - } - - /// Return new style narrowed to the given range. - pub fn sub(&self, range:Range) -> Self { - $(let $field = self.$field.sub(range);)* - Self {$($field),*} - } - - /// Replace the provided `range` with the `None` value (default), repeated over `len` - /// bytes. Use with care, as it's very easy to provide incorrect byte size value, which - /// may result in styles being applied to parts of grapheme clusters only. - pub fn set_resize_with_default(&mut self, range:Range, len:Bytes) { - $(self.$field.replace_resize(range,len,None);)* - } - - /// Iterate over style values for subsequent bytes of the buffer. - pub fn iter(&self) -> StyleIterator { - $(let $field = self.$field.to_vector().into_iter();)* - StyleIterator::new(StyleIteratorComponents {$($field),*}) - } - } - - $( - impl Setter> for Buffer { - fn replace(&self, range:impl enso_text::RangeBounds, data:Option<$field_type>) { - let range = self.crop_byte_range(range); - self.data.style.cell.borrow_mut().$field.replace_resize(range,range.size(),data) - } - } - - impl Setter<$field_type> for Buffer { - fn replace(&self, range:impl enso_text::RangeBounds, data:$field_type) { - self.replace(range,Some(data)) - } - } - - impl DefaultSetter<$field_type> for Buffer { - fn set_default(&self, data:$field_type) { - self.style.cell.borrow_mut().$field.default = data; - } - } - )* - }; -} - -/// Byte-based iterator for the `Style`. -#[derive(Debug)] -pub struct StyleIterator { - offset: Bytes, - value: StyleIteratorValue, - component: StyleIteratorComponents, -} - -impl StyleIterator { - fn new(component: StyleIteratorComponents) -> Self { - let offset = default(); - let value = default(); - Self { offset, value, component } - } - - /// Drop the given amount of bytes. - pub fn drop(&mut self, bytes: Bytes) { - for _ in 0..bytes.value { - self.next(); - } - } -} - - - -// ================ -// === Property === -// ================ - -/// Style property, like `color` or `bold`. Records text spans it is applied to and a default value -/// used for places not covered by spans. Please note that the default value can be changed at -/// runtime, which is useful when defining text field which should use white letters by default -/// (when new letter is written). -#[derive(Clone, Debug, Default)] -#[allow(missing_docs)] -pub struct Property { - pub spans: enso_text::Spans>, - default: T, -} - -impl Property { - /// Return new property narrowed to the given range. - pub fn sub(&self, range: Range) -> Self { - let spans = self.spans.sub(range); - let default = self.default.clone(); - Self { spans, default } - } - - /// Convert the property to a vector of spans. - pub fn to_vector(&self) -> Vec<(Range, T)> { - let spans_iter = self.spans.to_vector().into_iter(); - spans_iter.map(|t| (t.0, t.1.unwrap_or_else(|| self.default.clone()))).collect_vec() - } - - /// The default value of this property. - pub fn default(&self) -> &T { - &self.default - } -} - - -// === Deref === - -impl Deref for Property { - type Target = enso_text::Spans>; - fn deref(&self) -> &Self::Target { - &self.spans - } -} - -impl DerefMut for Property { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.spans - } -} - - - -// ============= -// === Style === -// ============= - -def_style_property!(Size(f32)); -def_style_property!(Bold(bool)); -def_style_property!(Italic(bool)); -def_style_property!(Underline(bool)); -def_style_property!(SdfBold(f32)); - -impl Default for Size { - fn default() -> Self { - Self::new(12.0) - } -} -impl Default for Bold { - fn default() -> Self { - Self::new(false) - } -} -impl Default for Italic { - fn default() -> Self { - Self::new(false) - } -} -impl Default for Underline { - fn default() -> Self { - Self::new(false) - } -} - -impl Default for SdfBold { - fn default() -> Self { - Self::new(0.0) - } -} - -define_styles! { - size : Size, - color : color::Rgba, - bold : Bold, - italics : Italic, - underline : Underline, - sdf_bold : SdfBold, -} - - - -// ================= -// === StyleCell === -// ================= - -/// Internally mutable version of `Style`. -#[derive(Clone, Debug, Default)] -pub struct StyleCell { - cell: RefCell