Skip to main content

iced_code_editor/
lib.rs

1//! A high-performance code editor widget for Iced.
2//!
3//! This crate provides a canvas-based code editor with syntax highlighting,
4//! line numbers, and text selection capabilities for the Iced GUI framework.
5//!
6//! # Features
7//!
8//! - **Syntax highlighting** for multiple programming languages
9//! - **Line numbers** with styled gutter
10//! - **Text selection** via mouse drag and keyboard
11//! - **Clipboard operations** (copy, paste)
12//! - **Custom scrollbars** with themed styling
13//! - **Focus management** for multiple editors
14//! - **Dark & light themes** support with customizable colors
15//! - **Undo/Redo** with command history
16//! - **Optional Vim mode** with Normal, Insert, Visual, and Visual Line modes
17//!
18//! # Example
19//!
20//! ```no_run
21//! use iced::widget::container;
22//! use iced::{Element, Task};
23//! use iced_code_editor::{CodeEditor, Message as EditorMessage};
24//!
25//! struct MyApp {
26//!     editor: CodeEditor,
27//! }
28//!
29//! #[derive(Debug, Clone)]
30//! enum Message {
31//!     EditorEvent(EditorMessage),
32//! }
33//!
34//! impl Default for MyApp {
35//!     fn default() -> Self {
36//!         let code = r#"fn main() {
37//!     println!("Hello, world!");
38//! }
39//! "#;
40//!
41//!         Self { editor: CodeEditor::new(code, "rust") }
42//!     }
43//! }
44//!
45//! impl MyApp {
46//!     fn update(&mut self, message: Message) -> Task<Message> {
47//!         match message {
48//!             Message::EditorEvent(event) => {
49//!                 self.editor.update(&event).map(Message::EditorEvent)
50//!             }
51//!         }
52//!     }
53//!
54//!     fn view(&self) -> Element<'_, Message> {
55//!         container(self.editor.view().map(Message::EditorEvent))
56//!             .padding(20)
57//!             .into()
58//!     }
59//! }
60//!
61//! fn main() -> iced::Result {
62//!     iced::run(MyApp::update, MyApp::view)
63//! }
64//! ```
65//!
66//! # Themes
67//!
68//! The editor supports all native Iced themes with automatic color adaptation:
69//!
70//! ```no_run
71//! use iced_code_editor::{CodeEditor, theme};
72//!
73//! // Create an editor (defaults to Tokyo Night Storm theme)
74//! let mut editor = CodeEditor::new("fn main() {}", "rs");
75//!
76//! // Switch to any Iced theme
77//! editor.set_theme(theme::from_iced_theme(&iced::Theme::Dracula));
78//! editor.set_theme(theme::from_iced_theme(&iced::Theme::CatppuccinMocha));
79//! editor.set_theme(theme::from_iced_theme(&iced::Theme::Nord));
80//! ```
81//!
82//! # Vim Mode
83//!
84//! Vim behavior is disabled by default and configured per editor instance:
85//!
86//! ```
87//! use iced_code_editor::{CodeEditor, VimMode};
88//!
89//! let mut editor =
90//!     CodeEditor::new("fn main() {}", "rs").with_vim_enabled(true);
91//! assert!(editor.vim_enabled());
92//! assert_eq!(editor.vim_mode(), Some(VimMode::Normal));
93//!
94//! editor.set_vim_enabled(false);
95//! assert_eq!(editor.vim_mode(), None);
96//! ```
97//!
98//! A focused editor can toggle Vim behavior with `Ctrl+Alt+V`, or
99//! `Command+Alt+V` on macOS. Regular `Ctrl`/`Command+V` system paste is
100//! unchanged. Counts support both target lines (`5G`/`5gg` jump to logical
101//! line 5) and line operators (`5yy`, `5dd`, and `5cc` affect five lines).
102//! A fixed bottom status line shows the current mode, pending keys, and active
103//! `/pattern` or `:N` input. Submit with Enter, edit with Backspace, or cancel
104//! with Escape. `n` and `N` repeat the last search forward or backward.
105//!
106//! The Vim MVP provides Normal, Insert, Visual, and Visual Line modes; counts;
107//! `h/j/k/l`, word and line motions; `d/c/y`, `x`, `p/P`, and `u`/`Ctrl+R`;
108//! `/` search with `n`/`N`, `:N` line jumps; and a per-editor unnamed register.
109//! It is single-cursor and does not include general Ex commands, regex/search
110//! history, text objects, macros, named registers, marks, `.` repeat, or
111//! configurable mappings. Platform clipboard shortcuts continue to use the
112//! system clipboard instead of the Vim register.
113//!
114//! # Keyboard Shortcuts
115//!
116//! The editor supports a comprehensive set of keyboard shortcuts:
117//!
118//! ## Navigation
119//!
120//! | Shortcut | Action |
121//! |----------|--------|
122//! | **Arrow Keys** (Up, Down, Left, Right) | Move cursor |
123//! | **Shift + Arrows** | Move cursor with selection |
124//! | **Home** / **End** | Jump to start/end of line |
125//! | **Shift + Home** / **Shift + End** | Select to start/end of line |
126//! | **Ctrl + Home** / **Ctrl + End** | Jump to start/end of document |
127//! | **Page Up** / **Page Down** | Scroll one page up/down |
128//!
129//! ## Editing
130//!
131//! | Shortcut | Action |
132//! |----------|--------|
133//! | **Backspace** | Delete character before cursor (or delete selection if text is selected) |
134//! | **Delete** | Delete character after cursor (or delete selection if text is selected) |
135//! | **Shift + Delete** | Delete selected text (same as Delete when selection exists) |
136//! | **Enter** | Insert new line |
137//!
138//! ## Clipboard
139//!
140//! | Shortcut | Action |
141//! |----------|--------|
142//! | **Ctrl + C** or **Ctrl + Insert** | Copy selected text |
143//! | **Ctrl + V** or **Shift + Insert** | Paste from clipboard |
144//!
145//! # Supported Languages
146//!
147//! The editor supports syntax highlighting through the `syntect` crate:
148//! - Python (`"py"` or `"python"`)
149//! - Lua (`"lua"`)
150//! - Rust (`"rs"` or `"rust"`)
151//! - JavaScript (`"js"` or `"javascript"`)
152//! - And many more...
153//!
154//! For a complete list, refer to the `syntect` crate documentation.
155//!
156//! # Command History Management
157//!
158//! The [`CommandHistory`] type provides fine-grained control over undo/redo operations.
159//! While the editor handles history automatically, you can access it directly for
160//! advanced use cases:
161//!
162//! ## Monitoring History State
163//!
164//! ```no_run
165//! use iced_code_editor::CommandHistory;
166//!
167//! let history = CommandHistory::new(100);
168//!
169//! // Check how many operations are available
170//! println!("Undo operations: {}", history.undo_count());
171//! println!("Redo operations: {}", history.redo_count());
172//!
173//! // Check if operations are possible
174//! if history.can_undo() {
175//!     println!("Can undo!");
176//! }
177//! ```
178//!
179//! ## Adjusting History Size
180//!
181//! You can dynamically adjust the maximum number of operations kept in history:
182//!
183//! ```no_run
184//! use iced_code_editor::CommandHistory;
185//!
186//! let history = CommandHistory::new(100);
187//!
188//! // Get current maximum
189//! assert_eq!(history.max_size(), 100);
190//!
191//! // Increase limit for memory-rich environments
192//! history.set_max_size(500);
193//!
194//! // Or decrease for constrained environments
195//! history.set_max_size(50);
196//! ```
197//!
198//! ## Clearing History
199//!
200//! You can reset the entire history when needed:
201//!
202//! ```no_run
203//! use iced_code_editor::CommandHistory;
204//!
205//! let history = CommandHistory::new(100);
206//!
207//! // Clear all undo/redo operations
208//! history.clear();
209//!
210//! assert_eq!(history.undo_count(), 0);
211//! assert_eq!(history.redo_count(), 0);
212//! ```
213//!
214//! ## Save Point Tracking
215//!
216//! Track whether the document has been modified since the last save:
217//!
218//! ```no_run
219//! use iced_code_editor::CommandHistory;
220//!
221//! let history = CommandHistory::new(100);
222//!
223//! // After loading or saving a file
224//! history.mark_saved();
225//!
226//! // Check if there are unsaved changes
227//! if history.is_modified() {
228//!     println!("Document has unsaved changes!");
229//! }
230//! ```
231
232// Initialize rust-i18n for the entire crate
233rust_i18n::i18n!("locales", fallback = "en");
234
235mod canvas_editor;
236mod text_buffer;
237mod text_utils;
238
239pub mod i18n;
240pub mod theme;
241
242/// Hidden re-exports for the `criterion` benchmark harness (`benches/`).
243///
244/// Compiled only with the `bench` feature; not part of the public API.
245#[doc(hidden)]
246#[cfg(feature = "bench")]
247pub use canvas_editor::bench_support;
248pub use canvas_editor::folding::FoldRegion;
249/// LSP integration types and traits for editor clients.
250pub use canvas_editor::lsp::{
251    LspClient, LspDocument, LspPosition, LspRange, LspTextChange,
252};
253pub use canvas_editor::{
254    ArrowDirection, CodeEditor, CommandHistory, ContextMenuEntry,
255    ContextMenuItem, IndentStyle, Message, VimMode,
256};
257pub use i18n::{Language, Translations};
258pub use theme::{Catalog, Style, StyleFn, from_iced_theme};
259
260#[cfg(all(feature = "lsp-process", not(target_arch = "wasm32")))]
261pub use canvas_editor::lsp_process::{LspEvent, LspProcessClient};
262
263#[cfg(all(feature = "lsp-process", not(target_arch = "wasm32")))]
264pub use canvas_editor::lsp_process::config::{
265    LspCommand, LspLanguage, LspServerConfig, ensure_rust_analyzer_config,
266    lsp_language_for_extension, lsp_language_for_path, lsp_server_config,
267    resolve_lsp_command,
268};
269
270#[cfg(all(feature = "lsp-process", not(target_arch = "wasm32")))]
271pub use canvas_editor::lsp_process::overlay::{
272    LspOverlayMessage, LspOverlayState, view_lsp_overlay,
273};