feat: render examples in docs (#772)

* feat: render examples in docs

* fix: create `hover-images` on `startHover`

* dev: update snapshot
This commit is contained in:
Myriad-Dreamin 2024-11-08 15:55:47 +08:00 committed by GitHub
parent d02fa18617
commit d75fd7e74e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 334 additions and 74 deletions

View file

@ -36,7 +36,7 @@ use crate::syntax::{
};
use crate::upstream::{tooltip_, Tooltip};
use crate::{
lsp_to_typst, path_to_url, typst_to_lsp, LspPosition, LspRange, PositionEncoding,
lsp_to_typst, path_to_url, typst_to_lsp, ColorTheme, LspPosition, LspRange, PositionEncoding,
SemanticTokenContext, TypstRange, VersionedDocument,
};
@ -45,6 +45,8 @@ use crate::{
pub struct Analysis {
/// The position encoding for the workspace.
pub position_encoding: PositionEncoding,
/// The editor's color theme.
pub color_theme: ColorTheme,
/// The periscope provider.
pub periscope: Option<Arc<dyn PeriscopeProvider + Send + Sync>>,
/// The semantic token context.

View file

@ -3,28 +3,30 @@ use std::sync::{Arc, LazyLock};
use ecow::{eco_format, EcoString};
use parking_lot::Mutex;
use tinymist_world::base::{EntryState, ShadowApi, TaskInputs};
use tinymist_world::LspWorld;
use typlite::scopes::Scopes;
use typlite::value::{Value, *};
use typlite::value::Value;
use typst::foundations::Bytes;
use typst::{
diag::StrResult,
syntax::{FileId, VirtualPath},
};
use crate::analysis::SharedContext;
// Unfortunately, we have only 65536 possible file ids and we cannot revoke
// them. So we share a global file id for all docs conversion.
static DOCS_CONVERT_ID: LazyLock<Mutex<FileId>> =
LazyLock::new(|| Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ"))));
pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoString> {
static DOCS_LIB: LazyLock<Arc<Scopes<Value>>> = LazyLock::new(lib);
pub(crate) fn convert_docs(ctx: &SharedContext, content: &str) -> StrResult<EcoString> {
static DOCS_LIB: LazyLock<Arc<Scopes<Value>>> =
LazyLock::new(|| Arc::new(typlite::library::docstring_lib()));
let conv_id = DOCS_CONVERT_ID.lock();
let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap();
let entry = entry.select_in_workspace(*conv_id);
let mut w = world.task(TaskInputs {
let mut w = ctx.world.task(TaskInputs {
entry: Some(entry),
inputs: None,
});
@ -34,29 +36,10 @@ pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoStri
let conv = typlite::Typlite::new(Arc::new(w))
.with_library(DOCS_LIB.clone())
.with_color_theme(ctx.analysis.color_theme)
.annotate_elements(true)
.convert()
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
Ok(conv.replace("```example", "```typ"))
}
pub(super) fn lib() -> Arc<Scopes<Value>> {
let mut scopes = typlite::library::library();
// todo: how to import this function correctly?
scopes.define("example", example as RawFunc);
Arc::new(scopes)
}
/// Evaluate a `example`.
pub fn example(mut args: Args) -> typlite::Result<Value> {
let body = get_pos_named!(args, body: Content).0;
let body = body.trim();
let ticks = body.chars().take_while(|t| *t == '`').collect::<String>();
let body = &body[ticks.len()..];
let body = eco_format!("{ticks}typ{body}");
Ok(Value::Content(body))
}

View file

@ -91,6 +91,8 @@ use typst::{model::Document as TypstDocument, syntax::Source};
/// The physical position in a document.
pub type FramePosition = typst::layout::Position;
pub use typlite::ColorTheme;
/// A compiled document with an self-incremented logical version.
#[derive(Debug, Clone)]
pub struct VersionedDocument {

View file

@ -107,7 +107,7 @@ struct DocsChecker<'a> {
impl<'a> DocsChecker<'a> {
pub fn check_func_docs(mut self, docs: String) -> Option<DocString> {
let converted = convert_docs(&self.ctx.world, &docs).ok()?;
let converted = convert_docs(self.ctx, &docs).ok()?;
let converted = identify_func_docs(&converted).ok()?;
let module = self.ctx.module_by_str(docs)?;
@ -135,7 +135,7 @@ impl<'a> DocsChecker<'a> {
}
pub fn check_var_docs(mut self, docs: String) -> Option<DocString> {
let converted = convert_docs(&self.ctx.world, &docs).ok()?;
let converted = convert_docs(self.ctx, &docs).ok()?;
let converted = identify_var_docs(converted).ok()?;
let module = self.ctx.module_by_str(docs)?;
@ -152,7 +152,7 @@ impl<'a> DocsChecker<'a> {
}
pub fn check_module_docs(self, docs: String) -> Option<DocString> {
let converted = convert_docs(&self.ctx.world, &docs).ok()?;
let converted = convert_docs(self.ctx, &docs).ok()?;
let converted = identify_tidy_module_docs(converted).ok()?;
Some(DocString {