mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 23:31:42 +03:00
Implement documentation IR (#4024)
[Task link](https://www.pivotaltracker.com/story/show/184012434) This PR implements Intermediate Representation for our documentation. Later these data structures would be used to generate HTML and CSS for the documentation panel. For now, we display it in the debug scene. https://user-images.githubusercontent.com/6566674/210674850-480a3e6e-76c3-4f34-a235-15c44dc9ec01.mp4 # Important Notes - `suggestion-database` now lives in a separate crate - also, two utility crates were introduced for the `notification` and `executor` modules of enso-gui - documentation debug scene is moved to a separate crate - All refactorings are done in the last two commits
This commit is contained in:
parent
c4c35c92b7
commit
6b8d8e9270
59
Cargo.lock
generated
59
Cargo.lock
generated
@ -1583,6 +1583,19 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debug-scene-documentation"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"enso-suggestion-database",
|
||||
"ensogl",
|
||||
"ensogl-hardcoded-theme",
|
||||
"ensogl-text-msdf",
|
||||
"ide-view-documentation",
|
||||
"ide-view-graph-editor",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debug-scene-icons"
|
||||
version = "0.1.0"
|
||||
@ -2003,12 +2016,23 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"debug-scene-cloud-dashboard",
|
||||
"debug-scene-component-list-panel-view",
|
||||
"debug-scene-documentation",
|
||||
"debug-scene-icons",
|
||||
"debug-scene-interface",
|
||||
"debug-scene-text-grid-visualization",
|
||||
"debug-scene-visualization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-executor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"enso-prelude",
|
||||
"enso-profiler",
|
||||
"ensogl-core",
|
||||
"futures 0.3.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-formatter"
|
||||
version = "0.1.0"
|
||||
@ -2062,11 +2086,14 @@ dependencies = [
|
||||
"enso-data-structures",
|
||||
"enso-debug-api",
|
||||
"enso-debug-scene",
|
||||
"enso-executor",
|
||||
"enso-frp",
|
||||
"enso-logger",
|
||||
"enso-notification",
|
||||
"enso-prelude",
|
||||
"enso-profiler",
|
||||
"enso-shapely",
|
||||
"enso-suggestion-database",
|
||||
"enso-text",
|
||||
"enso-web",
|
||||
"ensogl",
|
||||
@ -2106,6 +2133,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"approx 0.5.1",
|
||||
"engine-protocol",
|
||||
"enso-executor",
|
||||
"enso-frp",
|
||||
"enso-gui",
|
||||
"enso-prelude",
|
||||
@ -2159,6 +2187,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-notification"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"enso-executor",
|
||||
"enso-prelude",
|
||||
"flo_stream",
|
||||
"futures 0.3.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-parser"
|
||||
version = "0.1.0"
|
||||
@ -2391,6 +2429,27 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-suggestion-database"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ast",
|
||||
"convert_case 0.5.0",
|
||||
"double-representation",
|
||||
"engine-protocol",
|
||||
"enso-data-structures",
|
||||
"enso-executor",
|
||||
"enso-notification",
|
||||
"enso-prelude",
|
||||
"enso-text",
|
||||
"failure",
|
||||
"flo_stream",
|
||||
"futures 0.3.24",
|
||||
"parser-scala",
|
||||
"span-tree",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-text"
|
||||
version = "0.1.0"
|
||||
|
@ -19,9 +19,12 @@ enso-frp = { path = "../../lib/rust/frp" }
|
||||
enso-logger = { path = "../../lib/rust/logger" }
|
||||
enso-prelude = { path = "../../lib/rust/prelude" }
|
||||
enso-profiler = { path = "../../lib/rust/profiler" }
|
||||
enso-executor = { path = "../../lib/rust/executor" }
|
||||
enso-notification = { path = "../../lib/rust/notification" }
|
||||
enso-shapely = { path = "../../lib/rust/shapely" }
|
||||
enso-text = { path = "../../lib/rust/text" }
|
||||
enso-web = { path = "../../lib/rust/web" }
|
||||
enso-suggestion-database = { path = "suggestion-database" }
|
||||
ensogl = { path = "../../lib/rust/ensogl" }
|
||||
ensogl-examples = { path = "../../lib/rust/ensogl/example" }
|
||||
ensogl-component = { path = "../../lib/rust/ensogl/component" }
|
||||
|
@ -480,6 +480,24 @@ impl DefinitionProvider for known::Module {
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === MethodId ===
|
||||
// ================
|
||||
|
||||
/// A structure identifying a method.
|
||||
///
|
||||
/// It is very similar to MethodPointer from language_server API, however it may point to the method
|
||||
/// outside the currently opened project.
|
||||
#[derive(Clone, Debug, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct MethodId {
|
||||
pub module: QualifiedName,
|
||||
pub defined_on_type: QualifiedName,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Test ===
|
||||
// ============
|
||||
|
@ -8,6 +8,7 @@ use ast::constants::PROJECTS_MAIN_MODULE;
|
||||
use ast::opr::predefined::ACCESS;
|
||||
use enso_prelude::serde_reexports::Deserialize;
|
||||
use enso_prelude::serde_reexports::Serialize;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
||||
// ==============
|
||||
@ -459,3 +460,15 @@ impl<'a, Segments: AsRef<[ImString]>> PartialEq<NamePathRef<'a>>
|
||||
self.segments().eq(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Segments: AsRef<[ImString]>> PartialOrd for QualifiedNameTemplate<Segments> {
|
||||
fn partial_cmp(&self, other: &QualifiedNameTemplate<Segments>) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Segments: AsRef<[ImString]>> Ord for QualifiedNameTemplate<Segments> {
|
||||
fn cmp(&self, other: &QualifiedNameTemplate<Segments>) -> Ordering {
|
||||
self.project.cmp(&other.project).then(self.path.as_ref().cmp(other.path.as_ref()))
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ pub struct Handle {
|
||||
project: model::Project,
|
||||
/// The publisher allowing sending notification to subscribed entities. Note that its outputs
|
||||
/// is merged with publishers from the stored graph and execution controllers.
|
||||
notifier: crate::notification::Publisher<Notification>,
|
||||
notifier: notification::Publisher<Notification>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::notification;
|
||||
|
||||
use double_representation::name::project;
|
||||
use mockall::automock;
|
||||
use parser_scala::Parser;
|
||||
|
@ -9,7 +9,6 @@ use crate::controller::ide::Notification;
|
||||
use crate::controller::ide::StatusNotificationPublisher;
|
||||
use crate::controller::ide::API;
|
||||
use crate::ide::initializer;
|
||||
use crate::notification;
|
||||
|
||||
use double_representation::name::project;
|
||||
use engine_protocol::project_manager;
|
||||
|
@ -5,17 +5,16 @@ use crate::prelude::*;
|
||||
|
||||
use crate::controller::graph::FailedToCreateNode;
|
||||
use crate::controller::searcher::component::group;
|
||||
use crate::model::module::MethodId;
|
||||
use crate::model::module::NodeEditStatus;
|
||||
use crate::model::module::NodeMetadata;
|
||||
use crate::model::suggestion_database;
|
||||
use crate::notification;
|
||||
|
||||
use breadcrumbs::Breadcrumbs;
|
||||
use const_format::concatcp;
|
||||
use double_representation::graph::GraphInfo;
|
||||
use double_representation::graph::LocationHint;
|
||||
use double_representation::import;
|
||||
use double_representation::module::MethodId;
|
||||
use double_representation::name::project;
|
||||
use double_representation::name::QualifiedName;
|
||||
use double_representation::name::QualifiedNameRef;
|
||||
@ -1639,15 +1638,15 @@ pub mod test {
|
||||
|
||||
use crate::controller::ide::plain::ProjectOperationsNotSupported;
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
use crate::mock_suggestion_database;
|
||||
use crate::model::suggestion_database::entry::Argument;
|
||||
use crate::model::SuggestionDatabase;
|
||||
use crate::test::mock::data::project_qualified_name;
|
||||
use crate::test::mock::data::MAIN_FINISH;
|
||||
use crate::test::mock::data::MODULE_NAME;
|
||||
|
||||
use engine_protocol::language_server::types::test::value_update_with_type;
|
||||
use engine_protocol::language_server::SuggestionId;
|
||||
use enso_suggestion_database::entry::Argument;
|
||||
use enso_suggestion_database::mock_suggestion_database;
|
||||
use enso_suggestion_database::SuggestionDatabase;
|
||||
use json_rpc::expect_call;
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::model::module::MethodId;
|
||||
use crate::model::SuggestionDatabase;
|
||||
|
||||
use double_representation::module::MethodId;
|
||||
use double_representation::name::QualifiedNameRef;
|
||||
|
||||
|
||||
|
@ -306,8 +306,8 @@ impl List {
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::mock_suggestion_database;
|
||||
use double_representation::name::project;
|
||||
use enso_suggestion_database::mock_suggestion_database;
|
||||
|
||||
pub fn mock_suggestion_db() -> model::SuggestionDatabase {
|
||||
mock_suggestion_database! {
|
||||
|
@ -1,11 +0,0 @@
|
||||
//! Code dealing with executors, i.e. entities that are used to execute asynchronous
|
||||
//! computations, like `Future`s or `Stream`s.
|
||||
|
||||
|
||||
// ==============
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub mod global;
|
||||
pub mod test_utils;
|
||||
pub mod web;
|
@ -221,13 +221,6 @@ impl WithProjectManager {
|
||||
// === Utils ===
|
||||
// =============
|
||||
|
||||
/// Creates a new running executor with its own event loop. Registers them as a global executor.
|
||||
pub fn setup_global_executor() -> executor::web::EventLoopExecutor {
|
||||
let executor = executor::web::EventLoopExecutor::new_running();
|
||||
executor::global::set_spawner(executor.spawner.clone());
|
||||
executor
|
||||
}
|
||||
|
||||
/// Register all the standard views for the IDE.
|
||||
pub fn register_views(app: &Application) {
|
||||
app.views.register::<ide_view::root::View>();
|
||||
|
@ -5,8 +5,8 @@ use crate::prelude::*;
|
||||
use enso_web::traits::*;
|
||||
|
||||
use crate::config::InitialView;
|
||||
use crate::executor::setup_global_executor;
|
||||
use crate::executor::web::EventLoopExecutor;
|
||||
use crate::initializer::setup_global_executor;
|
||||
use crate::Ide;
|
||||
|
||||
use enso_web::Closure;
|
||||
|
@ -72,11 +72,9 @@ use wasm_bindgen::prelude::*;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
pub mod controller;
|
||||
pub mod executor;
|
||||
pub mod ide;
|
||||
pub mod integration_test;
|
||||
pub mod model;
|
||||
pub mod notification;
|
||||
pub mod presenter;
|
||||
pub mod sync;
|
||||
pub mod test;
|
||||
@ -91,8 +89,6 @@ pub use ide_view as view;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod documentation_debug_scene;
|
||||
|
||||
/// Common types that should be visible across the whole IDE crate.
|
||||
pub mod prelude {
|
||||
pub use ast::prelude::*;
|
||||
@ -101,7 +97,6 @@ pub mod prelude {
|
||||
|
||||
pub use crate::constants;
|
||||
pub use crate::controller;
|
||||
pub use crate::executor;
|
||||
pub use crate::model;
|
||||
pub use crate::model::traits::*;
|
||||
|
||||
@ -118,6 +113,9 @@ pub mod prelude {
|
||||
pub use futures::Stream;
|
||||
pub use futures::StreamExt;
|
||||
|
||||
pub use enso_executor as executor;
|
||||
pub use enso_notification as notification;
|
||||
|
||||
pub use std::ops::Range;
|
||||
|
||||
pub use uuid::Uuid;
|
||||
@ -161,7 +159,7 @@ pub fn main() {
|
||||
);
|
||||
let config =
|
||||
crate::config::Startup::from_web_arguments().expect("Failed to read configuration");
|
||||
let executor = crate::ide::initializer::setup_global_executor();
|
||||
let executor = crate::executor::setup_global_executor();
|
||||
let initializer = crate::ide::initializer::Initializer::new(config);
|
||||
executor::global::spawn(async move {
|
||||
let ide = initializer.start().await;
|
||||
|
@ -28,9 +28,9 @@ pub mod execution_context;
|
||||
pub mod module;
|
||||
pub mod project;
|
||||
pub mod registry;
|
||||
pub mod suggestion_database;
|
||||
pub mod undo_redo;
|
||||
|
||||
pub use enso_suggestion_database as suggestion_database;
|
||||
pub use execution_context::ExecutionContext;
|
||||
pub use module::Module;
|
||||
pub use project::Project;
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::notification::Publisher;
|
||||
|
||||
use double_representation::identifier::Identifier;
|
||||
use double_representation::name::project;
|
||||
use double_representation::name::QualifiedName;
|
||||
@ -16,6 +14,7 @@ use engine_protocol::language_server::VisualisationConfiguration;
|
||||
use ensogl::data::color;
|
||||
use flo_stream::Subscriber;
|
||||
use mockall::automock;
|
||||
use notification::Publisher;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
@ -8,6 +8,7 @@ use ast::constants::LANGUAGE_FILE_EXTENSION;
|
||||
use ast::constants::SOURCE_DIRECTORY;
|
||||
use double_representation::definition::DefinitionInfo;
|
||||
use double_representation::import;
|
||||
use double_representation::module::MethodId;
|
||||
use double_representation::name::project;
|
||||
use double_representation::name::QualifiedName;
|
||||
use engine_protocol::language_server::MethodPointer;
|
||||
@ -496,18 +497,6 @@ impl From<Vector2<f32>> for Position {
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure identifying a method.
|
||||
///
|
||||
/// It is very similar to MethodPointer from language_server API, however it may point to the method
|
||||
/// outside the currently opened project.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct MethodId {
|
||||
pub module: QualifiedName,
|
||||
pub defined_on_type: QualifiedName,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Uploading File Information
|
||||
///
|
||||
/// May be stored in node metadata, if the node's expression is reading content of file still
|
||||
|
@ -13,7 +13,6 @@ use crate::model::module::NotificationKind;
|
||||
use crate::model::module::Path;
|
||||
use crate::model::module::ProjectMetadata;
|
||||
use crate::model::module::TextChange;
|
||||
use crate::notification;
|
||||
|
||||
use double_representation::definition::DefinitionInfo;
|
||||
use double_representation::import;
|
||||
|
@ -8,7 +8,6 @@ use crate::model::execution_context::synchronized::Notification as ExecutionUpda
|
||||
use crate::model::execution_context::VisualizationUpdateData;
|
||||
use crate::model::module;
|
||||
use crate::model::SuggestionDatabase;
|
||||
use crate::notification;
|
||||
use crate::transport::web::WebSocket;
|
||||
|
||||
use double_representation::name::project;
|
||||
|
@ -2,9 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::notification::Publisher;
|
||||
|
||||
use flo_stream::Subscriber;
|
||||
use notification::Publisher;
|
||||
|
||||
|
||||
|
||||
|
@ -4,10 +4,10 @@ use crate::prelude::*;
|
||||
|
||||
use crate::controller::searcher::action::MatchInfo;
|
||||
use crate::controller::searcher::component;
|
||||
use crate::model::suggestion_database::entry::for_each_kind_variant;
|
||||
use crate::presenter;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_suggestion_database::entry::for_each_kind_variant;
|
||||
use enso_text as text;
|
||||
use ensogl_component::list_view;
|
||||
use ensogl_component::list_view::entry::GlyphHighlightedLabel;
|
||||
|
@ -10,7 +10,7 @@ use crate::prelude::*;
|
||||
#[derivative(Clone(bound = ""))]
|
||||
pub struct Synchronized<T> {
|
||||
value: Rc<RefCell<T>>,
|
||||
notifier: crate::notification::Publisher<()>,
|
||||
notifier: notification::Publisher<()>,
|
||||
}
|
||||
|
||||
impl<T> Synchronized<T> {
|
||||
|
27
app/gui/suggestion-database/Cargo.toml
Normal file
27
app/gui/suggestion-database/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "enso-suggestion-database"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { path = "../../../lib/rust/prelude" }
|
||||
convert_case = { version = "0.5.0" }
|
||||
span-tree = { path = "../language/span-tree" }
|
||||
ast = { path = "../language/ast/impl" }
|
||||
parser-scala = { path = "../language/parser" }
|
||||
enso-text = { path = "../../../lib/rust/text" }
|
||||
double-representation = { version = "0.1.0", path = "../controller/double-representation" }
|
||||
engine-protocol = { path = "../controller/engine-protocol" }
|
||||
enso-data-structures = { path = "../../../lib/rust/data-structures" }
|
||||
flo_stream = { version = "0.4.0" }
|
||||
failure = { version = "0.1.6" }
|
||||
enso-notification = { path = "../../../lib/rust/notification" }
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3.1" }
|
||||
enso-executor = { path = "../../../lib/rust/executor" }
|
||||
wasm-bindgen-test = { workspace = true }
|
757
app/gui/suggestion-database/src/documentation_ir.rs
Normal file
757
app/gui/suggestion-database/src/documentation_ir.rs
Normal file
@ -0,0 +1,757 @@
|
||||
//! The intermediate representation of the entry's documentation.
|
||||
//!
|
||||
//! [`EntryDocumentation`] contains all the necessary information to generate HTML
|
||||
//! documentation of the specific entry. [`EntryDocumentation`] is created by aggregating
|
||||
//! documentation of the entry, and also its children entries, such as methods of the type or types
|
||||
//! defined in the module.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::entry;
|
||||
use crate::entry::Argument;
|
||||
use crate::entry::Kind;
|
||||
use crate::Entry;
|
||||
use crate::NoSuchEntry;
|
||||
use crate::SuggestionDatabase;
|
||||
|
||||
use double_representation::name::QualifiedName;
|
||||
use engine_protocol::language_server::DocSection;
|
||||
use engine_protocol::language_server::Mark;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// === EntryDocumentation ===
|
||||
// ==========================
|
||||
|
||||
// === EntryDocumentation ===
|
||||
|
||||
/// The documentation of the specific entry.
|
||||
#[derive(Debug, PartialEq, From)]
|
||||
pub enum EntryDocumentation {
|
||||
/// No documentation available.
|
||||
Placeholder(Placeholder),
|
||||
/// Documentation of the entry.
|
||||
Docs(Documentation),
|
||||
}
|
||||
|
||||
impl Default for EntryDocumentation {
|
||||
fn default() -> Self {
|
||||
Placeholder::NoDocumentation.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EntryDocumentation {
|
||||
/// Constructor.
|
||||
pub fn new(db: &SuggestionDatabase, id: &entry::Id) -> Result<Self, NoSuchEntry> {
|
||||
let entry = db.lookup(*id);
|
||||
let result = match entry {
|
||||
Ok(entry) => match entry.kind {
|
||||
Kind::Type => {
|
||||
let type_docs = TypeDocumentation::new(*id, &entry, db)?.into();
|
||||
Documentation::Type(type_docs).into()
|
||||
}
|
||||
Kind::Module => {
|
||||
let module_docs = ModuleDocumentation::new(*id, &entry, db)?.into();
|
||||
Documentation::Module(module_docs).into()
|
||||
}
|
||||
Kind::Constructor => Self::constructor_docs(db, &entry)?,
|
||||
Kind::Method => Self::method_docs(db, &entry)?,
|
||||
Kind::Function => Placeholder::Function { name: ImString::new(&entry.name) }.into(),
|
||||
Kind::Local => Placeholder::Local { name: ImString::new(&entry.name) }.into(),
|
||||
},
|
||||
Err(_) => {
|
||||
error!("No entry found for id: {id:?}");
|
||||
Self::Placeholder(Placeholder::NoDocumentation)
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn method_docs(
|
||||
db: &SuggestionDatabase,
|
||||
entry: &Entry,
|
||||
) -> Result<EntryDocumentation, NoSuchEntry> {
|
||||
let self_type = match &entry.self_type {
|
||||
Some(self_type) => self_type,
|
||||
None => {
|
||||
error!("Method without self type: {}.", entry.qualified_name());
|
||||
return Ok(Placeholder::NoDocumentation.into());
|
||||
}
|
||||
};
|
||||
let return_type = db.lookup_by_qualified_name(self_type);
|
||||
match return_type {
|
||||
Some((id, parent)) => {
|
||||
let name = entry.qualified_name().into();
|
||||
match parent.kind {
|
||||
Kind::Type => {
|
||||
let type_docs = TypeDocumentation::new(id, &parent, db)?.into();
|
||||
Ok(Documentation::Method { name, type_docs }.into())
|
||||
}
|
||||
Kind::Module => {
|
||||
let module_docs = ModuleDocumentation::new(id, &parent, db)?;
|
||||
let module_docs = module_docs.into();
|
||||
Ok(Documentation::ModuleMethod { name, module_docs }.into())
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected parent kind for method {}.", entry.qualified_name());
|
||||
Ok(Placeholder::NoDocumentation.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
error!("Parent entry for method {} not found.", entry.qualified_name());
|
||||
Ok(Self::Placeholder(Placeholder::NoDocumentation))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn constructor_docs(
|
||||
db: &SuggestionDatabase,
|
||||
entry: &Entry,
|
||||
) -> Result<EntryDocumentation, NoSuchEntry> {
|
||||
let return_type = &entry.return_type;
|
||||
let return_type = db.lookup_by_qualified_name(return_type);
|
||||
|
||||
match return_type {
|
||||
Some((id, parent)) => {
|
||||
let name = entry.qualified_name().into();
|
||||
let type_docs = TypeDocumentation::new(id, &parent, db)?.into();
|
||||
Ok(Documentation::Constructor { name, type_docs }.into())
|
||||
}
|
||||
None => {
|
||||
error!("No return type found for constructor {}.", entry.qualified_name());
|
||||
Ok(Placeholder::NoDocumentation.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Placeholder ===
|
||||
|
||||
/// No documentation is available for the entry.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Placeholder {
|
||||
/// Documentation is empty.
|
||||
NoDocumentation,
|
||||
/// We temporarily do not support documentation of the `Local` entries because the language
|
||||
/// server does not provide us with any. See https://www.pivotaltracker.com/story/show/183970215.
|
||||
Local { name: ImString },
|
||||
/// We temporarily do not support documentation of the `Function` entries because the language
|
||||
/// server does not provide us with any. See https://www.pivotaltracker.com/story/show/183970215.
|
||||
Function { name: ImString },
|
||||
}
|
||||
|
||||
// === Documentation ===
|
||||
|
||||
/// Documentation of the entry, split into variants for each entry kind.
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Documentation {
|
||||
Module(Rc<ModuleDocumentation>),
|
||||
Type(Rc<TypeDocumentation>),
|
||||
Constructor {
|
||||
name: Rc<QualifiedName>,
|
||||
type_docs: Rc<TypeDocumentation>,
|
||||
},
|
||||
Method {
|
||||
name: Rc<QualifiedName>,
|
||||
type_docs: Rc<TypeDocumentation>,
|
||||
},
|
||||
ModuleMethod {
|
||||
name: Rc<QualifiedName>,
|
||||
module_docs: Rc<ModuleDocumentation>,
|
||||
},
|
||||
/// We temporarily do not support documentation of the `Local` entries because the language
|
||||
/// server does not provide us with any. See https://www.pivotaltracker.com/story/show/183970215.
|
||||
Function {},
|
||||
/// We temporarily do not support documentation of the `Local` entries because the language
|
||||
/// server does not provide us with any. See https://www.pivotaltracker.com/story/show/183970215.
|
||||
Local {},
|
||||
}
|
||||
|
||||
// =========================
|
||||
// === TypeDocumentation ===
|
||||
// =========================
|
||||
|
||||
/// Documentation of the [`EntryKind::Type`] entries.
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TypeDocumentation {
|
||||
pub name: Rc<QualifiedName>,
|
||||
pub arguments: Rc<Vec<Argument>>,
|
||||
pub tags: Tags,
|
||||
pub synopsis: Synopsis,
|
||||
pub constructors: Constructors,
|
||||
pub methods: Methods,
|
||||
pub examples: Examples,
|
||||
}
|
||||
|
||||
impl PartialOrd for TypeDocumentation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.name.cmp(&other.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypeDocumentation {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.name.cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDocumentation {
|
||||
/// Constructor.
|
||||
pub fn new(id: entry::Id, entry: &Entry, db: &SuggestionDatabase) -> Result<Self, NoSuchEntry> {
|
||||
let FilteredDocSections { tags, synopsis, examples } =
|
||||
FilteredDocSections::new(entry.documentation.iter());
|
||||
Ok(Self {
|
||||
name: entry.qualified_name().into(),
|
||||
arguments: entry.arguments.clone().into(),
|
||||
tags,
|
||||
synopsis,
|
||||
constructors: Constructors::of_type(id, db)?,
|
||||
methods: Methods::of_entry(id, db)?,
|
||||
examples,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// === ModuleDocumentation ===
|
||||
// ===========================
|
||||
|
||||
/// Documentation of the [`EntryKind::Module`] entries.
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ModuleDocumentation {
|
||||
pub name: Rc<QualifiedName>,
|
||||
pub tags: Tags,
|
||||
pub synopsis: Synopsis,
|
||||
pub types: Types,
|
||||
pub methods: Methods,
|
||||
pub examples: Examples,
|
||||
}
|
||||
|
||||
impl ModuleDocumentation {
|
||||
/// Constructor.
|
||||
pub fn new(id: entry::Id, entry: &Entry, db: &SuggestionDatabase) -> Result<Self, NoSuchEntry> {
|
||||
let FilteredDocSections { tags, synopsis, examples } =
|
||||
FilteredDocSections::new(entry.documentation.iter());
|
||||
Ok(Self {
|
||||
name: entry.qualified_name().into(),
|
||||
tags,
|
||||
synopsis,
|
||||
types: Types::of_module(id, db)?,
|
||||
methods: Methods::of_entry(id, db)?,
|
||||
examples,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ============
|
||||
// === Tags ===
|
||||
// ============
|
||||
|
||||
/// Tags attached to the entry. Corresponds to [`DocSection::Tagged`].
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq, Eq, Deref, Default)]
|
||||
pub struct Tags {
|
||||
list: SortedVec<Tag>,
|
||||
}
|
||||
|
||||
/// A single tag.
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq, Eq)]
|
||||
pub struct Tag {
|
||||
/// Name of the tag.
|
||||
pub name: ImString,
|
||||
/// Optional tag value. Can be empty.
|
||||
pub body: ImString,
|
||||
}
|
||||
|
||||
impl PartialOrd for Tag {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.name.cmp(&other.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Tag {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.name.cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
/// Constructor.
|
||||
pub fn new<N: Into<ImString>, B: Into<ImString>>(name: N, body: B) -> Self {
|
||||
Self { name: name.into(), body: body.into() }
|
||||
}
|
||||
}
|
||||
|
||||
// ================
|
||||
// === Synopsis ===
|
||||
// ================
|
||||
|
||||
/// The most common portion of the documentation. A list of paragraphs. Can contain
|
||||
/// [`DocSection::Paragraph`], [`DocSection::Keyed`] or [`DocSection::Marked`] (except for
|
||||
/// examples) items. Examples and tags are handled separately (see [`Examples`] and [`Tags`]).
|
||||
#[derive(Debug, Clone, CloneRef, Deref, PartialEq, Eq, Default)]
|
||||
pub struct Synopsis {
|
||||
list: Rc<Vec<DocSection>>,
|
||||
}
|
||||
|
||||
impl Synopsis {
|
||||
/// Constructor.
|
||||
pub fn from_doc_sections(sections: impl IntoIterator<Item = DocSection>) -> Self {
|
||||
let list = sections.into_iter().collect_vec();
|
||||
Self { list: Rc::new(list) }
|
||||
}
|
||||
}
|
||||
|
||||
// =============
|
||||
// === Types ===
|
||||
// =============
|
||||
|
||||
/// A list of types defined in the module.
|
||||
#[derive(Debug, Clone, CloneRef, PartialEq, Default, Deref)]
|
||||
pub struct Types {
|
||||
list: SortedVec<TypeDocumentation>,
|
||||
}
|
||||
|
||||
impl Types {
|
||||
/// Collect all types defined in the module.
|
||||
pub fn of_module(module_id: entry::Id, db: &SuggestionDatabase) -> Result<Self, NoSuchEntry> {
|
||||
let types = db.lookup_hierarchy(module_id)?;
|
||||
let lookup = |id: &entry::Id| {
|
||||
if let Ok(entry) = db.lookup(*id) {
|
||||
Some((*id, entry))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let entries = types.iter().flat_map(lookup);
|
||||
let entries = entries.flat_map(|(id, entry)| TypeDocumentation::new(id, &entry, db).ok());
|
||||
Ok(Self { list: entries.collect_vec().into() })
|
||||
}
|
||||
}
|
||||
|
||||
// =================
|
||||
// === Functions ===
|
||||
// =================
|
||||
|
||||
/// A list of functions. See [`Function`].
|
||||
#[derive(Debug, Clone, CloneRef, Deref, PartialEq, Eq, Default)]
|
||||
pub struct Functions {
|
||||
list: SortedVec<Function>,
|
||||
}
|
||||
|
||||
impl Functions {
|
||||
/// Build list from the list of entries.
|
||||
pub fn from_entries(entries: impl Iterator<Item = Rc<Entry>>) -> Self {
|
||||
let methods = entries.map(|entry| Function::from_entry(&entry)).collect_vec();
|
||||
Self { list: methods.into() }
|
||||
}
|
||||
}
|
||||
|
||||
// === Function ===
|
||||
|
||||
/// A documentation of a single function. It can be either a module function, or a type method, or a
|
||||
/// type constructor. It corresponds to entries with kinds [`EntryKind::Function`],
|
||||
/// [`EntryKind::Method`] and [`EntryKind::Constructor`].
|
||||
#[derive(PartialEq, Debug, Clone, CloneRef, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Function {
|
||||
pub name: Rc<QualifiedName>,
|
||||
pub tags: Tags,
|
||||
pub arguments: Rc<Vec<Argument>>,
|
||||
pub synopsis: Synopsis,
|
||||
pub examples: Examples,
|
||||
}
|
||||
|
||||
impl PartialOrd for Function {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.name.cmp(&other.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Function {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.name.cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Constructor.
|
||||
pub fn from_entry(entry: &Entry) -> Self {
|
||||
let FilteredDocSections { tags, synopsis, examples } =
|
||||
FilteredDocSections::new(entry.documentation.iter());
|
||||
Self {
|
||||
name: entry.qualified_name().into(),
|
||||
tags,
|
||||
arguments: entry.arguments.clone().into(),
|
||||
synopsis,
|
||||
examples,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Methods ===
|
||||
|
||||
/// A list of methods defined for the type or module.
|
||||
#[derive(Debug, Deref, PartialEq, Eq, Clone, CloneRef, Default, From)]
|
||||
pub struct Methods(Functions);
|
||||
|
||||
impl Methods {
|
||||
/// Collect all the methods defined for the type.
|
||||
pub fn of_entry(entry_id: entry::Id, db: &SuggestionDatabase) -> Result<Self, NoSuchEntry> {
|
||||
let methods = db.lookup_hierarchy(entry_id)?;
|
||||
let entries = methods.iter().flat_map(|id| db.lookup(*id).ok());
|
||||
let functions = entries.filter(|entry| entry.kind == Kind::Method);
|
||||
let functions = Functions::from_entries(functions);
|
||||
Ok(functions.into())
|
||||
}
|
||||
}
|
||||
|
||||
// === Constructors ===
|
||||
|
||||
/// A list of constructors defined for the type.
|
||||
#[derive(Debug, Deref, PartialEq, Eq, Clone, CloneRef, Default, From)]
|
||||
pub struct Constructors(Functions);
|
||||
|
||||
impl Constructors {
|
||||
/// Collect all the constructors defined for the type.
|
||||
pub fn of_type(entry_id: entry::Id, db: &SuggestionDatabase) -> Result<Self, NoSuchEntry> {
|
||||
let children = db.lookup_hierarchy(entry_id)?;
|
||||
let entries = children.iter().flat_map(|id| db.lookup(*id).ok());
|
||||
let constructors = entries.filter(|entry| entry.kind == Kind::Constructor);
|
||||
let methods = Functions::from_entries(constructors);
|
||||
Ok(Self(methods))
|
||||
}
|
||||
}
|
||||
|
||||
// ================
|
||||
// === Examples ===
|
||||
// ================
|
||||
|
||||
/// Examples of the entry usage. Can contain only [`DocSection::Marked`] with [`Mark::Example`]
|
||||
/// items.
|
||||
#[derive(Debug, Clone, CloneRef, Default, PartialEq, Eq, Deref)]
|
||||
pub struct Examples {
|
||||
list: Rc<Vec<DocSection>>,
|
||||
}
|
||||
|
||||
impl Examples {
|
||||
/// Constructor.
|
||||
pub fn from_doc_sections(sections: impl IntoIterator<Item = DocSection>) -> Self {
|
||||
let is_example = |section: &DocSection| {
|
||||
matches!(section, DocSection::Marked { mark: Mark::Example, .. })
|
||||
};
|
||||
let examples = sections.into_iter().filter(is_example).collect_vec();
|
||||
Self { list: examples.into() }
|
||||
}
|
||||
}
|
||||
|
||||
// =================
|
||||
// === SortedVec ===
|
||||
// =================
|
||||
|
||||
/// A vector that is always sorted. It is used to store documentation items (such as methods) in a
|
||||
/// sorted order.
|
||||
#[derive(Debug, Clone, CloneRef, Deref, PartialEq, Eq)]
|
||||
#[clone_ref(bound = "T: Clone")]
|
||||
pub struct SortedVec<T> {
|
||||
inner: Rc<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for SortedVec<T> {
|
||||
fn default() -> Self {
|
||||
Self { inner: default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd + Ord> SortedVec<T> {
|
||||
/// Constructor. Sorts the given items.
|
||||
pub fn new(items: impl IntoIterator<Item = T>) -> Self {
|
||||
let vec = items.into_iter().sorted().collect_vec().into();
|
||||
Self { inner: vec }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd + Ord> From<Vec<T>> for SortedVec<T> {
|
||||
fn from(mut vec: Vec<T>) -> Self {
|
||||
vec.sort();
|
||||
Self { inner: vec.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone + PartialOrd + Ord> From<&'a [T]> for SortedVec<T> {
|
||||
fn from(items: &'a [T]) -> Self {
|
||||
SortedVec::new(items.iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// === FilteredDocSections ===
|
||||
// ===========================
|
||||
|
||||
/// Helper structure for splitting entry's [`DocSection`]s into [`Tags`], [`Synopsis`], and
|
||||
/// [`Examples`].
|
||||
struct FilteredDocSections {
|
||||
tags: Tags,
|
||||
synopsis: Synopsis,
|
||||
examples: Examples,
|
||||
}
|
||||
|
||||
impl FilteredDocSections {
|
||||
/// Constructor.
|
||||
pub fn new<'a>(doc_sections: impl Iterator<Item = &'a DocSection>) -> Self {
|
||||
let mut tags = Vec::new();
|
||||
let mut synopsis = Vec::new();
|
||||
let mut examples = Vec::new();
|
||||
for section in doc_sections {
|
||||
match section {
|
||||
DocSection::Tag { name, body } => tags.push(Tag::new(name, body)),
|
||||
DocSection::Marked { mark: Mark::Example, .. } => examples.push(section.clone()),
|
||||
section => synopsis.push(section.clone()),
|
||||
}
|
||||
}
|
||||
Self {
|
||||
tags: Tags { list: SortedVec::new(tags) },
|
||||
synopsis: Synopsis { list: synopsis.into() },
|
||||
examples: Examples { list: examples.into() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============
|
||||
// === Tests ===
|
||||
// =============
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::doc_section;
|
||||
use crate::mock_suggestion_database;
|
||||
use double_representation::name::QualifiedName;
|
||||
|
||||
#[test]
|
||||
fn test_entry_documentation_not_found() {
|
||||
let db = mock_db();
|
||||
// Arbitrary non-existing entry id.
|
||||
let entry_id = 10;
|
||||
let docs = EntryDocumentation::new(&db, &entry_id).unwrap();
|
||||
assert_eq!(docs, EntryDocumentation::Placeholder(Placeholder::NoDocumentation));
|
||||
}
|
||||
|
||||
fn assert_docs(db: &SuggestionDatabase, name: Rc<QualifiedName>, expected: Documentation) {
|
||||
let (entry_id, _) = db.lookup_by_qualified_name(&*name).unwrap();
|
||||
let docs = EntryDocumentation::new(db, &entry_id).unwrap();
|
||||
assert_eq!(EntryDocumentation::Docs(expected), docs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_documentation_of_constructor() {
|
||||
let db = mock_db();
|
||||
let name = Rc::new(QualifiedName::from_text("Standard.Base.A.Foo").unwrap());
|
||||
let type_docs = a_type().into();
|
||||
let expected = Documentation::Constructor { name: name.clone(), type_docs };
|
||||
assert_docs(&db, name, expected);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_documentation_of_method() {
|
||||
let db = mock_db();
|
||||
|
||||
// === Type method ===
|
||||
|
||||
let name = Rc::new(QualifiedName::from_text("Standard.Base.A.baz").unwrap());
|
||||
let type_docs = a_type().into();
|
||||
let expected = Documentation::Method { name: name.clone(), type_docs };
|
||||
assert_docs(&db, name, expected);
|
||||
|
||||
|
||||
// === Module method ===
|
||||
|
||||
let name = Rc::new(QualifiedName::from_text("Standard.Base.module_method").unwrap());
|
||||
let module_docs = module_docs().into();
|
||||
let expected = Documentation::ModuleMethod { name: name.clone(), module_docs };
|
||||
assert_docs(&db, name, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_documentation_of_module() {
|
||||
let db = mock_db();
|
||||
let expected = Documentation::Module(Rc::new(module_docs()));
|
||||
let name = Rc::new(QualifiedName::from_text("Standard.Base").unwrap());
|
||||
assert_docs(&db, name, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_documentation_of_type() {
|
||||
let db = mock_db();
|
||||
|
||||
// === Type Standard.Base.A ===
|
||||
|
||||
let expected = Documentation::Type(Rc::new(a_type()));
|
||||
let name = QualifiedName::from_text("Standard.Base.A").unwrap();
|
||||
let (entry_id, _) = db.lookup_by_qualified_name(&name).unwrap();
|
||||
let docs = EntryDocumentation::new(&db, &entry_id).unwrap();
|
||||
assert_eq!(EntryDocumentation::Docs(expected), docs);
|
||||
|
||||
// === Type Standard.Base.B ===
|
||||
|
||||
let expected = Documentation::Type(Rc::new(b_type()));
|
||||
let name = Rc::new(QualifiedName::from_text("Standard.Base.B").unwrap());
|
||||
assert_docs(&db, name, expected);
|
||||
}
|
||||
|
||||
fn mock_db() -> SuggestionDatabase {
|
||||
mock_suggestion_database! {
|
||||
#[with_doc_section(doc_section!("Documentation of module."))]
|
||||
Standard.Base {
|
||||
#[with_doc_section(doc_section!("Documentation of type A."))]
|
||||
#[with_doc_section(doc_section!("Consists of multiple sections."))]
|
||||
#[with_doc_section(doc_section!(> "Example", "Example of type A usage."))]
|
||||
type A {
|
||||
#[with_doc_section(doc_section!("Documentation of constructor A.Foo."))]
|
||||
Foo (a);
|
||||
#[with_doc_section(doc_section!("Documentation of constructor A.Bar."))]
|
||||
Bar (b);
|
||||
|
||||
#[with_doc_section(doc_section!("Documentation of method A.baz."))]
|
||||
#[with_doc_section(doc_section!(@ "Tag", "Tag body."))]
|
||||
fn baz() -> Standard.Base.B;
|
||||
}
|
||||
|
||||
#[with_doc_section(doc_section!("Documentation of type B."))]
|
||||
type B {
|
||||
#[with_doc_section(doc_section!("Documentation of constructor B.New."))]
|
||||
#[with_doc_section(doc_section!(@ "AnotherTag", "Tag body."))]
|
||||
#[with_doc_section(doc_section!(! "Important", "Important note."))]
|
||||
#[with_doc_section(doc_section!(> "Example", "Example of constructor B.New usage."))]
|
||||
New;
|
||||
}
|
||||
|
||||
#[with_doc_section(doc_section!("Documentation of module method."))]
|
||||
#[with_doc_section(doc_section!(@ "Deprecated", ""))]
|
||||
#[with_doc_section(doc_section!(> "Example", "Example of module method usage."))]
|
||||
fn module_method() -> Standard.Base.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn module_docs() -> ModuleDocumentation {
|
||||
ModuleDocumentation {
|
||||
name: QualifiedName::from_text("Standard.Base").unwrap().into(),
|
||||
tags: default(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!("Documentation of module.")]),
|
||||
types: Types { list: SortedVec::new([a_type(), b_type()]) },
|
||||
methods: Functions { list: SortedVec::new([module_method_function()]) }.into(),
|
||||
examples: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn module_method_function() -> Function {
|
||||
Function {
|
||||
name: QualifiedName::from_text("Standard.Base.module_method").unwrap().into(),
|
||||
tags: Tags {
|
||||
list: SortedVec::new([Tag {
|
||||
name: "Deprecated".to_im_string(),
|
||||
body: "".to_im_string(),
|
||||
}]),
|
||||
},
|
||||
arguments: default(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!(
|
||||
"Documentation of module method."
|
||||
)]),
|
||||
examples: Examples::from_doc_sections([doc_section!(
|
||||
> "Example", "Example of module method usage."
|
||||
)]),
|
||||
}
|
||||
}
|
||||
|
||||
fn a_type() -> TypeDocumentation {
|
||||
TypeDocumentation {
|
||||
name: QualifiedName::from_text("Standard.Base.A").unwrap().into(),
|
||||
arguments: default(),
|
||||
tags: default(),
|
||||
synopsis: Synopsis::from_doc_sections([
|
||||
doc_section!("Documentation of type A."),
|
||||
doc_section!("Consists of multiple sections."),
|
||||
]),
|
||||
constructors: Functions {
|
||||
list: SortedVec::new(vec![a_bar_constructor(), a_foo_constructor()]),
|
||||
}
|
||||
.into(),
|
||||
methods: Methods(Functions { list: vec![a_baz_method()].into() }),
|
||||
examples: Examples::from_doc_sections([doc_section!(
|
||||
> "Example", "Example of type A usage."
|
||||
)]),
|
||||
}
|
||||
}
|
||||
|
||||
fn a_foo_constructor() -> Function {
|
||||
Function {
|
||||
name: QualifiedName::from_text("Standard.Base.A.Foo").unwrap().into(),
|
||||
tags: default(),
|
||||
arguments: vec![Argument::new("a", "Standard.Base.Any")].into(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!(
|
||||
"Documentation of constructor A.Foo."
|
||||
)]),
|
||||
examples: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn a_bar_constructor() -> Function {
|
||||
Function {
|
||||
name: QualifiedName::from_text("Standard.Base.A.Bar").unwrap().into(),
|
||||
tags: default(),
|
||||
arguments: vec![Argument::new("b", "Standard.Base.Any")].into(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!(
|
||||
"Documentation of constructor A.Bar."
|
||||
)]),
|
||||
examples: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn a_baz_method() -> Function {
|
||||
Function {
|
||||
name: QualifiedName::from_text("Standard.Base.A.baz").unwrap().into(),
|
||||
tags: Tags { list: vec![Tag::new("Tag", "Tag body.")].into() },
|
||||
arguments: default(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!(
|
||||
"Documentation of method A.baz."
|
||||
)]),
|
||||
examples: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn b_type() -> TypeDocumentation {
|
||||
TypeDocumentation {
|
||||
name: QualifiedName::from_text("Standard.Base.B").unwrap().into(),
|
||||
arguments: default(),
|
||||
tags: default(),
|
||||
synopsis: Synopsis::from_doc_sections([doc_section!("Documentation of type B.")]),
|
||||
constructors: Functions { list: SortedVec::new(vec![b_new_constructor()]) }.into(),
|
||||
methods: default(),
|
||||
examples: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn b_new_constructor() -> Function {
|
||||
Function {
|
||||
name: QualifiedName::from_text("Standard.Base.B.New").unwrap().into(),
|
||||
tags: Tags { list: vec![Tag::new("AnotherTag", "Tag body.")].into() },
|
||||
arguments: default(),
|
||||
synopsis: Synopsis::from_doc_sections([
|
||||
doc_section!("Documentation of constructor B.New."),
|
||||
doc_section!(!"Important", "Important note."),
|
||||
]),
|
||||
examples: Examples::from_doc_sections([doc_section!(
|
||||
> "Example", "Example of constructor B.New usage."
|
||||
)]),
|
||||
}
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::model::module::MethodId;
|
||||
use crate::model::SuggestionDatabase;
|
||||
use crate::SuggestionDatabase;
|
||||
|
||||
use ast::opr;
|
||||
use convert_case::Case;
|
||||
use convert_case::Casing;
|
||||
use double_representation::import;
|
||||
use double_representation::module::MethodId;
|
||||
use double_representation::name;
|
||||
use double_representation::name::QualifiedName;
|
||||
use double_representation::name::QualifiedNameRef;
|
||||
@ -900,8 +900,8 @@ mod test {
|
||||
|
||||
use engine_protocol::language_server::SuggestionArgumentUpdate;
|
||||
|
||||
use crate::mock;
|
||||
use crate::mock_suggestion_database;
|
||||
use crate::model::suggestion_database::mock;
|
||||
use enso_text::index::Line;
|
||||
use enso_text::Utf16CodeUnit;
|
||||
|
@ -45,7 +45,7 @@ impl Example {
|
||||
/// ```
|
||||
/// use enso_prelude::*;
|
||||
///
|
||||
/// use enso_gui::model::suggestion_database::Example;
|
||||
/// use enso_suggestion_database::Example;
|
||||
///
|
||||
/// let name = "With Spaces and Strange $ąę#%^& Characters.".to_owned();
|
||||
/// let example = Example { name, ..default() };
|
@ -1,17 +1,48 @@
|
||||
//! The module contains all structures for representing suggestions and their database.
|
||||
//! The crate contains all structures for representing suggestions and their database.
|
||||
|
||||
use crate::prelude::*;
|
||||
#![recursion_limit = "512"]
|
||||
// === Features ===
|
||||
#![feature(arc_unwrap_or_clone)]
|
||||
#![feature(async_closure)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(cell_update)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(iter_order_by)]
|
||||
#![feature(option_result_contains)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(result_option_inspect)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(hash_drain_filter)]
|
||||
#![feature(unwrap_infallible)]
|
||||
// === 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)]
|
||||
|
||||
use crate::model::module::MethodId;
|
||||
use crate::model::suggestion_database::entry::Kind;
|
||||
use crate::model::suggestion_database::entry::ModuleSpan;
|
||||
use crate::notification;
|
||||
use enso_prelude::*;
|
||||
|
||||
use crate::documentation_ir::EntryDocumentation;
|
||||
use crate::entry::Kind;
|
||||
use crate::entry::ModuleSpan;
|
||||
|
||||
use double_representation::module::MethodId;
|
||||
use double_representation::name::QualifiedName;
|
||||
use double_representation::name::QualifiedNameRef;
|
||||
use engine_protocol::language_server;
|
||||
use engine_protocol::language_server::SuggestionId;
|
||||
use ensogl::data::HashMapTree;
|
||||
use enso_data_structures::hash_map_tree::HashMapTree;
|
||||
use enso_notification as notification;
|
||||
use flo_stream::Subscriber;
|
||||
use language_server::types::SuggestionDatabaseUpdatesEvent;
|
||||
use language_server::types::SuggestionsDatabaseVersion;
|
||||
@ -21,15 +52,24 @@ use language_server::types::SuggestionsDatabaseVersion;
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub mod documentation_ir;
|
||||
pub mod entry;
|
||||
pub mod example;
|
||||
pub mod mock;
|
||||
|
||||
pub use engine_protocol;
|
||||
pub use entry::Entry;
|
||||
pub use example::Example;
|
||||
|
||||
|
||||
|
||||
/// Common types that should be visible across the whole IDE crate.
|
||||
pub mod prelude {
|
||||
pub use ast::prelude::*;
|
||||
pub use enso_prelude::*;
|
||||
}
|
||||
|
||||
|
||||
// ============================
|
||||
// === QualifiedNameToIdMap ===
|
||||
// ============================
|
||||
@ -175,9 +215,6 @@ impl HierarchyIndex {
|
||||
/// Get all "children" of the entry with the given id. Returns a set of Methods and Constructors
|
||||
/// of the Type entry, or a set of Types defined in the Module entry. Returns [`None`] for
|
||||
/// other entries.
|
||||
///
|
||||
/// TODO: Use this getter as part of the https://www.pivotaltracker.com/story/show/184012434.
|
||||
#[allow(dead_code)]
|
||||
pub fn get(&self, id: &entry::Id) -> Option<&HashSet<entry::Id>> {
|
||||
self.inner.get(id)
|
||||
}
|
||||
@ -428,6 +465,24 @@ impl SuggestionDatabase {
|
||||
})
|
||||
}
|
||||
|
||||
/// Lookup hierarchy index for given id. See [`HierarchyIndex`] for more information.
|
||||
pub fn lookup_hierarchy(&self, id: entry::Id) -> Result<HashSet<entry::Id>, NoSuchEntry> {
|
||||
let hierarchy = self.hierarchy_index.borrow();
|
||||
let children = hierarchy.get(&id).cloned().ok_or(NoSuchEntry(id))?;
|
||||
Ok(children)
|
||||
}
|
||||
|
||||
/// Lookup documentation of the given entry.
|
||||
pub fn documentation_for_entry(&self, id: entry::Id) -> EntryDocumentation {
|
||||
match EntryDocumentation::new(self, &id) {
|
||||
Ok(docs) => docs,
|
||||
Err(err) => {
|
||||
error!("Error when generating documentation for entry {id}: {}", err);
|
||||
default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over all examples gathered from suggestions.
|
||||
///
|
||||
/// If the database was modified during iteration, the iterator does not panic, but may return
|
||||
@ -520,13 +575,14 @@ fn swap_value_and_traverse_back_pruning_empty_subtrees<P, I>(
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
use enso_executor::test_utils::TestWithLocalPoolExecutor;
|
||||
|
||||
use double_representation::name::NamePath;
|
||||
use engine_protocol::language_server::FieldUpdate;
|
||||
use engine_protocol::language_server::SuggestionEntry;
|
||||
use engine_protocol::language_server::SuggestionsDatabaseEntry;
|
||||
use engine_protocol::language_server::SuggestionsDatabaseModification;
|
||||
use futures::stream::StreamExt;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
@ -3,10 +3,10 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::model::suggestion_database::entry;
|
||||
use crate::model::suggestion_database::entry::Argument;
|
||||
use crate::model::suggestion_database::Entry;
|
||||
use crate::model::SuggestionDatabase;
|
||||
use crate::entry;
|
||||
use crate::entry::Argument;
|
||||
use crate::Entry;
|
||||
use crate::SuggestionDatabase;
|
||||
|
||||
use double_representation::name::QualifiedName;
|
||||
|
||||
@ -39,8 +39,8 @@ pub const DEFAULT_TYPE: &str = "Standard.Base.Any";
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use enso_gui::model::suggestion_database::entry::IconName;
|
||||
/// use enso_gui::model::suggestion_database::mock::Builder;
|
||||
/// use enso_suggestion_database::entry::IconName;
|
||||
/// use enso_suggestion_database::mock::Builder;
|
||||
///
|
||||
/// let mut builder = Builder::new();
|
||||
/// builder.add_and_enter_module("local.Project", |e| e);
|
||||
@ -246,8 +246,8 @@ macro_rules! mock_suggestion_database_entries {
|
||||
///
|
||||
/// This macro takes a declaration of suggestion database entries in a kind of pseudo code:
|
||||
/// ```
|
||||
/// use enso_gui::mock_suggestion_database;
|
||||
/// use enso_gui::model::suggestion_database::entry;
|
||||
/// use enso_suggestion_database::entry;
|
||||
/// use enso_suggestion_database::mock_suggestion_database;
|
||||
///
|
||||
/// let db = mock_suggestion_database! {
|
||||
/// Standard.Base {
|
||||
@ -302,9 +302,9 @@ macro_rules! mock_suggestion_database {
|
||||
($($(#[$($attr_setter:tt)*])* $ns:ident.$project:ident { $($content:tt)* })*) => {
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::model::suggestion_database::mock::Builder;
|
||||
use $crate::mock::Builder;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::model::suggestion_database::mock::DEFAULT_TYPE;
|
||||
use $crate::mock::DEFAULT_TYPE;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::mock_suggestion_database_entries;
|
||||
#[allow(unused_imports)]
|
||||
@ -312,7 +312,7 @@ macro_rules! mock_suggestion_database {
|
||||
#[allow(unused_imports)]
|
||||
use $crate::mock_suggestion_database_entry_argument;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::model::suggestion_database::entry::Argument;
|
||||
use $crate::entry::Argument;
|
||||
|
||||
let mut builder = Builder::new();
|
||||
$(
|
||||
@ -343,25 +343,25 @@ macro_rules! doc_section_mark {
|
||||
///
|
||||
/// ### [`DocSection::Paragrah`]
|
||||
/// ```
|
||||
/// # use enso_gui::doc_section;
|
||||
/// # use enso_suggestion_database::doc_section;
|
||||
/// doc_section!("Some text.");
|
||||
/// ```
|
||||
///
|
||||
/// ### [`DocSection::Tag`]
|
||||
/// ```
|
||||
/// # use enso_gui::doc_section;
|
||||
/// # use enso_suggestion_database::doc_section;
|
||||
/// doc_section!(@ "Tag name", "Tag body.");
|
||||
/// ```
|
||||
///
|
||||
/// ### [`DocSection::Keyed`]
|
||||
/// ```
|
||||
/// # use enso_gui::doc_section;
|
||||
/// # use enso_suggestion_database::doc_section;
|
||||
/// doc_section!("Key" => "Value");
|
||||
/// ```
|
||||
///
|
||||
/// ### [`DocSection::Marked`]
|
||||
/// ```
|
||||
/// # use enso_gui::doc_section;
|
||||
/// # use enso_suggestion_database::doc_section;
|
||||
/// doc_section!(! "Marked as important");
|
||||
/// doc_section!(! "Optional header", "Marked as important");
|
||||
/// doc_section!(? "Marked as info");
|
||||
@ -372,7 +372,10 @@ macro_rules! doc_section_mark {
|
||||
#[macro_export]
|
||||
macro_rules! doc_section {
|
||||
(@ $tag:expr, $body:expr) => {
|
||||
engine_protocol::language_server::DocSection::Tag { name: $tag.into(), body: $body.into() }
|
||||
$crate::engine_protocol::language_server::DocSection::Tag {
|
||||
name: $tag.into(),
|
||||
body: $body.into(),
|
||||
}
|
||||
};
|
||||
($mark:tt $body:expr) => {
|
||||
$crate::engine_protocol::language_server::DocSection::Marked {
|
||||
@ -389,10 +392,10 @@ macro_rules! doc_section {
|
||||
}
|
||||
};
|
||||
($paragraph:expr) => {
|
||||
engine_protocol::language_server::DocSection::Paragraph { body: $paragraph.into() }
|
||||
$crate::engine_protocol::language_server::DocSection::Paragraph { body: $paragraph.into() }
|
||||
};
|
||||
($key:expr => $body:expr) => {
|
||||
engine_protocol::language_server::DocSection::Keyed {
|
||||
$crate::engine_protocol::language_server::DocSection::Keyed {
|
||||
key: $key.into(),
|
||||
body: $body.into(),
|
||||
}
|
@ -71,7 +71,7 @@ wasm_bindgen_test_configure!(run_in_browser);
|
||||
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
|
||||
#[allow(dead_code)]
|
||||
async fn ls_text_protocol_test() {
|
||||
let _guard = enso_gui::initializer::setup_global_executor();
|
||||
let _guard = enso_executor::setup_global_executor();
|
||||
let ide = setup_ide().await;
|
||||
let project = ide.current_project().expect("IDE is configured without an open project.");
|
||||
let client = project.json_rpc();
|
||||
@ -259,7 +259,7 @@ async fn file_events() {
|
||||
let ws = ws.expect("Couldn't connect to WebSocket server.");
|
||||
let client = Client::new(ws);
|
||||
let mut stream = client.events();
|
||||
let _executor = enso_gui::initializer::setup_global_executor();
|
||||
let _executor = enso_executor::setup_global_executor();
|
||||
|
||||
executor::global::spawn(client.runner());
|
||||
|
||||
@ -317,7 +317,7 @@ async fn setup_ide() -> controller::Ide {
|
||||
#[allow(dead_code)]
|
||||
/// This integration test covers writing and reading a file using the binary protocol
|
||||
async fn file_operations_test() {
|
||||
let _guard = enso_gui::initializer::setup_global_executor();
|
||||
let _guard = enso_executor::setup_global_executor();
|
||||
let ide = setup_ide().await;
|
||||
let project = ide.current_project().expect("IDE is configured without an open project.");
|
||||
info!("Got project: {project:?}");
|
||||
@ -386,8 +386,8 @@ async fn binary_visualization_updates_test_hlp() {
|
||||
#[allow(dead_code)]
|
||||
/// This integration test covers attaching visualizations and receiving their updates.
|
||||
fn binary_visualization_updates_test() {
|
||||
let executor = enso_gui::executor::web::EventLoopExecutor::new_running();
|
||||
enso_gui::executor::global::set_spawner(executor.spawner.clone());
|
||||
let executor = enso_executor::web::EventLoopExecutor::new_running();
|
||||
enso_executor::global::set_spawner(executor.spawner.clone());
|
||||
executor.spawn_local(binary_visualization_updates_test_hlp()).unwrap();
|
||||
std::mem::forget(executor);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ mod tests {
|
||||
let ws = WebSocket::new_opened(logger, "ws://localhost:30535").await;
|
||||
let ws = ws.expect("Couldn't connect to WebSocket server.");
|
||||
let client = Client::new(ws);
|
||||
let _executor = enso_gui::initializer::setup_global_executor();
|
||||
let _executor = enso_executor::setup_global_executor();
|
||||
|
||||
executor::global::spawn(client.runner());
|
||||
|
||||
|
@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
debug-scene-component-list-panel-view = { path = "component-list-panel-view" }
|
||||
debug-scene-documentation = { path = "documentation" }
|
||||
debug-scene-icons = { path = "icons" }
|
||||
debug-scene-interface = { path = "interface" }
|
||||
debug-scene-text-grid-visualization = { path = "text-grid-visualization" }
|
||||
|
17
app/gui/view/debug_scene/documentation/Cargo.toml
Normal file
17
app/gui/view/debug_scene/documentation/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "debug-scene-documentation"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
ensogl = { path = "../../../../../lib/rust/ensogl" }
|
||||
ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" }
|
||||
enso-suggestion-database = { path = "../../../suggestion-database" }
|
||||
ensogl-text-msdf = { path = "../../../../../lib/rust/ensogl/component/text/src/font/msdf" }
|
||||
ide-view-documentation = { path = "../../documentation" }
|
||||
ide-view-graph-editor = { path = "../../graph-editor" }
|
||||
wasm-bindgen = { workspace = true }
|
@ -1,15 +1,18 @@
|
||||
//! Example scene showing a documentation panel of the component browser.
|
||||
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::bool_to_int_with_if)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use ensogl::display::shape::*;
|
||||
use ensogl::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::doc_section;
|
||||
use crate::mock_suggestion_database;
|
||||
use crate::model::suggestion_database;
|
||||
use crate::model::suggestion_database::Entry;
|
||||
use crate::model::SuggestionDatabase;
|
||||
|
||||
use enso_suggestion_database as suggestion_database;
|
||||
use enso_suggestion_database::doc_section;
|
||||
use enso_suggestion_database::mock_suggestion_database;
|
||||
use ensogl::application::Application;
|
||||
use ensogl::data::color;
|
||||
use ensogl::display;
|
||||
@ -19,9 +22,16 @@ use ensogl::frp;
|
||||
use ensogl::shape;
|
||||
use ensogl::system::web;
|
||||
use ensogl_hardcoded_theme::application::component_browser as theme;
|
||||
use ide_view::documentation;
|
||||
use ide_view::graph_editor::component::visualization::Registry;
|
||||
use ide_view_documentation as documentation;
|
||||
use ide_view_graph_editor::component::visualization::Registry;
|
||||
use std::f32::consts::PI;
|
||||
use std::fmt::Write;
|
||||
use suggestion_database::documentation_ir::Documentation;
|
||||
use suggestion_database::documentation_ir::EntryDocumentation;
|
||||
use suggestion_database::documentation_ir::ModuleDocumentation;
|
||||
use suggestion_database::documentation_ir::Placeholder;
|
||||
use suggestion_database::documentation_ir::TypeDocumentation;
|
||||
use suggestion_database::SuggestionDatabase;
|
||||
|
||||
|
||||
|
||||
@ -63,12 +73,83 @@ impl DatabaseWrapper {
|
||||
let index = self.current_entry.get();
|
||||
let ids = self.database.keys();
|
||||
let id = ids[index];
|
||||
self.database.lookup(id).map(entry_documentation).unwrap_or_default()
|
||||
render_documentation(self.database.documentation_for_entry(id))
|
||||
}
|
||||
}
|
||||
|
||||
fn entry_documentation(entry: Rc<Entry>) -> String {
|
||||
format!("{:#?}", entry.documentation)
|
||||
/// This is a temporary function for easier testing of the documentation panel. It will be replaced
|
||||
/// by a proper HTML generation in the future. See https://www.pivotaltracker.com/story/show/180872953.
|
||||
fn render_documentation(doc: EntryDocumentation) -> String {
|
||||
let mut result = String::new();
|
||||
match doc {
|
||||
EntryDocumentation::Placeholder(placeholder) => match placeholder {
|
||||
Placeholder::NoDocumentation => result.push_str("No documentation available."),
|
||||
Placeholder::Local { name } => writeln!(result, "Local variable: {}", name).unwrap(),
|
||||
Placeholder::Function { name } => writeln!(result, "Function: {}", name).unwrap(),
|
||||
},
|
||||
EntryDocumentation::Docs(docs) => match docs {
|
||||
Documentation::Module(docs) => {
|
||||
write_module_docs(&mut result, &docs);
|
||||
}
|
||||
Documentation::Type(docs) => {
|
||||
write_type_docs(&mut result, docs);
|
||||
}
|
||||
Documentation::Constructor { name, type_docs } => {
|
||||
let name = name.to_string_with_main_segment();
|
||||
let type_name = type_docs.name.to_string_with_main_segment();
|
||||
writeln!(result, "Constructor {} of type {}", name, type_name).unwrap();
|
||||
write_type_docs(&mut result, type_docs);
|
||||
}
|
||||
Documentation::Method { name, type_docs } => {
|
||||
let name = name.to_string_with_main_segment();
|
||||
let type_name = type_docs.name.to_string_with_main_segment();
|
||||
writeln!(result, "Method {} of type {}", name, type_name).unwrap();
|
||||
write_type_docs(&mut result, type_docs);
|
||||
}
|
||||
Documentation::ModuleMethod { name, module_docs } => {
|
||||
let name = name.to_string_with_main_segment();
|
||||
let module_name = module_docs.name.to_string_with_main_segment();
|
||||
writeln!(result, "Method {} of module {}", name, module_name).unwrap();
|
||||
write_module_docs(&mut result, &module_docs);
|
||||
}
|
||||
Documentation::Function { .. } => {}
|
||||
Documentation::Local { .. } => {}
|
||||
},
|
||||
}
|
||||
result.replace('\n', "<br/>")
|
||||
}
|
||||
|
||||
fn write_module_docs(result: &mut String, docs: &Rc<ModuleDocumentation>) {
|
||||
writeln!(result, "Module: {}", docs.name.to_string_with_main_segment()).unwrap();
|
||||
writeln!(result, "Tags: {:?}", docs.tags).unwrap();
|
||||
writeln!(result, "Summary: {:?}", docs.synopsis).unwrap();
|
||||
writeln!(result, "Types:").unwrap();
|
||||
for ty in docs.types.iter() {
|
||||
writeln!(result, "{:?}", ty).unwrap();
|
||||
}
|
||||
writeln!(result, "Functions:").unwrap();
|
||||
for ty in docs.methods.iter() {
|
||||
writeln!(result, "{:?}", ty).unwrap();
|
||||
}
|
||||
writeln!(result, "Examples:").unwrap();
|
||||
for ty in docs.examples.iter() {
|
||||
writeln!(result, "{:?}", ty).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn write_type_docs(result: &mut String, docs: Rc<TypeDocumentation>) {
|
||||
writeln!(result, "Type: {}", docs.name.to_string_with_main_segment()).unwrap();
|
||||
writeln!(result, "Tags: {:?}", docs.tags).unwrap();
|
||||
writeln!(result, "Summary: {:?}", docs.synopsis).unwrap();
|
||||
writeln!(result, "Constructors:").unwrap();
|
||||
for constructor in docs.constructors.iter() {
|
||||
writeln!(result, "{:?}", constructor).unwrap();
|
||||
}
|
||||
writeln!(result, "Methods:").unwrap();
|
||||
for method in docs.methods.iter() {
|
||||
writeln!(result, "{:?}", method).unwrap();
|
||||
}
|
||||
writeln!(result, "Examples: {:?}", docs.examples).unwrap();
|
||||
}
|
||||
|
||||
fn database() -> SuggestionDatabase {
|
@ -24,6 +24,7 @@
|
||||
|
||||
pub use debug_scene_cloud_dashboard as cloud_dashboard;
|
||||
pub use debug_scene_component_list_panel_view as new_component_list_panel_view;
|
||||
pub use debug_scene_documentation as documentation;
|
||||
pub use debug_scene_icons as icons;
|
||||
pub use debug_scene_interface as interface;
|
||||
pub use debug_scene_text_grid_visualization as text_grid_visualization;
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Options intended to be common for all developers.
|
||||
|
||||
wasm-size-limit: 15.45 MiB
|
||||
wasm-size-limit: 15.47 MiB
|
||||
|
||||
required-versions:
|
||||
cargo-watch: ^8.1.1
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
approx = "0.5.1"
|
||||
engine-protocol = { path = "../app/gui/controller/engine-protocol" }
|
||||
ensogl = { path = "../lib/rust/ensogl" }
|
||||
enso-executor = { path = "../lib/rust/executor" }
|
||||
enso-frp = { path = "../lib/rust/frp" }
|
||||
enso-prelude = { path = "../lib/rust/prelude" }
|
||||
enso-gui = { path = "../app/gui" }
|
||||
|
@ -6,9 +6,9 @@ use enso_gui::integration_test::prelude::*;
|
||||
use engine_protocol::language_server::ExplicitCall;
|
||||
use engine_protocol::language_server::MethodPointer;
|
||||
use engine_protocol::language_server::StackItem;
|
||||
use enso_executor::setup_global_executor;
|
||||
use enso_executor::web::EventLoopExecutor;
|
||||
use enso_gui::controller::project::MAIN_DEFINITION_NAME;
|
||||
use enso_gui::executor::web::EventLoopExecutor;
|
||||
use enso_gui::initializer::setup_global_executor;
|
||||
use enso_web::sleep;
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
12
lib/rust/executor/Cargo.toml
Normal file
12
lib/rust/executor/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "enso-executor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
futures = { version = "0.3.1" }
|
||||
enso-prelude = { path = "../prelude" }
|
||||
ensogl-core = { path = "../ensogl/core" }
|
||||
enso-profiler = { path = "../profiler" }
|
39
lib/rust/executor/src/lib.rs
Normal file
39
lib/rust/executor/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
||||
//! Code dealing with executors, i.e. entities that are used to execute asynchronous
|
||||
//! computations, like `Future`s or `Stream`s.
|
||||
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::bool_to_int_with_if)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
|
||||
// ==============
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub mod global;
|
||||
pub mod test_utils;
|
||||
pub mod web;
|
||||
|
||||
|
||||
|
||||
pub mod prelude {
|
||||
pub use enso_prelude::*;
|
||||
|
||||
pub use futures::task::LocalSpawnExt;
|
||||
pub use futures::Future;
|
||||
pub use futures::FutureExt;
|
||||
pub use futures::Stream;
|
||||
pub use futures::StreamExt;
|
||||
|
||||
pub use enso_profiler as profiler;
|
||||
pub use enso_profiler::prelude::*;
|
||||
}
|
||||
|
||||
/// Creates a new running executor with its own event loop. Registers them as a global executor.
|
||||
pub fn setup_global_executor() -> crate::web::EventLoopExecutor {
|
||||
let executor = crate::web::EventLoopExecutor::new_running();
|
||||
crate::global::set_spawner(executor.spawner.clone());
|
||||
executor
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::executor::global::set_spawner;
|
||||
use crate::executor::global::spawn;
|
||||
use crate::global::set_spawner;
|
||||
use crate::global::spawn;
|
||||
|
||||
use futures::executor;
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use ensogl::animation;
|
||||
use ensogl::control::callback;
|
||||
use ensogl_core::animation;
|
||||
use ensogl_core::control::callback;
|
||||
use futures::executor::LocalPool;
|
||||
use futures::executor::LocalSpawner;
|
||||
use futures::task::LocalFutureObj;
|
@ -6,5 +6,5 @@
|
||||
/// Leaks it handle so it will run indefinitely.
|
||||
/// To be used in asynchronous wasm tests.
|
||||
pub fn setup_and_forget() {
|
||||
std::mem::forget(crate::initializer::setup_global_executor());
|
||||
std::mem::forget(crate::setup_global_executor());
|
||||
}
|
13
lib/rust/notification/Cargo.toml
Normal file
13
lib/rust/notification/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "enso-notification"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { path = "../prelude" }
|
||||
enso-executor = { path = "../executor" }
|
||||
flo_stream = { version = "0.4.0" }
|
||||
futures = { version = "0.3.1" }
|
@ -1,8 +1,15 @@
|
||||
//! Here are the common structures used by Controllers notifications (sent between controllers and
|
||||
//! from controller to view).
|
||||
|
||||
use crate::prelude::*;
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::bool_to_int_with_if)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use enso_prelude::*;
|
||||
|
||||
use enso_executor as executor;
|
||||
use flo_stream::MessagePublisher;
|
||||
use flo_stream::Subscriber;
|
||||
|
||||
@ -12,6 +19,8 @@ use flo_stream::Subscriber;
|
||||
// === Publisher ===
|
||||
// =================
|
||||
|
||||
pub type StaticBoxFuture<T> = futures::future::LocalBoxFuture<'static, T>;
|
||||
|
||||
/// A buffer size for notification publisher.
|
||||
///
|
||||
/// If Publisher buffer will be full, the thread sending next notification will be blocked until
|
@ -39,6 +39,6 @@ fn derive_for_enum(decl: &syn::DeriveInput, data: &syn::DataEnum) -> TokenStream
|
||||
( $f:ident($( $args:tt )*) ) => { $f!([ #variant_names ] $($args)*) }
|
||||
}
|
||||
|
||||
pub(crate) use #macro_name;
|
||||
pub use #macro_name;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user