Struct CodeEditorSession

Source
pub struct CodeEditorSession {
Show 14 fields pub document: Document, pub editor: EditorSession, pub config: CodeEditorConfig, pub diagnostics: Vec<Diagnostic>, pub active_diagnostic_index: Option<usize>, pub search: SearchState, pub syntax_tokens: Vec<SyntaxToken>, pub syntax_tokens_document_id: Option<DocumentId>, pub syntax_tokens_version: Option<DocumentVersion>, pub syntax_tokens_language: Option<EditorLanguage>, pub fold_regions: Vec<FoldRegion>, pub fold_regions_document_id: Option<DocumentId>, pub fold_regions_version: Option<DocumentVersion>, pub fold_regions_language: Option<EditorLanguage>,
}
Expand description

Code-editor session: document metadata plus generic editor state and caches.

The public fields support adapter composition, but direct mutation can break the version stamps that guard syntax, fold, and search caches. Prefer the provided synchronization and setter methods when cache coherence matters.

§Examples

use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("fn main() {}")), CodeEditorConfig::default());
assert_eq!(session.editor.buffer.as_str(), session.document.buffer.as_str());
assert!(session.diagnostics.is_empty() && session.fold_regions.is_empty());

Fields§

§document: Document

Canonical document metadata and content.

§editor: EditorSession

Generic editable buffer, caret, selection, scroll, and input state.

§config: CodeEditorConfig

Generic and code-specific behavior/style configuration.

§diagnostics: Vec<Diagnostic>

Local and current-version LSP diagnostics in display order.

§active_diagnostic_index: Option<usize>

Selected diagnostic index, or None.

§search: SearchState

Query, cached matches, and active search result.

§syntax_tokens: Vec<SyntaxToken>

Current syntax-token cache.

§syntax_tokens_document_id: Option<DocumentId>

Document identity from which syntax tokens were derived.

§syntax_tokens_version: Option<DocumentVersion>

Document version from which syntax tokens were derived.

§syntax_tokens_language: Option<EditorLanguage>

Language used to derive syntax tokens.

§fold_regions: Vec<FoldRegion>

Current logical fold regions.

§fold_regions_document_id: Option<DocumentId>

Document identity from which folds were derived.

§fold_regions_version: Option<DocumentVersion>

Document version from which folds were derived.

§fold_regions_language: Option<EditorLanguage>

Language used to derive folds.

Implementations§

Source§

impl CodeEditorSession

Synchronizes document/editor state and derived code caches.

Source

pub fn new(document: Document, config: CodeEditorConfig) -> Self

Creates a session with synchronized buffers and empty derived state.

The document buffer is cheaply cloned into a generic EditorSession using config.editor. Document version, dirty state, and metadata are preserved; every cache provenance stamp starts as None.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let session = CodeEditorSession::new(Document::new(DocumentId(7), TextBuffer::from_string("text")), CodeEditorConfig::default());
assert_eq!(session.document.id, DocumentId(7));
assert!(session.syntax_tokens_document_id.is_none());
Source

pub fn editor_session(&self) -> &EditorSession

Borrows the generic editor session.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("abc")), CodeEditorConfig::default());
assert_eq!(session.editor_session().buffer.as_str(), "abc");
Source

pub fn editor_session_mut(&mut self) -> &mut EditorSession

Mutably borrows the generic editor session.

Buffer mutations made through this reference do not update the document until Self::sync_document_from_editor is called.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("abc")), CodeEditorConfig::default());
session.editor_session_mut().edit.caret_byte = 2;
assert_eq!(session.editor.edit.caret_byte, 2);
Source

pub fn sync_document_from_editor(&mut self) -> bool

Copies changed editor text into the document.

Returns false when strings are identical. A change rebuilds the document buffer, sets dirty = true, and increments its i32 version with saturation at i32::MAX. Other document metadata is unchanged.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, DocumentVersion};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("old")), CodeEditorConfig::default());
session.editor.buffer = TextBuffer::from_string("new");
assert!(session.sync_document_from_editor());
assert_eq!(session.document.version, DocumentVersion(1));
assert!(session.document.dirty);
Source

