diff --git a/.idea/runConfigurations/clippy-parser-scala-wasm-linux.xml b/.idea/runConfigurations/clippy-parser-scala-wasm-linux.xml
deleted file mode 100644
index ee8051bb01db..000000000000
--- a/.idea/runConfigurations/clippy-parser-scala-wasm-linux.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/clippy-parser-scala-wasm-macos.xml b/.idea/runConfigurations/clippy-parser-scala-wasm-macos.xml
deleted file mode 100644
index e52454623d7f..000000000000
--- a/.idea/runConfigurations/clippy-parser-scala-wasm-macos.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/clippy-parser-scala-wasm-windows.xml b/.idea/runConfigurations/clippy-parser-scala-wasm-windows.xml
deleted file mode 100644
index fc596a9a55a9..000000000000
--- a/.idea/runConfigurations/clippy-parser-scala-wasm-windows.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/clippy-parser-scala-wasm.xml b/.idea/runConfigurations/clippy-parser-scala-wasm.xml
deleted file mode 100644
index 92fa83f7aca3..000000000000
--- a/.idea/runConfigurations/clippy-parser-scala-wasm.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/doc-test-parser-scala-native.xml b/.idea/runConfigurations/doc-test-parser-scala-native.xml
deleted file mode 100644
index eba868e36b81..000000000000
--- a/.idea/runConfigurations/doc-test-parser-scala-native.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/test-parser-scala-native.xml b/.idea/runConfigurations/test-parser-scala-native.xml
deleted file mode 100644
index c26f0b996b77..000000000000
--- a/.idea/runConfigurations/test-parser-scala-native.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fcb931d9aff1..16478de5fccf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -117,6 +117,8 @@
open them in the IDE (which is not part of the React app, but can be switched
to from the dashboard). The PR also adds authentication+authorization (i.e.,
sign up and sign in for users), via either email/password or GitHub/Google.
+- [New Enso documentation parser][5917]. Smaller and faster; enables planned
+ improvements to internal documentation representation.
#### EnsoGL (rendering engine)
@@ -527,6 +529,7 @@
[5802]: https://github.com/enso-org/enso/pull/5802
[5850]: https://github.com/enso-org/enso/pull/5850
[5863]: https://github.com/enso-org/enso/pull/5863
+[5917]: https://github.com/enso-org/enso/pull/5917
#### Enso Compiler
diff --git a/Cargo.lock b/Cargo.lock
index bbf6a72b328a..0f3733615ab6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1603,6 +1603,7 @@ dependencies = [
name = "debug-scene-documentation"
version = "0.1.0"
dependencies = [
+ "enso-doc-parser",
"enso-suggestion-database",
"ensogl",
"ensogl-hardcoded-theme",
@@ -2100,6 +2101,19 @@ dependencies = [
"debug-scene-visualization",
]
+[[package]]
+name = "enso-doc-parser"
+version = "0.1.0"
+dependencies = [
+ "enso-metamodel",
+ "enso-metamodel-lexpr",
+ "enso-parser",
+ "enso-prelude",
+ "enso-profiler",
+ "enso-reflect",
+ "lexpr",
+]
+
[[package]]
name = "enso-executor"
version = "0.1.0"
@@ -2162,6 +2176,7 @@ dependencies = [
"enso-data-structures",
"enso-debug-api",
"enso-debug-scene",
+ "enso-doc-parser",
"enso-executor",
"enso-frp",
"enso-notification",
@@ -2189,7 +2204,6 @@ dependencies = [
"mockall",
"nalgebra",
"parser",
- "parser-scala",
"regex",
"semver 1.0.16",
"serde",
@@ -2520,6 +2534,7 @@ dependencies = [
"double-representation",
"engine-protocol",
"enso-data-structures",
+ "enso-doc-parser",
"enso-executor",
"enso-notification",
"enso-prelude",
@@ -2529,7 +2544,6 @@ dependencies = [
"flo_stream",
"futures 0.3.26",
"parser",
- "parser-scala",
"span-tree",
"wasm-bindgen-test",
]
@@ -4238,10 +4252,16 @@ dependencies = [
name = "ide-view-component-browser"
version = "0.1.0"
dependencies = [
+ "enso-frp",
"enso-prelude",
+ "ensogl",
+ "ensogl-gui-component",
+ "ensogl-hardcoded-theme",
"ensogl-text",
"ide-view-component-list-panel",
"ide-view-component-list-panel-breadcrumbs",
+ "ide-view-documentation",
+ "ide-view-graph-editor",
]
[[package]]
@@ -4308,8 +4328,10 @@ name = "ide-view-documentation"
version = "0.1.0"
dependencies = [
"double-representation",
+ "enso-doc-parser",
"enso-frp",
"enso-prelude",
+ "enso-profiler",
"enso-suggestion-database",
"ensogl",
"ensogl-component",
@@ -5362,29 +5384,6 @@ dependencies = [
"uuid 0.8.2",
]
-[[package]]
-name = "parser-scala"
-version = "0.1.0"
-dependencies = [
- "ast",
- "bytes 1.1.0",
- "console_error_panic_hook",
- "enso-prelude",
- "enso-profiler",
- "failure",
- "futures 0.3.26",
- "ide-ci",
- "js-sys",
- "matches",
- "reqwest",
- "serde",
- "serde_json",
- "tokio",
- "wasm-bindgen",
- "wasm-bindgen-test",
- "websocket",
-]
-
[[package]]
name = "paste"
version = "1.0.11"
diff --git a/Cargo.toml b/Cargo.toml
index 9d41e1d49a19..95d7563700a2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ members = [
"build/deprecated/rust-scripts",
"build/shader-tools",
"lib/rust/*",
+ "lib/rust/parser/doc-parser",
"lib/rust/parser/src/syntax/tree/visitor",
"lib/rust/parser/jni",
"lib/rust/parser/generate-java",
diff --git a/app/gui/Cargo.toml b/app/gui/Cargo.toml
index be07ab4f1708..a6aa3080e08d 100644
--- a/app/gui/Cargo.toml
+++ b/app/gui/Cargo.toml
@@ -16,6 +16,7 @@ enso-data-structures = { path = "../../lib/rust/data-structures" }
enso-debug-api = { path = "../../lib/rust/debug-api" }
enso-debug-scene = { path = "view/examples" }
enso-frp = { path = "../../lib/rust/frp" }
+enso-doc-parser = { path = "../../lib/rust/parser/doc-parser" }
enso-prelude = { path = "../../lib/rust/prelude" }
enso-profiler = { path = "../../lib/rust/profiler" }
enso-executor = { path = "../../lib/rust/executor" }
@@ -34,7 +35,6 @@ ensogl-drop-manager = { path = "../../lib/rust/ensogl/component/drop-manager" }
fuzzly = { path = "../../lib/rust/fuzzly" }
ast = { path = "language/ast/impl" }
parser = { path = "language/parser" }
-parser-scala = { path = "language/parser-scala" }
ide-view = { path = "view" }
engine-protocol = { path = "controller/engine-protocol" }
json-rpc = { path = "../../lib/rust/json-rpc" }
diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs
index 3bd1dab1084a..5a580451cdd9 100644
--- a/app/gui/controller/engine-protocol/src/language_server/types.rs
+++ b/app/gui/controller/engine-protocol/src/language_server/types.rs
@@ -847,62 +847,6 @@ pub enum RegisterOptions {
-// ===================
-// === Doc Section ===
-// ===================
-
-/// Text rendered as HTML (may contain HTML tags).
-pub type HtmlString = String;
-
-/// Documentation section mark.
-#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
-#[allow(missing_docs)]
-pub enum Mark {
- Important,
- Info,
- Example,
-}
-
-/// A single section of the documentation.
-#[derive(Hash, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-#[allow(missing_docs)]
-#[serde(tag = "type")]
-#[serde(rename_all = "camelCase")]
-pub enum DocSection {
- /// The documentation tag.
- #[serde(rename_all = "camelCase")]
- Tag {
- /// The tag name.
- name: String,
- /// The tag text.
- body: HtmlString,
- },
- /// The paragraph of the text.
- #[serde(rename_all = "camelCase")]
- Paragraph {
- /// The elements that make up this paragraph.
- body: HtmlString,
- },
- /// The section that starts with the key followed by the colon and the body.
- #[serde(rename_all = "camelCase")]
- Keyed {
- /// The section key.
- key: String,
- /// The elements that make up the body of the section.
- body: HtmlString,
- },
- /// The section that starts with the mark followed by the header and the body.
- #[serde(rename_all = "camelCase")]
- Marked {
- /// The section mark.
- mark: Mark,
- /// The section header.
- header: Option,
- /// The elements that make up the body of the section.
- body: HtmlString,
- },
-}
-
// ===========================
// === Suggestion Database ===
// ===========================
@@ -988,79 +932,57 @@ pub enum SuggestionEntryType {
pub enum SuggestionEntry {
#[serde(rename_all = "camelCase")]
Type {
- external_id: Option,
- name: String,
- module: String,
- params: Vec,
- parent_type: Option,
- reexport: Option,
- documentation: Option,
- documentation_html: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
+ external_id: Option,
+ name: String,
+ module: String,
+ params: Vec,
+ parent_type: Option,
+ reexport: Option,
+ documentation: Option,
},
#[serde(rename_all = "camelCase")]
Constructor {
- external_id: Option,
- name: String,
- module: String,
- arguments: Vec,
- return_type: String,
- reexport: Option,
- documentation: Option,
- documentation_html: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
+ external_id: Option,
+ name: String,
+ module: String,
+ arguments: Vec,
+ return_type: String,
+ reexport: Option,
+ documentation: Option,
},
#[serde(rename_all = "camelCase")]
Method {
- external_id: Option,
- name: String,
- module: String,
- arguments: Vec,
- self_type: String,
- return_type: String,
- is_static: bool,
- reexport: Option,
- documentation: Option,
- documentation_html: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
+ external_id: Option,
+ name: String,
+ module: String,
+ arguments: Vec,
+ self_type: String,
+ return_type: String,
+ is_static: bool,
+ reexport: Option,
+ documentation: Option,
},
#[serde(rename_all = "camelCase")]
Function {
- external_id: Option,
- name: String,
- module: String,
- arguments: Vec,
- return_type: String,
- scope: SuggestionEntryScope,
- documentation: Option,
- documentation_html: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
+ external_id: Option,
+ name: String,
+ module: String,
+ arguments: Vec,
+ return_type: String,
+ scope: SuggestionEntryScope,
+ documentation: Option,
},
#[serde(rename_all = "camelCase")]
Local {
- external_id: Option,
- name: String,
- module: String,
- return_type: String,
- scope: SuggestionEntryScope,
- documentation: Option,
- documentation_html: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
+ external_id: Option,
+ name: String,
+ module: String,
+ return_type: String,
+ scope: SuggestionEntryScope,
+ documentation: Option,
},
#[serde(rename_all = "camelCase")]
- Module {
- module: String,
- documentation: Option,
- documentation_html: Option,
- reexport: Option,
- #[serde(default, deserialize_with = "enso_prelude::deserialize_null_as_default")]
- documentation_sections: Vec,
- },
+ Module { module: String, documentation: Option, reexport: Option },
}
impl SuggestionEntry {
@@ -1116,7 +1038,7 @@ impl FieldUpdate {
}
/// Maps the field update by applying `f` on the underlying value.
- pub fn map(self, f: impl Fn(T) -> U) -> FieldUpdate {
+ pub fn map(self, f: impl FnOnce(T) -> U) -> FieldUpdate {
FieldUpdate { tag: self.tag, value: self.value.map(f) }
}
@@ -1124,7 +1046,7 @@ impl FieldUpdate {
/// Otherwise returns the error returned by `f`.
pub fn try_map(
self,
- f: impl Fn(T) -> std::result::Result,
+ f: impl FnOnce(T) -> std::result::Result,
) -> std::result::Result, E> {
Ok(FieldUpdate { tag: self.tag, value: self.value.map(f).transpose()? })
}
@@ -1182,14 +1104,13 @@ pub enum SuggestionsDatabaseUpdate {
#[serde(rename_all = "camelCase")]
pub struct SuggestionsDatabaseModification {
#[serde(default)]
- pub arguments: Vec,
- pub module: Option>,
- pub self_type: Option>,
- pub return_type: Option>,
- pub documentation: Option>,
- pub documentation_sections: Option>>,
- pub scope: Option>,
- pub reexport: Option>,
+ pub arguments: Vec,
+ pub module: Option>,
+ pub self_type: Option>,
+ pub return_type: Option>,
+ pub documentation: Option>,
+ pub scope: Option>,
+ pub reexport: Option>,
}
/// Notification about change in the suggestions database.
diff --git a/app/gui/language/parser-scala/.gitignore b/app/gui/language/parser-scala/.gitignore
deleted file mode 100644
index 9110e4072ac4..000000000000
--- a/app/gui/language/parser-scala/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Downloaded parser artifacts
-pkg
diff --git a/app/gui/language/parser-scala/Cargo.toml b/app/gui/language/parser-scala/Cargo.toml
deleted file mode 100644
index e93083c63d55..000000000000
--- a/app/gui/language/parser-scala/Cargo.toml
+++ /dev/null
@@ -1,34 +0,0 @@
-[package]
-name = "parser-scala"
-version = "0.1.0"
-authors = ["Enso Team "]
-edition = "2021"
-build = "build.rs"
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-ast = { path = "../ast/impl" }
-enso-prelude = { path = "../../../../lib/rust/prelude" }
-enso-profiler = { path = "../../../../lib/rust/profiler" }
-console_error_panic_hook = { workspace = true }
-failure = { workspace = true }
-js-sys = { workspace = true }
-matches = { workspace = true }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = { version = "1.0", features = ["unbounded_depth"] }
-wasm-bindgen = { workspace = true }
-
-[dev-dependencies]
-wasm-bindgen-test = { workspace = true }
-
-[build-dependencies]
-ide-ci = { path = "../../../../build/ci_utils" }
-bytes = { workspace = true }
-futures = { workspace = true }
-reqwest = { workspace = true }
-tokio = { workspace = true }
-
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-websocket = "0.26.5"
diff --git a/app/gui/language/parser-scala/build.rs b/app/gui/language/parser-scala/build.rs
deleted file mode 100644
index 925f2b248fdd..000000000000
--- a/app/gui/language/parser-scala/build.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-//! This build script is responsible for ensuring that if parser targets wasm,
-//! the JS Parser package is available at the expected location for
-//! `wasm_bindgen` tool.
-
-// === Features ===
-#![feature(option_result_contains)]
-
-use ide_ci::prelude::*;
-
-
-
-// =========================
-// == Hardcoded constants ==
-// =========================
-
-/// Where the crate expects to find file with compiled parser.
-/// Path relative to the crate directory.
-const PARSER_PATH: &str = "./pkg/scala-parser.js";
-
-/// Commit from `enso` repository that will be used to obtain parser from.
-const PARSER_COMMIT: &str = "649fe33ccf148d47deb6ba6a06f3babc48078e3e";
-
-/// Magic code that needs to be prepended to ScalaJS generated parser due to:
-/// https://github.com/scala-js/scala-js/issues/3677/
-const PARSER_PREAMBLE: &str = "var __ScalaJSEnv = { global: window };";
-
-/// Obtains a URL where this parser version can be downloaded.
-pub fn parser_url(version: &ParserVersion) -> reqwest::Url {
- let url_string = format!(
- "https://packages.luna-lang.org/parser-js/nightly/{}/scala-parser.js",
- version.commit
- );
- let invalid_url_msg = format!("{url_string} is an invalid URL.");
- reqwest::Url::parse(&url_string).expect(&invalid_url_msg)
-}
-
-
-
-// ===================
-// == ParserVersion ==
-// ===================
-
-/// Parser version described as commit hash from `enso` repository.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct ParserVersion {
- pub commit: String,
-}
-
-impl ParserVersion {
- /// Create a version described by given commit hash.
- pub fn from_commit(commit: String) -> ParserVersion {
- ParserVersion { commit }
- }
-
- /// The JS parser version required for this crate.
- pub fn required() -> ParserVersion {
- ParserVersion { commit: PARSER_COMMIT.into() }
- }
-}
-
-
-
-// ========================
-// == Downloading parser ==
-// ========================
-
-/// Stores information which parser version should be provided where.
-///
-/// Implementation provides methods that download desired parser version, patch it and store to the
-/// file, so parser can be consumed by `wasm_bindgen`.
-struct ParserProvider {
- /// Required parser version.
- version: ParserVersion,
- /// The path where JS file needs to be provided.
- parser_path: PathBuf,
-}
-
-impl ParserProvider {
- /// Creates a provider that obtains given parser version to a given path.
- pub fn new(version: ParserVersion, parser_path: impl AsRef) -> ParserProvider {
- let parser_path = PathBuf::from(parser_path.as_ref());
- ParserProvider { version, parser_path }
- }
-
- /// Downloads contents of JS parser into memory.
- pub async fn download(&self) -> Result {
- let url = parser_url(&self.version);
- ide_ci::io::download_all(url.clone()).await.context("Failed to download the parser.")
- }
-
- /// Stores JS parser into file, after patching with a `PARSER_PREAMBLE`.
- pub async fn patch_and_store(&self, js_parser: bytes::Bytes) -> Result {
- ide_ci::fs::tokio::write_iter(&self.parser_path, [
- PARSER_PREAMBLE.as_bytes(),
- js_parser.as_ref(),
- ])
- .await
- }
-
- /// Places required parser version in the target location.
- pub async fn run(&self) -> Result {
- let fingerprint = self.parser_path.with_file_name("parser.fingerprint");
- let opt_version = ide_ci::fs::tokio::read_to_string(&fingerprint).await;
- let changed = match opt_version {
- Err(_) => true,
- Ok(hash) => hash != PARSER_COMMIT,
- };
- if changed {
- let parser_js = self.download().await?;
- self.patch_and_store(parser_js).await?;
- ide_ci::fs::tokio::write(&fingerprint, PARSER_COMMIT).await?;
- }
- Ok(())
- }
-}
-
-
-
-// ==========
-// == main ==
-// ==========
-
-#[tokio::main]
-async fn main() -> Result {
- if ide_ci::programs::cargo::build_env::targeting_wasm() {
- let required_version = ParserVersion::required();
- let parser_path = Path::new(PARSER_PATH).absolutize()?;
- let provider = ParserProvider::new(required_version, &parser_path);
- provider.run().await?;
- }
- println!("cargo:rerun-if-changed=build.rs");
- println!("cargo:rerun-if-changed={PARSER_PATH}");
-
- Ok(())
-}
diff --git a/app/gui/language/parser-scala/src/api.rs b/app/gui/language/parser-scala/src/api.rs
deleted file mode 100644
index f057d7a1dfce..000000000000
--- a/app/gui/language/parser-scala/src/api.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! A module containing structures and traits used in parser API.
-
-use crate::prelude::*;
-
-
-
-// ===========
-// == Error ==
-// ===========
-
-/// A result of parsing code.
-pub type Result = std::result::Result;
-
-/// An error which may be result of parsing code.
-#[derive(Debug, Fail)]
-pub enum Error {
- /// Error due to inner workings of the parser.
- #[fail(display = "Internal parser error: {:?}.", _0)]
- ParsingError(String),
- /// Error related to wrapping = communication with the parser service.
- #[fail(display = "Interop error: {}.", _0)]
- InteropError(#[cause] Box),
-}
-
-/// Wraps an arbitrary `std::error::Error` as an `InteropError.`
-pub fn interop_error(error: T) -> Error
-where T: Fail {
- Error::InteropError(Box::new(error))
-}
diff --git a/app/gui/language/parser-scala/src/jsclient.rs b/app/gui/language/parser-scala/src/jsclient.rs
deleted file mode 100644
index f93255497384..000000000000
--- a/app/gui/language/parser-scala/src/jsclient.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-#![cfg(target_arch = "wasm32")]
-
-use crate::prelude::*;
-use wasm_bindgen::prelude::*;
-
-use crate::api;
-
-
-
-pub type Result = std::result::Result;
-
-#[derive(Debug, Fail)]
-pub enum Error {
- #[fail(display = "JSON (de)serialization failed: {:?}", _0)]
- JsonSerializationError(#[cause] serde_json::error::Error),
-
- #[fail(display = "Scala parser failed: {:?}.", _0)]
- ScalaException(String),
-}
-
-impl From for api::Error {
- fn from(e: Error) -> Self {
- api::interop_error(e)
- }
-}
-
-impl From for Error {
- fn from(error: serde_json::error::Error) -> Self {
- Error::JsonSerializationError(error)
- }
-}
-
-impl From for Error {
- fn from(jsvalue: JsValue) -> Self {
- Error::ScalaException(format!("{jsvalue:?}"))
- }
-}
-
-#[wasm_bindgen(module = "/pkg/scala-parser.js")]
-extern "C" {
- #[wasm_bindgen(catch)]
- fn doc_parser_generate_html_from_doc(content: String) -> std::result::Result;
-}
-
-/// Wrapper over the JS-compiled parser.
-///
-/// Can only be used when targeting WebAssembly.
-#[derive(Debug, Clone, Copy)]
-pub struct Client {}
-
-impl Client {
- /// Creates a `Client`.
- pub fn new() -> Result {
- Ok(Client {})
- }
-
- /// Calls JS doc parser to generate HTML from pure doc code without Enso's AST.
- #[profile(Detail)]
- pub fn generate_html_doc_pure(&self, code: String) -> api::Result {
- let html_code = || {
- let html_code = doc_parser_generate_html_from_doc(code)?;
- Result::Ok(html_code)
- };
- Ok(html_code()?)
- }
-}
diff --git a/app/gui/language/parser-scala/src/lib.rs b/app/gui/language/parser-scala/src/lib.rs
deleted file mode 100644
index 94b090111d67..000000000000
--- a/app/gui/language/parser-scala/src/lib.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-//! Crate wrapping parser API in nice-to-use Rust code.
-//!
-//! The Parser is a library written in scala. There are two implementations of Rust wrappers to
-//! this parser: one for local parser which binds scala parser compiled to WebAssembly to the Rust
-//! crate. The second is calling a Parser running remotely using WebSockets.
-
-// === Features ===
-#![feature(trait_alias)]
-// === Standard Linter Configuration ===
-#![deny(non_ascii_idents)]
-#![warn(unsafe_code)]
-#![allow(clippy::bool_to_int_with_if)]
-#![allow(clippy::let_and_return)]
-// === Non-Standard Linter Configuration ===
-#![warn(missing_docs)]
-#![warn(trivial_casts)]
-#![warn(trivial_numeric_casts)]
-#![warn(unused_import_braces)]
-#![warn(unused_qualifications)]
-#![warn(missing_copy_implementations)]
-#![warn(missing_debug_implementations)]
-
-
-// ==============
-// === Export ===
-// ==============
-
-pub mod api;
-
-
-
-mod jsclient;
-mod wsclient;
-
-use crate::prelude::*;
-
-use std::panic;
-
-
-
-#[allow(missing_docs)]
-pub mod prelude {
- pub use ast::traits::*;
- pub use enso_prelude::*;
- pub use enso_profiler as profiler;
- pub use enso_profiler::prelude::*;
-}
-
-
-
-// ==========================================
-// === 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, Deref, DerefMut)]
-pub struct DocParser(pub Rc>);
-
-impl DocParser {
- /// Obtains a default doc parser implementation.
- #[cfg(not(target_arch = "wasm32"))]
- pub fn new() -> api::Result {
- 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 {
- 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 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 {
- self.borrow_mut().generate_html_doc_pure(code)
- }
-}
-
-
-// === Support ===
-
-/// Websocket parser client.
-/// Used as an interface for our (scala) parser.
-#[cfg(not(target_arch = "wasm32"))]
-type Client = wsclient::Client;
-/// Javascript parser client.
-/// Used as an interface for our (scala) parser.
-#[cfg(target_arch = "wasm32")]
-type Client = jsclient::Client;
diff --git a/app/gui/language/parser-scala/src/test_utils.rs b/app/gui/language/parser-scala/src/test_utils.rs
deleted file mode 100644
index 126f7a4759c7..000000000000
--- a/app/gui/language/parser-scala/src/test_utils.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-//! Utilities for writing tests using parser. Should not be used in production parts.
-
-use crate::prelude::*;
-use enso_text::unit::*;
-
-use crate::Parser;
-
-use ast::test_utils::expect_shape;
-use ast::test_utils::expect_single_line;
-use ast::test_utils::validate_spans;
-use ast::Ast;
-use ast::HasRepr;
-use ast::Shape;
-
-
-
-/// Additional methods for parser to ease writing tests.
-pub trait ParserTestExts {
- /// Program is expected to be a module with a single non-emty line. Its AST
- /// is reinterpret as given `Shape`.
- fn parse_shape(&self, program: impl Str) -> T
- where
- for<'t> &'t Shape: TryInto<&'t T>,
- T: Clone + 'static;
-
- /// Runs parser on given input, panics on any error.
- fn parse_testing(&self, program: impl Str) -> Ast;
-}
-
-impl ParserTestExts for Parser {
- fn parse_shape(&self, program: impl Str) -> T
- where
- for<'t> &'t Shape: TryInto<&'t T>,
- T: Clone + 'static, {
- let ast = self.parse_testing(program);
- let line = expect_single_line(&ast);
- let shape = expect_shape(line);
- shape.clone()
- }
-
- fn parse_testing(&self, program: impl Str) -> Ast {
- let program = program.into();
- debug!("parsing {}", program);
- let ast = self.parse(program.clone(), default()).unwrap();
- assert_eq!(ast.shape().len(), program.len().bytes());
- validate_spans(&ast);
- assert_eq!(ast.repr(), program, "{ast:?}");
- ast
- }
-}
diff --git a/app/gui/language/parser-scala/src/wsclient.rs b/app/gui/language/parser-scala/src/wsclient.rs
deleted file mode 100644
index 03fb536e010d..000000000000
--- a/app/gui/language/parser-scala/src/wsclient.rs
+++ /dev/null
@@ -1,229 +0,0 @@
-#![cfg(not(target_arch = "wasm32"))]
-
-use crate::api::Error::*;
-use crate::prelude::*;
-
-use crate::api;
-
-use std::fmt::Formatter;
-use websocket::stream::sync::TcpStream;
-use websocket::ClientBuilder;
-use websocket::Message;
-
-
-
-type WsTcpClient = websocket::sync::Client;
-
-
-
-// ==========================
-// == Constants & literals ==
-// ==========================
-
-pub const LOCALHOST: &str = "localhost";
-pub const DEFAULT_PORT: i32 = 30615;
-pub const DEFAULT_HOSTNAME: &str = LOCALHOST;
-
-pub const HOSTNAME_VAR: &str = "ENSO_PARSER_HOSTNAME";
-pub const PORT_VAR: &str = "ENSO_PARSER_PORT";
-
-
-
-// ===========
-// == Error ==
-// ===========
-
-pub type Result = std::result::Result;
-
-#[allow(clippy::enum_variant_names)]
-#[derive(Debug, Fail)]
-pub enum Error {
- #[fail(display = "Failed to parse given address url: {}", _0)]
- WrongUrl(#[cause] websocket::client::ParseError),
-
- #[fail(display = "Connection error: {}", _0)]
- ConnectivityError(#[cause] websocket::WebSocketError),
-
- #[fail(display = "Peer has closed the connection")]
- PeerClosedConnection,
-
- #[fail(display = "Received non-text response: {:?}", _0)]
- NonTextResponse(websocket::OwnedMessage),
-
- #[fail(display = "JSON (de)serialization failed: {:?}", _0)]
- JsonSerializationError(#[cause] serde_json::error::Error),
-
- #[fail(display = "JSON deserialization failed: {:?}, JSON was: {}", _0, _1)]
- JsonDeserializationError(#[cause] serde_json::error::Error, String),
-}
-
-impl From for api::Error {
- fn from(e: Error) -> Self {
- api::interop_error(e)
- }
-}
-impl From for Error {
- fn from(error: websocket::client::ParseError) -> Self {
- Error::WrongUrl(error)
- }
-}
-impl From for Error {
- fn from(error: websocket::WebSocketError) -> Self {
- Error::ConnectivityError(error)
- }
-}
-impl From for Error {
- fn from(error: serde_json::error::Error) -> Self {
- Error::JsonSerializationError(error)
- }
-}
-
-
-
-// ==============
-// == Protocol ==
-// ==============
-
-/// All request supported by the Parser Service.
-#[allow(clippy::enum_variant_names)]
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-pub enum Request {
- DocParserGenerateHtmlFromDoc { code: String },
-}
-
-/// All responses that Doc Parser Service might reply with.
-#[derive(Debug, serde::Deserialize)]
-pub enum ResponseDoc {
- SuccessDoc { code: String },
- Error { message: String },
-}
-
-
-
-// ============
-// == Config ==
-// ============
-
-/// Describes a WS endpoint.
-#[derive(Debug, Clone)]
-pub struct Config {
- pub host: String,
- pub port: i32,
-}
-
-impl Config {
- /// Formats URL String describing a WS endpoint.
- pub fn address_string(&self) -> String {
- format!("ws://{}:{}", self.host, self.port)
- }
-
- /// Obtains a default WS endpoint to use to connect to parser service
- /// using environment variables or, if they are not set, hardcoded
- /// defaults.
- pub fn from_env() -> Config {
- let host = env::env_var_or(HOSTNAME_VAR, DEFAULT_HOSTNAME);
- let port = env::parse_var_or(PORT_VAR, DEFAULT_PORT);
- Config { host, port }
- }
-}
-
-
-
-// ============
-// == Client ==
-// ============
-
-/// Client to the Parser Service written in Scala.
-///
-/// Connects through WebSocket to the running service.
-pub struct Client {
- connection: WsTcpClient,
-}
-
-mod internal {
- use super::*;
- impl Client {
- /// Serializes `Request` to JSON and sends to peer as a text message.
- pub fn send_request(&mut self, request: Request) -> Result<()> {
- let request_txt = serde_json::to_string(&request)?;
- let message = Message::text(request_txt);
- self.connection.send_message(&message)?;
- Ok(())
- }
-
- /// 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 {
- 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 `ResponseDoc`.
- ///
- /// Both request and response are exchanged in JSON using text messages
- /// over WebSocket.
- pub fn rpc_call_doc(&mut self, request: Request) -> Result {
- self.send_request(request)?;
- self.recv_response_doc()
- }
- }
-}
-
-impl Client {
- /// Creates a new `Client` connected to the already running parser service.
- pub fn from_conf(config: &Config) -> Result {
- let address = config.address_string();
- let mut builder = ClientBuilder::new(&address)?;
- let connection = builder.connect_insecure()?;
- Ok(Client { connection })
- }
-
- /// Creates a `Client` using configuration defined by environment or
- /// defaults if environment is not set.
- pub fn new() -> Result {
- // This parser is used only for native debugging, it is not used in production.
- // As such, we can use debug macros here.
- let config = Config::from_env();
- debug!("Connecting to {}", config.address_string());
- let client = Client::from_conf(&config)?;
- debug!("Established connection with {}", config.address_string());
- Ok(client)
- }
-
- /// Sends a request to parser service to generate HTML code from pure documentation code.
- #[profile(Detail)]
- pub fn generate_html_doc_pure(&mut self, code: String) -> api::Result {
- 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)),
- }
- }
-}
-
-impl Debug for Client {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- write!(f, "")
- }
-}
-
-
-
-// ===========
-// == tests ==
-// ===========
-
-#[test]
-fn wrong_url_reported() {
- let invalid_hostname = String::from("bgjhkb 7");
- let wrong_config = Config { host: invalid_hostname, port: 8080 };
- let client = Client::from_conf(&wrong_config);
- let got_wrong_url_error = matches::matches!(client, Err(Error::WrongUrl(_)));
- assert!(got_wrong_url_error, "expected WrongUrl error");
-}
diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs
index ad51bdd2a2c6..bf011c48dbef 100644
--- a/app/gui/src/controller/searcher.rs
+++ b/app/gui/src/controller/searcher.rs
@@ -1424,18 +1424,17 @@ impl Searcher {
let self_type = module.clone();
for method in &["data", "root"] {
let entry = model::suggestion_database::Entry {
- name: (*method).to_owned(),
- kind: model::suggestion_database::entry::Kind::Method,
- defined_in: module.clone(),
- arguments: vec![],
- return_type: "Standard.Base.System.File.File".try_into().unwrap(),
- documentation: vec![],
- documentation_html: None,
- self_type: Some(self_type.clone()),
- is_static: true,
- scope: model::suggestion_database::entry::Scope::Everywhere,
- icon_name: None,
- reexported_in: None,
+ name: (*method).to_owned(),
+ kind: model::suggestion_database::entry::Kind::Method,
+ defined_in: module.clone(),
+ arguments: vec![],
+ return_type: "Standard.Base.System.File.File".try_into().unwrap(),
+ documentation: vec![],
+ self_type: Some(self_type.clone()),
+ is_static: true,
+ scope: model::suggestion_database::entry::Scope::Everywhere,
+ icon_name: None,
+ reexported_in: None,
};
let action = Action::Suggestion(action::Suggestion::FromDatabase(Rc::new(entry)));
libraries_cat_builder.add_action(action);
diff --git a/app/gui/src/controller/searcher/action.rs b/app/gui/src/controller/searcher/action.rs
index d052681b196b..22c1a689a937 100644
--- a/app/gui/src/controller/searcher/action.rs
+++ b/app/gui/src/controller/searcher/action.rs
@@ -60,11 +60,10 @@ impl Suggestion {
/// Return the documentation assigned to the suggestion.
pub fn documentation_html(&self) -> Option<&str> {
- let doc_html = match self {
- Suggestion::FromDatabase(s) => &s.documentation_html,
- Suggestion::Hardcoded(s) => &s.documentation_html,
- };
- doc_html.as_ref().map(AsRef::::as_ref)
+ // This module is mostly obsolete and used as a test API (#5661). This functionality has not
+ // been ported to the new documentation parser, but is not needed for anything the old
+ // searcher is still used for.
+ None
}
/// The Id of the method called by a suggestion, or [`None`] if the suggestion is not a method
diff --git a/app/gui/src/controller/searcher/component.rs b/app/gui/src/controller/searcher/component.rs
index 1c32d6f59ed6..2c22986e29ff 100644
--- a/app/gui/src/controller/searcher/component.rs
+++ b/app/gui/src/controller/searcher/component.rs
@@ -10,7 +10,8 @@ use controller::searcher::action::MatchKind;
use convert_case::Case;
use convert_case::Casing;
use double_representation::name::QualifiedName;
-use engine_protocol::language_server::DocSection;
+use enso_doc_parser::DocSection;
+
// ==============
@@ -223,7 +224,7 @@ impl Component {
match &self.data {
Data::FromDatabase { entry, .. } => entry.documentation.iter().any(|doc| match doc {
DocSection::Tag { name, .. } =>
- name == ast::constants::PRIVATE_DOC_SECTION_TAG_NAME,
+ name == &ast::constants::PRIVATE_DOC_SECTION_TAG_NAME,
_ => false,
}),
_ => false,
@@ -237,7 +238,7 @@ impl Component {
Data::FromDatabase { entry, .. } => {
let aliases = entry.documentation.iter().filter_map(|doc| match doc {
DocSection::Tag { name, body }
- if name == ast::constants::ALIAS_DOC_SECTION_TAG_NAME =>
+ if name == &ast::constants::ALIAS_DOC_SECTION_TAG_NAME =>
Some(body.as_str().split(',').map(|s| s.trim())),
_ => None,
});
diff --git a/app/gui/src/controller/searcher/component/hardcoded.rs b/app/gui/src/controller/searcher/component/hardcoded.rs
index 435b565b6039..ff94aa03db0b 100644
--- a/app/gui/src/controller/searcher/component/hardcoded.rs
+++ b/app/gui/src/controller/searcher/component/hardcoded.rs
@@ -8,6 +8,7 @@
use crate::prelude::*;
use double_representation::name::QualifiedName;
+use enso_doc_parser::DocSection;
use ide_view::component_browser::component_list_panel::grid::entry::icon::Id as IconId;
@@ -72,18 +73,18 @@ pub fn input_snippets_with_matching_return_type(
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Snippet {
/// The name displayed in the [Component Browser](crate::controller::searcher).
- pub name: &'static str,
+ pub name: &'static str,
/// The code inserted when picking the snippet.
- pub code: &'static str,
+ pub code: &'static str,
/// A list of types that the return value of this snippet's code typechecks as. Used by the
/// [Component Browser](crate::controller::searcher) to decide whether to display the
/// snippet when filtering components by return type.
- pub return_types: Vec,
+ pub return_types: Vec,
/// The documentation bound to the snippet.
- pub documentation_html: Option,
+ pub documentation: Option>,
/// The ID of the icon bound to this snippet's entry in the [Component
/// Browser](crate::controller::searcher).
- pub icon: IconId,
+ pub icon: IconId,
}
impl Snippet {
@@ -101,15 +102,10 @@ impl Snippet {
self
}
- /// Returns a modified suggestion with [`Snippet::documentation_html`] field set. This method
- /// is only intended to be used when defining hardcoded suggestions and panics if a
- /// documentation parser cannot be created or the argument fails to parse as valid
- /// documentation.
+ /// Returns a modified suggestion with the [`Snippet::documentation`] field set. This method
+ /// is only intended to be used when defining hardcoded suggestions.
fn with_documentation(mut self, documentation: &str) -> Self {
- let doc_parser = parser_scala::DocParser::new().unwrap();
- let doc_string = documentation.to_string();
- let documentation_html = doc_parser.generate_html_doc_pure(doc_string);
- self.documentation_html = Some(documentation_html.unwrap());
+ self.documentation = Some(enso_doc_parser::parse(documentation));
self
}
}
diff --git a/app/gui/src/presenter/searcher.rs b/app/gui/src/presenter/searcher.rs
index 1d5bf1c20d04..d837ca99b60a 100644
--- a/app/gui/src/presenter/searcher.rs
+++ b/app/gui/src/presenter/searcher.rs
@@ -8,7 +8,6 @@ use crate::controller::searcher::action::Suggestion;
use crate::controller::searcher::component;
use crate::controller::searcher::Mode;
use crate::controller::searcher::Notification;
-use crate::controller::searcher::UserAction;
use crate::executor::global::spawn_stream_handler;
use crate::model::module::NodeMetadata;
use crate::model::suggestion_database::entry::Kind;
@@ -27,7 +26,6 @@ use ide_view::component_browser::component_list_panel::SECTION_NAME_CRUMB_INDEX;
use ide_view::graph_editor::component::node as node_view;
use ide_view::graph_editor::GraphEditor;
use ide_view::project::SearcherParams;
-use ide_view::project::SearcherVariant;
// ==============
@@ -104,31 +102,6 @@ impl Model {
}
}
- fn entry_used_as_suggestion(
- &self,
- entry_id: view::searcher::entry::Id,
- ) -> Option<(ViewNodeId, node_view::Expression)> {
- match self.controller.use_as_suggestion(entry_id) {
- Ok(new_code) => {
- let new_code_and_trees = node_view::Expression::new_plain(new_code);
- self.update_breadcrumbs();
- Some((self.input_view, new_code_and_trees))
- }
- Err(err) => {
- error!("Error while applying suggestion: {err}.");
- None
- }
- }
- }
-
- /// Should be called if an entry is selected but not used yet. Only used for the old searcher
- /// API.
- fn entry_selected_as_suggestion(&self, entry_id: view::searcher::entry::Id) {
- if let Err(error) = self.controller.preview_entry_as_suggestion(entry_id) {
- warn!("Failed to preview entry {entry_id:?} because of error: {error:?}.");
- }
- }
-
fn commit_editing(&self, entry_id: Option) -> Option {
let result = match entry_id {
Some(id) => self.controller.execute_action_by_index(id),
@@ -140,10 +113,6 @@ impl Model {
})
}
- fn create_providers(&self) -> provider::Any {
- provider::create_providers_from_controller(&self.controller)
- }
-
fn suggestion_for_entry_id(
&self,
id: component_grid::GroupEntryId,
@@ -205,26 +174,23 @@ impl Model {
fn update_breadcrumbs(&self) {
let names = self.controller.breadcrumbs().into_iter();
- if let SearcherVariant::ComponentBrowser(browser) = self.view.searcher() {
- // We only update the breadcrumbs starting from the second element because the first
- // one is reserved as a section name.
- let from = 1;
- let breadcrumbs_from = (names.map(Into::into).collect(), from);
- browser.model().list.model().breadcrumbs.set_entries_from(breadcrumbs_from);
- }
+ let browser = self.view.searcher();
+ // We only update the breadcrumbs starting from the second element because the first
+ // one is reserved as a section name.
+ let from = 1;
+ let breadcrumbs_from = (names.map(Into::into).collect(), from);
+ browser.model().list.model().breadcrumbs.set_entries_from(breadcrumbs_from);
}
fn show_breadcrumbs_ellipsis(&self, show: bool) {
- if let SearcherVariant::ComponentBrowser(browser) = self.view.searcher() {
- browser.model().list.model().breadcrumbs.show_ellipsis(show);
- }
+ let browser = self.view.searcher();
+ browser.model().list.model().breadcrumbs.show_ellipsis(show);
}
fn set_section_name_crumb(&self, text: ImString) {
- if let SearcherVariant::ComponentBrowser(browser) = self.view.searcher() {
- let breadcrumbs = &browser.model().list.model().breadcrumbs;
- breadcrumbs.set_entry((SECTION_NAME_CRUMB_INDEX, text.into()));
- }
+ let browser = self.view.searcher();
+ let breadcrumbs = &browser.model().list.model().breadcrumbs;
+ breadcrumbs.set_entry((SECTION_NAME_CRUMB_INDEX, text.into()));
}
fn on_active_section_change(&self, section_id: component_grid::SectionId) {
@@ -281,13 +247,8 @@ impl Model {
match component.data {
component::Data::FromDatabase { id, .. } =>
self.controller.documentation_for_entry(*id),
- component::Data::Virtual { snippet } => {
- if let Some(documentation) = &snippet.documentation_html {
- EntryDocumentation::Builtin(documentation.into())
- } else {
- default()
- }
- }
+ component::Data::Virtual { snippet } =>
+ snippet.documentation.as_ref().map_or_default(EntryDocumentation::builtin),
}
} else {
default()
@@ -306,15 +267,6 @@ impl Model {
default()
}
}
-
- fn should_auto_select_first_action(&self) -> bool {
- let user_action = self.controller.current_user_action();
- let list_not_empty = matches!(self.controller.actions(), controller::searcher::Actions::Loaded {list} if list.matching_count() > 0);
- // Usually we don't want to select first entry and display docs when user finished typing
- // function or argument.
- let starting_typing = user_action == UserAction::StartingTypingArgument;
- !starting_typing && list_not_empty
- }
}
/// The Searcher presenter, synchronizing state between searcher view and searcher controller.
@@ -345,81 +297,61 @@ impl Searcher {
eval model.view.searcher_input_changed ((expr) model.input_changed(expr));
action_list_changed <- source::<()>();
- select_entry <- action_list_changed.filter(f_!(model.should_auto_select_first_action()));
eval_ model.view.toggle_component_browser_private_entries_visibility (
model.controller.reload_list());
}
- match model.view.searcher() {
- SearcherVariant::ComponentBrowser(browser) => {
- let grid = &browser.model().list.model().grid;
- let navigator = &browser.model().list.model().section_navigator;
- let breadcrumbs = &browser.model().list.model().breadcrumbs;
- let documentation = &browser.model().documentation;
- frp::extend! { network
- eval_ action_list_changed ([model, grid, navigator] {
- model.provider.take();
- let controller_provider = model.controller.provider();
- let namespace_section_count = controller_provider.namespace_section_count();
- navigator.set_namespace_section_count.emit(namespace_section_count);
- let provider = provider::Component::provide_new_list(controller_provider, &grid);
- *model.provider.borrow_mut() = Some(provider);
- });
- new_input <- grid.suggestion_accepted.filter_map(f!((e) model.suggestion_accepted(*e)));
- graph.set_node_expression <+ new_input;
-
- entry_selected <- grid.active.filter_map(|&s| s?.as_entry_id());
- selected_entry_changed <- entry_selected.on_change().constant(());
- grid.unhover_element <+ any2(
- &selected_entry_changed,
- &model.view.toggle_component_browser_private_entries_visibility,
- );
- hovered_not_selected <- all_with(&grid.hovered, &grid.active, |h, s| {
- match (h, s) {
- (Some(h), Some(s)) => h != s,
- _ => false,
- }
- });
- documentation.frp.show_hovered_item_preview_caption <+ hovered_not_selected;
- docs_params <- all3(&action_list_changed, &grid.active, &grid.hovered);
- docs <- docs_params.filter_map(f!([model]((_, selected, hovered)) {
- let entry = hovered.as_ref().or(selected.as_ref());
- entry.map(|entry| {
- if let Some(group_id) = entry.as_header() {
- model.documentation_of_group(group_id)
- } else {
- let entry_id = entry.as_entry_id().expect("GroupEntryId");
- model.documentation_of_component(entry_id)
- }
- })
- }));
- documentation.frp.display_documentation <+ docs;
-
- eval_ grid.suggestion_accepted([]analytics::remote_log_event("component_browser::suggestion_accepted"));
- eval entry_selected((entry) model.suggestion_selected(*entry));
- eval grid.module_entered((id) model.module_entered(*id));
- eval breadcrumbs.selected((id) model.breadcrumb_selected(*id));
- active_section <- grid.active_section.filter_map(|s| *s);
- eval active_section((section) model.on_active_section_change(*section));
- }
- }
- SearcherVariant::OldNodeSearcher(searcher) => {
- let searcher = &searcher.frp;
-
- frp::extend! { network
- new_providers <- action_list_changed.map(f_!(model.create_providers()));
- searcher.set_actions <+ new_providers;
- searcher.select_action <+ select_entry.constant(0);
- used_as_suggestion <- searcher.used_as_suggestion.filter_map(|entry| *entry);
- new_input <- used_as_suggestion.filter_map(f!((e) model.entry_used_as_suggestion(*e)));
- graph.set_node_expression <+ new_input;
- eval searcher.selected_entry([model](entry)
- if let Some(id) = entry { model.entry_selected_as_suggestion(*id)});
-
- eval_ searcher.used_as_suggestion([]analytics::remote_log_event("searcher::used_as_suggestion"));
+ let browser = model.view.searcher();
+ let grid = &browser.model().list.model().grid;
+ let navigator = &browser.model().list.model().section_navigator;
+ let breadcrumbs = &browser.model().list.model().breadcrumbs;
+ let documentation = &browser.model().documentation;
+ frp::extend! { network
+ eval_ action_list_changed ([model, grid, navigator] {
+ model.provider.take();
+ let controller_provider = model.controller.provider();
+ let namespace_section_count = controller_provider.namespace_section_count();
+ navigator.set_namespace_section_count.emit(namespace_section_count);
+ let provider = provider::Component::provide_new_list(controller_provider, &grid);
+ *model.provider.borrow_mut() = Some(provider);
+ });
+ new_input <- grid.suggestion_accepted.filter_map(f!((e) model.suggestion_accepted(*e)));
+ graph.set_node_expression <+ new_input;
+
+ entry_selected <- grid.active.filter_map(|&s| s?.as_entry_id());
+ selected_entry_changed <- entry_selected.on_change().constant(());
+ grid.unhover_element <+ any2(
+ &selected_entry_changed,
+ &model.view.toggle_component_browser_private_entries_visibility,
+ );
+ hovered_not_selected <- all_with(&grid.hovered, &grid.active, |h, s| {
+ match (h, s) {
+ (Some(h), Some(s)) => h != s,
+ _ => false,
}
- }
+ });
+ documentation.frp.show_hovered_item_preview_caption <+ hovered_not_selected;
+ docs_params <- all3(&action_list_changed, &grid.active, &grid.hovered);
+ docs <- docs_params.filter_map(f!([model]((_, selected, hovered)) {
+ let entry = hovered.as_ref().or(selected.as_ref());
+ entry.map(|entry| {
+ if let Some(group_id) = entry.as_header() {
+ model.documentation_of_group(group_id)
+ } else {
+ let entry_id = entry.as_entry_id().expect("GroupEntryId");
+ model.documentation_of_component(entry_id)
+ }
+ })
+ }));
+ documentation.frp.display_documentation <+ docs;
+
+ eval_ grid.suggestion_accepted([]analytics::remote_log_event("component_browser::suggestion_accepted"));
+ eval entry_selected((entry) model.suggestion_selected(*entry));
+ eval grid.module_entered((id) model.module_entered(*id));
+ eval breadcrumbs.selected((id) model.breadcrumb_selected(*id));
+ active_section <- grid.active_section.filter_map(|s| *s);
+ eval active_section((section) model.on_active_section_change(*section));
}
let weak_model = Rc::downgrade(&model);
diff --git a/app/gui/suggestion-database/Cargo.toml b/app/gui/suggestion-database/Cargo.toml
index a5f5a67ca0e0..4a518646f741 100644
--- a/app/gui/suggestion-database/Cargo.toml
+++ b/app/gui/suggestion-database/Cargo.toml
@@ -9,11 +9,11 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
enso-prelude = { path = "../../../lib/rust/prelude" }
+enso-doc-parser = { path = "../../../lib/rust/parser/doc-parser" }
convert_case = { workspace = true }
span-tree = { path = "../language/span-tree" }
ast = { path = "../language/ast/impl" }
parser = { path = "../language/parser" }
-parser-scala = { path = "../language/parser-scala" }
enso-profiler = { path = "../../../lib/rust/profiler" }
enso-text = { path = "../../../lib/rust/text" }
double-representation = { path = "../controller/double-representation" }
diff --git a/app/gui/suggestion-database/src/documentation_ir.rs b/app/gui/suggestion-database/src/documentation_ir.rs
index 6a6590b2afd8..51ecbc89eb94 100644
--- a/app/gui/suggestion-database/src/documentation_ir.rs
+++ b/app/gui/suggestion-database/src/documentation_ir.rs
@@ -15,8 +15,8 @@ use crate::NoSuchEntry;
use crate::SuggestionDatabase;
use double_representation::name::QualifiedName;
-use engine_protocol::language_server::DocSection;
-use engine_protocol::language_server::Mark;
+use enso_doc_parser::DocSection;
+use enso_doc_parser::Mark;
use std::cmp::Ordering;
@@ -32,10 +32,8 @@ use std::cmp::Ordering;
pub enum EntryDocumentation {
/// No documentation available.
Placeholder(Placeholder),
- /// Documentation of the entry provided by the Engine.
+ /// Documentation for the entry.
Docs(Documentation),
- /// Documentation of builtin components that are not included in the suggestion database.
- Builtin(ImString),
}
impl Default for EntryDocumentation {
@@ -85,6 +83,12 @@ impl EntryDocumentation {
}
}
+ /// Create documentation for a hard-coded builtin entry.
+ pub fn builtin(sections: impl IntoIterator- ) -> Self {
+ let sections = Rc::new(BuiltinDocumentation::from_doc_sections(sections.into_iter()));
+ Self::Docs(Documentation::Builtin(sections))
+ }
+
fn method_docs(
db: &SuggestionDatabase,
entry: &Entry,
@@ -169,6 +173,7 @@ pub enum Documentation {
ModuleMethod { name: Rc, module_docs: Rc },
Function(Rc),
Local(Rc),
+ Builtin(Rc),
}
impl Documentation {
@@ -329,6 +334,31 @@ impl LocalDocumentation {
}
+
+// ============================
+// === BuiltinDocumentation ===
+// ============================
+
+/// Documentation of hard-coded builtin entries.
+#[derive(Debug, Clone, CloneRef, PartialEq)]
+#[allow(missing_docs)]
+pub struct BuiltinDocumentation {
+ pub synopsis: Synopsis,
+}
+
+impl BuiltinDocumentation {
+ /// Constructor.
+ pub fn from_doc_sections(sections: impl IntoIterator
- ) -> Self {
+ let FilteredDocSections { tags, synopsis, examples } =
+ FilteredDocSections::new(sections.into_iter());
+ debug_assert!(tags.is_empty());
+ debug_assert!(examples.is_empty());
+ Self { synopsis }
+ }
+}
+
+
+
// ============
// === Tags ===
// ============
diff --git a/app/gui/suggestion-database/src/entry.rs b/app/gui/suggestion-database/src/entry.rs
index bf66203e54df..26184161ebd0 100644
--- a/app/gui/suggestion-database/src/entry.rs
+++ b/app/gui/suggestion-database/src/entry.rs
@@ -13,9 +13,9 @@ use double_representation::name;
use double_representation::name::QualifiedName;
use double_representation::name::QualifiedNameRef;
use engine_protocol::language_server;
-use engine_protocol::language_server::DocSection;
use engine_protocol::language_server::FieldUpdate;
use engine_protocol::language_server::SuggestionsDatabaseModification;
+use enso_doc_parser::DocSection;
use enso_text::Location;
use language_server::types::FieldAction;
@@ -226,30 +226,28 @@ pub enum Scope {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Entry {
/// A type of suggestion.
- pub kind: Kind,
+ pub kind: Kind,
/// A module where the suggested object is defined.
- pub defined_in: QualifiedName,
+ pub defined_in: QualifiedName,
/// A name of suggested object.
- pub name: String,
+ pub name: String,
/// Argument lists of suggested object (atom or function). If the object does not take any
/// arguments, the list is empty.
- pub arguments: Vec,
+ pub arguments: Vec,
/// A type returned by the suggested object.
- pub return_type: QualifiedName,
+ pub return_type: QualifiedName,
/// A module reexporting this entity.
- pub reexported_in: Option,
- /// A HTML documentation associated with object.
- pub documentation_html: Option,
+ pub reexported_in: Option,
/// A list of documentation sections associated with object.
- pub documentation: Vec,
+ pub documentation: Vec,
/// A type of the "self" argument. This field is `None` for non-method suggestions.
- pub self_type: Option,
+ pub self_type: Option,
/// A flag set to true if the method is a static or module method.
- pub is_static: bool,
+ pub is_static: bool,
/// A scope where this suggestion is visible.
- pub scope: Scope,
+ pub scope: Scope,
/// A name of a custom icon to use when displaying the entry.
- pub icon_name: Option,
+ pub icon_name: Option,
}
@@ -258,7 +256,7 @@ pub struct Entry {
impl Entry {
/// Create new entry with required parameters only.
///
- /// The entry will be flagget as non-static, with [`Scope::Everywhere`] and all optional fields
+ /// The entry will be flagged as non-static, with [`Scope::Everywhere`] and all optional fields
/// will be [`None`].
pub fn new(
kind: Kind,
@@ -274,7 +272,6 @@ impl Entry {
return_type: return_type.into(),
is_static: false,
reexported_in: None,
- documentation_html: None,
documentation: default(),
self_type: None,
scope: Scope::Everywhere,
@@ -392,12 +389,6 @@ impl Entry {
self
}
- /// Takes self and returns it with new [`documentation_html`] value.
- pub fn with_documentation(mut self, html: impl Into) -> Self {
- self.documentation_html = Some(html.into());
- self
- }
-
/// Takes self and returns it with new [`documentation`] value;
pub fn with_doc_sections(mut self, sections: Vec) -> Self {
self.documentation = sections;
@@ -604,19 +595,17 @@ impl Entry {
})
}
- let (documentation, icon_name, doc_sections) = match &mut entry {
- Type { documentation, documentation_html, documentation_sections, .. }
- | Constructor { documentation, documentation_html, documentation_sections, .. }
- | Method { documentation, documentation_html, documentation_sections, .. }
- | Module { documentation, documentation_html, documentation_sections, .. }
- | Function { documentation, documentation_html, documentation_sections, .. }
- | Local { documentation, documentation_html, documentation_sections, .. } => {
- let documentation =
- Self::make_html_docs(mem::take(documentation), mem::take(documentation_html));
- let icon_name = find_icon_name_in_doc_sections(&*documentation_sections);
- (documentation, icon_name, mem::take(documentation_sections))
- }
+ let documentation = match &entry {
+ Type { documentation, .. }
+ | Constructor { documentation, .. }
+ | Method { documentation, .. }
+ | Module { documentation, .. }
+ | Function { documentation, .. }
+ | Local { documentation, .. } =>
+ documentation.as_ref().map(|s| s.as_ref()).unwrap_or_default(),
};
+ let doc_sections = enso_doc_parser::parse(documentation);
+ let icon_name = find_icon_name_in_doc_sections(&doc_sections);
let reexported_in: Option = match &mut entry {
Type { reexport: Some(reexport), .. }
| Constructor { reexport: Some(reexport), .. }
@@ -654,36 +643,12 @@ impl Entry {
),
Module { module, .. } => Self::new_module(to_qualified_name(module)),
};
- this.documentation_html = documentation;
this.documentation = doc_sections;
this.icon_name = icon_name;
this.reexported_in = reexported_in;
this
}
- /// Returns the documentation in html depending on the information received from the Engine.
- ///
- /// Depending on the engine version, we may receive the documentation in HTML format already,
- /// or the raw text which needs to be parsed. This function takes two fields of
- /// [`language_server::types::SuggestionEntry`] and depending on availability, returns the
- /// HTML docs fields, or parsed raw docs field.
- fn make_html_docs(docs: Option, docs_html: Option) -> Option {
- if docs_html.is_some() {
- docs_html
- } else {
- docs.map(|docs| {
- let parser = parser_scala::DocParser::new();
- match parser {
- Ok(p) => {
- let output = p.generate_html_doc_pure((*docs).to_string());
- output.unwrap_or(docs)
- }
- Err(_) => docs,
- }
- })
- }
- }
-
/// Apply modification to the entry.
pub fn apply_modifications(
&mut self,
@@ -694,19 +659,11 @@ impl Entry {
let return_type = m.return_type.map(|f| f.try_map(QualifiedName::from_text)).transpose();
let self_type = m.self_type.map(|f| f.try_map(QualifiedName::from_text)).transpose();
let reexport = m.reexport.map(|f| f.try_map(QualifiedName::from_text)).transpose();
+ let docs = m.documentation.map(|docs| docs.map(|docs| enso_doc_parser::parse(&docs)));
let update_results = [
return_type
.and_then(|m| Entry::apply_field_update("return_type", &mut self.return_type, m)),
- Entry::apply_opt_field_update(
- "documentation",
- &mut self.documentation_html,
- m.documentation,
- ),
- Entry::apply_field_update(
- "documentation_sections",
- &mut self.documentation,
- m.documentation_sections,
- ),
+ Entry::apply_default_field_update("documentation", &mut self.documentation, docs),
module.and_then(|m| Entry::apply_field_update("module", &mut self.defined_in, m)),
self_type
.and_then(|s| Entry::apply_opt_field_update("self_type", &mut self.self_type, s)),
@@ -815,11 +772,21 @@ impl Entry {
}
Ok(())
}
-}
-impl From for Entry {
- fn from(entry: language_server::types::SuggestionEntry) -> Self {
- Self::from_ls_entry(entry)
+ /// Apply an update to a field that can be removed by settings its value to its type's default.
+ fn apply_default_field_update(
+ field_name: &'static str,
+ field: &mut T,
+ update: Option>,
+ ) -> FallibleResult {
+ let err = InvalidFieldUpdate(field_name);
+ if let Some(update) = update {
+ *field = match update.tag {
+ FieldAction::Set => update.value.ok_or(err)?,
+ FieldAction::Remove => default(),
+ };
+ }
+ Ok(())
}
}
@@ -1132,7 +1099,7 @@ mod test {
/// of a keyed [`DocSection`] which has its key equal to the `Icon` string.
#[test]
fn find_icon_name_in_doc_section_with_icon_key() {
- use language_server::types::DocSection;
+ use enso_doc_parser::DocSection;
let doc_sections = [
DocSection::Paragraph { body: "Some paragraph.".into() },
DocSection::Keyed { key: "NotIcon".into(), body: "example_not_icon_body".into() },
@@ -1174,8 +1141,7 @@ mod test {
tag_values: Vec::new(),
};
let entry = Entry::new_method(defined_in, on_type, "entry", return_type, true)
- .with_arguments(vec![argument])
- .with_documentation("Some docs");
+ .with_arguments(vec![argument]);
Self { modified_entry: entry.clone(), expected_entry: entry }
}
@@ -1199,25 +1165,23 @@ mod test {
#[test]
fn applying_simple_fields_modification() {
let mut test = ApplyModificationTest::new();
+ let new_documentation = "NewDocumentation";
let modification = SuggestionsDatabaseModification {
- arguments: vec![],
- module: Some(FieldUpdate::set("local.Project.NewModule".to_owned())),
- self_type: Some(FieldUpdate::set(
- "local.Project.NewModule.NewType".to_owned(),
- )),
- return_type: Some(FieldUpdate::set(
+ arguments: vec![],
+ module: Some(FieldUpdate::set("local.Project.NewModule".to_owned())),
+ self_type: Some(FieldUpdate::set("local.Project.NewModule.NewType".to_owned())),
+ return_type: Some(FieldUpdate::set(
"local.Project.NewModule.NewReturnType".to_owned(),
)),
- documentation: Some(FieldUpdate::set("NewDocumentation".to_owned())),
- documentation_sections: None,
- scope: None,
- reexport: Some(FieldUpdate::set("local.Project.NewReexport".to_owned())),
+ documentation: Some(FieldUpdate::set(new_documentation.to_owned())),
+ scope: None,
+ reexport: Some(FieldUpdate::set("local.Project.NewReexport".to_owned())),
};
test.expected_entry.defined_in = "local.Project.NewModule".try_into().unwrap();
test.expected_entry.self_type = Some("local.Project.NewModule.NewType".try_into().unwrap());
test.expected_entry.return_type =
"local.Project.NewModule.NewReturnType".try_into().unwrap();
- test.expected_entry.documentation_html = Some("NewDocumentation".to_owned());
+ test.expected_entry.documentation = enso_doc_parser::parse(new_documentation);
test.expected_entry.reexported_in = Some("local.Project.NewReexport".try_into().unwrap());
let result = test.check_modification(modification);
assert!(result.is_empty());
@@ -1230,7 +1194,6 @@ mod test {
documentation: Some(FieldUpdate::remove()),
..default()
};
- test.expected_entry.documentation_html = None;
let result = test.check_modification(modification);
assert!(result.is_empty());
}
@@ -1249,7 +1212,6 @@ mod test {
..default()
};
test.expected_entry.defined_in = "local.Project.NewModule".try_into().unwrap();
- test.expected_entry.documentation_html = None;
let result = test.check_modification(modification);
assert_eq!(result.len(), 1);
}
diff --git a/app/gui/suggestion-database/src/lib.rs b/app/gui/suggestion-database/src/lib.rs
index 4c6f76fce864..28ad0421ac77 100644
--- a/app/gui/suggestion-database/src/lib.rs
+++ b/app/gui/suggestion-database/src/lib.rs
@@ -2,6 +2,7 @@
#![recursion_limit = "512"]
// === Features ===
+#![feature(anonymous_lifetime_in_impl_trait)]
#![feature(arc_unwrap_or_clone)]
#![feature(async_closure)]
#![feature(associated_type_bounds)]
@@ -316,7 +317,7 @@ pub enum Notification {
/// often-called Language Server methods returns the list of keys of this database instead of the
/// whole entries. Additionally the suggestions contains information about functions and their
/// argument names and types.
-#[derive(Clone, Debug, Default)]
+#[derive(Debug, Default)]
pub struct SuggestionDatabase {
entries: RefCell>>,
qualified_name_to_id_map: RefCell,
@@ -420,17 +421,13 @@ impl SuggestionDatabase {
let mut mp_to_id_map = self.method_pointer_to_id_map.borrow_mut();
let mut hierarchy_index = self.hierarchy_index.borrow_mut();
match update {
- entry::Update::Add { id, suggestion } => match (*suggestion).try_into() {
- Ok(entry) => {
- qn_to_id_map.set_and_warn_if_existed(&Entry::qualified_name(&entry), id);
- mp_to_id_map.set(&entry, id);
- hierarchy_index.add(id, &entry, &qn_to_id_map);
- entries.insert(id, Rc::new(entry));
- }
- Err(err) => {
- error!("Discarding update for {id}: {err}")
- }
- },
+ entry::Update::Add { id, suggestion } => {
+ let entry = Entry::from_ls_entry(*suggestion);
+ qn_to_id_map.set_and_warn_if_existed(&Entry::qualified_name(&entry), id);
+ mp_to_id_map.set(&entry, id);
+ hierarchy_index.add(id, &entry, &qn_to_id_map);
+ entries.insert(id, Rc::new(entry));
+ }
entry::Update::Remove { id } => {
let removed = entries.remove(&id);
match removed {
@@ -672,15 +669,13 @@ pub mod test {
// Non-empty db
let entry = SuggestionEntry::Constructor {
- name: "TextAtom".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- arguments: vec![],
- return_type: "test.TestProject.TestModule.TestAtom".to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
- reexport: None,
+ name: "TextAtom".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ arguments: vec![],
+ return_type: "test.TestProject.TestModule.TestAtom".to_string(),
+ documentation: None,
+ external_id: None,
+ reexport: None,
};
let db_entry = SuggestionsDatabaseEntry { id: 12, suggestion: entry };
let response = language_server::response::GetSuggestionDatabase {
@@ -701,15 +696,13 @@ pub mod test {
let replaced_entry = QualifiedName::from_text("Standard.Base.Number").unwrap();
let (replaced_id, _) = db.lookup_by_qualified_name(&replaced_entry).unwrap();
let new_entry = SuggestionEntry::Constructor {
- name: "NewEntry".to_owned(),
- module: "test.TestProject.TestModule".to_owned(),
- arguments: vec![],
- return_type: "test.TestProject.TestModule.TestAtom".to_owned(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
- reexport: None,
+ name: "NewEntry".to_owned(),
+ module: "test.TestProject.TestModule".to_owned(),
+ arguments: vec![],
+ return_type: "test.TestProject.TestModule.TestAtom".to_owned(),
+ documentation: None,
+ external_id: None,
+ reexport: None,
};
let new_entry_modification = SuggestionsDatabaseModification {
module: Some(FieldUpdate::set("test.TestProject.TestModule2".to_owned())),
@@ -812,67 +805,55 @@ pub mod test {
fn lookup_by_fully_qualified_name_in_db_created_from_ls_response() {
// Initialize a suggestion database with sample entries.
let entry0 = SuggestionEntry::Type {
- name: "TestType".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- params: vec![],
- parent_type: Some("Any".to_string()),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "TestType".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ params: vec![],
+ parent_type: Some("Any".to_string()),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry1 = SuggestionEntry::Constructor {
- name: "TestAtom".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- arguments: vec![],
- return_type: "test.TestProject.TestModule.TestType".to_string(),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "TestAtom".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ arguments: vec![],
+ return_type: "test.TestProject.TestModule.TestType".to_string(),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry2 = SuggestionEntry::Method {
- name: "create_process".to_string(),
- module: "Standard.Builtins.Main".to_string(),
- self_type: "Standard.Builtins.Main.System".to_string(),
- arguments: vec![],
- return_type: "Standard.Builtins.Main.System_Process_Result".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "create_process".to_string(),
+ module: "Standard.Builtins.Main".to_string(),
+ self_type: "Standard.Builtins.Main.System".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Builtins.Main.System_Process_Result".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry3 = SuggestionEntry::Module {
- module: "local.Unnamed_6.Main".to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- reexport: None,
+ module: "local.Unnamed_6.Main".to_string(),
+ documentation: None,
+ reexport: None,
};
let entry4 = SuggestionEntry::Local {
- module: "local.Unnamed_6.Main".to_string(),
- name: "operator1".to_string(),
- return_type: "Standard.Base.Data.Vector.Vector".to_string(),
- external_id: None,
- scope: (default()..=default()).into(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "local.Unnamed_6.Main".to_string(),
+ name: "operator1".to_string(),
+ return_type: "Standard.Base.Data.Vector.Vector".to_string(),
+ external_id: None,
+ scope: (default()..=default()).into(),
+ documentation: None,
};
let entry5 = SuggestionEntry::Function {
- module: "NewProject.NewModule".to_string(),
- name: "testFunction1".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Data.Vector.Vector".to_string(),
- scope: (default()..=default()).into(),
- external_id: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "NewProject.NewModule".to_string(),
+ name: "testFunction1".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Data.Vector.Vector".to_string(),
+ scope: (default()..=default()).into(),
+ external_id: None,
+ documentation: None,
};
let ls_response = language_server::response::GetSuggestionDatabase {
entries: vec![
@@ -909,54 +890,46 @@ pub mod test {
fn lookup_by_method_pointer_in_db_created_from_ls_response() {
// Initialize a suggestion database with sample entries.
let method1 = SuggestionEntry::Method {
- name: "create_process".to_string(),
- module: "Standard.Base".to_string(),
- self_type: "Standard.Base.System".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.System.System_Process_Result".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "create_process".to_string(),
+ module: "Standard.Base".to_string(),
+ self_type: "Standard.Base.System".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.System.System_Process_Result".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let method2 = SuggestionEntry::Method {
- name: "exit".to_string(),
- module: "Standard.Base".to_string(),
- self_type: "Standard.Base.System".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Data.Numbers.Integer".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "exit".to_string(),
+ module: "Standard.Base".to_string(),
+ self_type: "Standard.Base.System".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Data.Numbers.Integer".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let method3 = SuggestionEntry::Method {
- name: "println".to_string(),
- module: "Standard.Base.IO".to_string(),
- self_type: "Standard.Base.IO".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Nothing.Nothing".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "println".to_string(),
+ module: "Standard.Base.IO".to_string(),
+ self_type: "Standard.Base.IO".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Nothing.Nothing".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let function = SuggestionEntry::Function {
- module: "NewProject.NewModule".to_string(),
- name: "test_function".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Data.Vector.Vector".to_string(),
- scope: (default()..=default()).into(),
- external_id: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "NewProject.NewModule".to_string(),
+ name: "test_function".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Data.Vector.Vector".to_string(),
+ scope: (default()..=default()).into(),
+ external_id: None,
+ documentation: None,
};
let ls_response = language_server::response::GetSuggestionDatabase {
entries: vec![
@@ -1009,32 +982,26 @@ pub mod test {
fn initialize_database_with_invalid_entries() {
// Prepare some nonsense inputs from the Engine.
let entry_with_empty_name = SuggestionEntry::Constructor {
- name: "".to_string(),
- module: "Empty.Entry".to_string(),
- arguments: vec![],
- return_type: "".to_string(),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "".to_string(),
+ module: "Empty.Entry".to_string(),
+ arguments: vec![],
+ return_type: "".to_string(),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let empty_entry = SuggestionEntry::Local {
- module: "".to_string(),
- name: "".to_string(),
- return_type: "".to_string(),
- external_id: None,
- scope: (default()..=default()).into(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "".to_string(),
+ name: "".to_string(),
+ return_type: "".to_string(),
+ external_id: None,
+ scope: (default()..=default()).into(),
+ documentation: None,
};
let gibberish_entry = SuggestionEntry::Module {
- module: GIBBERISH_MODULE_NAME.to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- reexport: None,
+ module: GIBBERISH_MODULE_NAME.to_string(),
+ documentation: None,
+ reexport: None,
};
let ls_response = language_server::response::GetSuggestionDatabase {
@@ -1067,33 +1034,27 @@ pub mod test {
let entry_added_id = 123;
// Modify the database contents by applying an update event.
let entry_doc_change = Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: None,
- self_type: None,
- return_type: None,
- documentation: Some(FieldUpdate::set("New doc".to_string())),
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: None,
+ self_type: None,
+ return_type: None,
+ documentation: Some(FieldUpdate::set("New doc".to_string())),
+ scope: None,
+ reexport: None,
});
let entry_mod_change = Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: Some(FieldUpdate::set(
- "new_project.NewProject.NewModule".to_string(),
- )),
- self_type: None,
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: Some(FieldUpdate::set("new_project.NewProject.NewModule".to_string())),
+ self_type: None,
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
});
let new_entry = SuggestionEntry::Module {
- module: "local.Unnamed_6.Main".to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- reexport: None,
+ module: "local.Unnamed_6.Main".to_string(),
+ documentation: None,
+ reexport: None,
};
let update = SuggestionDatabaseUpdatesEvent {
updates: vec![
@@ -1131,43 +1092,37 @@ pub mod test {
// Modify the database contents by applying an update event.
let method1 = SuggestionEntry::Method {
- name: "create_process".to_string(),
- module: "Standard.Base".to_string(),
- self_type: "Standard.Base.System".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.System.System_Process_Result".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "create_process".to_string(),
+ module: "Standard.Base".to_string(),
+ self_type: "Standard.Base.System".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.System.System_Process_Result".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let method2 = SuggestionEntry::Method {
- name: "exit".to_string(),
- module: "Standard.Base".to_string(),
- self_type: "Standard.Base.System".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Data.Numbers.Integer".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "exit".to_string(),
+ module: "Standard.Base".to_string(),
+ self_type: "Standard.Base.System".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Data.Numbers.Integer".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let method3 = SuggestionEntry::Method {
- name: "println".to_string(),
- module: "Standard.Base.IO".to_string(),
- self_type: "Standard.Base.IO".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Nothing.Nothing".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "println".to_string(),
+ module: "Standard.Base.IO".to_string(),
+ self_type: "Standard.Base.IO".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Nothing.Nothing".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let update1 = SuggestionDatabaseUpdatesEvent {
updates: vec![
@@ -1200,14 +1155,13 @@ pub mod test {
lookup_method_pointer(&db, &method_pointer3);
let method_change = Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: Some(FieldUpdate::set("Standard.Base".to_owned())),
- self_type: None,
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: Some(FieldUpdate::set("Standard.Base".to_owned())),
+ self_type: None,
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
});
let update2 = SuggestionDatabaseUpdatesEvent {
updates: vec![entry::Update::Modify {
@@ -1250,15 +1204,13 @@ pub mod test {
fn lookup_by_fully_qualified_name_after_db_update_reuses_id() {
// Initialize a suggestion database with a sample entry.
let entry1 = SuggestionEntry::Constructor {
- name: "TextAtom".to_string(),
- module: "TestProject.TestModule".to_string(),
- arguments: vec![],
- return_type: "TestAtom".to_string(),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "TextAtom".to_string(),
+ module: "TestProject.TestModule".to_string(),
+ arguments: vec![],
+ return_type: "TestAtom".to_string(),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let id = 1;
let response = language_server::response::GetSuggestionDatabase {
@@ -1276,11 +1228,9 @@ pub mod test {
// Apply a DB update adding a different entry at the same `id`.
let entry2 = SuggestionEntry::Module {
- module: "local.Unnamed_6.Main".to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- reexport: None,
+ module: "local.Unnamed_6.Main".to_string(),
+ documentation: None,
+ reexport: None,
};
let update = SuggestionDatabaseUpdatesEvent {
updates: vec![entry::Update::Add { id, suggestion: Box::new(entry2) }],
@@ -1380,67 +1330,55 @@ pub mod test {
fn hierarchy_index_is_populated_after_construction() {
// Initialize a suggestion database with sample entries.
let entry0 = SuggestionEntry::Type {
- name: "TestType".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- params: vec![],
- parent_type: Some("Any".to_string()),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "TestType".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ params: vec![],
+ parent_type: Some("Any".to_string()),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry1 = SuggestionEntry::Constructor {
- name: "Empty".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- arguments: vec![],
- return_type: "test.TestProject.TestModule.TestType".to_string(),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "Empty".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ arguments: vec![],
+ return_type: "test.TestProject.TestModule.TestType".to_string(),
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry2 = SuggestionEntry::Method {
- name: "test_method".to_string(),
- module: "test.TestProject.TestModule".to_string(),
- self_type: "test.TestProject.TestModule.TestType".to_string(),
- arguments: vec![],
- return_type: "Standard.Builtins.Main.System_Process_Result".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "test_method".to_string(),
+ module: "test.TestProject.TestModule".to_string(),
+ self_type: "test.TestProject.TestModule.TestType".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Builtins.Main.System_Process_Result".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
};
let entry3 = SuggestionEntry::Module {
- module: "test.TestProject.TestModule".to_string(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- reexport: None,
+ module: "test.TestProject.TestModule".to_string(),
+ documentation: None,
+ reexport: None,
};
let entry4 = SuggestionEntry::Local {
- module: "test.TestProject.TestModule".to_string(),
- name: "operator1".to_string(),
- return_type: "Standard.Base.Data.Vector.Vector".to_string(),
- external_id: None,
- scope: (default()..=default()).into(),
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "test.TestProject.TestModule".to_string(),
+ name: "operator1".to_string(),
+ return_type: "Standard.Base.Data.Vector.Vector".to_string(),
+ external_id: None,
+ scope: (default()..=default()).into(),
+ documentation: None,
};
let entry5 = SuggestionEntry::Function {
- module: "test.TestProject.TestModule".to_string(),
- name: "testFunction1".to_string(),
- arguments: vec![],
- return_type: "Standard.Base.Data.Vector.Vector".to_string(),
- scope: (default()..=default()).into(),
- external_id: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
+ module: "test.TestProject.TestModule".to_string(),
+ name: "testFunction1".to_string(),
+ arguments: vec![],
+ return_type: "Standard.Base.Data.Vector.Vector".to_string(),
+ scope: (default()..=default()).into(),
+ external_id: None,
+ documentation: None,
};
let ls_response = language_server::response::GetSuggestionDatabase {
entries: vec![
@@ -1507,17 +1445,15 @@ pub mod test {
updates: vec![entry::Update::Add {
id: added_id,
suggestion: Box::new(SuggestionEntry::Method {
- name: "new_method".to_string(),
- module: "local.Project.Submodule".to_string(),
- self_type: "local.Project.Submodule.TestType".to_string(),
- arguments: vec![],
- return_type: "Some.Type".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: default(),
- external_id: None,
+ name: "new_method".to_string(),
+ module: "local.Project.Submodule".to_string(),
+ self_type: "local.Project.Submodule.TestType".to_string(),
+ arguments: vec![],
+ return_type: "Some.Type".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
+ external_id: None,
}),
}],
current_version: 1,
@@ -1537,16 +1473,13 @@ pub mod test {
id: added_id,
external_id: None,
modification: Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: None,
- self_type: Some(FieldUpdate::set(
- "Standard.Base.Maybe".to_string(),
- )),
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: None,
+ self_type: Some(FieldUpdate::set("Standard.Base.Maybe".to_string())),
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
}),
}],
current_version: 2,
@@ -1593,11 +1526,9 @@ pub mod test {
entry::Update::Add {
id: 20,
suggestion: Box::new(SuggestionEntry::Module {
- module: "Standard.NewModule".to_string(),
- documentation: None,
- documentation_html: None,
- reexport: None,
- documentation_sections: vec![],
+ module: "Standard.NewModule".to_string(),
+ documentation: None,
+ reexport: None,
}),
},
// Move Number and Boolean to the new module.
@@ -1605,32 +1536,26 @@ pub mod test {
id: modify_id_1,
external_id: None,
modification: Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: Some(FieldUpdate::set(
- "Standard.NewModule".to_string(),
- )),
- self_type: None,
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: Some(FieldUpdate::set("Standard.NewModule".to_string())),
+ self_type: None,
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
}),
},
entry::Update::Modify {
id: modify_id_2,
external_id: None,
modification: Box::new(SuggestionsDatabaseModification {
- arguments: vec![],
- module: Some(FieldUpdate::set(
- "Standard.NewModule".to_string(),
- )),
- self_type: None,
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![],
+ module: Some(FieldUpdate::set("Standard.NewModule".to_string())),
+ self_type: None,
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
}),
},
// Modify the definition of Maybe. It should not affect the hierarchy index.
@@ -1638,14 +1563,13 @@ pub mod test {
id: maybe_id,
external_id: None,
modification: Box::new(SuggestionsDatabaseModification {
- arguments: vec![], // remove arguments
- module: None,
- self_type: None,
- return_type: None,
- documentation: None,
- documentation_sections: None,
- scope: None,
- reexport: None,
+ arguments: vec![], // remove arguments
+ module: None,
+ self_type: None,
+ return_type: None,
+ documentation: None,
+ scope: None,
+ reexport: None,
}),
},
],
@@ -1676,17 +1600,15 @@ pub mod test {
updates: vec![entry::Update::Add {
id: 20,
suggestion: Box::new(SuggestionEntry::Method {
- external_id: None,
- name: "new_method".to_string(),
- module: "Standard.Base".to_string(),
- arguments: vec![],
- self_type: "Standard.Base.NewType".to_string(),
- return_type: "Standard.Base.Maybe".to_string(),
- is_static: false,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: vec![],
+ external_id: None,
+ name: "new_method".to_string(),
+ module: "Standard.Base".to_string(),
+ arguments: vec![],
+ self_type: "Standard.Base.NewType".to_string(),
+ return_type: "Standard.Base.Maybe".to_string(),
+ is_static: false,
+ reexport: None,
+ documentation: None,
}),
}],
current_version: 1,
@@ -1697,15 +1619,13 @@ pub mod test {
updates: vec![entry::Update::Add {
id: 21,
suggestion: Box::new(SuggestionEntry::Type {
- external_id: None,
- name: "NewType".to_string(),
- module: "Standard.Base".to_string(),
- params: vec![],
- parent_type: None,
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: vec![],
+ external_id: None,
+ name: "NewType".to_string(),
+ module: "Standard.Base".to_string(),
+ params: vec![],
+ parent_type: None,
+ reexport: None,
+ documentation: None,
}),
}],
current_version: 2,
@@ -1724,15 +1644,13 @@ pub mod test {
updates: vec![entry::Update::Add {
id: 20,
suggestion: Box::new(SuggestionEntry::Type {
- external_id: None,
- name: "NewType".to_string(),
- module: "Standard.NewModule".to_string(),
- params: vec![],
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: vec![],
- parent_type: None,
+ external_id: None,
+ name: "NewType".to_string(),
+ module: "Standard.NewModule".to_string(),
+ params: vec![],
+ reexport: None,
+ documentation: None,
+ parent_type: None,
}),
}],
current_version: 1,
@@ -1743,11 +1661,9 @@ pub mod test {
updates: vec![entry::Update::Add {
id: 21,
suggestion: Box::new(SuggestionEntry::Module {
- module: "Standard.NewModule".to_string(),
- reexport: None,
- documentation: None,
- documentation_html: None,
- documentation_sections: vec![],
+ module: "Standard.NewModule".to_string(),
+ reexport: None,
+ documentation: None,
}),
}],
current_version: 2,
diff --git a/app/gui/suggestion-database/src/mock.rs b/app/gui/suggestion-database/src/mock.rs
index 6f1d451ce521..3b9b2d9df1be 100644
--- a/app/gui/suggestion-database/src/mock.rs
+++ b/app/gui/suggestion-database/src/mock.rs
@@ -13,6 +13,15 @@ use enso_text::Location;
+// ===============
+// === Exports ===
+// ===============
+
+// Re-exported here so it can be referenced by macro-generated code.
+pub use enso_doc_parser;
+
+
+
// =================
// === Constants ===
// =================
@@ -49,9 +58,7 @@ pub const DEFAULT_TYPE: &str = "Standard.Base.Any";
/// .add_and_enter_type("Type", vec![], |e| e.with_icon(IconName::from_snake_case("an_icon")));
/// builder.add_constructor("Constructor", vec![], |e| e);
/// builder.leave();
-/// builder.add_method("module_method", vec![], "local.Project.Type", true, |e| {
-/// e.with_documentation("A module method")
-/// });
+/// builder.add_method("module_method", vec![], "local.Project.Type", true, |e| e);
/// builder.leave();
/// let db = builder.result;
///
@@ -311,7 +318,6 @@ macro_rules! mock_suggestion_database_entries {
/// }
/// local.Project {
/// mod Submodule {
-/// #[with_documentation("Some test type")]
/// type TestType (a: Standard.Base.Number, b: Standard.Base.Maybe) {
/// static fn static_method(x) -> Standard.Base.Number;
/// }
@@ -380,13 +386,13 @@ macro_rules! mock_suggestion_database {
#[macro_export]
macro_rules! doc_section_mark {
(!) => {
- $crate::engine_protocol::language_server::Mark::Important
+ $crate::mock::enso_doc_parser::Mark::Important
};
(>) => {
- $crate::engine_protocol::language_server::Mark::Example
+ $crate::mock::enso_doc_parser::Mark::Example
};
(?) => {
- $crate::engine_protocol::language_server::Mark::Info
+ $crate::mock::enso_doc_parser::Mark::Info
};
}
@@ -423,33 +429,27 @@ macro_rules! doc_section_mark {
#[macro_export]
macro_rules! doc_section {
(@ $tag:expr, $body:expr) => {
- $crate::engine_protocol::language_server::DocSection::Tag {
- name: $tag.into(),
- body: $body.into(),
- }
+ $crate::mock::enso_doc_parser::DocSection::Tag { name: $tag.into(), body: $body.into() }
};
($mark:tt $body:expr) => {
- $crate::engine_protocol::language_server::DocSection::Marked {
+ $crate::mock::enso_doc_parser::DocSection::Marked {
mark: $crate::doc_section_mark!($mark),
header: None,
body: $body.into(),
}
};
($mark:tt $header:expr, $body:expr) => {
- $crate::engine_protocol::language_server::DocSection::Marked {
+ $crate::mock::enso_doc_parser::DocSection::Marked {
mark: $crate::doc_section_mark!($mark),
header: Some($header.into()),
body: $body.into(),
}
};
($paragraph:expr) => {
- $crate::engine_protocol::language_server::DocSection::Paragraph { body: $paragraph.into() }
+ $crate::mock::enso_doc_parser::DocSection::Paragraph { body: $paragraph.into() }
};
($key:expr => $body:expr) => {
- $crate::engine_protocol::language_server::DocSection::Keyed {
- key: $key.into(),
- body: $body.into(),
- }
+ $crate::mock::enso_doc_parser::DocSection::Keyed { key: $key.into(), body: $body.into() }
};
}
@@ -475,7 +475,6 @@ pub fn standard_db_mock() -> SuggestionDatabase {
}
local.Project {
mod Submodule {
- #[with_documentation("Some test type")]
type TestType (a: Standard.Base.Number, b: Standard.Base.Maybe) {
static fn static_method(x) -> Standard.Base.Number;
}
diff --git a/app/gui/view/component-browser/Cargo.toml b/app/gui/view/component-browser/Cargo.toml
index e2da82b2562b..a4bd13f0129d 100644
--- a/app/gui/view/component-browser/Cargo.toml
+++ b/app/gui/view/component-browser/Cargo.toml
@@ -8,7 +8,13 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
+enso-frp = { path = "../../../../lib/rust/frp" }
+ensogl = { path = "../../../../lib/rust/ensogl" }
+ensogl-gui-component = { path = "../../../../lib/rust/ensogl/component/gui" }
+ensogl-hardcoded-theme = { path = "../../../../lib/rust/ensogl/app/theme/hardcoded" }
enso-prelude = { path = "../../../../lib/rust/prelude" }
ide-view-component-list-panel-breadcrumbs = { path = "component-list-panel/breadcrumbs" }
ide-view-component-list-panel = { path = "component-list-panel" }
+ide-view-documentation = { path = "../documentation" }
+ide-view-graph-editor = { path = "../graph-editor" }
ensogl-text = { path = "../../../../lib/rust/ensogl/component/text" }
diff --git a/app/gui/view/component-browser/src/lib.rs b/app/gui/view/component-browser/src/lib.rs
index dc725ac96695..ffc4bf7b711e 100644
--- a/app/gui/view/component-browser/src/lib.rs
+++ b/app/gui/view/component-browser/src/lib.rs
@@ -4,6 +4,7 @@
//! in `ide_view` crate, because the Documentation panel is used by old node searcher as well, and
//! we need to avoid crates' circular dependencies.
+#![recursion_limit = "256"]
// === Standard Linter Configuration ===
#![deny(non_ascii_idents)]
#![warn(unsafe_code)]
@@ -18,6 +19,21 @@
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
+use enso_prelude::*;
+use ensogl::prelude::*;
+
+use ide_view_documentation as documentation;
+use ide_view_graph_editor::component::node::HEIGHT as NODE_HEIGHT;
+
+use enso_frp as frp;
+use ensogl::application::frp::API;
+use ensogl::application::Application;
+use ensogl::display;
+use ensogl::display::shape::StyleWatchFrp;
+use ensogl_gui_component::component;
+use ensogl_hardcoded_theme::application::component_browser as theme;
+
+
// ==============
// === Export ===
@@ -25,3 +41,151 @@
pub use ide_view_component_list_panel as component_list_panel;
pub use ide_view_component_list_panel_breadcrumbs as breadcrumbs;
+
+
+
+/// The Model of Component Browser View.
+#[allow(missing_docs)]
+#[derive(Clone, CloneRef, Debug)]
+pub struct Model {
+ display_object: display::object::Instance,
+ pub list: component_list_panel::View,
+ pub documentation: documentation::View,
+}
+
+impl component::Model for Model {
+ fn label() -> &'static str {
+ "ComponentBrowser"
+ }
+
+ fn new(app: &Application) -> Self {
+ let display_object = display::object::Instance::new();
+ let list = app.new_view::();
+ let documentation = documentation::View::new(app);
+ app.display.default_scene.layers.node_searcher.add(&display_object);
+ display_object.add_child(&list);
+ display_object.add_child(&documentation);
+
+ // We need to set the shapes order for the documentation panel overlay to be on top of the
+ // CB background; otherwise, the background would block some mouse events. The CB background
+ // is big because of the surrounding shadow and partially overlaps the left side of the
+ // documentation panel.
+ let scene = &app.display.default_scene;
+ shapes_order_dependencies! {
+ scene => {
+ component_list_panel::background -> documentation::overlay;
+ }
+ }
+
+ Self { display_object, list, documentation }
+ }
+}
+
+impl Model {
+ fn expression_input_position(
+ size: &Vector2,
+ vertical_gap: &f32,
+ snap_to_pixel_offset: &Vector2,
+ ) -> Vector2 {
+ let half_node_height = NODE_HEIGHT / 2.0;
+ let panel_left = -size.x / 2.0;
+ let panel_bottom = -size.y / 2.0;
+ let x = panel_left;
+ let y = panel_bottom - vertical_gap - half_node_height;
+ Vector2(x, y) + snap_to_pixel_offset
+ }
+
+ fn snap_to_pixel_offset(size: Vector2, scene_shape: &display::scene::Shape) -> Vector2 {
+ let device_size = scene_shape.device_pixels();
+ let origin_left_top_pos = Vector2(device_size.width, device_size.height) / 2.0;
+ let origin_snapped = Vector2(origin_left_top_pos.x.floor(), origin_left_top_pos.y.floor());
+ let origin_offset = origin_snapped - origin_left_top_pos;
+ let panel_left_top_pos = (size * scene_shape.pixel_ratio) / 2.0;
+ let panel_snapped = Vector2(panel_left_top_pos.x.floor(), panel_left_top_pos.y.floor());
+ let panel_offset = panel_snapped - panel_left_top_pos;
+ origin_offset - panel_offset
+ }
+}
+
+impl display::Object for Model {
+ fn display_object(&self) -> &display::object::Instance {
+ &self.display_object
+ }
+}
+
+ensogl::define_endpoints_2! {
+ Input {
+ show(),
+ hide(),
+ }
+ Output {
+ is_visible(bool),
+ size(Vector2),
+ expression_input_position(Vector2),
+ is_hovered(bool),
+ editing_committed(),
+ is_empty(bool),
+ }
+}
+
+impl component::Frp for Frp {
+ fn init(
+ network: &frp::Network,
+ frp_api: &::Private,
+ app: &Application,
+ model: &Model,
+ style: &StyleWatchFrp,
+ ) {
+ let scene = &app.display.default_scene;
+ let input = &frp_api.input;
+ let out = &frp_api.output;
+ let list_panel = &model.list.output;
+ let documentation = &model.documentation;
+ let grid = &model.list.model().grid;
+
+ let gap = style.get_number(theme::panels_gap);
+ let doc_width = style.get_number(theme::documentation::width);
+ let doc_height = style.get_number(theme::documentation::height);
+ frp::extend! { network
+ init <- source_();
+
+ doc_size <- all_with3(&init, &doc_width, &doc_height, |_, w, h| Vector2(*w, *h));
+ size <- all_with4(&init, &list_panel.size, &doc_size, &gap, |(), list_size, doc_size, gap| {
+ let width = list_size.x + gap + doc_size.x;
+ let height = max(list_size.y, doc_size.y);
+ Vector2(width, height)
+ });
+ snap <- all_with(&size, &scene.frp.shape, |sz, sh| Model::snap_to_pixel_offset(*sz, sh));
+ list_position_x <-
+ all_with3(&size, &list_panel.size, &snap, |sz, list_sz, snap| list_sz.x / 2.0 - sz.x / 2.0 + snap.x);
+ doc_position_x <- all_with3(&size, &doc_size, &snap, |sz, doc_sz, snap| sz.x / 2.0 - doc_sz.x / 2.0 + snap.x);
+ eval list_position_x ((x) model.list.set_x(*x));
+ eval doc_position_x ((x) model.documentation.set_x(*x));
+
+ model.list.input.show <+ input.show;
+ model.list.input.hide <+ input.hide;
+ out.is_visible <+ bool(&input.hide, &input.show);
+ out.size <+ size;
+ out.expression_input_position <+ all_with3(
+ &size,
+ &gap,
+ &snap,
+ Model::expression_input_position
+ );
+
+ out.is_hovered <+ list_panel.is_hovered || documentation.frp.is_hovered;
+ out.is_hovered <+ input.hide.constant(false);
+
+ out.editing_committed <+ grid.expression_accepted.constant(());
+ }
+ init.emit(());
+ }
+}
+
+/// Component Browser View.
+///
+/// The Component Browser is a panel where user searches for types, variables and methods which can
+/// be used to construct new nodes. The components are arranged in sections and groups, and
+/// displayed in Component List Panel. The Component Browser View contains also Documentation Panel,
+/// displaying documentation of currently selected or hovered component.
+pub type View = component::ComponentView;
diff --git a/app/gui/view/documentation/Cargo.toml b/app/gui/view/documentation/Cargo.toml
index bd3e5d35c1cc..ab1ba93e4386 100644
--- a/app/gui/view/documentation/Cargo.toml
+++ b/app/gui/view/documentation/Cargo.toml
@@ -8,8 +8,10 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
+enso-doc-parser = { path = "../../../../lib/rust/parser/doc-parser" }
enso-frp = { path = "../../../../lib/rust/frp" }
enso-prelude = { path = "../../../../lib/rust/prelude" }
+enso-profiler = { path = "../../../../lib/rust/profiler" }
ensogl = { path = "../../../../lib/rust/ensogl" }
ensogl-component = { path = "../../../../lib/rust/ensogl/component" }
ensogl-hardcoded-theme = { path = "../../../../lib/rust/ensogl/app/theme/hardcoded" }
diff --git a/app/gui/view/documentation/src/html.rs b/app/gui/view/documentation/src/html.rs
index 082fd5318375..4d28ce1de4b9 100644
--- a/app/gui/view/documentation/src/html.rs
+++ b/app/gui/view/documentation/src/html.rs
@@ -4,6 +4,11 @@ use enso_prelude::*;
use horrorshow::prelude::*;
use double_representation::name::QualifiedName;
+use enso_doc_parser::DocSection;
+use enso_doc_parser::Mark;
+use enso_profiler as profiler;
+use enso_profiler::profile;
+use enso_suggestion_database::documentation_ir::BuiltinDocumentation;
use enso_suggestion_database::documentation_ir::Constructors;
use enso_suggestion_database::documentation_ir::Documentation;
use enso_suggestion_database::documentation_ir::EntryDocumentation;
@@ -17,8 +22,6 @@ use enso_suggestion_database::documentation_ir::Synopsis;
use enso_suggestion_database::documentation_ir::Tag;
use enso_suggestion_database::documentation_ir::TypeDocumentation;
use enso_suggestion_database::documentation_ir::Types;
-use enso_suggestion_database::engine_protocol::language_server::DocSection;
-use enso_suggestion_database::engine_protocol::language_server::Mark;
use enso_suggestion_database::entry::Argument;
use horrorshow::box_html;
use horrorshow::labels;
@@ -58,6 +61,7 @@ fn svg_icon(content: &'static str) -> impl Render {
// ==============
/// Render entry documentation to HTML code with Tailwind CSS styles.
+#[profile(Detail)]
pub fn render(docs: EntryDocumentation) -> String {
match docs {
EntryDocumentation::Placeholder(placeholder) => match placeholder {
@@ -66,7 +70,6 @@ pub fn render(docs: EntryDocumentation) -> String {
render_virtual_component_group_docs(name),
},
EntryDocumentation::Docs(docs) => render_documentation(docs),
- EntryDocumentation::Builtin(docs) => render_builtin_docs(docs),
}
}
@@ -82,6 +85,7 @@ fn render_documentation(docs: Documentation) -> String {
render_type_documentation(&type_docs, Some(&name)),
Documentation::ModuleMethod { module_docs, name } =>
render_module_documentation(&module_docs, Some(&name)),
+ Documentation::Builtin(builtin_docs) => render_builtin_documentation(&builtin_docs),
}
}
@@ -95,13 +99,6 @@ fn render_virtual_component_group_docs(name: ImString) -> String {
docs_content(content).into_string().unwrap()
}
-/// Render a documentation of a builtin entry that is not present in the suggestion database.
-fn render_builtin_docs(html_docs: ImString) -> String {
- let content = owned_html! {
- : Raw(&*html_docs);
- };
- docs_content(content).into_string().unwrap()
-}
// === Types ===
@@ -475,6 +472,20 @@ fn local_synopsis<'a>(synopsis: &'a Synopsis) -> Box {
}
+// === Builtin entries ===
+
+/// Render documentation for built-in entries.
+///
+/// Consists of only a synopsis.
+fn render_builtin_documentation(docs: &BuiltinDocumentation) -> String {
+ let synopsis = section_content(module_synopsis(&docs.synopsis));
+ let content = owned_html! {
+ : &synopsis;
+ };
+ docs_content(content).into_string().unwrap()
+}
+
+
// =======================
// === Common elements ===
diff --git a/app/gui/view/examples/documentation/Cargo.toml b/app/gui/view/examples/documentation/Cargo.toml
index 65fe66bb9a8e..85c823a6831f 100644
--- a/app/gui/view/examples/documentation/Cargo.toml
+++ b/app/gui/view/examples/documentation/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
+enso-doc-parser = { path = "../../../../../lib/rust/parser/doc-parser" }
ensogl = { path = "../../../../../lib/rust/ensogl" }
ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" }
enso-suggestion-database = { path = "../../../suggestion-database" }
diff --git a/app/gui/view/examples/documentation/src/lib.rs b/app/gui/view/examples/documentation/src/lib.rs
index 07bb4aea94ec..6c52b5f72301 100644
--- a/app/gui/view/examples/documentation/src/lib.rs
+++ b/app/gui/view/examples/documentation/src/lib.rs
@@ -10,11 +10,11 @@
use ensogl::display::shape::*;
use ensogl::prelude::*;
+use enso_doc_parser::DocSection;
+use enso_doc_parser::Mark;
use enso_suggestion_database as suggestion_database;
use enso_suggestion_database::doc_section;
use enso_suggestion_database::documentation_ir::EntryDocumentation;
-use enso_suggestion_database::engine_protocol::language_server::DocSection;
-use enso_suggestion_database::engine_protocol::language_server::Mark;
use enso_suggestion_database::entry::Argument;
use enso_suggestion_database::mock;
use enso_suggestion_database::mock_suggestion_database;
@@ -135,7 +135,7 @@ fn database() -> SuggestionDatabase {
builder.add_function("bar", args, "Standard.Base.Boolean", scope.clone(), |e| {
e.with_doc_sections(vec![
DocSection::Paragraph { body: "Documentation for the bar function.".into() },
- DocSection::Tag { name: "DEPRECATED".into(), body: default() },
+ DocSection::Tag { name: "DEPRECATED", body: default() },
DocSection::Marked {
mark: Mark::Example,
header: None,
@@ -147,7 +147,7 @@ fn database() -> SuggestionDatabase {
builder.add_local("local1", "Standard.Base.Boolean", scope, |e| {
e.with_doc_sections(vec![
DocSection::Paragraph { body: "Documentation for the local1 variable.".into() },
- DocSection::Tag { name: "SOMETAG".into(), body: default() },
+ DocSection::Tag { name: "SOMETAG", body: default() },
])
});
diff --git a/app/gui/view/src/component_browser.rs b/app/gui/view/src/component_browser.rs
deleted file mode 100644
index 962d43f94281..000000000000
--- a/app/gui/view/src/component_browser.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-//! A module containing the Component Browser [`View`].
-
-// TODO[ao]: This module should be extracted to a separate crate in the future; now it shares
-// the documentation panel with old Node Searcher, that's why it must be in the view crate.
-
-use crate::prelude::*;
-
-use crate::documentation;
-use crate::graph_editor::component::node::HEIGHT as NODE_HEIGHT;
-
-use enso_frp as frp;
-use ensogl::application::frp::API;
-use ensogl::application::Application;
-use ensogl::display;
-use ensogl::display::shape::StyleWatchFrp;
-use ensogl_gui_component::component;
-use ensogl_hardcoded_theme::application::component_browser as theme;
-
-
-// ==============
-// === Export ===
-// ==============
-
-pub use ide_view_component_browser::*;
-
-
-
-/// The Model of Component Browser View.
-#[allow(missing_docs)]
-#[derive(Clone, CloneRef, Debug)]
-pub struct Model {
- display_object: display::object::Instance,
- pub list: component_list_panel::View,
- pub documentation: documentation::View,
-}
-
-impl component::Model for Model {
- fn label() -> &'static str {
- "ComponentBrowser"
- }
-
- fn new(app: &Application) -> Self {
- let display_object = display::object::Instance::new();
- let list = app.new_view::();
- let documentation = documentation::View::new(app);
- app.display.default_scene.layers.node_searcher.add(&display_object);
- display_object.add_child(&list);
- display_object.add_child(&documentation);
-
- // We need to set the shapes order for the documentation panel overlay to be on top of the
- // CB background; otherwise, the background would block some mouse events. The CB background
- // is big because of the surrounding shadow and partially overlaps the left side of the
- // documentation panel.
- let scene = &app.display.default_scene;
- shapes_order_dependencies! {
- scene => {
- component_list_panel::background -> documentation::overlay;
- }
- }
-
- Self { display_object, list, documentation }
- }
-}
-
-impl Model {
- fn expression_input_position(
- size: &Vector2,
- vertical_gap: &f32,
- snap_to_pixel_offset: &Vector2,
- ) -> Vector2 {
- let half_node_height = NODE_HEIGHT / 2.0;
- let panel_left = -size.x / 2.0;
- let panel_bottom = -size.y / 2.0;
- let x = panel_left;
- let y = panel_bottom - vertical_gap - half_node_height;
- Vector2(x, y) + snap_to_pixel_offset
- }
-
- fn snap_to_pixel_offset(size: Vector2, scene_shape: &display::scene::Shape) -> Vector2 {
- let device_size = scene_shape.device_pixels();
- let origin_left_top_pos = Vector2(device_size.width, device_size.height) / 2.0;
- let origin_snapped = Vector2(origin_left_top_pos.x.floor(), origin_left_top_pos.y.floor());
- let origin_offset = origin_snapped - origin_left_top_pos;
- let panel_left_top_pos = (size * scene_shape.pixel_ratio) / 2.0;
- let panel_snapped = Vector2(panel_left_top_pos.x.floor(), panel_left_top_pos.y.floor());
- let panel_offset = panel_snapped - panel_left_top_pos;
- origin_offset - panel_offset
- }
-}
-
-impl display::Object for Model {
- fn display_object(&self) -> &display::object::Instance {
- &self.display_object
- }
-}
-
-ensogl::define_endpoints_2! {
- Input {
- show(),
- hide(),
- }
- Output {
- is_visible(bool),
- size(Vector2),
- expression_input_position(Vector2),
- is_hovered(bool),
- }
-}
-
-impl component::Frp for Frp {
- fn init(
- network: &frp::Network,
- frp_api: &::Private,
- app: &Application,
- model: &Model,
- style: &StyleWatchFrp,
- ) {
- let scene = &app.display.default_scene;
- let input = &frp_api.input;
- let out = &frp_api.output;
- let list_panel = &model.list.output;
- let documentation = &model.documentation;
-
- let gap = style.get_number(theme::panels_gap);
- let doc_width = style.get_number(theme::documentation::width);
- let doc_height = style.get_number(theme::documentation::height);
- frp::extend! { network
- init <- source_();
-
- doc_size <- all_with3(&init, &doc_width, &doc_height, |_, w, h| Vector2(*w, *h));
- size <- all_with4(&init, &list_panel.size, &doc_size, &gap, |(), list_size, doc_size, gap| {
- let width = list_size.x + gap + doc_size.x;
- let height = max(list_size.y, doc_size.y);
- Vector2(width, height)
- });
- snap <- all_with(&size, &scene.frp.shape, |sz, sh| Model::snap_to_pixel_offset(*sz, sh));
- list_position_x <-
- all_with3(&size, &list_panel.size, &snap, |sz, list_sz, snap| list_sz.x / 2.0 - sz.x / 2.0 + snap.x);
- doc_position_x <- all_with3(&size, &doc_size, &snap, |sz, doc_sz, snap| sz.x / 2.0 - doc_sz.x / 2.0 + snap.x);
- eval list_position_x ((x) model.list.set_x(*x));
- eval doc_position_x ((x) model.documentation.set_x(*x));
-
- model.list.input.show <+ input.show;
- model.list.input.hide <+ input.hide;
- out.is_visible <+ bool(&input.hide, &input.show);
- out.size <+ size;
- out.expression_input_position <+ all_with3(
- &size,
- &gap,
- &snap,
- Model::expression_input_position
- );
-
- out.is_hovered <+ list_panel.is_hovered || documentation.frp.is_hovered;
- out.is_hovered <+ input.hide.constant(false);
- }
- init.emit(());
- }
-}
-
-/// Component Browser View.
-///
-/// The Component Browser is a panel where user searches for types, variables and methods which can
-/// be used to construct new nodes. The components are arranged in sections and groups, and
-/// displayed in Component List Panel. The Component Browser View contains also Documentation Panel,
-/// displaying documentation of currently selected or hovered component.
-pub type View = component::ComponentView;
diff --git a/app/gui/view/src/lib.rs b/app/gui/view/src/lib.rs
index 60eff01f1cd0..cb05d146633c 100644
--- a/app/gui/view/src/lib.rs
+++ b/app/gui/view/src/lib.rs
@@ -31,7 +31,6 @@
#[allow(clippy::option_map_unit_fn)]
pub mod code_editor;
-pub mod component_browser;
pub mod debug_mode_popup;
pub mod project;
pub mod project_list;
@@ -40,6 +39,7 @@ pub mod searcher;
pub mod status_bar;
pub mod window_control_buttons;
+pub use ide_view_component_browser as component_browser;
pub use ide_view_documentation as documentation;
pub use ide_view_graph_editor as graph_editor;
pub use welcome_screen;
diff --git a/app/gui/view/src/project.rs b/app/gui/view/src/project.rs
index 775e8e9c9895..319f8cf5e8dc 100644
--- a/app/gui/view/src/project.rs
+++ b/app/gui/view/src/project.rs
@@ -9,8 +9,6 @@ use crate::component_browser;
use crate::component_browser::component_list_panel;
use crate::debug_mode_popup;
use crate::debug_mode_popup::DEBUG_MODE_SHORTCUT;
-use crate::documentation;
-use crate::graph_editor::component::node;
use crate::graph_editor::component::node::Expression;
use crate::graph_editor::component::visualization;
use crate::graph_editor::GraphEditor;
@@ -131,119 +129,6 @@ ensogl::define_endpoints! {
// === Model ===
// =============
-/// Common FRP endpoints for both Searcher variants. See [`SearcherVariant`].
-#[derive(Clone, CloneRef, Debug)]
-struct SearcherFrp {
- editing_committed: frp::Stream,
- is_visible: frp::Stream,
- is_empty: frp::Stream,
- is_hovered: frp::Stream,
-}
-
-/// A structure containing the Searcher View: the old Node Searcher of a new Component Browser.
-///
-/// As Component Browser is unstable, it is available only under the feature flag. Thus the Project
-/// View must be able to handle any of those views.
-#[allow(missing_docs)]
-#[derive(Clone, CloneRef, Debug)]
-pub enum SearcherVariant {
- ComponentBrowser(component_browser::View),
- /// We keep the old searcher in a Rc, as its memory size is much greater than the new one.
- OldNodeSearcher(Rc),
-}
-
-impl SearcherVariant {
- fn new(app: &Application) -> Self {
- if ARGS.groups.feature_preview.options.new_component_browser.value {
- Self::ComponentBrowser(app.new_view::())
- } else {
- Self::OldNodeSearcher(Rc::new(app.new_view::()))
- }
- }
-
- fn frp(&self, project_view_network: &frp::Network) -> SearcherFrp {
- match self {
- SearcherVariant::ComponentBrowser(view) => {
- let grid = &view.model().list.model().grid;
- frp::extend! {project_view_network
- is_empty <- source::();
- editing_committed <- grid.expression_accepted.constant(());
- }
- is_empty.emit(false);
- SearcherFrp {
- editing_committed,
- is_visible: view.output.is_visible.clone_ref().into(),
- is_empty: is_empty.into(),
- is_hovered: view.output.is_hovered.clone_ref().into(),
- }
- }
- SearcherVariant::OldNodeSearcher(view) => {
- let documentation = view.documentation();
- frp::extend! {project_view_network
- editing_committed <- view.editing_committed.constant(());
- }
- SearcherFrp {
- editing_committed,
- is_visible: view.output.is_visible.clone_ref().into(),
- is_empty: view.output.is_empty.clone_ref().into(),
- is_hovered: documentation.frp.is_hovered.clone_ref().into(),
- }
- }
- }
- }
-
- fn documentation(&self) -> &documentation::View {
- match self {
- SearcherVariant::ComponentBrowser(view) => &view.model().documentation,
- SearcherVariant::OldNodeSearcher(view) => view.documentation(),
- }
- }
-
- fn show(&self) {
- match self {
- SearcherVariant::ComponentBrowser(view) => view.show(),
- SearcherVariant::OldNodeSearcher(view) => view.show(),
- }
- }
-
- fn hide(&self) {
- match self {
- SearcherVariant::ComponentBrowser(view) => view.hide(),
- SearcherVariant::OldNodeSearcher(view) => view.hide(),
- }
- }
-
- fn setup_anchor(&self, network: &frp::Network, anchor: &frp::Stream>) {
- match self {
- SearcherVariant::ComponentBrowser(view) => {
- frp::extend! {network
- cb_position <- all_with(anchor, &view.expression_input_position, |anchor, pos| anchor - pos);
- eval cb_position ((pos) view.set_xy(*pos));
- }
- }
- SearcherVariant::OldNodeSearcher(view) => {
- frp::extend! {network
- searcher_pos <- all_with(anchor, &view.size, |anchor, size| {
- let x = anchor.x + size.x / 2.0;
- let y = anchor.y - node::HEIGHT / 2.0 - size.y / 2.0;
- Vector2(x, y)
- });
- eval searcher_pos ((pos) view.set_xy(*pos));
- }
- }
- }
- }
-}
-
-impl display::Object for SearcherVariant {
- fn display_object(&self) -> &display::object::Instance {
- match self {
- SearcherVariant::ComponentBrowser(view) => view.display_object(),
- SearcherVariant::OldNodeSearcher(view) => view.display_object(),
- }
- }
-}
-
#[derive(Clone, CloneRef, Debug)]
struct Model {
app: Application,
@@ -251,7 +136,7 @@ struct Model {
/// These buttons are present only in a cloud environment.
window_control_buttons: Immutable