This commit is contained in:
Adrian Wozniak 2019-01-01 11:43:10 +01:00
commit 399b0b7144
No known key found for this signature in database
GPG Key ID: 3B441F7808FC43C7
20 changed files with 1232 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
.idea

288
Cargo.lock generated Normal file
View File

@ -0,0 +1,288 @@
[[package]]
name = "bit-set"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-vec"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c_vec"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "editor"
version = "0.1.0"
dependencies = [
"plex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lalr"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "plex"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lalr 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"redfa 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redfa"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sdl2"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"c_vec 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sdl2-sys"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum c_vec 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c32b15e95ce816aaf991a41420854e6ba772a2679a9296d906eab1114f1b4e9"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum lalr 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "106d7548f95adbe3019b4fc4954554d7b72535867aa9ce326d2f766b68958de7"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum plex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "59d79bd74b3d0be2619e58217e8b2b96372e3feca8426e5c560623205d70c146"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum redfa 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "29cc2771cc9f5fb0061cdedc05a37170254694dffec6b89920a6e767f08c4220"
"checksum sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74c2a98a354b20713b90cce70aef9e927e46110d1bc4ef728fd74e0d53eba60"
"checksum sdl2-sys 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c543ce8a6e33a30cb909612eeeb22e693848211a84558d5a00bb11e791b7ab7"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "editor"
version = "0.1.0"
authors = ["Adrian Wozniak <adrian.wozniak@ita-prog.pl>"]
edition = "2018"
[dependencies]
rand = "0.5"
plex = "*"
[dependencies.sdl2]
version = "0.31.0"
features = ["gfx", "image", "mixer", "ttf"]

Binary file not shown.

Binary file not shown.

44
src/app/app_state.rs Normal file
View File

@ -0,0 +1,44 @@
use std::rc::Rc;
use std::sync::Arc;
use std::boxed::Box;
use crate::app::{UpdateResult, WindowCanvas};
use crate::file::*;
use crate::renderer::Renderer;
use crate::file::editor_file::EditorFile;
pub struct AppState<'a> {
pub files: Vec<EditorFile<'a>>,
pub current_file: i16,
}
impl<'a> AppState<'a> {
pub fn new() -> Self {
Self {
files: vec![],
current_file: -1,
}
}
pub fn open_file(&mut self, file_path: String, renderer: &mut Renderer) {
use std::fs::read_to_string;
if let Ok(buffer) = read_to_string(&file_path) {
println!("read: {}\n{}", file_path, buffer);
let file = EditorFile::new(file_path.clone(), buffer, renderer);
self.current_file = self.files.len() as i16;
self.files.push(file);
};
}
pub fn update(&mut self, ticks: i32) -> UpdateResult {
if let Some(ref mut file) = self.files.get(self.current_file as usize) {
file.update(ticks);
}
UpdateResult::NoOp
}
pub fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) {
if let Some(ref mut file) = self.files.get(self.current_file as usize) {
file.render(canvas, renderer);
}
}
}

25
src/app/config.rs Normal file
View File

@ -0,0 +1,25 @@
#[derive(Debug, Clone)]
pub struct EditorConfig {
pub character_size: u16,
pub font_path: String,
}
#[derive(Debug, Clone)]
pub struct Config {
pub width: u32,
pub height: u32,
pub editor_config: EditorConfig,
}
impl Config {
pub fn new() -> Self {
Self {
width: 1024,
height: 860,
editor_config: EditorConfig {
character_size: 24,
font_path: "./assets/fonts/hinted-ElaineSans-Medium.ttf".to_string(),
},
}
}
}

137
src/app/mod.rs Normal file
View File

