Fix location_of_text_end method (#3170)

This commit is contained in:
Adam Obuchowicz 2021-12-01 10:12:42 +01:00 committed by GitHub
parent 73abebf7aa
commit 850a16cb40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 559 deletions

View File

@ -1,412 +0,0 @@
#![allow(missing_docs)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unsafe_code)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
//! NOTE
//! This file is under a heavy development. It contains commented lines of code and some code may
//! be of poor quality. Expect drastic changes.
use crate::prelude::*;
use crate::graph_editor;
use crate::graph_editor::GraphEditor;
use crate::graph_editor::NodeProfilingStatus;
use crate::graph_editor::Type;
use crate::project;
use crate::root;
use crate::status_bar;
use enso_frp as frp;
use ensogl::application::Application;
use ensogl::display::navigation::navigator::Navigator;
use ensogl::display::object::ObjectOps;
use ensogl::display::shape::StyleWatch;
use ensogl::system::web;
use ensogl_hardcoded_theme as theme;
use ensogl_text as text;
use parser::Parser;
use wasm_bindgen::prelude::*;
const STUB_MODULE: &str = "from Base import all\n\nmain = IO.println \"Hello\"\n";
#[wasm_bindgen]
#[allow(dead_code)]
pub fn entry_point_interface() {
web::forward_panic_hook_to_console();
web::set_stack_trace_limit();
run_once_initialized(|| {
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
init(&app);
mem::forget(app);
});
}
fn _fence<T, Out>(network: &frp::Network, trigger: T) -> (frp::Stream, frp::Stream<bool>)
where
T: frp::HasOutput<Output = Out>,
T: Into<frp::Stream<Out>>,
Out: frp::Data, {
let trigger = trigger.into();
frp::extend! { network
def trigger_ = trigger.constant(());
def runner = source::<()>();
def switch = any_mut();
switch.attach(&trigger_);
def triggered = trigger.map(f_!(runner.emit(())));
switch.attach(&triggered);
def condition = switch.toggle_true();
}
let runner = runner.into();
(runner, condition)
}
// ==================
// === Mock Types ===
// ==================
/// Allows the creation of arbitrary unique `Type`s.
#[derive(Clone, Debug, Default)]
struct DummyTypeGenerator {
type_counter: u32,
}
impl DummyTypeGenerator {
fn get_dummy_type(&mut self) -> Type {
self.type_counter += 1;
Type::from(format!("dummy_type_{}", self.type_counter))
}
}
// ========================
// === Init Application ===
// ========================
fn init(app: &Application) {
let _bg = app.display.scene().style_sheet.var(theme::application::background);
let world = &app.display;
let scene = world.scene();
let camera = scene.camera();
let navigator = Navigator::new(scene, &camera);
app.views.register::<root::View>();
app.views.register::<project::View>();
app.views.register::<text::Area>();
app.views.register::<GraphEditor>();
let root_view = app.new_view::<root::View>();
root_view.switch_view_to_project();
let graph_editor = root_view.project().graph().clone_ref();
let code_editor = root_view.project().code_editor().clone_ref();
world.add_child(&root_view);
code_editor.text_area().set_content(STUB_MODULE.to_owned());
root_view.status_bar().add_event(status_bar::event::Label::new("This is a status message."));
graph_editor.debug_push_breadcrumb();
// === Nodes ===
let node1_id = graph_editor.add_node();
let node2_id = graph_editor.add_node();
let node3_id = graph_editor.add_node();
graph_editor.frp.set_node_position.emit((node1_id, Vector2(-150.0, 50.0)));
graph_editor.frp.set_node_position.emit((node2_id, Vector2(50.0, 50.0)));
graph_editor.frp.set_node_position.emit((node3_id, Vector2(150.0, 250.0)));
let expression_1 = expression_mock();
graph_editor.frp.set_node_expression.emit((node1_id, expression_1.clone()));
let comment_1 = String::from("Sample documentation comment.");
graph_editor.frp.set_node_comment.emit((node1_id, comment_1));
let expression_2 = expression_mock3();
graph_editor.frp.set_node_expression.emit((node2_id, expression_2.clone()));
let expression_3 = expression_mock2();
graph_editor.frp.set_node_expression.emit((node3_id, expression_3));
let kind = Immutable(graph_editor::component::node::error::Kind::Panic);
let message = Rc::new(Some("Runtime Error".to_owned()));
let propagated = Immutable(false);
let error = graph_editor::component::node::Error { kind, message, propagated };
graph_editor.frp.set_node_error_status.emit((node3_id, Some(error)));
let foo_node = graph_editor.add_node_below(node3_id);
graph_editor.set_node_expression.emit((foo_node, Expression::new_plain("foo")));
let baz_node = graph_editor.add_node_below(node3_id);
graph_editor.set_node_expression.emit((baz_node, Expression::new_plain("baz")));
let (_, baz_position) = graph_editor.node_position_set.value();
let styles = StyleWatch::new(&scene.style_sheet);
let min_spacing = styles.get_number(theme::graph_editor::minimal_x_spacing_for_new_nodes);
let gap_between_nodes = styles.get_number(theme::graph_editor::default_x_gap_between_nodes);
let gap_for_bar_node = min_spacing + gap_between_nodes + f32::EPSILON;
graph_editor.set_node_position((baz_node, baz_position + Vector2(gap_for_bar_node, 0.0)));
let bar_node = graph_editor.add_node_below(node3_id);
graph_editor.set_node_expression.emit((bar_node, Expression::new_plain("bar")));
// === Connections ===
let src = graph_editor::EdgeEndpoint::new(node1_id, span_tree::Crumbs::new(default()));
let tgt =
graph_editor::EdgeEndpoint::new(node2_id, span_tree::Crumbs::new(vec![0, 0, 0, 0, 1]));
graph_editor.frp.connect_nodes.emit((src, tgt));
// === VCS ===
let dummy_node_added_id = graph_editor.add_node();
let dummy_node_edited_id = graph_editor.add_node();
let dummy_node_unchanged_id = graph_editor.add_node();
graph_editor.frp.set_node_position.emit((dummy_node_added_id, Vector2(-450.0, 50.0)));
graph_editor.frp.set_node_position.emit((dummy_node_edited_id, Vector2(-450.0, 125.0)));
graph_editor.frp.set_node_position.emit((dummy_node_unchanged_id, Vector2(-450.0, 200.0)));
let dummy_node_added_expr = expression_mock_string("This node was added.");
let dummy_node_edited_expr = expression_mock_string("This node was edited.");
let dummy_node_unchanged_expr = expression_mock_string("This node was not changed.");
graph_editor.frp.set_node_expression.emit((dummy_node_added_id, dummy_node_added_expr));
graph_editor.frp.set_node_expression.emit((dummy_node_edited_id, dummy_node_edited_expr));
graph_editor.frp.set_node_expression.emit((dummy_node_unchanged_id, dummy_node_unchanged_expr));
graph_editor.frp.set_node_vcs_status.emit((dummy_node_added_id, Some(vcs::Status::Edited)));
graph_editor.frp.set_node_vcs_status.emit((dummy_node_edited_id, Some(vcs::Status::Added)));
graph_editor
.frp
.set_node_vcs_status
.emit((dummy_node_unchanged_id, Some(vcs::Status::Unchanged)));
// === Types (Port Coloring) ===
let mut dummy_type_generator = DummyTypeGenerator::default();
expression_1.input_span_tree.root_ref().leaf_iter().for_each(|node| {
if let Some(expr_id) = node.ast_id {
let dummy_type = Some(dummy_type_generator.get_dummy_type());
graph_editor.frp.set_expression_usage_type.emit((node1_id, expr_id, dummy_type));
}
});
expression_1.output_span_tree.root_ref().leaf_iter().for_each(|node| {
if let Some(expr_id) = node.ast_id {
let dummy_type = Some(dummy_type_generator.get_dummy_type());
graph_editor.frp.set_expression_usage_type.emit((node1_id, expr_id, dummy_type));
}
});
expression_2.input_span_tree.root_ref().leaf_iter().for_each(|node| {
if let Some(expr_id) = node.ast_id {
let dummy_type = Some(dummy_type_generator.get_dummy_type());
graph_editor.frp.set_expression_usage_type.emit((node2_id, expr_id, dummy_type));
}
});
expression_2.output_span_tree.root_ref().leaf_iter().for_each(|node| {
if let Some(expr_id) = node.ast_id {
let dummy_type = Some(dummy_type_generator.get_dummy_type());
graph_editor.frp.set_expression_usage_type.emit((node2_id, expr_id, dummy_type));
}
});
root_view.project().show_prompt();
// === Profiling ===
let node1_status = NodeProfilingStatus::Finished { duration: 500.0 };
graph_editor.set_node_profiling_status(node1_id, node1_status);
let node2_status = NodeProfilingStatus::Finished { duration: 1000.0 };
graph_editor.set_node_profiling_status(node2_id, node2_status);
let node3_status = NodeProfilingStatus::Finished { duration: 1500.0 };
graph_editor.set_node_profiling_status(node3_id, node3_status);
// let tgt_type = dummy_type_generator.get_dummy_type();
let mut was_rendered = false;
let mut loader_hidden = false;
let mut to_theme_switch = 100;
world
.on_frame(move |_| {
let _keep_alive = &navigator;
let _keep_alive = &root_view;
if to_theme_switch == 0 {
// println!("THEME SWITCH !!!");
// scene.style_sheet.set("application.background",color::Rgba(0.0,0.0,0.0,1.0));
// ensogl_hardcoded_theme::builtin::dark::enable(&app);
//
// println!(">>> {:?}", "lcha(1,0,0,1)".parse::<color::Lcha>());
}
to_theme_switch -= 1;
// if i > 0 { i -= 1 } else {
// println!("CHANGING TYPES OF EXPRESSIONS");
// i = 10000;
// graph_editor.frp.set_node_expression.emit((node2_id,expression_2.clone()));
// // expression_1.input_span_tree.root_ref().leaf_iter().for_each(|node|{
// // if let Some(expr_id) = node.ast_id {
// // let dummy_type = Some(tgt_type.clone());
// // // if j != 0 {
// // // j -= 1;
// // println!("----\n");
// //
// graph_editor.frp.set_expression_usage_type.emit((node1_id,expr_id,dummy_type));
// // // } else {
// // // println!(">> null change");
// // // j = 3;
// // //
// graph_editor.frp.set_expression_usage_type.emit((node1_id,expr_id,None));
// // //
// graph_editor.frp.set_expression_usage_type.emit((node1_id,expr_id,dummy_type));
// // // };
// // }
// // });
// }
// Temporary code removing the web-loader instance.
// To be changed in the future.
if was_rendered && !loader_hidden {
web::get_element_by_id("loader")
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
.ok();
loader_hidden = true;
}
was_rendered = true;
})
.forget();
}
// =============
// === Mocks ===
// =============
use crate::graph_editor::component::node::vcs;
use crate::graph_editor::component::node::Expression;
use ast::crumbs::PatternMatchCrumb::*;
use ast::crumbs::*;
use engine_protocol::prelude::Uuid;
use ensogl_text_msdf_sys::run_once_initialized;
use span_tree::traits::*;
pub fn expression_mock_string(label: &str) -> Expression {
let pattern = Some(label.to_string());
let code = format!("\"{}\"", label);
let parser = Parser::new_or_panic();
let parameters = vec![];
let ast = parser.parse_line_ast(&code).unwrap();
let invocation_info = span_tree::generate::context::CalledMethodInfo { parameters };
let ctx = span_tree::generate::MockContext::new_single(ast.id.unwrap(), invocation_info);
let output_span_tree = span_tree::SpanTree::default();
let input_span_tree = span_tree::SpanTree::new(&ast, &ctx).unwrap();
let whole_expression_id = default();
Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree }
}
pub fn expression_mock() -> Expression {
let pattern = Some("var1".to_string());
let code = "[1,2,3]".to_string();
let parser = Parser::new_or_panic();
let this_param =
span_tree::ArgumentInfo { name: Some("this".to_owned()), tp: Some("Text".to_owned()) };
let parameters = vec![this_param];
let ast = parser.parse_line_ast(&code).unwrap();
let invocation_info = span_tree::generate::context::CalledMethodInfo { parameters };
let ctx = span_tree::generate::MockContext::new_single(ast.id.unwrap(), invocation_info);
let output_span_tree = span_tree::SpanTree::default();
let input_span_tree = span_tree::SpanTree::new(&ast, &ctx).unwrap();
let whole_expression_id = default();
Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree }
}
pub fn expression_mock2() -> Expression {
let pattern = Some("var1".to_string());
let pattern_cr = vec![Seq { right: false }, Or, Or, Build];
let val = ast::crumbs::SegmentMatchCrumb::Body { val: pattern_cr };
let parens_cr = ast::crumbs::MatchCrumb::Segs { val, index: 0 };
let code = "make_maps size (distribution normal)".into();
let output_span_tree = span_tree::SpanTree::default();
let input_span_tree = span_tree::builder::TreeBuilder::new(36)
.add_child(0, 14, span_tree::node::Kind::Chained, PrefixCrumb::Func)
.add_child(0, 9, span_tree::node::Kind::Operation, PrefixCrumb::Func)
.set_ast_id(Uuid::new_v4())
.done()
.add_empty_child(10, span_tree::node::InsertionPointType::BeforeTarget)
.add_child(10, 4, span_tree::node::Kind::this().removable(), PrefixCrumb::Arg)
.set_ast_id(Uuid::new_v4())
.done()
.add_empty_child(14, span_tree::node::InsertionPointType::Append)
.set_ast_id(Uuid::new_v4())
.done()
.add_child(15, 21, span_tree::node::Kind::argument().removable(), PrefixCrumb::Arg)
.set_ast_id(Uuid::new_v4())
.add_child(1, 19, span_tree::node::Kind::argument(), parens_cr)
.set_ast_id(Uuid::new_v4())
.add_child(0, 12, span_tree::node::Kind::Operation, PrefixCrumb::Func)
.set_ast_id(Uuid::new_v4())
.done()
.add_empty_child(13, span_tree::node::InsertionPointType::BeforeTarget)
.add_child(13, 6, span_tree::node::Kind::this(), PrefixCrumb::Arg)
.set_ast_id(Uuid::new_v4())
.done()
.add_empty_child(19, span_tree::node::InsertionPointType::Append)
.done()
.done()
.add_empty_child(36, span_tree::node::InsertionPointType::Append)
.build();
let whole_expression_id = default();
Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree }
}
pub fn expression_mock3() -> Expression {
let pattern = Some("Vector x y z".to_string());
// let code = "image.blur ((foo bar) baz)".to_string();
let code = "Vector x y z".to_string();
let parser = Parser::new_or_panic();
let this_param =
span_tree::ArgumentInfo { name: Some("this".to_owned()), tp: Some("Image".to_owned()) };
let param0 = span_tree::ArgumentInfo {
name: Some("radius".to_owned()),
tp: Some("Number".to_owned()),
};
let param1 =
span_tree::ArgumentInfo { name: Some("name".to_owned()), tp: Some("Text".to_owned()) };
let param2 = span_tree::ArgumentInfo {
name: Some("area".to_owned()),
tp: Some("Vector Int".to_owned()),
};
let param3 = span_tree::ArgumentInfo {
name: Some("matrix".to_owned()),
tp: Some("Vector String".to_owned()),
};
let parameters = vec![this_param, param0, param1, param2, param3];
let ast = parser.parse_line_ast(&code).unwrap();
let invocation_info = span_tree::generate::context::CalledMethodInfo { parameters };
let ctx = span_tree::generate::MockContext::new_single(ast.id.unwrap(), invocation_info);
let output_span_tree = span_tree::SpanTree::new(&ast, &ctx).unwrap(); //span_tree::SpanTree::default();
let input_span_tree = span_tree::SpanTree::new(&ast, &ctx).unwrap();
let whole_expression_id = default();
Expression { pattern, code, whole_expression_id, input_span_tree, output_span_tree }
}

