diff --git a/editor/editor-ideas.md b/editor/editor-ideas.md index 74ba6a27d7..a07958c73e 100644 --- a/editor/editor-ideas.md +++ b/editor/editor-ideas.md @@ -60,6 +60,7 @@ These are potentially inspirational resources for the editor's design. * Automatically create all "arms" when pattern matching after entering `when var is` based on the type. - All `when ... is` should be updated if the type is changed, e.g. adding Indigo to the Color type should add an arm everywhere where `when color is` is used. * When a function is called like `foo(false)`, the name of the boolean argument should be shown automatically; `foo(`*is_active:*`false)`. This should be done for booleans and numbers. +* Suggest automatically creating a function if the compiler says it does not exist. ### Non-Code Related Inspiration diff --git a/editor/src/graphics/colors.rs b/editor/src/graphics/colors.rs index 89867b8e14..76450d809e 100644 --- a/editor/src/graphics/colors.rs +++ b/editor/src/graphics/colors.rs @@ -1,11 +1,14 @@ -pub const WHITE: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0); -pub const TXT_COLOR: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0); -pub const CODE_COLOR: (f32, f32, f32, f32) = (0.21, 0.55, 0.83, 1.0); -pub const CARET_COLOR: (f32, f32, f32, f32) = WHITE; -pub const SELECT_COLOR: (f32, f32, f32, f32) = (0.45, 0.61, 1.0, 1.0); -pub const BG_COLOR: (f32, f32, f32, f32) = (0.11, 0.11, 0.13, 1.0); -pub fn to_wgpu_color((r, g, b, a): (f32, f32, f32, f32)) -> wgpu::Color { +pub type ColorTup = (f32, f32, f32, f32); +pub const WHITE: ColorTup = (1.0, 1.0, 1.0, 1.0); +pub const BLACK: ColorTup = (0.0, 0.0, 0.0, 1.0); +pub const TXT_COLOR: ColorTup = (1.0, 1.0, 1.0, 1.0); +pub const CODE_COLOR: ColorTup = (0.21, 0.55, 0.83, 1.0); +pub const CARET_COLOR: ColorTup = WHITE; +pub const SELECT_COLOR: ColorTup = (0.45, 0.61, 1.0, 1.0); +pub const BG_COLOR: ColorTup = (0.11, 0.11, 0.13, 1.0); + +pub fn to_wgpu_color((r, g, b, a): ColorTup) -> wgpu::Color { wgpu::Color { r: r as f64, g: g as f64, @@ -14,6 +17,6 @@ pub fn to_wgpu_color((r, g, b, a): (f32, f32, f32, f32)) -> wgpu::Color { } } -pub fn to_slice((r, g, b, a): (f32, f32, f32, f32)) -> [f32; 4] { +pub fn to_slice((r, g, b, a): ColorTup) -> [f32; 4] { [r, g, b, a] } diff --git a/editor/src/graphics/mod.rs b/editor/src/graphics/mod.rs index 0eb7fcd6da..7acfe6a0d8 100644 --- a/editor/src/graphics/mod.rs +++ b/editor/src/graphics/mod.rs @@ -2,3 +2,4 @@ pub mod colors; pub mod lowlevel; pub mod primitives; pub mod style; +mod syntax_highlight; diff --git a/editor/src/graphics/primitives/text.rs b/editor/src/graphics/primitives/text.rs index 27479708a7..4878048a32 100644 --- a/editor/src/graphics/primitives/text.rs +++ b/editor/src/graphics/primitives/text.rs @@ -2,12 +2,15 @@ // by Benjamin Hansen, licensed under the MIT license use super::rect::Rect; -use crate::graphics::colors::{CODE_COLOR, WHITE}; +use crate::graphics::colors; +use colors::{CODE_COLOR, WHITE, ColorTup}; use crate::graphics::style::{CODE_FONT_SIZE, CODE_TXT_XY}; +use crate::graphics::syntax_highlight; use ab_glyph::{FontArc, Glyph, InvalidFont}; use cgmath::{Vector2, Vector4}; use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section}; + #[derive(Debug)] pub struct Text { pub position: Vector2, @@ -82,7 +85,28 @@ fn section_from_text( ) } -// returns glyphs per line +fn section_from_glyph_text( + text: Vec, + screen_position: (f32, f32), + area_bounds: (f32, f32), + layout: wgpu_glyph::Layout, +) -> wgpu_glyph::Section { + Section { + screen_position, + bounds: area_bounds, + layout, + text + } +} + +fn colored_text_to_glyph_text<'a>(text_tups: &'a [(String, ColorTup)]) -> Vec> { + text_tups.iter().map(|(word_string, color_tup)| { + wgpu_glyph::Text::new(&word_string) + .with_color(colors::to_slice(*color_tup)) + .with_scale(CODE_FONT_SIZE) + }).collect() +} + pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) { let layout = layout_from_text(text); @@ -91,6 +115,23 @@ pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) { glyph_brush.queue(section.clone()); } +pub fn queue_code_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) { + let layout = layout_from_text(text); + + let mut all_text_tups: Vec<(String, ColorTup)> = Vec::new(); + syntax_highlight::highlight_code(text, &mut all_text_tups); + let glyph_text_vec = colored_text_to_glyph_text(&all_text_tups); + + let section = section_from_glyph_text( + glyph_text_vec, + text.position.into(), + text.area_bounds.into(), + layout + ); + + glyph_brush.queue(section.clone()); +} + fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect { let position = glyph.glyph.position; let px_scale = glyph.glyph.scale; diff --git a/editor/src/graphics/style.rs b/editor/src/graphics/style.rs index c166fd3808..36b14da463 100644 --- a/editor/src/graphics/style.rs +++ b/editor/src/graphics/style.rs @@ -1,2 +1,2 @@ pub const CODE_FONT_SIZE: f32 = 30.0; -pub const CODE_TXT_XY: (f32, f32) = (30.0, 90.0); +pub const CODE_TXT_XY: (f32, f32) = (30.0, 30.0); diff --git a/editor/src/graphics/syntax_highlight.rs b/editor/src/graphics/syntax_highlight.rs new file mode 100644 index 0000000000..349afbfcc0 --- /dev/null +++ b/editor/src/graphics/syntax_highlight.rs @@ -0,0 +1,74 @@ + +use crate::graphics::primitives; +use crate::graphics::colors; +use colors::ColorTup; + + +//TODO optimize memory allocation +//TODO this is a demo function, the AST should be used for highlighting, see #904. +pub fn highlight_code(code_text: &primitives::text::Text, all_text_tups: &mut Vec<(String, ColorTup)>) { + let split_code = split_inclusive(&code_text.text); + + let mut active_color = colors::WHITE; + let mut same_type_str = String::new(); + + for token_seq in split_code { + let new_word_color = if token_seq.contains(&'\"'.to_string()) { + colors::CODE_COLOR + } else if token_seq.contains(&'='.to_string()) { + colors::BLACK + } else { + colors::WHITE + }; + + if new_word_color != active_color { + all_text_tups.push( + ( + same_type_str, + active_color + ) + ); + + active_color = new_word_color; + same_type_str = String::new(); + } + + same_type_str.push_str(&token_seq); + } + + if !same_type_str.is_empty() { + all_text_tups.push( + ( + same_type_str, + active_color + ) + ); + } +} + +//TODO use rust's split_inclusive once rust 1.50 is released +fn split_inclusive(code_str: &str) -> Vec { + let mut split_vec: Vec = Vec::new(); + let mut temp_str = String::new(); + let mut non_space_encountered = false; + + for token in code_str.chars() { + if token != ' ' && token != '\n' { + non_space_encountered = true; + temp_str.push(token); + } else if non_space_encountered { + split_vec.push(temp_str); + temp_str = String::new(); + temp_str.push(token); + non_space_encountered = false; + } else { + temp_str.push(token); + } + } + + if !temp_str.is_empty() { + split_vec.push(temp_str); + } + + split_vec +} \ No newline at end of file diff --git a/editor/src/lib.rs b/editor/src/lib.rs index fbfffd29bb..82fcc9a69a 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -19,7 +19,7 @@ use crate::graphics::lowlevel::buffer::create_rect_buffers; use crate::graphics::lowlevel::ortho::update_ortho_buffer; use crate::graphics::lowlevel::pipelines; use crate::graphics::primitives::text::{ - build_glyph_brush, example_code_glyph_rect, queue_text_draw, Text, + build_glyph_brush, example_code_glyph_rect, queue_text_draw, queue_code_text_draw, Text, }; use crate::graphics::style::CODE_FONT_SIZE; use crate::graphics::style::CODE_TXT_XY; @@ -392,7 +392,7 @@ fn queue_editor_text( queue_text_draw(&caret_pos_label, glyph_brush); - queue_text_draw(&code_text, glyph_brush); + queue_code_text_draw(&code_text, glyph_brush); } fn queue_no_file_text(