@ -0,0 +1,137 @@
pub mod app_state;
pub mod config;
use sdl2::event::Event;
use sdl2::hint;
use sdl2::pixels::Color;
use sdl2::render::Canvas;
use sdl2::video::Window;
use sdl2::EventPump;
use sdl2::{Sdl, TimerSubsystem};
use std::thread::sleep;
use std::time::Duration;
pub type WindowCanvas = Canvas<Window>;
use crate::app::app_state::AppState;
use crate::app::config::Config;
use crate::renderer::Renderer;
#[derive(PartialEq, Clone, Debug)]
pub enum UpdateResult {
NoOp,
Stop,
RefreshPositions,
}
pub enum Task {
OpenFile { file_path: String },
}
pub struct Application {
config: Config,
sdl_context: Sdl,
canvas: WindowCanvas,
tasks: Vec<Task>,
}
impl Application {
pub fn new() -> Self {
let config = Config::new();
let sdl_context = sdl2::init().unwrap();
hint::set("SDL_GL_MULTISAMPLEBUFFERS", "1");
hint::set("SDL_GL_MULTISAMPLESAMPLES", "8");
hint::set("SDL_GL_ACCELERATED_VISUAL", "1");
hint::set("SDL_HINT_RENDER_SCALE_QUALITY", "2");
hint::set("SDL_HINT_VIDEO_ALLOW_SCREENSAVER", "1");
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("Editor", config.width, config.height)
.position_centered()
.opengl()
.build()
.unwrap();
let canvas = window.into_canvas().accelerated().build().unwrap();
Self {
config,
sdl_context,
canvas,
tasks: vec![],
}
}
pub fn init(&mut self) {
self.clear();
}
pub fn run(&mut self) {
let mut timer: TimerSubsystem = self.sdl_context.timer().unwrap();
let mut event_pump = self.sdl_context.event_pump().unwrap();
let font_context = sdl2::ttf::init().unwrap();
let sleep_time = Duration::new(0, 1_000_000_000u32 / 60);
let mut app_state = AppState::new();
let mut renderer = Renderer::new(
self.config.clone(),
&font_context,
self.canvas.texture_creator()
);
'running: loop {
match self.handle_events(&mut event_pump) {
UpdateResult::Stop => break 'running,
UpdateResult::RefreshPositions => (),
UpdateResult::NoOp => (),
}
for task in self.tasks.iter() {
match task {
Task::OpenFile { file_path } => {
use crate::file::editor_file::*;
app_state.open_file(file_path.clone(), &mut renderer);
// use std::fs::read_to_string;
// if let Ok(buffer) = read_to_string(&file_path) {
// println!("read: {}\n{}", file_path, buffer);
// let file = EditorFile::new(file_path.clone(), buffer, &mut renderer);
// app_state.current_file = app_state.files.len() as i16;
// app_state.files.push(file);
// }
},
}
}
self.tasks.clear();
self.clear();
app_state.update(timer.ticks() as i32);
app_state.render(&mut self.canvas, &mut renderer);
self.present();
sleep(sleep_time);
}
}
pub fn open_file(&mut self, file_path: String) {
self.tasks.push(Task::OpenFile { file_path });
}
fn present(&mut self) {
self.canvas.present();
}
fn clear(&mut self) {
self.canvas.set_draw_color(Color::RGB(255, 255, 255));
self.canvas.clear();
}
fn handle_events(&mut self, event_pump: &mut EventPump) -> UpdateResult {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => return UpdateResult::Stop,
_ => (),
}
}
UpdateResult::NoOp
}
}

19
src/file/caret.rs Normal file
View File

@ -0,0 +1,19 @@
pub struct Caret<'a> {
character: char,
source: Rect,
dest: Rect,
visible: bool,
texture: Option<Rc<Texture<'a>>>,
}
impl<'a> Caret<'a> {
pub fn new() -> Self {
Self {
character: '',
source: Rect::new(0, 0, 0, 0),
dest: Rect::new(0, 0, 0, 0),
visible: true,
texture: None,
}
}
}

45
src/file/editor_file.rs Normal file
View File

@ -0,0 +1,45 @@
use sdl2::rect::Rect;
use crate::file::editor_file_section::EditorFileSection;
use crate::renderer::Renderer;
use crate::app::UpdateResult;
use crate::app::WindowCanvas;
#[derive(Clone)]
pub struct EditorFile<'l> {
pub path: String,
pub sections: Vec<EditorFileSection<'l>>,
}
impl<'l> EditorFile<'l> {
pub fn new(path: String, buffer: String, renderer: &'l mut Renderer) -> Self {
let section = EditorFileSection::new(buffer, renderer);
let sections = vec![section];
Self { path, sections }
}
pub fn update(&mut self, ticks: i32) -> UpdateResult {
let mut result = UpdateResult::NoOp;
for section in self.sections.iter_mut() {
result = section.update(ticks);
}
if result == UpdateResult::RefreshPositions {
self.refresh_characters_position();
result = UpdateResult::NoOp;
}
result
}
pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) {
for ref section in self.sections.iter() {
section.render(canvas, renderer);
}
}
fn refresh_characters_position(&mut self) {
let mut current: Rect = Rect::new(0, 0, 0, 0);
for section in self.sections.iter_mut() {
section.update_positions(&mut current);
}
}
}

