Configure whether to enable syntax-only mode for the language server. In
syntax-only mode, the language server will only provide syntax checking
and basic code completion, but will not perform full document
compilation or code analysis. This can be useful for improving
performance on low-end devices, devices under power-saving mode, or when
working with large documents.
Default behavior: Always disable syntax-only mode. The strategy may be
changed in the future, for example, automatically enable syntax-only
mode when the system is in power-saving mode.
---------
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This PR corrects destructuring handling in `ExprWorker` by switching
`DestructuringItem::Named` to use `named.pattern()` rather than
`named.expr()`. According to ast definition (see `Named::pattern` in
0da0165954/crates/typst-syntax/src/ast.rs (L1418-L1433)
, the rhs of a `Named` pair must be interpreted as a pattern when it
appears inside a destructuring context, not as an expression. This
change ensures nested destructuring patterns are analyzed correctly.
Also, spread items (`..rest`) are normalized into pattern form and
registered as proper declarations.
So that now complex patterns like `#let (x: (used_a, unused_x), y: (c1,
c2, c3)) = (...)` and array spreads like `#let (first, ..rest) = (1, 2,
3)` are allowed to resolve and lint as intended.
This is used for custom paste scripts
- by pattern: e.g. `$root`
- by code: e.g. `{ root }`
- on conflict callback: e.g. `{ (dir: root, on-conflict: root + "/" +
random() + ".png") }`
This would help:
- compile compiled typst document on demand for analysis.
- remove the concept about `StatefulRequest`
- prepare for introducing stateful code context query (lsp command
`interactCodeContext`).
Adds capability to introspect complations happening in the language
server, to help improve efficiency. I expect most compilations are
caused by tracing for analyzing dynamic expressions, but I haven't
really profiled a document. Then introspection will help confirm or
refute the expectation.
## Overview
This PR enhances documentation in the expression analysis system to
clarify how goto_definition works and when the `RefExpr` fields are set
versus `None`. This addresses feedback from @BlueQuantumx and @jo3-l who
requested better documentation to understand the expression analysis
flow.
## Problem
The existing documentation was insufficient for developers trying to
understand:
1. How the goto_definition feature works through the expression analysis
system
2. When the `root`, `step`, and `term` fields in `RefExpr` are `Some`
versus `None`
3. The overall flow of expression analysis in `expr_of`
The brief one-line comments didn't provide enough context about the
resolution chain concept or concrete examples of when fields are
populated.
## Changes
### RefExpr Structure Documentation
(`crates/tinymist-analysis/src/syntax/def.rs`)
Added comprehensive documentation including:
- Explanation of the resolution chain concept: `root` -> `step` ->
`decl`
- Three concrete examples showing different use cases:
- Simple identifier reference (`let y = x`)
- Module field access (`mod.field`)
- Import with rename (`import: old as new`)
- Detailed field documentation explaining when each is set:
- `step`: Set for imports, field access, chained references, renamed
imports
- `root`: Set for module imports, field selection, propagated in chains
- `term`: Set when type inference succeeds, `None` when type unknown or
deferred
### Expression Analysis Documentation
(`crates/tinymist-query/src/syntax/expr.rs`)
Enhanced `expr_of` function documentation to explain:
- Two-pass analysis architecture (init_stage for forward references,
then full resolution)
- How it builds the resolves map that powers goto_definition
- Caching strategy based on source content and import hashes
Added detailed documentation for helper functions:
- `resolve_ident_`: Resolution process and reference chain building
- `eval_ident`: Lexical scope lookup order and type availability
- `extract_ref`: Reference chain propagation mechanism
Added inline comments at all RefExpr creation sites explaining the
context and field values for:
- Module imports
- Import/include paths
- Named imports with rename
- Module field selection
## Example
**Before:** The RefExpr struct had minimal documentation
```rust
/// The intermediate step in resolution (if any).
pub step: Option<Expr>,
```
**After:** Clear explanation of when the field is set
```rust
/// The intermediate expression in the resolution chain.
///
/// Set in the following cases:
/// - **Import/include**: The module expression being imported
/// - **Field access**: The selected field's expression
/// - **Chained references**: When an identifier resolves to another reference
/// - **Renamed imports**: The original name before renaming
///
/// `None` when the identifier is a direct definition (not a reference).
pub step: Option<Expr>,
```
## Testing
- ✅ All core tests pass (expr module, tinymist-analysis package)
- ✅ Clippy passes with no warnings
- ✅ Build succeeds
- ✅ Code formatted with `yarn fmt`
Note: Package-related test failures (cetz, fletcher, tidy, touying,
import_package) are expected in environments without network access per
project guidelines.
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `packages.typst.org`
> - Triggering command:
`/home/REDACTED/work/tinymist/tinymist/target/debug/deps/tinymist_query-1cacdd437723df09
--skip=e2e` (dns block)
> - Triggering command:
`/home/REDACTED/work/tinymist/tinymist/target/debug/deps/tinymist_query-1cacdd437723df09
goto_definition::tests::test --exact` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/Myriad-Dreamin/tinymist/settings/copilot/coding_agent)
(admins only)
>
> </details>
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>Improve code comments in expr.rs</issue_title>
> <issue_description>> I'd like to push this forward, but I need some
tips/docs about how the goto_definition works. I've walked through `fn
def_of_syntax_or_dyn`, `fn definition` ... and finally focused on
`crates/tinymist-query/src/syntax/expr.rs -> fn expr_of`. Maybe there
could have some concise code comments?
>
> _Originally posted by @BlueQuantumx in
[#1960](https://github.com/Myriad-Dreamin/tinymist/issues/1960#issuecomment-3294723078)_
>
> > I read those comments when I was originally writing the code, but
I'm mainly interested in when `type` and `root` are set (or, conversely,
when they are `None`.) The comments don't really go into that detail
AFAICS?
>
> _Originally posted by @jo3-l in
https://github.com/Myriad-Dreamin/tinymist/pull/2065#discussion_r2315906981_</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
FixesMyriad-Dreamin/tinymist#2122
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/Myriad-Dreamin/tinymist/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com>
Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com>
fix https://github.com/Myriad-Dreamin/tinymist/issues/1845
Fallback for tables with block-level cells now converts the Typst
`HtmlElement` tree into a `cmark_writer::ast::Node::HtmlElement` and
lets `HtmlWriter` render it.
---------
Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com>
## Features
- Support multi-page export in the server export command. Added an extra
arg to decide whether to write to the file or only return data for
client use (page preview in exporter). VSCode users can use this feature
via the quick-export command or code lens.
- (refactor) Move most export logic from tinymist to tinymist-task,
excluding typlite-related, which already depends on tinymist-task.
- Added relevant export tests. The export e2e test now includes hash
checking for all targets.
## Not done
- Support new args in `TypstExtraArgs` for CLI compilation.
---------
Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com>
When a file, for example a rust source file, is sent to the language
server by `didOpen`, it is implicitly focused and starts a typst
compilation with the file. However, this is not expected. The error is
rarely triggered because vscode extension also explicitly send focus
events which prevents the implicit focus happens. So in conclusion this
bug was triggered stably by following steps:
- activate tinymist for some reason, and no typst source file is opened.
- open a rust source file and tinymist reports typst syntax error in the
file.
Add a lint warning for unknown math variables[^1], powered by Tinymist's
existing reference analysis. This allows users to see all such undefined
variables at once if they have the linter enabled, instead of just the
first error from the compiler. The autofix from #2062 is also applicable
to these warnings.
Example:

(The first error is pre-existing and emitted by the Typst compiler; the
second warning is new from this PR and emitted by us.)
Implementation notes:
- The generated diagnostic tries to closely match the corresponding
Typst compiler error, with one deliberate change: to differentiate the
Tinymist warning and the compiler error, we use the message `potentially
unknown variable: ...` instead of `unknown variable: ...`. I am not the
biggest fan of this choice, but I think it is very important that users
don't blame the Typst compiler for warnings that we generate; changing
the message so that it isn't an exact clone is the best way I thought of
for now.
- We avoid duplicating a warning if the compiler has already generated
an error for a given identifier by computing a set of `KnownLintIssues`
from the compiler diagnostics and threading this information through the
lint routines.
[^1]: If users like this warning, we can extend it later to apply to
undefined variables outside of math as well.
---------
Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com>
This PR fixes#1796 by correcting the parent_last_loc parameter passed
to child elements when processing heading folding ranges.
This is because heading's `lsp_range` only covers the line it is on, but
it has children. This causes the `parent_last_loc` passed when
recursively calling the `calc_folding_range` function to be incorrect:
it is expected to be the end of the line before the next sibling
heading, but actually it is the end of the current heading's line. This
further prevents the correction of the heading's `folding_range` from
working properly, ultimately causing the returned `folding_range` to
retain the original `lsp_range`, which is just the heading line itself.
The fix uses the already-calculated `folding_range.end_line` for
headings when determining the `parent_last_loc` for child elements,
ensuring consistency between the parent's actual folding boundary and
the boundary passed to its children.
Hierarchy of comment group is always wrong previously, causing #2021.
This PR fixed it and added a test case.
BTW, the changes in the lexical hierarchy module influenced a folding
range test case, but it's just changed the order of items, which doesn't
affect the correctness (the order is even more correct).
> Hm. Sorry about the lint failures here, they slipped my attention
locally since they show up as warnings, not errors, in my editor. I'm
happy to put a PR fixing them later unless you get to it first.
>
> Not too sure how to deal with the unused DiagWorker::check method,
since it is used, just in a test-only module. We can certainly mark it
as #[cfg(test)] but that feels a little nasty.
https://github.com/Myriad-Dreamin/tinymist/pull/2062#issuecomment-3237812684
I ended up inlining the logic of `DiagWorker::check` into the test.
Examples:
| Input | Suggested fix |
| -- | -- |
| `$xyz$` | `$x y z$` |
| `$a_ij$` | `$a_(i j)$` |
| `$a_(ij)$` | `$a_(i j)$` |
| `$xy/z$` | `$(x y)/z$` |
If the unknown identifier appears as a subscript or as the
numerator/denominator of a fraction, we parenthesize the suggested fix.
For example, `a_ij` turns into `a_(i j)`, not `a_i j`, because the
latter is unlikely to be what the user intended.
This PR adds comprehensive documentation for all public items in
`crates/tinymist-query/src/syntax/expr.rs` following Rust documentation
conventions.
## Changes Made
The following public items now have proper documentation comments:
- **`ExprRoute`** type alias - Documents its purpose as a mapping
structure for file identifiers to lexical scopes during expression
analysis routing
- **`expr_of`** function - Describes its role in analyzing expressions
within source files and producing expression information including
resolves, imports, docstrings, and lexical scoping data
- **`ExprWorker`** struct - Explains its functionality as a worker for
processing expressions during source file analysis, handling expression
checking, scope management, symbol resolution, and docstring collection
## Documentation Style
All documentation follows Rust conventions by:
- Using third person singular verbs
- Focusing on the purpose and functionality rather than implementation
details
- Providing clear, concise descriptions of what each item does
The code has been formatted with `cargo fmt` and compiles successfully
after the changes.
Fixes#2048.
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com>
Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com>
using clippy 0.1.91 (898aff704d 2025-08-14), but some warnings already
exist in the latest stable version
mostly about elided lifetime and if-chain
only remaining warnings:
```
warning: struct `HashRepr` is never constructed
--> crates\tinymist-query\src\tests.rs:462:12
|
462 | pub struct HashRepr<T>(pub T);
| ^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: a method with this name may be added to the standard library in the future
--> crates\tinymist\src\actor\editor.rs:103:30
|
103 | ... .map_or_default(|fid| unix_slash(fid.vpath().as_rooted_path()));
| ^^^^^^^^^^^^^^
|
= warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
= note: for more information, see issue #48919 <https://github.com/rust-lang/rust/issues/48919>
= help: call with fully qualified syntax `typst::typst_utils::OptionExt::map_or_default(...)` to keep using the current method
= note: `#[warn(unstable_name_collisions)]` on by default
help: add `#![feature(result_option_map_or_default)]` to the crate attributes to enable `std::option::Option::<T>::map_or_default`
--> crates\tinymist\src\lib.rs:3:1
|
3 + #![feature(result_option_map_or_default)]
|
```
It provides a `package-doc.json` and HTML is rendered using a
`package-doc` function.
```typ
#let package-doc(info, path: none) [
#metadata((
// more sub paths
)) <static-paths>
#render-page(info, path) // the content of $path/.html
]
#package-doc(json("package-doc.json"))
```
When `f` is a scope with at least one member:
- completes `#f` as `#f()` if `f` will never be a type or element
(merely a function).
- Otherwise, completes `#f` as `#f` and adds a variant `#f.paren`
completed as `#f(|)` or `#f()|`
This excepts for the following cases:
- When the surrounding syntax doesn't like parentheses (for example,
function fields)
> Complete the field name along with its value. Notes:
> No parentheses since function fields cannot currently be called
> with method syntax;
- When the surrounding syntax requires parentheses, for example set
rules, e.g. `#set r|` is completed as `#set raw(|)`.
When passing configuration items with null values, the default
configurations are used. Note: I don't ensure this to be always true,
some configuration items may have different non-default behaviors when
accepting a null value now or in future. The `deserialize_null_default`
is taken from https://github.com/serde-rs/serde/issues/1098.
Configuration parsing changes:
+ some configurations only accepting boolean now coerce null to `false`
(default).
+ some configurations only accepting an object now coerce null to
default.
+ The `tinymist.preview.invertColors` now now coerces null to `"never"`
(default).