mirror of
https://github.com/enso-org/enso.git
synced 2024-12-24 10:02:17 +03:00
Connect Doc Parser and generator to IDE (https://github.com/enso-org/ide/pull/675)
* up parser version
* js client conn
* up parser
* test with parser right before integration
* checkpoint [skip ci]
* save state
* tests
* changes
* rev
* Adam Review
* requested changes
* Ability to generate Docstr from pure doc code
* bump
Original commit: 1e31d3b1db
This commit is contained in:
parent
eb17ad8352
commit
e03f109085
@ -25,7 +25,7 @@ use std::path::PathBuf;
|
||||
const PARSER_PATH: &str = "./pkg/scala-parser.js";
|
||||
|
||||
/// Commit from `enso` repository that will be used to obtain parser from.
|
||||
const PARSER_COMMIT: &str = "157582b81edb1e02a91e7866fde7e0ae72d5570f";
|
||||
const PARSER_COMMIT: &str = "84e565e8bc9575d95d12739df0cf9bd50d77af62";
|
||||
|
||||
/// Magic code that needs to be prepended to ScalaJS generated parser due to:
|
||||
/// https://github.com/scala-js/scala-js/issues/3677/
|
||||
|
@ -46,6 +46,12 @@ extern "C" {
|
||||
#[wasm_bindgen(catch)]
|
||||
fn parse_with_metadata
|
||||
(content:String) -> std::result::Result<String,JsValue>;
|
||||
#[wasm_bindgen(catch)]
|
||||
fn doc_parser_generate_html_source
|
||||
(content:String) -> std::result::Result<String,JsValue>;
|
||||
#[wasm_bindgen(catch)]
|
||||
fn doc_parser_generate_html_from_doc
|
||||
(content:String) -> std::result::Result<String,JsValue>;
|
||||
}
|
||||
|
||||
/// Wrapper over the JS-compiled parser.
|
||||
@ -55,10 +61,12 @@ extern "C" {
|
||||
pub struct Client {}
|
||||
|
||||
impl Client {
|
||||
/// Creates a `Client`
|
||||
pub fn new() -> Result<Client> {
|
||||
Ok(Client {})
|
||||
}
|
||||
|
||||
/// Parses Enso code with JS-based parser
|
||||
pub fn parse(&self, program:String, ids:IdMap) -> api::Result<Ast> {
|
||||
let ast = || {
|
||||
let json_ids = serde_json::to_string(&ids)?;
|
||||
@ -69,6 +77,7 @@ impl Client {
|
||||
Ok(ast()?)
|
||||
}
|
||||
|
||||
/// Parses Enso code with metadata
|
||||
pub fn parse_with_metadata<M:api::Metadata>
|
||||
(&self, program:String) -> api::Result<api::ParsedSourceFile<M>> {
|
||||
let result = || {
|
||||
@ -78,4 +87,22 @@ impl Client {
|
||||
};
|
||||
Ok(result()?)
|
||||
}
|
||||
|
||||
/// Calls JS doc parser to generate HTML from documented Enso code
|
||||
pub fn generate_html_docs(&self, program:String) -> api::Result<String> {
|
||||
let html_code = || {
|
||||
let html_code = doc_parser_generate_html_source(program)?;
|
||||
Result::Ok(html_code)
|
||||
};
|
||||
Ok(html_code()?)
|
||||
}
|
||||
|
||||
/// Calls JS doc parser to generate HTML from pure doc code w/o Enso's AST
|
||||
pub fn generate_html_doc_pure(&self, code:String) -> api::Result<String> {
|
||||
let html_code = || {
|
||||
let html_code = doc_parser_generate_html_from_doc(code)?;
|
||||
Result::Ok(html_code)
|
||||
};
|
||||
Ok(html_code()?)
|
||||
}
|
||||
}
|
||||
|
@ -120,3 +120,53 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==========================================
|
||||
// === Documentation Parser and Generator ===
|
||||
// ==========================================
|
||||
|
||||
/// Handle to a doc parser implementation.
|
||||
///
|
||||
/// Currently this component is implemented as a wrapper over documentation
|
||||
/// parser written in Scala. Depending on compilation target (native or wasm)
|
||||
/// it uses either implementation provided by `wsclient` or `jsclient`.
|
||||
#[derive(Clone,CloneRef,Debug,Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct DocParser(pub Rc<RefCell<Client>>);
|
||||
|
||||
impl DocParser {
|
||||
/// Obtains a default doc parser implementation.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new() -> api::Result<DocParser> {
|
||||
let client = wsclient::Client::new()?;
|
||||
let doc_parser = Rc::new(RefCell::new(client));
|
||||
Ok(DocParser(doc_parser))
|
||||
}
|
||||
|
||||
/// Obtains a default doc parser implementation.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn new() -> api::Result<DocParser> {
|
||||
let client = jsclient::Client::new()?;
|
||||
let doc_parser = Rc::new(RefCell::new(client));
|
||||
Ok(DocParser(doc_parser))
|
||||
}
|
||||
|
||||
/// Obtains a default doc parser implementation, panicking in case of failure.
|
||||
pub fn new_or_panic() -> DocParser {
|
||||
DocParser::new().unwrap_or_else(|e| panic!("Failed to create doc parser: {:?}", e))
|
||||
}
|
||||
|
||||
/// Parses program with documentation and generates HTML code.
|
||||
/// If the program does not have any documentation will return empty string.
|
||||
pub fn generate_html_docs(&self, program:String) -> api::Result<String> {
|
||||
self.borrow_mut().generate_html_docs(program)
|
||||
}
|
||||
|
||||
/// Parses pure documentation code and generates HTML code.
|
||||
/// Will return empty string for empty entry
|
||||
pub fn generate_html_doc_pure(&self, code:String) -> api::Result<String> {
|
||||
self.borrow_mut().generate_html_doc_pure(code)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ use enso_prelude::*;
|
||||
|
||||
|
||||
/// Simple interactive tester - calls parser with its argument (or a
|
||||
/// hardcoded default) and prints the result
|
||||
/// hardcoded default) and prints the result, then calls doc parser
|
||||
/// and prints the HTML code or an error message.
|
||||
fn main() {
|
||||
let default_input = String::from("import Foo.Bar\nfoo = a + 2");
|
||||
let program = std::env::args().nth(1).unwrap_or(default_input);
|
||||
@ -15,4 +16,28 @@ fn main() {
|
||||
Ok(result) => println!("Parser responded with: {:?}", result),
|
||||
Err(e) => println!("Failed to obtain a response: {:?}", e),
|
||||
}
|
||||
|
||||
|
||||
let default_input = String::from("##\n DEPRECATED\n Foo bar baz\ntype Foo\n type Bar");
|
||||
let program = std::env::args().nth(1).unwrap_or(default_input);
|
||||
println!("Will parse: {}", program);
|
||||
|
||||
let parser = parser::DocParser::new_or_panic();
|
||||
let output = parser.generate_html_docs(program);
|
||||
match output {
|
||||
Ok(result) => println!("Doc parser responded with: {:?}", result),
|
||||
Err(e) => println!("Failed to obtain a response: {:?}", e),
|
||||
}
|
||||
|
||||
|
||||
let default_input = String::from("Computes the _logical_ conjunction of *two* booleans");
|
||||
let program = std::env::args().nth(1).unwrap_or(default_input);
|
||||
println!("Will parse: {}", program);
|
||||
|
||||
let parser = parser::DocParser::new_or_panic();
|
||||
let output = parser.generate_html_doc_pure(program);
|
||||
match output {
|
||||
Ok(result) => println!("Doc parser responded with: {:?}", result),
|
||||
Err(e) => println!("Failed to obtain a response: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +89,10 @@ impl From<serde_json::error::Error> for Error {
|
||||
/// All request supported by the Parser Service.
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Request {
|
||||
ParseRequest { program: String, ids: IdMap },
|
||||
ParseRequestWithMetadata { content: String },
|
||||
ParseRequest { program : String, ids : IdMap },
|
||||
ParseRequestWithMetadata { content : String },
|
||||
DocParserGenerateHtmlSource { program : String },
|
||||
DocParserGenerateHtmlFromDoc { code : String },
|
||||
}
|
||||
|
||||
/// All responses that Parser Service might reply with.
|
||||
@ -100,6 +102,13 @@ pub enum Response<Metadata> {
|
||||
Error { message : String },
|
||||
}
|
||||
|
||||
/// All responses that Doc Parser Service might reply with.
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub enum ResponseDoc {
|
||||
SuccessDoc { code : String },
|
||||
Error { message : String },
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
@ -165,15 +174,37 @@ mod internal {
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtains a text message from peer and deserializes it using JSON
|
||||
/// into a `ResponseDoc`.
|
||||
///
|
||||
/// Should be called exactly once after each `send_request` invocation.
|
||||
pub fn recv_response_doc(&mut self) -> Result<ResponseDoc> {
|
||||
let response = self.connection.recv_message()?;
|
||||
match response {
|
||||
websocket::OwnedMessage::Text(code) => Ok(serde_json::from_str(&code)?),
|
||||
_ => Err(Error::NonTextResponse(response)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends given `Request` to peer and receives a `Response`.
|
||||
///
|
||||
/// Both request and response are exchanged in JSON using text messages
|
||||
/// over WebSocket.
|
||||
pub fn rpc_call<M:Metadata>
|
||||
(&mut self, request: Request) -> Result<Response<M>> {
|
||||
(&mut self, request:Request) -> Result<Response<M>> {
|
||||
self.send_request(request)?;
|
||||
self.recv_response()
|
||||
}
|
||||
|
||||
/// Sends given `Request` to peer and receives a `ResponseDoc`.
|
||||
///
|
||||
/// Both request and response are exchanged in JSON using text messages
|
||||
/// over WebSocket.
|
||||
pub fn rpc_call_doc
|
||||
(&mut self, request:Request) -> Result<ResponseDoc> {
|
||||
self.send_request(request)?;
|
||||
self.recv_response_doc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,22 +227,44 @@ impl Client {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
/// Sends a request to parser service to parse Enso code
|
||||
pub fn parse(&mut self, program:String, ids:IdMap) -> api::Result<Ast> {
|
||||
let request = Request::ParseRequest {program,ids};
|
||||
let response = self.rpc_call::<serde_json::Value>(request)?;
|
||||
match response {
|
||||
Response::Success {module} => Ok(module.ast.into()),
|
||||
Response::Error {message} => Err(ParsingError(message)),
|
||||
Response::Error {message} => Err(ParsingError(message)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a request to parser service to parse code with metadata
|
||||
pub fn parse_with_metadata<M:Metadata>
|
||||
(&mut self, program:String) -> api::Result<ParsedSourceFile<M>> {
|
||||
let request = Request::ParseRequestWithMetadata {content:program};
|
||||
let response = self.rpc_call(request)?;
|
||||
match response {
|
||||
Response::Success {module} => Ok(module),
|
||||
Response::Error {message} => Err(ParsingError(message)),
|
||||
Response::Error {message} => Err(ParsingError(message)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a request to parser service to generate HTML code from documented Enso code
|
||||
pub fn generate_html_docs(&mut self, program:String) -> api::Result<String> {
|
||||
let request = Request::DocParserGenerateHtmlSource {program};
|
||||
let response_doc = self.rpc_call_doc(request)?;
|
||||
match response_doc {
|
||||
ResponseDoc::SuccessDoc {code} => Ok(code),
|
||||
ResponseDoc::Error {message} => Err(ParsingError(message)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a request to parser service to generate HTML code from pure documentation code
|
||||
pub fn generate_html_doc_pure(&mut self, code:String) -> api::Result<String> {
|
||||
let request = Request::DocParserGenerateHtmlFromDoc {code};
|
||||
let response_doc = self.rpc_call_doc(request)?;
|
||||
match response_doc {
|
||||
ResponseDoc::SuccessDoc {code} => Ok(code),
|
||||
ResponseDoc::Error {message} => Err(ParsingError(message)),
|
||||
}
|
||||
}
|
||||
|
||||
|
37
gui/src/rust/ide/lib/parser/tests/doc-gen.rs
Normal file
37
gui/src/rust/ide/lib/parser/tests/doc-gen.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use parser::DocParser;
|
||||
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn doc_gen_test() {
|
||||
// no doc case
|
||||
let input = String::from("type Foo\n type Bar");
|
||||
let program = std::env::args().nth(1).unwrap_or(input);
|
||||
let parser = DocParser::new_or_panic();
|
||||
let gen_code = parser.generate_html_docs(program).unwrap();
|
||||
assert_eq!(gen_code.len(), 0);
|
||||
|
||||
// only doc case
|
||||
let input = String::from("Foo *Bar* Baz");
|
||||
let program = std::env::args().nth(1).unwrap_or(input);
|
||||
let parser = DocParser::new_or_panic();
|
||||
let gen_code = parser.generate_html_doc_pure(program).unwrap();
|
||||
assert_ne!(gen_code.len(), 0);
|
||||
|
||||
let input = String::from("##\n foo\ntype Foo\n");
|
||||
let program = std::env::args().nth(1).unwrap_or(input);
|
||||
let parser = DocParser::new_or_panic();
|
||||
let gen_code = parser.generate_html_docs(program).unwrap();
|
||||
assert_ne!(gen_code.len(), 0);
|
||||
|
||||
let input = String::from("##\n DEPRECATED\n Foo bar baz\ntype Foo\n type Bar");
|
||||
let program = std::env::args().nth(1).unwrap_or(input);
|
||||
let parser = DocParser::new_or_panic();
|
||||
let gen_code = parser.generate_html_docs(program).unwrap();
|
||||
assert_ne!(gen_code.len(), 0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user