View File

@ -0,0 +1,51 @@
use sdl2::rect::Rect;
use crate::lexer::Language;
use crate::app::UpdateResult;
use crate::app::WindowCanvas;
use crate::renderer::Renderer;
use crate::file::editor_file_token::EditorFileToken;
#[derive(Clone)]
pub struct EditorFileSection<'l> {
pub tokens: Vec<EditorFileToken<'l>>,
pub language: Language,
}
impl<'l> EditorFileSection<'l> {
pub fn new(buffer: String, renderer: &'l mut Renderer) -> Self {
use crate::lexer;
let lexer_tokens = lexer::parse(buffer.clone(), Language::PlainText);
let mut tokens: Vec<EditorFileToken> = vec![];
for token_type in lexer_tokens {
let token = EditorFileToken::new(
renderer,
token_type.get_start(),
token_type.clone(),
);
tokens.push(token.clone());
}
let language = Language::PlainText;
Self { tokens, language }
}
pub fn update(&mut self, ticks: i32) -> UpdateResult {
let mut result = UpdateResult::NoOp;
for file_char in self.tokens.iter_mut() {
result = file_char.update(ticks)
}
result
}
pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) {
for ref character in self.tokens.iter() {
character.render(canvas, renderer);
}
}
pub fn update_positions(&mut self, current: &mut Rect) {
for c in self.tokens.iter_mut() {
c.update_position(current);
}
}
}

View File

@ -0,0 +1,117 @@
use std::rc::Rc;
use sdl2::rect::Rect;
use sdl2::render::Texture;
use sdl2::ttf::Font;
use crate::lexer::TokenType;
use crate::renderer::Renderer;
use crate::renderer::managers::TextDetails;
use crate::renderer::resolve_color::resolve_color;
use crate::app::UpdateResult;
use crate::app::WindowCanvas;
use crate::renderer::managers::FontDetails;
#[derive(Clone)]
pub struct EditorFileToken<'l> {
pos: usize,
text: String,
font_size: u16,
source: Rect,
dest: Rect,
token_type: TokenType,
texture: Option<Rc<Texture<'l>>>,
}
impl<'l> EditorFileToken<'l> {
pub fn new(renderer: &'l mut Renderer, pos: usize, token_type: TokenType) -> Self {
let c = match token_type {
_ if token_type.is_space() => "°".to_string(),
_ if token_type.is_new_line() => "\n".to_string(),
TokenType::Whitespace { .. } => "°".to_string(),
_ => token_type.get_text(),
};
let details = TextDetails {
text: c.clone(),
font_details: FontDetails::new(
renderer.config.editor_config.font_path.as_str(),
renderer.config.editor_config.character_size.clone(),
),
color: resolve_color(&token_type),
};
Self {
pos,
text: c,
font_size: 0,
source: Rect::new(0, 0, 0, 0),
dest: Rect::new(0, 0, 0, 0),
token_type,
texture: renderer.render_text(details).clone(),
}
}
pub fn update(&mut self, _ticks: i32) -> UpdateResult {
// if self.font_size != config.editor_config.character_size {
// self.update_view(renderer);
// return UpdateResult::RefreshPositions;
// }
UpdateResult::NoOp
}
pub fn render(&self, canvas: &mut WindowCanvas, renderer: &mut Renderer) {
if self.token_type.is_new_line() {
return;
}
match &self.texture {
Some(texture) => {
renderer.render_texture(canvas, &texture, &self.source, &self.dest)
}
_ => {}
}
}
pub fn update_position(&mut self, current: &mut Rect) {
match self.token_type {
_ if self.token_type.is_new_line() => {
current.set_x(0);
current.set_y(
(self.token_type.line() as usize * self.source.height() as usize) as i32,
);
}
_ => {
self.dest.set_x(current.x());
self.dest.set_y(current.y());
self.dest.set_width(self.source.width());
self.dest.set_height(self.source.height());
current.set_x(self.dest.x() + self.source.width() as i32);
}
};
}
fn update_view(&mut self, renderer: &mut Renderer) {
self.font_size = renderer.config.editor_config.character_size.clone();
let font_details = FontDetails::new(
renderer.config.editor_config.font_path.as_str(),
self.font_size.clone(),
);
if let Ok(font) = renderer.font_manager.load(&font_details) {
if let Some((width, height)) = self.measure_text(&font) {
self.source.set_width(width as u32);
self.source.set_height(height as u32);
}
};
}
fn measure_text(&self, font: &Rc<Font>) -> Option<(usize, usize)> {
let mut w: usize = 0;
let mut h: usize = 0;
for c in self.text.chars() {
if let Ok((width, height)) = font.size_of_char(c) {
w += width as usize;
h = height as usize;
} else {
return None;
}
}
Some((w, h))
}
}

