Skip to main content

scrive_iced/
lib.rs

1//! `scrive-iced` — the iced 0.14 integration for the scrive code editor.
2//!
3//! Turns [`scrive_core`] into an on-screen widget: a direct
4//! `iced::advanced::Widget` (deliberately *not* a `canvas::Program`, because
5//! only the low-level widget API exposes the `operate()` hook needed to join
6//! iced's focus/operation protocol), with a gutter, N-caret selections, syntect
7//! highlighting, diagnostic squiggles, a completion popup, and hover.
8//!
9//! # Two tiers
10//!
11//! - [`CodeEditor`] — **start here.** The batteries-included tier: it owns a
12//!   [`scrive_core::Document`] and runs the highlighting, find, focus, and
13//!   language-intelligence plumbing internally, so integrating is three wires
14//!   ([`update`](CodeEditor::update), [`view`](CodeEditor::view),
15//!   [`subscription`](CodeEditor::subscription)) plus registering
16//!   [`required_fonts`] at startup. Highlighting is coloured at load with no
17//!   scroll needed; the find bar, selection, undo, and folding are on by default.
18//!   See `examples/minimal.rs`.
19//! - [`Editor`] — the low-level *controlled* widget: it renders a `Document` and
20//!   emits semantic [`Action`]s the application applies by hand. Full control,
21//!   all the plumbing on you. See `examples/scratch.rs`.
22//!
23//! The minimal integration:
24//!
25//! ```no_run
26//! use iced::{Element, Subscription, Task};
27//! use scrive_iced::{CodeEditor, Event};
28//!
29//! struct App { editor: CodeEditor }
30//!
31//! #[derive(Debug, Clone)]
32//! enum Message { Editor(Event) }
33//!
34//! impl App {
35//!     fn new() -> Self { Self { editor: CodeEditor::new("fn main() {}\n") } }
36//!     fn update(&mut self, m: Message) -> Task<Message> {
37//!         match m { Message::Editor(e) => self.editor.update(e).map(Message::Editor) }
38//!     }
39//!     fn view(&self) -> Element<'_, Message> { self.editor.view().map(Message::Editor) }
40//!     fn subscription(&self) -> Subscription<Message> {
41//!         self.editor.subscription().map(Message::Editor)
42//!     }
43//! }
44//! ```
45
46#![deny(missing_docs)]
47#![forbid(unsafe_code)]
48
49mod clipboard;
50pub mod code_editor;
51pub mod editor;
52mod geo;
53mod highlight_pool;
54pub mod metrics;
55pub mod popup;
56
57pub use code_editor::{CodeEditor, Event};
58pub use editor::{default_autoscroll_margin, Action, Editor, SCROLLBAR_WIDTH};
59pub use metrics::Metrics;
60
61/// The bundled [Codicon](https://github.com/microsoft/vscode-codicons) icon font
62/// (v0.0.45) — VS Code's own UI glyph set. The host application **must** load
63/// these bytes into iced's font system at startup (e.g.
64/// `iced::application(..).font(scrive_iced::CODICON_FONT)`); after that the
65/// widget's fold-gutter chevrons and any app chrome can draw glyphs in the
66/// [`CODICON`] font. Icons © Microsoft, CC BY 4.0 (see `assets/CODICON-LICENSE.md`).
67pub const CODICON_FONT: &[u8] = include_bytes!("../assets/codicon.ttf");
68
69/// The [`iced::Font`] handle for the bundled [`CODICON_FONT`] (family `"codicon"`).
70pub const CODICON: iced::Font = iced::Font::with_name("codicon");
71
72/// Every font the widget needs registered in iced's font system at startup —
73/// register them all and the fold-gutter chevrons and find-bar icons render;
74/// omit one and its glyphs fall back to per-machine tofu. One owner so an
75/// integrator can load the whole set instead of enumerating it by hand (today
76/// just [`CODICON_FONT`]):
77/// `scrive_iced::required_fonts().iter().fold(app, |app, f| app.font(*f))`.
78#[must_use]
79pub fn required_fonts() -> &'static [&'static [u8]] {
80    &[CODICON_FONT]
81}
82
83/// The bundled **Scrive Dark** syntax theme — an original, MIT-licensed dark
84/// theme (the one that shipped with 0.1.0). It is the sensible default so a host
85/// gets colored text from a grammar alone, without supplying a `.tmTheme`: the
86/// batteries-included editor tier applies it unless the host overrides it with
87/// another [`TokenTheme`](scrive_core::TokenTheme).
88///
89/// The theme is compiled in, so parsing it cannot fail at runtime — a malformed
90/// asset is a packaging bug the crate's own tests catch, not a caller error.
91/// That is why this returns the theme directly rather than a `Result`.
92#[must_use]
93pub fn scrive_dark_theme() -> scrive_core::TokenTheme {
94    scrive_core::TokenTheme::from_tm_theme(include_str!("../assets/scrive-dark.tmTheme"))
95        .expect("bundled Scrive Dark theme parses")
96}
97
98/// Codicon glyph codepoints scrive draws. Names and values are from the codicon
99/// `mapping.json` (verified against v0.0.45); the private-use-area codepoints are
100/// only meaningful rendered in the [`CODICON`] font.
101pub mod icon {
102    /// `chevron-right` (U+EAB6) — the collapsed-fold gutter indicator, and the
103    /// find bar's collapsed replace-row toggle.
104    pub const CHEVRON_RIGHT: char = '\u{eab6}';
105    /// `chevron-down` (U+EAB4) — the expanded-fold gutter indicator, and the
106    /// find bar's expanded replace-row toggle.
107    pub const CHEVRON_DOWN: char = '\u{eab4}';
108    /// `arrow-up` (U+EAA1) — find "previous match".
109    pub const ARROW_UP: char = '\u{eaa1}';
110    /// `arrow-down` (U+EA9A) — find "next match".
111    pub const ARROW_DOWN: char = '\u{ea9a}';
112    /// `close` (U+EA76) — find "close".
113    pub const CLOSE: char = '\u{ea76}';
114    /// `replace` (U+EB3D) — find "replace this match".
115    pub const REPLACE: char = '\u{eb3d}';
116    /// `replace-all` (U+EB3C) — find "replace every match".
117    pub const REPLACE_ALL: char = '\u{eb3c}';
118    /// `case-sensitive` (U+EAB1) — the find bar's `Aa` option toggle.
119    pub const CASE_SENSITIVE: char = '\u{eab1}';
120    /// `preserve-case` (U+EB2E) — the replace bar's `AB` option toggle.
121    pub const PRESERVE_CASE: char = '\u{eb2e}';
122    /// `whole-word` (U+EB7E) — the find bar's `ab|` option toggle.
123    pub const WHOLE_WORD: char = '\u{eb7e}';
124    /// `regex` (U+EB38) — the find bar's `.*` option toggle.
125    pub const REGEX: char = '\u{eb38}';
126    /// `list-selection` (U+EB85) — the find bar's "find in selection" toggle.
127    /// The codicon set names this glyph `list-selection`; `selection` is an
128    /// alias for it, and is what VS Code calls the same button.
129    pub const SELECTION: char = '\u{eb85}';
130}
131
132#[cfg(test)]
133mod tests {
134    /// The compiled-in Scrive Dark theme must parse — the `expect` in
135    /// [`super::scrive_dark_theme`] would otherwise panic in every host that
136    /// takes the default. This is the packaging guard the doc comment promises.
137    #[test]
138    fn bundled_scrive_dark_theme_parses() {
139        let _ = super::scrive_dark_theme();
140    }
141}