View File

@ -1,146 +0,0 @@
//! This is a visualization example scene which creates a sinusoidal graph.
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unsafe_code)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
use crate::graph_editor::component::visualization;
use crate::graph_editor::component::visualization::Data;
use crate::graph_editor::component::visualization::Registry;
use ensogl::application::Application;
use ensogl::display::navigation::navigator::Navigator;
use ensogl::system::web;
use ensogl_text_msdf_sys::run_once_initialized;
use js_sys::Math::sin;
use nalgebra::Vector2;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
fn generate_data(seconds: f64) -> Vec<Vector2<f32>> {
let mut data = Vec::new();
for x in 0..100 {
let x = x as f64 / 50.0 - 1.0;
let y = sin(x * std::f64::consts::PI + seconds);
data.push(Vector2::new(x as f32, y as f32));
}
data
}
fn constructor_graph() -> visualization::java_script::Definition {
let source = r#"
class Graph extends Visualization {
static inputType = "[[Float,Float,Float]]"
onDataReceived(data) {
if (!this.canvas) {
this.canvas = document.createElement("canvas");
this.canvas.setAttribute("tabindex","0");
this.context = this.canvas.getContext("2d");
this.dom.appendChild(this.canvas);
this.dom.addEventListener("keydown", function(e) {
console.log("pressed",e);
})
}
this.setPreprocessor(`x ->\n IO.println "Preprocessor set after receiving ${data}`)
let first = data.shift();
if (first) {
this.context.clearRect(0,0,this.canvas.width,this.canvas.height);
this.context.save();
this.context.scale(this.canvas.width/2,this.canvas.height/2);
this.context.translate(1,1);
this.context.lineWidth = 1/Math.min(this.canvas.width,this.canvas.height);
this.context.beginPath();
this.context.moveTo(first[0],first[1]);
data.forEach(data => {
this.context.lineTo(data[0],data[1]);
});
this.context.stroke();
this.context.restore();
this.context.beginPath();
this.context.moveTo(first[0],first[1]);
this.context.stroke();
}
}
setSize(size) {
if (this.canvas) {
this.canvas.width = size[0];
this.canvas.height = size[1];
}
}
}
return Graph
"#;
visualization::java_script::Definition::new_builtin(source).unwrap()
}
#[wasm_bindgen]
#[allow(dead_code, missing_docs)]
pub fn entry_point_visualization() {
web::forward_panic_hook_to_console();
web::set_stack_trace_limit();
run_once_initialized(|| {
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
init(&app);
std::mem::forget(app);
});
}
fn init(app: &Application) {
let world = &app.display;
let scene = world.scene();
let camera = scene.camera();
let navigator = Navigator::new(scene, &camera);
let registry = Registry::new();
registry.add(constructor_graph());
let vis_factories = registry.valid_sources(&"[[Float,Float,Float]]".into());
let vis_class = vis_factories
.iter()
.find(|class| &*class.signature.name == "Graph")
.expect("Couldn't find Graph class.");
let visualization = vis_class.new_instance(scene).expect("Couldn't create visualiser.");
visualization.activate.emit(());
let network = enso_frp::Network::new("VisualizationExample");
enso_frp::extend! { network
trace visualization.on_preprocessor_change;
};
std::mem::forget(network);
let mut was_rendered = false;
let mut loader_hidden = false;
world
.on_frame(move |time_info| {
let _keep_alive = &navigator;
let data = generate_data((time_info.local / 1000.0).into());
let data = Rc::new(data);
let content = serde_json::to_value(data).unwrap();
let data = Data::from(content);
visualization.send_data.emit(data);
// Temporary code removing the web-loader instance.
// To be changed in the future.
if was_rendered && !loader_hidden {
web::get_element_by_id("loader")
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
.ok();
loader_hidden = true;
}
was_rendered = true;
})
.forget();
}