3
src/file/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod editor_file;
pub mod editor_file_section;
pub mod editor_file_token;

153
src/lexer/mod.rs Normal file
View File

@ -0,0 +1,153 @@
use std::ops::Deref;
pub mod plain;
#[derive(Debug, Clone)]
pub enum Language {
PlainText,
}
#[derive(Debug, Clone)]
pub enum TokenType {
Whitespace { token: Token },
Keyword { token: Token },
String { token: Token },
Number { token: Token },
Identifier { token: Token },
}
impl TokenType {
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
match self {
TokenType::Whitespace { token } => TokenType::Whitespace {
token: token.move_to(line, character, start, end),
},
TokenType::Keyword { token } => TokenType::Keyword {
token: token.move_to(line, character, start, end),
},
TokenType::String { token } => TokenType::String {
token: token.move_to(line, character, start, end),
},
TokenType::Number { token } => TokenType::Number {
token: token.move_to(line, character, start, end),
},
TokenType::Identifier { token } => TokenType::Identifier {
token: token.move_to(line, character, start, end),
},
}
}
pub fn is_new_line(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text() == "\n".to_string(),
_ => false,
}
}
pub fn is_space(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text() == " ".to_string(),
_ => false,
}
}
pub fn get_start(&self) -> usize {
match self {
TokenType::Whitespace { token } => token.start(),
TokenType::Keyword { token } => token.start(),
TokenType::String { token } => token.start(),
TokenType::Number { token } => token.start(),
TokenType::Identifier { token } => token.start(),
}
}
pub fn get_text(&self) -> String {
match self {
TokenType::Whitespace { token } => token.text(),
TokenType::Keyword { token } => token.text(),
TokenType::String { token } => token.text(),
TokenType::Number { token } => token.text(),
TokenType::Identifier { token } => token.text(),
}
}
}
impl Deref for TokenType {
type Target = Token;
fn deref(&self) -> &<Self as Deref>::Target {
match self {
TokenType::Whitespace { token } => token,
TokenType::Keyword { token } => token,
TokenType::String { token } => token,
TokenType::Number { token } => token,
TokenType::Identifier { token } => token,
}
}
}
#[derive(Debug, Clone)]
pub struct Token {
line: usize,
character: usize,
start: usize,
end: usize,
pub text: String,
}
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub lo: usize,
pub hi: usize,
}
impl Token {
pub fn new(text: String, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text,
line,
character,
start,
end,
}
}
pub fn text(&self) -> String {
self.text.clone()
}
pub fn line(&self) -> usize {
self.line.clone()
}
pub fn character(&self) -> usize {
self.character.clone()
}
pub fn start(&self) -> usize {
self.start.clone()
}
pub fn end(&self) -> usize {
self.end.clone()
}
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text: self.text.clone(),
line,
character,
start,
end,
}
}
}
pub fn parse(text: String, language: Language) -> Vec<TokenType> {
match language {
Language::PlainText => plain::lexer::Lexer::new(text.as_str())
.inspect(|tok| eprintln!("tok: {:?}", tok))
.map(|t| t.0)
.collect(),
}
}

79
src/lexer/plain.rs Normal file
View File

