mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-09 12:16:43 +03:00
File module with read, fmt, and write functionality for the editor
This commit is contained in:
commit
1326219415
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2569,6 +2569,7 @@ dependencies = [
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_fmt",
|
||||
"roc_gen",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
|
@ -23,6 +23,8 @@ roc_solve = { path = "../compiler/solve" }
|
||||
roc_mono = { path = "../compiler/mono" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
roc_gen = { path = "../compiler/gen" }
|
||||
roc_fmt = { path = "../compiler/fmt" }
|
||||
|
||||
roc_reporting = { path = "../compiler/reporting" }
|
||||
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
|
@ -27,6 +27,7 @@ These are potentially inspirational resources for the editor's design.
|
||||
* [Sketch-n-Sketch: Interactive SVG Programming with Direct Manipulation](https://youtu.be/YuGVC8VqXz0) by [Ravi Chugh](http://people.cs.uchicago.edu/~rchugh/)
|
||||
* [Xi](https://xi-editor.io/) modern text editor with concurrent editing (related to [Druid](https://github.com/linebender/druid))
|
||||
* [Self](https://selflanguage.org/) programming language
|
||||
* [Primitive](https://primitive.io/) code exploration in Virtual Reality
|
||||
|
||||
### Debugging
|
||||
|
||||
@ -72,6 +73,9 @@ Thoughts and ideas possibly taken from above inspirations or separate.
|
||||
* Maybe easier to manually trigger a test related to exactly what code you're writing
|
||||
* Ability to generate unit tests for a selected function in context menu
|
||||
* A table should appear to enter input and expected output pairs quickly
|
||||
* Ability to show import connection within project visually
|
||||
* This could be done by drawing connections between files or functions in the tree view. This would make it easier for people to get their bearings in new big projects.
|
||||
* Connections could also be drawn between functions that call each other in the tree view. The connections could be animated to show the execution flow of the program.
|
||||
* "Error mode" where the editor jumps you to the next error
|
||||
* Similar in theory to diff tools that jump you to the next merge conflict
|
||||
* dependency recommendation
|
||||
|
@ -1,6 +1,98 @@
|
||||
pub struct File {
|
||||
path: String,
|
||||
content: String,
|
||||
use crate::file::ReadError::DoesntHaveRocExtension;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_fmt::def::fmt_def;
|
||||
use roc_fmt::module::fmt_module;
|
||||
use roc_parse::ast::{Attempting, Def, Module};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser;
|
||||
use roc_parse::parser::Parser;
|
||||
use roc_region::all::Located;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct File<'a> {
|
||||
path: &'a Path,
|
||||
module_header: Module<'a>,
|
||||
content: Vec<'a, Located<Def<'a>>>,
|
||||
}
|
||||
|
||||
impl File {}
|
||||
#[derive(Debug)]
|
||||
pub enum ReadError {
|
||||
Read(std::io::Error),
|
||||
ParseDefs(parser::Fail),
|
||||
ParseHeader(parser::Fail),
|
||||
DoesntHaveRocExtension,
|
||||
}
|
||||
|
||||
impl<'a> File<'a> {
|
||||
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError> {
|
||||
if path.extension() != Some(OsStr::new("roc")) {
|
||||
return Err(DoesntHaveRocExtension);
|
||||
}
|
||||
|
||||
let bytes = fs::read(path).map_err(ReadError::Read)?;
|
||||
|
||||
let allocation = arena.alloc(bytes);
|
||||
|
||||
let module_parse_state = parser::State::new(allocation, Attempting::Module);
|
||||
let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state);
|
||||
|
||||
match parsed_module {
|
||||
Ok((module, state)) => {
|
||||
let parsed_defs = module_defs().parse(&arena, state);
|
||||
|
||||
match parsed_defs {
|
||||
Ok((defs, _)) => Ok(File {
|
||||
path,
|
||||
module_header: module,
|
||||
content: defs,
|
||||
}),
|
||||
Err((error, _)) => Err(ReadError::ParseDefs(error)),
|
||||
}
|
||||
}
|
||||
Err((error, _)) => Err(ReadError::ParseHeader(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt(&self) -> String {
|
||||
let arena = Bump::new();
|
||||
let mut formatted_file = String::new();
|
||||
|
||||
let mut module_header_buf = bumpalo::collections::String::new_in(&arena);
|
||||
fmt_module(&mut module_header_buf, &self.module_header);
|
||||
|
||||
formatted_file.push_str(module_header_buf.as_str());
|
||||
|
||||
for def in &self.content {
|
||||
let mut def_buf = bumpalo::collections::String::new_in(&arena);
|
||||
|
||||
fmt_def(&mut def_buf, &def.value, 0);
|
||||
|
||||
formatted_file.push_str(def_buf.as_str());
|
||||
}
|
||||
|
||||
formatted_file
|
||||
}
|
||||
|
||||
pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> {
|
||||
let formatted_file = self.fmt();
|
||||
|
||||
fs::write(write_path, formatted_file)
|
||||
}
|
||||
|
||||
pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> {
|
||||
self.fmt_then_write_to(
|
||||
self.path
|
||||
.with_file_name(new_name)
|
||||
.with_extension("roc")
|
||||
.as_path(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmt_then_write(&self) -> io::Result<()> {
|
||||
self.fmt_then_write_to(self.path)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||
use winit::event_loop::ControlFlow;
|
||||
|
||||
pub mod ast;
|
||||
mod file;
|
||||
pub mod file;
|
||||
mod rect;
|
||||
pub mod text_state;
|
||||
mod vertex;
|
||||
|
17
editor/tests/modules/Simple.roc
Normal file
17
editor/tests/modules/Simple.roc
Normal file
@ -0,0 +1,17 @@
|
||||
interface Simple
|
||||
exposes [
|
||||
v
|
||||
]
|
||||
imports []
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v : Str
|
||||
|
||||
v = "Value!"
|
8
editor/tests/modules/SimpleFmt.roc
Normal file
8
editor/tests/modules/SimpleFmt.roc
Normal file
@ -0,0 +1,8 @@
|
||||
interface Simple
|
||||
exposes [
|
||||
v
|
||||
]
|
||||
imports []
|
||||
|
||||
v : Str
|
||||
v = "Value!"
|
68
editor/tests/modules/Storage.roc
Normal file
68
editor/tests/modules/Storage.roc
Normal file
@ -0,0 +1,68 @@
|
||||
interface Storage
|
||||
exposes [
|
||||
Storage,
|
||||
decoder,
|
||||
get,
|
||||
listener,
|
||||
set
|
||||
]
|
||||
imports [
|
||||
Map.{ Map },
|
||||
Json.Decode.{ Decoder } as Decode
|
||||
Json.Encode as Encode
|
||||
Ports.FromJs as FromJs
|
||||
Ports.ToJs as ToJs
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## TYPES ##
|
||||
################################################################################
|
||||
|
||||
|
||||
Storage : [
|
||||
@Storage (Map Str Decode.Value)
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## API ##
|
||||
################################################################################
|
||||
|
||||
|
||||
get : Storage, Str, Decoder a -> [ Ok a, NotInStorage, DecodeError Decode.Error ]*
|
||||
get = \key, decoder, @Storage map ->
|
||||
when Map.get map key is
|
||||
Ok json ->
|
||||
Decode.decodeValue decoder json
|
||||
|
||||
Err NotFound ->
|
||||
NotInStorage
|
||||
|
||||
|
||||
set : Encode.Value, Str -> Effect {}
|
||||
set json str =
|
||||
ToJs.type "setStorage"
|
||||
|> ToJs.setFields [
|
||||
Field "key" (Encode.str str),
|
||||
Field "value" json
|
||||
]
|
||||
|> ToJs.send
|
||||
|
||||
|
||||
decoder : Decoder Storage
|
||||
decoder =
|
||||
Decode.mapType Decode.value
|
||||
|> Decode.map \map -> @Storage map
|
||||
|
||||
|
||||
################################################################################
|
||||
## PORTS INCOMING ##
|
||||
################################################################################
|
||||
|
||||
|
||||
listener : (Storage -> msg) -> FromJs.Listener msg
|
||||
listener toMsg =
|
||||
FromJs.listen "storageUpdated"
|
||||
(Decode.map decoder toMsg)
|
||||
|
37
editor/tests/test_file.rs
Normal file
37
editor/tests/test_file.rs
Normal file
@ -0,0 +1,37 @@
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_file {
|
||||
use bumpalo::Bump;
|
||||
use roc_editor::file::File;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn read_storage() {
|
||||
let simple_module_path = Path::new("./tests/modules/Simple.roc");
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let file = File::read(simple_module_path, &arena)
|
||||
.expect("Could not read Simple.roc in test_file test");
|
||||
|
||||
assert_eq!(
|
||||
file.fmt(),
|
||||
indoc!(
|
||||
r#"
|
||||
interface Simple
|
||||
exposes [
|
||||
v
|
||||
]
|
||||
imports []
|
||||
|
||||
v : Str
|
||||
|
||||
v = "Value!""#
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user