pub fn sync_editor_from_document(&mut self) -> bool

Replaces generic editor text from the document when its string differs.

The generic session clamps its edit state and clears incompatible input state according to EditorSession::replace_buffer_if_changed. Document metadata and derived code caches are not changed here.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("old")), CodeEditorConfig::default());
session.document.buffer = TextBuffer::from_string("new");
assert!(session.sync_editor_from_document());
assert_eq!(session.editor.buffer.as_str(), "new");
Source

pub fn replace_document_if_changed(&mut self, document: Document) -> bool

Replaces the complete document when any compared field differs.

Text, ID, path, source, language, version, and dirty state participate; internal buffer revision metadata does not when text is equal. A new ID invalidates syntax/fold provenance and the search key. Text changes with the same ID invalidate syntax/fold versions and search. Cached values are retained until their next refresh.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("one")), CodeEditorConfig::default());
let replacement = Document::new(DocumentId(2), TextBuffer::from_string("two"));
assert!(session.replace_document_if_changed(replacement));
assert_eq!(session.editor.buffer.as_str(), "two");
Source

pub fn set_diagnostics(&mut self, diagnostics: Vec<Diagnostic>)

Replaces diagnostics and retains only an in-range active index.

Input order, ranges, duplicates, and sources are preserved verbatim.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Diagnostic, DiagnosticSeverity, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::new()), CodeEditorConfig::default());
session.set_diagnostics(vec![Diagnostic::new(0..0, DiagnosticSeverity::Hint, "hint")]);
assert_eq!(session.diagnostics.len(), 1);
Source

pub fn apply_lsp_diagnostics(&mut self, diagnostics: &[LspDiagnostic]) -> bool

Replaces all LSP diagnostics with current-version mapped inputs.

Local diagnostics remain in order. Stale LSP inputs are discarded, ranges clamp to document length, and empty ranges are removed. Returns whether the resulting complete diagnostic vector differs.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, DiagnosticSeverity, Document, DocumentId, DocumentVersion, LspDiagnostic};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("abc")), CodeEditorConfig::default());
let input = [LspDiagnostic { range: 1..9, severity: DiagnosticSeverity::Error, message: "bad".into(), document_version: DocumentVersion(0) }];
assert!(session.apply_lsp_diagnostics(&input));
assert_eq!(session.diagnostics[0].range, 1..3);
Source

pub fn apply_lsp_enrichment(&mut self, enrichment: &LspEnrichment) -> bool

Applies only the diagnostic portion of current-version LSP enrichment.

A stale enrichment returns false without changes. Current enrichment removes prior LSP diagnostics, retains local diagnostics, and accepts only entries explicitly marked LSP with the current document version. Symbols, references, and capabilities are intentionally ignored by this session.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, DocumentVersion, LspCapabilities, LspEnrichment};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::new()), CodeEditorConfig::default());
let stale = LspEnrichment { document_version: DocumentVersion(9), capabilities: LspCapabilities::default(), symbols: vec![], references: vec![], diagnostics: vec![] };
assert!(!session.apply_lsp_enrichment(&stale));
Source

pub fn set_active_diagnostic_index( &mut self, active_diagnostic_index: Option<usize>, )

Selects an in-range diagnostic index, otherwise stores None.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Diagnostic, DiagnosticSeverity, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::new()), CodeEditorConfig::default());
session.set_diagnostics(vec![Diagnostic::new(0..0, DiagnosticSeverity::Info, "note")]);
session.set_active_diagnostic_index(Some(1));
assert_eq!(session.active_diagnostic_index, None);
Source

pub fn diagnostic_at_byte(&self, byte: usize) -> Option<DiagnosticHit>

Returns the first diagnostic whose inclusive range contains byte.