@ -0,0 +1,79 @@
use crate::lexer::{Token, TokenType};
pub mod lexer {
use crate::lexer::{Span, Token, TokenType};
use plex::lexer;
lexer! {
fn next_token(text: 'a) -> (TokenType, &'a str);
r"[ \t\r\n]" => (TokenType::Whitespace {
token: Token::new(text.to_string(), 0, 0, 0, 0)
}, text),
r"[^ \t\r\n]+" => (TokenType::Identifier {
token: Token::new(text.to_string(), 0, 0, 0, 0)
}, text),
}
pub struct Lexer<'a> {
original: &'a str,
remaining: &'a str,
line: usize,
character: usize,
}
impl<'a> Lexer<'a> {
pub fn new(s: &'a str) -> Self {
Self {
original: s,
remaining: s,
line: 0,
character: 0,
}
}
}
impl<'a> Iterator for Lexer<'a> {
type Item = (TokenType, Span);
fn next(&mut self) -> Option<(TokenType, Span)> {
loop {
let tok: (TokenType, &str) =
if let Some(((token_type, text), new_remaining)) = next_token(self.remaining) {
self.remaining = new_remaining;
if token_type.is_new_line() {
self.line += 1;
self.character = text.len();
} else {
self.character += text.len();
}
(token_type, text)
} else {
return None;
};
match tok {
(tok, text) => {
let span = self.span_in(text);
let token = tok.move_to(
self.line.clone(),
self.character - text.len(),
span.lo.clone(),
span.hi.clone(),
);
return Some((token, span));
}
}
}
}
}
impl<'a> Lexer<'a> {
fn span_in(&self, s: &str) -> Span {
let lo = s.as_ptr() as usize - self.original.as_ptr() as usize;
Span {
lo,
hi: lo + s.len(),
}
}
}
}

20
src/main.rs Normal file
View File

@ -0,0 +1,20 @@
#![allow(unused_imports)]
#![feature(use_extern_macros)]
extern crate plex;
extern crate rand;
extern crate sdl2;
pub mod app;
pub mod file;
pub mod lexer;
pub mod renderer;
use crate::app::Application;
fn main() {
let mut app = Application::new();
app.init();
app.open_file("./tests/example.txt".to_string());
app.run();
}

148
src/renderer/managers.rs Normal file
View File

