diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8d73c28..66884a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,7 @@ on: push: branches: - master + - trying env: CARGO_INCREMENTAL: 0 @@ -36,7 +37,11 @@ jobs: node-version: '15' - name: Build wasm - run: wasm-pack build --target web + run: | + cd rust-pack + cargo run + cd ../ra-wasm + wasm-pack build --target web - name: Install www uses: borales/actions-yarn@v2.0.0 diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index 3af8323..fff340c 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -35,7 +35,11 @@ jobs: node-version: '15' - name: Build wasm - run: wasm-pack build --target web + run: | + cd rust-pack + cargo run + cd ../ra-wasm + wasm-pack build --target web - name: Install www uses: borales/actions-yarn@v2.0.0 diff --git a/.gitignore b/.gitignore index 108f953..41c0f47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /.bundle /vendor -/target +target /_site .sass-cache .jekyll-cache diff --git a/README.md b/README.md index 295199d..cd4851d 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,11 @@ To run: ```shell +$ cd rust-pack +$ cargo run +$ cd ../ra-wasm $ wasm-pack build --target web -$ cd www +$ cd ../www $ yarn $ yarn start ``` diff --git a/.cargo/config b/ra-wasm/.cargo/config similarity index 100% rename from .cargo/config rename to ra-wasm/.cargo/config diff --git a/Cargo.lock b/ra-wasm/Cargo.lock similarity index 99% rename from Cargo.lock rename to ra-wasm/Cargo.lock index ee0dfac..0c15944 100644 --- a/Cargo.lock +++ b/ra-wasm/Cargo.lock @@ -1444,6 +1444,7 @@ dependencies = [ "console_error_panic_hook", "instant", "log", + "ra_ap_cfg", "ra_ap_ide", "ra_ap_ide_db", "serde", diff --git a/Cargo.toml b/ra-wasm/Cargo.toml similarity index 83% rename from Cargo.toml rename to ra-wasm/Cargo.toml index f4587a5..332cdc5 100644 --- a/Cargo.toml +++ b/ra-wasm/Cargo.toml @@ -7,11 +7,8 @@ edition = "2018" [lib] crate-type = ["cdylib"] -[features] -dev = ["console_error_panic_hook"] - [dependencies] -console_error_panic_hook = { version = "0.1.6", optional = true } +console_error_panic_hook = { version = "0.1.6" } instant = { version = "0.1", features = ["wasm-bindgen"] } log = { version = "0.4.14", features = ["release_max_level_warn"] } serde = { version = "1.0.125", features = ["derive"] } @@ -22,4 +19,5 @@ wasm-bindgen = "0.2.72" wasm-bindgen-rayon = "1.0.2" ide = { version = "0.0.44", package = "ra_ap_ide" } +cfg = { version = "0.0.44", package = "ra_ap_cfg" } ide_db = { version = "0.0.44", package = "ra_ap_ide_db" } diff --git a/src/lib.rs b/ra-wasm/src/lib.rs similarity index 62% rename from src/lib.rs rename to ra-wasm/src/lib.rs index df6768e..9d2da78 100644 --- a/src/lib.rs +++ b/ra-wasm/src/lib.rs @@ -1,11 +1,14 @@ #![cfg(target_arch = "wasm32")] #![allow(non_snake_case)] -use ide::{Analysis, CompletionConfig, DiagnosticsConfig, FileId, FilePosition, Indel, TextSize}; -use ide_db::helpers::{ - insert_use::{InsertUseConfig, MergeBehavior, PrefixKind}, - SnippetCap, -}; +use std::sync::Arc; + +use ide::{Analysis, AnalysisHost, Change, CompletionConfig, CrateGraph, CrateId, DiagnosticsConfig, Edition, FileId, FilePosition, Indel, InlayHintsConfig, InlayKind, SourceRoot, TextSize}; +use ide_db::{base_db::{CrateName, Env, FileSet, VfsPath}, helpers::{ + insert_use::{InsertUseConfig, MergeBehavior, PrefixKind}, + SnippetCap, + }}; +use cfg::CfgOptions; use wasm_bindgen::prelude::*; mod to_proto; @@ -17,37 +20,130 @@ pub use wasm_bindgen_rayon::init_thread_pool; #[wasm_bindgen(start)] pub fn start() { - #[cfg(feature = "dev")] - { - console_error_panic_hook::set_once(); - } + console_error_panic_hook::set_once(); log::info!("worker initialized") } #[wasm_bindgen] pub struct WorldState { - analysis: Analysis, + host: AnalysisHost, file_id: FileId, } +pub fn create_source_root(name: &str, f: FileId) -> SourceRoot { + let mut file_set = FileSet::default(); + file_set.insert(f, VfsPath::new_virtual_path(format!("/{}/src/lib.rs", name))); + SourceRoot::new_library(file_set) +} + +pub fn create_crate(crate_graph: &mut CrateGraph, f: FileId) -> CrateId { + let mut cfg = CfgOptions::default(); + cfg.insert_atom("unix".into()); + cfg.insert_key_value("target_arch".into(), "x86_64".into()); + cfg.insert_key_value("target_pointer_width".into(), "64".into()); + crate_graph.add_crate_root( + f, + Edition::Edition2018, + None, + cfg, + Env::default(), + Default::default(), + ) +} + +pub fn from_single_file(text: String, fake_std: String, fake_core: String, fake_alloc: String) -> (AnalysisHost, FileId) { + let mut host = AnalysisHost::default(); + let file_id = FileId(0); + let std_id = FileId(1); + let core_id = FileId(2); + let alloc_id = FileId(3); + + let mut file_set = FileSet::default(); + file_set.insert(file_id, VfsPath::new_virtual_path("/my_crate/main.rs".to_string())); + let source_root = SourceRoot::new_local(file_set); + + let mut change = Change::new(); + change.set_roots(vec![ + source_root, + create_source_root("std", std_id), + create_source_root("core", core_id), + create_source_root("alloc", alloc_id), + ]); + let mut crate_graph = CrateGraph::default(); + let my_crate = create_crate(&mut crate_graph, file_id); + let std_crate = create_crate(&mut crate_graph, std_id); + let core_crate = create_crate(&mut crate_graph, core_id); + let alloc_crate = create_crate(&mut crate_graph, alloc_id); + crate_graph.add_dep( + std_crate, + CrateName::new("core").unwrap(), + core_crate, + ).unwrap(); + crate_graph.add_dep( + std_crate, + CrateName::new("alloc").unwrap(), + alloc_crate, + ).unwrap(); + crate_graph.add_dep( + alloc_crate, + CrateName::new("core").unwrap(), + core_crate, + ).unwrap(); + crate_graph.add_dep( + my_crate, + CrateName::new("core").unwrap(), + core_crate, + ).unwrap(); + crate_graph.add_dep( + my_crate, + CrateName::new("alloc").unwrap(), + alloc_crate, + ).unwrap(); + crate_graph.add_dep( + my_crate, + CrateName::new("std").unwrap(), + std_crate, + ).unwrap(); + change.change_file(file_id, Some(Arc::new(text))); + change.change_file(std_id, Some(Arc::new(fake_std))); + change.change_file(core_id, Some(Arc::new(fake_core))); + change.change_file(alloc_id, Some(Arc::new(fake_alloc))); + change.set_crate_graph(crate_graph); + host.apply_change(change); + (host, file_id) +} + +impl WorldState { + fn analysis(&self) -> Analysis { + self.host.analysis() + } +} + #[wasm_bindgen] impl WorldState { #[wasm_bindgen(constructor)] pub fn new() -> Self { - let (analysis, file_id) = Analysis::from_single_file("".to_owned()); - Self { analysis, file_id } + let (host, file_id) = from_single_file("".to_owned(), "".to_owned(), "".to_owned(), "".to_owned()); + Self { host, file_id } + } + + pub fn init(&mut self, code: String, fake_std: String, fake_core: String, fake_alloc: String) { + let (host, file_id) = from_single_file(code, fake_std, fake_core, fake_alloc); + self.host = host; + self.file_id = file_id; } pub fn update(&mut self, code: String) -> JsValue { log::warn!("update"); - let (analysis, file_id) = Analysis::from_single_file(code); - self.analysis = analysis; - self.file_id = file_id; + let file_id = FileId(0); + let mut change = Change::new(); + change.change_file(file_id, Some(Arc::new(code))); + self.host.apply_change(change); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let highlights: Vec<_> = self - .analysis + .analysis() .highlight(file_id) .unwrap() .into_iter() @@ -60,7 +156,7 @@ impl WorldState { let config = DiagnosticsConfig::default(); let diagnostics: Vec<_> = self - .analysis + .analysis() .diagnostics(&config, file_id) .unwrap() .into_iter() @@ -81,6 +177,33 @@ impl WorldState { serde_wasm_bindgen::to_value(&UpdateResult { diagnostics, highlights }).unwrap() } + pub fn inlay_hints(&self) -> JsValue { + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); + let results: Vec<_> = self + .analysis() + .inlay_hints( + self.file_id, + &InlayHintsConfig { + type_hints: true, + parameter_hints: true, + chaining_hints: true, + max_length: Some(25), + }, + ) + .unwrap() + .into_iter() + .map(|ih| InlayHint { + label: Some(ih.label.to_string()), + hint_type: match ih.kind { + InlayKind::TypeHint | InlayKind::ChainingHint => InlayHintType::Type, + InlayKind::ParameterHint => InlayHintType::Parameter, + }, + range: to_proto::text_range(ih.range, &line_index), + }) + .collect(); + serde_wasm_bindgen::to_value(&results).unwrap() + } + pub fn completions(&self, line_number: u32, column: u32) -> JsValue { const COMPLETION_CONFIG: CompletionConfig = CompletionConfig { enable_postfix_completions: true, @@ -96,10 +219,10 @@ impl WorldState { }; log::warn!("completions"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let res = match self.analysis.completions(&COMPLETION_CONFIG, pos).unwrap() { + let res = match self.analysis().completions(&COMPLETION_CONFIG, pos).unwrap() { Some(items) => items, None => return JsValue::NULL, }; @@ -111,10 +234,10 @@ impl WorldState { pub fn hover(&self, line_number: u32, column: u32) -> JsValue { log::warn!("hover"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let info = match self.analysis.hover(pos, true, true).unwrap() { + let info = match self.analysis().hover(pos, true, true).unwrap() { Some(info) => info, _ => return JsValue::NULL, }; @@ -130,10 +253,10 @@ impl WorldState { pub fn code_lenses(&self) -> JsValue { log::warn!("code_lenses"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let results: Vec<_> = self - .analysis + .analysis() .file_structure(self.file_id) .unwrap() .into_iter() @@ -149,7 +272,7 @@ impl WorldState { .filter_map(|it| { let position = FilePosition { file_id: self.file_id, offset: it.node_range.start() }; - let nav_info = self.analysis.goto_implementation(position).unwrap()?; + let nav_info = self.analysis().goto_implementation(position).unwrap()?; let title = if nav_info.info.len() == 1 { "1 implementation".into() @@ -180,10 +303,10 @@ impl WorldState { pub fn references(&self, line_number: u32, column: u32, include_declaration: bool) -> JsValue { log::warn!("references"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let info = match self.analysis.find_all_refs(pos, None).unwrap() { + let info = match self.analysis().find_all_refs(pos, None).unwrap() { Some(info) => info, _ => return JsValue::NULL, }; @@ -207,10 +330,10 @@ impl WorldState { pub fn prepare_rename(&self, line_number: u32, column: u32) -> JsValue { log::warn!("prepare_rename"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let refs = match self.analysis.find_all_refs(pos, None).unwrap() { + let refs = match self.analysis().find_all_refs(pos, None).unwrap() { None => return JsValue::NULL, Some(refs) => refs, }; @@ -224,10 +347,10 @@ impl WorldState { pub fn rename(&self, line_number: u32, column: u32, new_name: &str) -> JsValue { log::warn!("rename"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let change = match self.analysis.rename(pos, new_name).unwrap() { + let change = match self.analysis().rename(pos, new_name).unwrap() { Ok(change) => change, Err(_) => return JsValue::NULL, }; @@ -244,10 +367,10 @@ impl WorldState { pub fn signature_help(&self, line_number: u32, column: u32) -> JsValue { log::warn!("signature_help"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let call_info = match self.analysis.call_info(pos) { + let call_info = match self.analysis().call_info(pos) { Ok(Some(call_info)) => call_info, _ => return JsValue::NULL, }; @@ -265,10 +388,10 @@ impl WorldState { pub fn definition(&self, line_number: u32, column: u32) -> JsValue { log::warn!("definition"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let nav_info = match self.analysis.goto_definition(pos) { + let nav_info = match self.analysis().goto_definition(pos) { Ok(Some(nav_info)) => nav_info, _ => return JsValue::NULL, }; @@ -279,10 +402,10 @@ impl WorldState { pub fn type_definition(&self, line_number: u32, column: u32) -> JsValue { log::warn!("type_definition"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let nav_info = match self.analysis.goto_type_definition(pos) { + let nav_info = match self.analysis().goto_type_definition(pos) { Ok(Some(nav_info)) => nav_info, _ => return JsValue::NULL, }; @@ -293,9 +416,9 @@ impl WorldState { pub fn document_symbols(&self) -> JsValue { log::warn!("document_symbols"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); - let struct_nodes = match self.analysis.file_structure(self.file_id) { + let struct_nodes = match self.analysis().file_structure(self.file_id) { Ok(struct_nodes) => struct_nodes, _ => return JsValue::NULL, }; @@ -333,12 +456,12 @@ impl WorldState { pub fn type_formatting(&self, line_number: u32, column: u32, ch: char) -> JsValue { log::warn!("type_formatting"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let mut pos = file_position(line_number, column, &line_index, self.file_id); pos.offset -= TextSize::of('.'); - let edit = self.analysis.on_char_typed(pos, ch); + let edit = self.analysis().on_char_typed(pos, ch); let (_file, edit) = match edit { Ok(Some(it)) => it.source_file_edits.into_iter().next().unwrap(), @@ -351,8 +474,8 @@ impl WorldState { pub fn folding_ranges(&self) -> JsValue { log::warn!("folding_ranges"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); - if let Ok(folds) = self.analysis.folding_ranges(self.file_id) { + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); + if let Ok(folds) = self.analysis().folding_ranges(self.file_id) { let res: Vec<_> = folds.into_iter().map(|fold| to_proto::folding_range(fold, &line_index)).collect(); serde_wasm_bindgen::to_value(&res).unwrap() @@ -363,10 +486,10 @@ impl WorldState { pub fn goto_implementation(&self, line_number: u32, column: u32) -> JsValue { log::warn!("goto_implementation"); - let line_index = self.analysis.file_line_index(self.file_id).unwrap(); + let line_index = self.analysis().file_line_index(self.file_id).unwrap(); let pos = file_position(line_number, column, &line_index, self.file_id); - let nav_info = match self.analysis.goto_implementation(pos) { + let nav_info = match self.analysis().goto_implementation(pos) { Ok(Some(it)) => it, _ => return JsValue::NULL, }; diff --git a/src/return_types.rs b/ra-wasm/src/return_types.rs similarity index 95% rename from src/return_types.rs rename to ra-wasm/src/return_types.rs index 07378cd..a712ab4 100644 --- a/src/return_types.rs +++ b/ra-wasm/src/return_types.rs @@ -39,6 +39,20 @@ pub struct Highlight { pub range: Range, } +#[derive(Serialize_repr)] +#[repr(u8)] +pub enum InlayHintType { + Type = 1, + Parameter = 2, +} + +#[derive(Serialize)] +pub struct InlayHint { + pub label: Option, + pub hint_type: InlayHintType, + pub range: Range, +} + #[derive(Serialize)] pub struct TextEdit { pub range: Range, diff --git a/src/to_proto.rs b/ra-wasm/src/to_proto.rs similarity index 100% rename from src/to_proto.rs rename to ra-wasm/src/to_proto.rs diff --git a/rust-pack/Cargo.lock b/rust-pack/Cargo.lock new file mode 100644 index 0000000..75799ae --- /dev/null +++ b/rust-pack/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust-pack" +version = "0.1.0" diff --git a/rust-pack/Cargo.toml b/rust-pack/Cargo.toml new file mode 100644 index 0000000..61e16e3 --- /dev/null +++ b/rust-pack/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust-pack" +version = "0.1.0" +edition = "2018" + +[[bin]] +name = "rust-pack" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/rust-pack/src/main.rs b/rust-pack/src/main.rs new file mode 100644 index 0000000..bf1c1b7 --- /dev/null +++ b/rust-pack/src/main.rs @@ -0,0 +1,193 @@ +#![feature(str_split_once)] + +use core::panic; +use std::fs; +use std::fs::read_to_string; +use std::path::Path; +use std::process::Command; + +mod remove_function; + +struct Mod<'a> { + pub_prefix: &'a str, + explicit_path: Option<&'a str>, + name: &'a str, +} + +#[derive(Default, Debug)] +struct ModState<'a> { + path_seen: Option<&'a str>, + in_attribute: bool, +} + +fn remove_comment<'a>(line: &'a str) -> &'a str { + if let Some((l, _)) = line.split_once("//") { + l.trim() + } else { + line + } +} + +fn clean_token<'a>(line: &'a str) -> &'a str { + if let Some(l) = line.strip_prefix("r#") { + l + } else { + line + } +} + +fn is_external_mod<'a>(mod_state: &mut ModState<'a>, line: &'a str) -> Option> { + let line = remove_comment(line); + if line.is_empty() { + return None; + } + if line.starts_with("#[path = ") { + mod_state.path_seen = Some(&line[10..line.len() - 2]); + return None; + } + if line.starts_with("#[") { + if !line.ends_with(']') { + mod_state.in_attribute = true; + } + return None; + } + if mod_state.in_attribute { + if line.ends_with(']') { + mod_state.in_attribute = false; + } + return None; + } + let current_mod_state = std::mem::take(mod_state); + if !line.ends_with(';') { + return None; + } + let line = &line[..line.len() - 1]; + if let Some(line) = line.strip_prefix("mod ") { + Some(Mod { + explicit_path: current_mod_state.path_seen, + pub_prefix: "", + name: clean_token(line), + }) + } else if let Some(line) = line.strip_prefix("pub mod ") { + Some(Mod { + explicit_path: current_mod_state.path_seen, + pub_prefix: "pub ", + name: clean_token(line), + }) + } else if let Some(line) = line.strip_prefix("pub(crate) mod ") { + Some(Mod { + explicit_path: current_mod_state.path_seen, + pub_prefix: "pub(crate) ", + name: clean_token(line), + }) + } else if let Some(line) = line.strip_prefix("pub(self) mod ") { + Some(Mod { + explicit_path: current_mod_state.path_seen, + pub_prefix: "pub(self) ", + name: clean_token(line), + }) + } else if let Some(line) = line.strip_prefix("pub(super) mod ") { + Some(Mod { + explicit_path: current_mod_state.path_seen, + pub_prefix: "pub(super) ", + name: clean_token(line), + }) + } else if let Some(line) = line.strip_prefix("pub(in ") { + panic!("pub in not supported: {}", line); + } else { + None + } +} + +trait MyStringMethods { + fn push_line(&mut self, line: &str); +} + +impl MyStringMethods for String { + fn push_line(&mut self, line: &str) { + self.push_str(line); + self.push('\n'); + } +} + +#[derive(Default, Debug)] +struct MyError { + libstack: Vec, + cause_module: String, +} + +fn put_module_in_string( + output: &mut String, + path: &Path, + depth: usize, + mut expand_cnt: i32, +) -> Result<(), MyError> { + let src = read_to_string(path).map_err(|_x| MyError { + libstack: vec![], + cause_module: path.to_string_lossy().to_string(), + })?; + let mut mod_state = ModState::default(); + for line in src.lines() { + if let Some(m) = is_external_mod(&mut mod_state, line) { + if expand_cnt == 0 { + continue; + }; + let rr = 10000; + expand_cnt -= 1; + println!("{} mod found: {}", ">".repeat(depth), line); + output.push_line(&format!("{}mod {} {{", m.pub_prefix, m.name)); + let mut parent_path = path.parent().unwrap().to_owned(); + let file_name = + path.file_name().unwrap().to_str().unwrap().strip_suffix(".rs").unwrap(); + if file_name != "lib" && file_name != "mod" { + parent_path = parent_path.join(file_name); + } + let same_level_path = parent_path.join(format!("{}.rs", m.name)); + let folder_path = parent_path.join(format!("{}/mod.rs", m.name)); + let child_path = if let Some(ep) = m.explicit_path { + println!("explicit path found: {:?}", ep); + parent_path.join(ep) + } else if same_level_path.exists() { + same_level_path + } else if folder_path.exists() { + folder_path + } else { + println!( + "same_level_path: {:?}\nfolder_path: {:?}\n", + same_level_path, folder_path + ); + return Err(MyError { + libstack: vec![path.to_string_lossy().to_string()], + cause_module: folder_path.to_string_lossy().to_string(), + }); + }; + if let Err(mut e) = put_module_in_string(output, &child_path, depth + 1, rr) { + e.libstack.push(path.to_string_lossy().to_string()); + return Err(e); + } + output.push_line("}"); + } else { + output.push_line(line); + } + } + Ok(()) +} + +fn main() { + let rustc_result = Command::new("rustc") + .args(&["--print", "sysroot"]) + .output() + .expect("Failed to execute rustc") + .stdout; + let sysroot = std::str::from_utf8(&rustc_result).expect("rustc output wasn't utf8"); + for what in &["std", "alloc", "core"] { + let path_string = + &format!("{}/lib/rustlib/src/rust/library/{}/src/lib.rs", sysroot.trim(), what); + let path = Path::new(&path_string); + let output_path = format!("../www/fake_{}.rs", what); + let mut output = String::default(); + put_module_in_string(&mut output, path, 0, 4000).unwrap(); + //FIXME: add it when ready: output = remove_function_body(&output); + fs::write(output_path, output.clone()).unwrap(); + } +} diff --git a/rust-pack/src/remove_function.rs b/rust-pack/src/remove_function.rs new file mode 100644 index 0000000..8e4373b --- /dev/null +++ b/rust-pack/src/remove_function.rs @@ -0,0 +1,51 @@ +fn pick_char(input: &mut &str) -> char { + let mut iter = input.chars(); + let r = iter.next().unwrap(); + *input = iter.as_str(); + r +} + +fn eat_until(input: &mut &str, output: &mut String, goal: char) { + let mut paran = if goal == 'x' { 2 } else { 0 }; + while input.len() > 0 { + let c = input.chars().next().unwrap(); + if paran > 0 { + if c == ')' || c == ']' || c == '}' { + paran -= 1; + } + } else { + if c == goal || goal == 'x' { + return; + } + if c == '(' || c == '[' || c == '{' { + paran += 1; + } + } + pick_char(input); + output.push(c); + } +} + +pub fn remove_function_body(mut input: &str) -> String { + let mut output = String::new(); + let mut char_seened = 'x'; + while input.len() > 0 { + if char_seened.is_whitespace() { + if let Some(remain) = input.strip_prefix("fn ") { + output.push_str("fn "); + input = remain; + eat_until(&mut input, &mut output, '{'); + output.push_str("{ loop {} }"); + eat_until(&mut input, &mut String::new(), 'x'); + } + } + if input.starts_with("//") { + let (comment, remain) = input.split_once("\n").unwrap(); + output.push_str(comment); + input = remain; + } + char_seened = pick_char(&mut input); + output.push(char_seened); + } + output +} diff --git a/www/.gitignore b/www/.gitignore index f06235c..3bafa5f 100644 --- a/www/.gitignore +++ b/www/.gitignore @@ -1,2 +1,3 @@ node_modules dist +fake_*.rs diff --git a/www/example-code.js b/www/example-code.js deleted file mode 100644 index 0aac950..0000000 --- a/www/example-code.js +++ /dev/null @@ -1,112 +0,0 @@ -export default `use crate::List::*; - -mod unresolved_module; - -/// println macro -macro_rules! println { - ($s:literal, $($e:expr),*) => {}; -} - -/// format macro -macro_rules! format { - ($s:literal) => {}; - ($s:literal, $($e:expr),*) => {}; -} - -enum List { - // Cons: Tuple struct that wraps an element and a pointer to the next node - Cons(u32, Box), - // Nil: A node that signifies the end of the linked list - Nil, -} - -// Methods can be attached to an enum -impl List { - /// Create an empty list - fn new() -> List { - // \`Nil\` has type \`List\` - Nil - } - - /// Consume a list, and return the same list with a new element at its front - fn prepend(self, elem: u32) -> List { - // \`Cons\` also has type List - Cons(elem, Box::new(self)) - } - - /// Return the length of the list - fn len(&self) -> u32 { - // \`self\` has to be matched, because the behavior of this method - // depends on the variant of \`self\` - // \`self\` has type \`&List\`, and \`*self\` has type \`List\`, matching on a - // concrete type \`T\` is preferred over a match on a reference \`&T\` - match *self { - // Can't take ownership of the tail, because \`self\` is borrowed; - // instead take a reference to the tail - Cons(_, ref tail) => 1 + tail.len(), - // Base Case: An empty list has zero length - Nil => 0 - } - } - - /// Return representation of the list as a (heap allocated) string - fn stringify(&self) -> String { - match *self { - Cons(head, ref tail) => { - // \`format!\` is similar to \`print!\`, but returns a heap - // allocated string instead of printing to the console - format!("{}, {}", head, tail.stringify()) - }, - Nil => { - format!("Nil") - }, - } - } -} - - -/// \`\`\`rust -/// fn main_in_comment() { -/// // This binding lives in the main function -/// let long_lived_binding = 1; -/// -/// // This is a block, and has a smaller scope than the main function -/// { -/// // This binding only exists in this block -/// let short_lived_binding = 2; -/// -/// println!("inner short: {}", short_lived_binding); -/// -/// // This binding *shadows* the outer one -/// let long_lived_binding = 5_f32; -/// -/// println!("inner long: {}", long_lived_binding); -/// } -/// // End of the block -/// -/// // Error! \`short_lived_binding\` doesn't exist in this scope -/// println!("outer short: {}", short_lived_binding); -/// // FIXME ^ Comment out this line -/// -/// println!("outer long: {}", long_lived_binding); -/// -/// // This binding also *shadows* the previous binding -/// let long_lived_binding = 'a'; -/// -/// println!("outer long: {}", long_lived_binding); -/// } -/// \`\`\` -fn main() { - // Create an empty linked list - let mut list = List::new(); - - // Prepend some elements - list = list.prepend(1); - list = list.prepend(2); - list = list.prepend(3); - - // Show the final state of the list - println!("linked list has length: {}", list.len()); - println!("{}", list.stringify()); -} -`; diff --git a/www/example-code.rs b/www/example-code.rs new file mode 100644 index 0000000..b43604d --- /dev/null +++ b/www/example-code.rs @@ -0,0 +1,27 @@ +use std::ops::Range; + +fn gav(x: i32, y: i32) -> i64 { + (x - y) * (x + y) +} + +fn main() { + let num = 5; + let a = vec![1, 2, 3]; + let b = Some(2); + let c = None; + let d = Range { start: 1, end: num }; + let e = 1..num; + let f = "sssss".to_string(); + for a in d { + for b in e { + let c = gav(gav(a, b), a); + assert_eq!(gav(a, b), a * a - b * b); + } + } + let f = d + .reduce(|a, b| { + println!("{}", a); + a * b + }) + .unwrap(); +} diff --git a/www/index.js b/www/index.js index ab340b1..742ccff 100644 --- a/www/index.js +++ b/www/index.js @@ -25,7 +25,7 @@ import 'monaco-editor/esm/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosi import 'monaco-editor/esm/vs/editor/contrib/hover/hover'; import 'monaco-editor/esm/vs/editor/contrib/inPlaceReplace/inPlaceReplace'; import 'monaco-editor/esm/vs/editor/contrib/indentation/indentation'; -import 'monaco-editor/esm/vs/editor/contrib/inlineHints/inlineHintsController'; +import 'monaco-editor/esm/vs/editor/contrib/inlayHints/inlayHintsController'; import 'monaco-editor/esm/vs/editor/contrib/linesOperations/linesOperations'; import 'monaco-editor/esm/vs/editor/contrib/linkedEditing/linkedEditing'; import 'monaco-editor/esm/vs/editor/contrib/links/links'; @@ -52,17 +52,13 @@ import 'monaco-editor/esm/vs/editor/standalone/browser/referenceSearch/standalon import 'monaco-editor/esm/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import * as rustConf from 'monaco-editor/esm/vs/basic-languages/rust/rust'; -import exampleCode from './example-code'; -import encoding from 'text-encoding'; - -if (typeof TextEncoder === "undefined") { - // Edge polyfill, https://rustwasm.github.io/docs/wasm-bindgen/reference/browser-support.html - self.TextEncoder = encoding.TextEncoder; - self.TextDecoder = encoding.TextDecoder; -} +import exampleCode from './example-code.rs'; import './index.css'; +import { conf, grammar } from './rust-grammar'; +import fake_std from './fake_std.rs'; +import fake_core from './fake_core.rs'; +import fake_alloc from './fake_alloc.rs'; var state; var allTokens; @@ -71,21 +67,21 @@ self.MonacoEnvironment = { getWorkerUrl: () => './editor.worker.bundle.js', }; -const modeId = 'ra-rust'; // not "rust" to circumvent conflict -monaco.languages.register({ // language for editor +const modeId = 'rust'; +monaco.languages.register({ id: modeId, }); -monaco.languages.register({ // language for hover info - id: 'rust', -}); + +const delay = (ms) => new Promise((res) => setTimeout(res, ms)); monaco.languages.onLanguage(modeId, async () => { console.log(modeId); - monaco.languages.setLanguageConfiguration(modeId, rustConf.conf); - monaco.languages.setLanguageConfiguration('rust', rustConf.conf); - monaco.languages.setMonarchTokensProvider('rust', rustConf.language); + monaco.languages.setLanguageConfiguration(modeId, conf); + monaco.languages.setMonarchTokensProvider(modeId, grammar); +}); +const registerRA = async () => { monaco.languages.registerHoverProvider(modeId, { provideHover: (_, pos) => state.hover(pos.lineNumber, pos.column), }); @@ -124,6 +120,28 @@ monaco.languages.onLanguage(modeId, async () => { } }, }); + monaco.languages.registerInlayHintsProvider(modeId, { + async provideInlayHints(model, range, token) { + let hints = await state.inlay_hints(); + return hints.map((hint) => { + if (hint.hint_type == 1) { + return { + kind: 1, + position: { column: hint.range.endColumn, lineNumber: hint.range.endLineNumber }, + text: `: ${hint.label}`, + }; + } + if (hint.hint_type == 2) { + return { + kind: 2, + position: { column: hint.range.startColumn, lineNumber: hint.range.startLineNumber }, + text: `${hint.label}:`, + whitespaceAfter: true, + }; + } + }) + } + }); monaco.languages.registerDocumentHighlightProvider(modeId, { async provideDocumentHighlights(_, pos) { return await state.references(pos.lineNumber, pos.column, true); @@ -229,7 +247,7 @@ monaco.languages.onLanguage(modeId, async () => { } } - monaco.languages.setTokensProvider(modeId, { + /*monaco.languages.setTokensProvider(modeId, { getInitialState: () => new TokenState(), tokenize(_, st) { const filteredTokens = allTokens @@ -246,8 +264,8 @@ monaco.languages.onLanguage(modeId, async () => { endState: new TokenState(st.line + 1), }; }, - }); -}); + });*/ +}; // Create an RA Web worker @@ -303,20 +321,39 @@ const start = async () => { document.body.appendChild(loadingText); let model = monaco.editor.createModel(exampleCode, modeId); - state = await createRA(); + window.editor = monaco.editor; + state = null; //await createRA(); async function update() { const res = await state.update(model.getValue()); monaco.editor.setModelMarkers(model, modeId, res.diagnostics); allTokens = res.highlights; } - await update(); - model.onDidChangeContent(update); + monaco.editor.defineTheme('vscode-dark-plus', { + base: 'vs-dark', + inherit: true, + colors: { + 'editorInlayHint.foreground': '#A0A0A0F0', + 'editorInlayHint.background': '#11223300', + }, + rules: [ + { token: 'keyword.control', foreground: 'C586C0' }, + { token: 'variable', foreground: '9CDCFE' }, + { token: 'support.function', foreground: 'DCDCAA' }, + ], + }); document.body.removeChild(loadingText); - + const initRA = async () => { + state = await createRA(); + await registerRA(); + await state.init(model.getValue(), fake_std, fake_core, fake_alloc); + await update(); + model.onDidChangeContent(update); + }; + initRA(); const myEditor = monaco.editor.create(document.body, { - theme: 'vs-dark', + theme: 'vscode-dark-plus', model: model }); diff --git a/www/package.json b/www/package.json index 9a805d9..17b5f15 100644 --- a/www/package.json +++ b/www/package.json @@ -9,7 +9,8 @@ "author": "rust-analyzer developers", "license": "(MIT OR Apache-2.0)", "dependencies": { - "monaco-editor": "^0.23.0", + "monaco-editor": "^0.27.0", + "raw-loader": "^4.0.2", "text-encoding": "^0.7.0" }, "devDependencies": { diff --git a/www/ra-worker.js b/www/ra-worker.js index 3830b0e..f3202f0 100644 --- a/www/ra-worker.js +++ b/www/ra-worker.js @@ -1,4 +1,4 @@ -import init, { initThreadPool, WorldState } from '../pkg/wasm_demo.js'; +import init, { initThreadPool, WorldState } from '../ra-wasm/pkg/wasm_demo.js'; const start = async () => { await init(); diff --git a/www/rust-grammar.js b/www/rust-grammar.js new file mode 100644 index 0000000..c0bd5be --- /dev/null +++ b/www/rust-grammar.js @@ -0,0 +1,142 @@ +export const conf = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '[', close: ']' }, + { open: '{', close: '}' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + folding: { + markers: { + start: new RegExp('^\\s*#pragma\\s+region\\b'), + end: new RegExp('^\\s*#pragma\\s+endregion\\b') + } + } +}; + +export const grammar = { + // Set defaultToken to invalid to see what you do not tokenize yet + // defaultToken: 'invalid', + + keywords: [ + 'as', 'break', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', + 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'Self', 'static', + 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', + 'macro_rules', + ], + + controlFlowKeywords: [ + 'continue', 'else', 'for', 'if', 'while', 'loop', 'match', + ], + + typeKeywords: [ + 'Self', 'm32', 'm64', 'm128', 'f80', 'f16', 'f128', 'int', 'uint', 'float', 'char', + 'bool', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'i8', 'i16', 'i32', 'i64', 'str', + 'Option', 'Either', 'c_float', 'c_double', 'c_void', 'FILE', 'fpos_t', 'DIR', 'dirent', + 'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort', 'c_int', 'c_uint', 'c_long', 'c_ulong', + 'size_t', 'ptrdiff_t', 'clock_t', 'time_t', 'c_longlong', 'c_ulonglong', 'intptr_t', + 'uintptr_t', 'off_t', 'dev_t', 'ino_t', 'pid_t', 'mode_t', 'ssize_t', + ], + + operators: [ + '=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=', + '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%', + '<<', '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=', '^=', + '%=', '<<=', '>>=', '>>>=' + ], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, '@brackets'], + [/@symbols/, { cases: { '@operators': 'operator', + '@default' : '' } } ], + + // @ annotations. + // As an example, we emit a debugging log message on these tokens. + // Note: message are supressed during the first load -- change some lines to see them. + [/@\s*[a-zA-Z_\$][\w\$]*/, { token: 'annotation', log: 'annotation token: $0' }], + + // numbers + [/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'], + [/0[xX][0-9a-fA-F]+/, 'number.hex'], + [/\d+/, 'number'], + + // delimiter: after number because of .\d floats + [/[;,.]/, 'delimiter'], + + // strings + [/"([^"\\]|\\.)*$/, 'string.invalid' ], // non-teminated string + [/"/, { token: 'string.quote', bracket: '@open', next: '@string' } ], + + // characters + [/'[^\\']'/, 'string'], + [/(')(@escapes)(')/, ['string','string.escape','string']], + [/'/, 'string.invalid'] + ], + + comment: [ + [/[^\/*]+/, 'comment' ], + [/\/\*/, 'comment', '@push' ], // nested comment + ["\\*/", 'comment', '@pop' ], + [/[\/*]/, 'comment' ] + ], + + string: [ + [/[^\\"]+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/"/, { token: 'string.quote', bracket: '@close', next: '@pop' } ] + ], + + whitespace: [ + [/[ \t\r\n]+/, 'white'], + [/\/\*/, 'comment', '@comment' ], + [/\/\/.*$/, 'comment'], + ], + + func_decl: [ + [ + /[a-z_$][\w$]*/, 'support.function', '@pop', + ], + ], + }, +}; diff --git a/www/webpack.config.js b/www/webpack.config.js index 4dad214..8e745e7 100644 --- a/www/webpack.config.js +++ b/www/webpack.config.js @@ -19,6 +19,10 @@ module.exports = { test: /\.css$/, use: ["style-loader", "css-loader"], }, + { + test: /\.rs$/, + use: ['raw-loader'], + }, { test: /\.ttf$/, use: ['file-loader'] @@ -27,7 +31,7 @@ module.exports = { }, plugins: [ new HtmlWebPackPlugin({ - title: "Rust Analyzer", + title: "Rust Analyzer Playground", chunks: ["app"], }), ], diff --git a/www/yarn.lock b/www/yarn.lock index c4cc0e9..0a8ac2e 100644 --- a/www/yarn.lock +++ b/www/yarn.lock @@ -2045,10 +2045,10 @@ mkdirp@^0.5.1, mkdirp@^0.5.5: dependencies: minimist "^1.2.5" -monaco-editor@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.23.0.tgz#24844ba5640c7adb3a2a3ff3b520cf2d7170a6f0" - integrity sha512-q+CP5zMR/aFiMTE9QlIavGyGicKnG2v/H8qVvybLzeFsARM8f6G9fL0sMST2tyVYCwDKkGamZUI6647A0jR/Lg== +monaco-editor@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.27.0.tgz#4b69108bb1dc1f60174c5dcdf51bc5306ab5ba26" + integrity sha512-UhwP78Wb8w0ZSYoKXQNTV/0CHObp6NS3nCt51QfKE6sKyBo5PBsvuDOHoI2ooBakc6uIwByRLHVeT7+yXQe2fQ== ms@2.0.0: version "2.0.0" @@ -2566,6 +2566,14 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + readable-stream@^2.0.1, readable-stream@^2.0.2: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"