The returned diagnostic is cloned and includes its slice index.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Diagnostic, DiagnosticSeverity, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::new()), CodeEditorConfig::default());
session.set_diagnostics(vec![Diagnostic::new(2..4, DiagnosticSeverity::Error, "bad")]);
assert_eq!(session.diagnostic_at_byte(4).unwrap().index, 0);
Source

pub fn set_search_matches(&mut self, search_matches: Vec<SearchMatch>)

Installs externally computed matches and clears active selection.

The query and existing cache key are left unchanged. A later refresh may preserve these matches on a cache hit or replace them after invalidation. Ranges are not validated against the document.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchMatch};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("abc")), CodeEditorConfig::default());
session.set_search_matches(vec![SearchMatch { range: 1..2 }]);
assert_eq!(session.search.matches[0].range, 1..2);
assert_eq!(session.search.active_index, None);
Source

pub fn set_search_query(&mut self, query: SearchQuery)

Sets a query and immediately refreshes it against the current document.

An equal query can reuse the current version/query cache; a changed query clears old results before recomputation.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("go go")), CodeEditorConfig::default());
session.set_search_query(SearchQuery::new("go"));
assert_eq!(session.search.matches.len(), 2);
Source

pub fn set_search_active_index(&mut self, active_index: Option<usize>)

Selects an in-range search match, otherwise clears the selection.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("x x")), CodeEditorConfig::default());
session.set_search_query(SearchQuery::new("x"));
session.set_search_active_index(Some(1));
assert_eq!(session.search.active_index, Some(1));

Refreshes search results for the current text, version, and query.

Returns true when recomputed and false on a cache hit. Direct text mutation without a corresponding document-version change can therefore produce a stale cache hit.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("abc")), CodeEditorConfig::default());
session.search.set_query(SearchQuery::new("b"));
assert!(session.refresh_search());
assert!(!session.refresh_search());
Source

pub fn search_next(&mut self) -> Option<&SearchMatch>

Refreshes, advances to, and returns the next match with wraparound.

A refresh selects index zero, so for multiple fresh matches this call returns index one. With no matches it returns None.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("x x")), CodeEditorConfig::default());
session.search.set_query(SearchQuery::new("x"));
assert_eq!(session.search_next().map(|m| m.range.clone()), Some(2..3));
Source

pub fn search_previous(&mut self) -> Option<&SearchMatch>

Refreshes, moves to, and returns the previous match with wraparound.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("x x")), CodeEditorConfig::default());
session.search.set_query(SearchQuery::new("x"));
assert_eq!(session.search_previous().map(|m| m.range.clone()), Some(2..3));

Restores empty query, results, active index, and search cache.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, SearchQuery};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("x")), CodeEditorConfig::default());
session.set_search_query(SearchQuery::new("x"));
session.clear_search();
assert!(session.search.query.text.is_empty());
Source

pub fn select_word_at_byte(&mut self, byte: usize) -> bool

Refreshes syntax and selects a syntax token or lexical word at byte.

Returns whether generic editor caret/selection state changed. Byte input is clamped by selection helpers and the editor buffer remains unchanged.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, EditorLanguage};
use ailloli_ui_text::TextBuffer;
let document = Document::new(DocumentId(1), TextBuffer::from_string("hello world")).with_language(EditorLanguage::PlainText);
let mut session = CodeEditorSession::new(document, CodeEditorConfig::default());
assert!(session.select_word_at_byte(1));
assert_eq!(session.editor.edit.selection.unwrap().normalized(), (0, 5));
Source

pub fn select_line_at_byte(&mut self, byte: usize) -> bool

Selects the logical line containing byte, excluding its newline.

Returns whether generic editor state changed.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("one\ntwo")), CodeEditorConfig::default());
assert!(session.select_line_at_byte(5));
assert_eq!(session.editor.edit.selection.unwrap().normalized(), (4, 7));
Source

pub fn set_syntax_tokens(&mut self, syntax_tokens: Vec<SyntaxToken>)

Installs manual syntax tokens and clears all provenance stamps.

Tokens are stored without normalization. Because stamps become None, the next syntax refresh (including word selection) replaces this manual vector with language-derived tokens.