@ -0,0 +1,148 @@
use std::borrow::Borrow;
use std::collections::HashMap;
#[allow(unused_imports)]
use std::env;
use std::hash::Hash;
use std::rc::Rc;
use sdl2::image::LoadTexture;
use sdl2::pixels::Color;
use sdl2::render::{Texture, TextureCreator};
use sdl2::ttf::{Font, Sdl2TtfContext};
pub trait ResourceLoader<'l, R> {
type Args: ?Sized;
fn load(&'l self, data: &Self::Args) -> Result<R, String>;
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct FontDetails {
pub path: String,
pub size: u16,
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct TextDetails {
pub text: String,
pub color: Color,
pub font_details: FontDetails,
}
impl TextDetails {
pub fn get_cache_key(&self) -> String {
format!(
"text({}) size({}) {:?}",
self.text, self.font_details.size, self.color
)
.to_string()
}
}
impl<'a> From<&'a TextDetails> for TextDetails {
fn from(details: &'a Self) -> Self {
Self {
text: details.text.clone(),
color: details.color.clone(),
font_details: details.font_details.clone(),
}
}
}
impl FontDetails {
pub fn new(path: &str, size: u16) -> FontDetails {
Self {
path: path.to_string(),
size,
}
}
}
impl<'a> From<&'a FontDetails> for FontDetails {
fn from(details: &'a FontDetails) -> Self {
Self {
path: details.path.clone(),
size: details.size,
}
}
}
pub type TextureManager<'l, T> = ResourceManager<'l, String, Texture<'l>, TextureCreator<T>>;
pub type FontManager<'l> = ResourceManager<'l, FontDetails, Font<'l, 'static>, Sdl2TtfContext>;
#[derive(Clone)]
pub struct ResourceManager<'l, K, R, L>
where
K: Hash + Eq,
L: 'l + ResourceLoader<'l, R>,
{
loader: &'l L,
cache: HashMap<K, Rc<R>>,
}
impl<'l, K, R, L> ResourceManager<'l, K, R, L>
where
K: Hash + Eq,
L: ResourceLoader<'l, R>,
{
pub fn new(loader: &'l L) -> Self {
Self { cache: HashMap::new(), loader }
}
pub fn load<D>(&mut self, details: &D) -> Result<Rc<R>, String>
where
L: ResourceLoader<'l, R, Args = D>,
D: Eq + Hash + ?Sized,
K: Borrow<D> + for<'a> From<&'a D>,
{
self.cache.get(details).cloned().map_or_else(
|| {
let resource = Rc::new(self.loader.load(details)?);
self.cache.insert(details.into(), resource.clone());
Ok(resource)
},
Ok,
)
}
}
impl<'l, T> ResourceLoader<'l, Texture<'l>> for TextureCreator<T> {
type Args = str;
fn load(&'l self, path: &str) -> Result<Texture, String> {
println!("Loading {}...", path);
self.load_texture(path)
}
}
impl<'l> ResourceLoader<'l, Font<'l, 'static>> for Sdl2TtfContext {
type Args = FontDetails;
fn load(&'l self, data: &FontDetails) -> Result<Font<'l, 'static>, String> {
println!("Loading font {}...", data.path);
self.load_font(&data.path, data.size)
}
}
//impl<'l, T> TextureManager<'l, T> {
// pub fn load_text(
// &mut self,
// details: &mut TextDetails,
// font: &Font,
// ) -> Result<Rc<Texture<'l>>, String> {
// let key = details.get_cache_key();
// self.cache.get(key.as_str()).cloned().map_or_else(
// || {
// let surface = font
// .render(details.text.as_str())
// .blended(details.color)
// .unwrap();
// let texture = self.loader.create_texture_from_surface(&surface).unwrap();
// let resource = Rc::new(texture);
// self.cache.insert(key, resource.clone());
// Ok(resource)
// },
// Ok,
// )
// }
//}

74
src/renderer/mod.rs Normal file
View File

@ -0,0 +1,74 @@
pub mod managers;
pub mod resolve_color;
use crate::renderer::managers::{FontManager, TextureManager};
use sdl2::rect::{Point, Rect};
use sdl2::render::{Texture, TextureCreator};
use sdl2::ttf::Sdl2TtfContext;
use sdl2::video::WindowContext;
use std::rc::Rc;
use crate::app::config::Config;
use crate::app::WindowCanvas;
use crate::renderer::managers::TextDetails;
pub struct Renderer<'a> {
pub config: Config,
pub font_manager: FontManager<'a>,
pub texture_manager: TextureManager<'a, WindowContext>,
pub texture_creator: TextureCreator<WindowContext>,
pub scroll: Point,
}
impl<'a> Renderer<'a> {
pub fn new(
config: Config,
font_context: &'a Sdl2TtfContext,
texture_creator: TextureCreator<WindowContext>,
) -> Self {
Self {
config,
font_manager: FontManager::new(&font_context),
texture_manager: TextureManager::new(&texture_creator),
texture_creator,
scroll: (0, 0).into(),
}
}
pub fn render_texture(&mut self, canvas: &mut WindowCanvas, texture: &Rc<Texture>, src: &Rect, dest: &Rect) {
canvas
.copy_ex(
texture,
Some(src.clone()),
Some(dest.clone()),
0.0,
None,
false,
false,
)
.unwrap();
}
pub fn render_text(&mut self, details: TextDetails) -> Option<Rc<Texture>> {
let font = self.font_manager.load(&details.font_details).unwrap();
let surface = font
.render(details.text.as_str())
.blended(details.color);
let surface = if let Ok(s) = surface {
s
} else {
return None;
};
let texture = self
.texture_creator
.create_texture_from_surface(&surface);
let texture = if let Ok(t) = texture {
Rc::new(t)
} else {
return None;
};
Some(texture)
}
}

View File

@ -0,0 +1,10 @@
use sdl2::pixels::Color;
use crate::lexer::TokenType;
pub fn resolve_color(token_type: &TokenType) -> Color {
match token_type {
&TokenType::Whitespace { .. } => Color::RGBA(220, 220, 220, 90),
_ => Color::RGBA(0, 0, 0, 0),
}
}

3
tests/example.txt Normal file
View File

@ -0,0 +1,3 @@
Hello world
foo bar
example com