View File

@ -457,7 +457,13 @@ impl Text {
/// The location of text end.
pub fn location_of_text_end(&self) -> Location {
let lines_count = self.lines(self.byte_range()).count();
if lines_count == 0 {
let last_char_off = self.rope.prev_codepoint_offset(self.len());
let last_char = last_char_off.map(|off| self.rope.slice_to_cow(off..));
let ends_with_eol = last_char.map_or(false, |ch| ch.starts_with('\n'));
if ends_with_eol {
let line: Line = lines_count.into();
Location(line, 0.column())
} else if lines_count == 0 {
default()
} else {
let line = ((lines_count - 1) as i32).line();
@ -949,3 +955,44 @@ impl<S: AsRef<str>> Change<Bytes, S> {
Ok(string)
}
}
// =============
// === Tests ===
// =============
#[cfg(test)]
mod test {
use super::*;
#[test]
fn location_of_text_end() {
struct Case {
text: &'static str,
expected: (usize, usize), // Line and column
}
impl Case {
fn run(&self) {
let text: Text = self.text.into();
let (exp_line, exp_column) = self.expected;
let expected = Location { line: exp_line.into(), column: exp_column.into() };
let result = text.location_of_text_end();
assert_eq!(result, expected, "Wrong text end location in case \"{}\"", text);
}
}
let cases = &[
Case { text: "", expected: (0, 0) },
Case { text: "single line", expected: (0, 11) },
Case { text: "single line with eol\n", expected: (1, 0) },
Case { text: "\nMany\nLines", expected: (2, 5) },
Case { text: "Many\nLines\nwith eol\n", expected: (3, 0) },
];
for case in cases {
case.run()
}
}
}