§Examples
use ailloli_ui_editor::{code::{SyntaxKind, SyntaxToken}, CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("x")), CodeEditorConfig::default());
session.set_syntax_tokens(vec![SyntaxToken { range: 0..1, kind: SyntaxKind::Identifier }]);
assert_eq!(session.syntax_tokens.len(), 1);
assert!(session.syntax_tokens_version.is_none());
Source

pub fn set_fold_regions(&mut self, fold_regions: Vec<FoldRegion>)

Installs manual fold regions and clears all provenance stamps.

Regions and collapsed states are stored without validation. The next fold refresh treats them as belonging to no document and replaces them rather than preserving their collapsed state.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, FoldRegion};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::new()), CodeEditorConfig::default());
session.set_fold_regions(vec![FoldRegion::new(0, 2)]);
assert_eq!(session.fold_regions.len(), 1);
assert!(session.fold_regions_document_id.is_none());
Source

pub fn refresh_fold_regions(&mut self)

Refreshes language-derived fold regions when identity/version/language changes.

On the same document ID, matching region IDs inherit prior collapsed state. A different ID discards it. Without tree_sitter, or for an unsupported language, the refreshed vector is empty. Provenance stamps are updated even when discovery returns no regions.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("plain")), CodeEditorConfig::default());
session.refresh_fold_regions();
assert_eq!(session.fold_regions_document_id, Some(DocumentId(1)));
assert!(session.fold_regions.is_empty());
Source

pub fn toggle_fold_region(&mut self, index: usize) -> bool

Toggles one fold region by slice index.

Returns false for an out-of-range index. Collapsing also moves a caret hidden by any collapsed region to that region’s header; expanding does not move it. A valid index returns true even if caret state is unchanged.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, FoldRegion};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("a\nb")), CodeEditorConfig::default());
session.set_fold_regions(vec![FoldRegion::new(0, 1)]);
assert!(session.toggle_fold_region(0));
assert!(session.fold_regions[0].collapsed);
assert!(!session.toggle_fold_region(9));
Source

pub fn move_caret_out_of_folded_regions(&mut self) -> bool

Moves a caret hidden by the first collapsed region to its header line.

Returns false when the caret is visible or setting it causes no editor state change. Selection is cleared and desired horizontal position reset by the generic caret setter. The document and editor buffers are expected to be synchronized and the caret to be a valid UTF-8 boundary.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, FoldRegion};
use ailloli_ui_text::TextBuffer;
let mut session = CodeEditorSession::new(Document::new(DocumentId(1), TextBuffer::from_string("a\nb\nc")), CodeEditorConfig::default());
session.editor.edit.caret_byte = 4;
session.fold_regions = vec![FoldRegion::new(0, 2).collapsed(true)];
assert!(session.move_caret_out_of_folded_regions());
assert_eq!(session.editor.edit.caret_byte, 0);
Source

pub fn refresh_syntax_tokens(&mut self)

Refreshes syntax tokens when document ID, version, or language changes.

Rust uses hybrid Tree-sitter tokens when enabled and successful, then falls back to the lexical highlighter. Every other language yields an empty vector. Provenance stamps are updated even for empty results, so an unchanged subsequent call is a no-op.

§Examples
use ailloli_ui_editor::{CodeEditorConfig, CodeEditorSession, Document, DocumentId, EditorLanguage};
use ailloli_ui_text::TextBuffer;
let document = Document::new(DocumentId(1), TextBuffer::from_string("fn main() {}"))
    .with_language(EditorLanguage::Rust);
let mut session = CodeEditorSession::new(document, CodeEditorConfig::default());
session.refresh_syntax_tokens();
assert!(!session.syntax_tokens.is_empty());
assert_eq!(session.syntax_tokens_language, Some(EditorLanguage::Rust));

Trait Implementations§

Source§

impl Clone for CodeEditorSession

Source§

fn clone(&self) -> CodeEditorSession

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for CodeEditorSession

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> ErasedDestructor for T
where T: 'static,