use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU64, Ordering};
use crate::dom::{DomId, DomNodeId, NodeId};
use crate::geom::{LogicalPosition, LogicalRect};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ContentIndex {
pub run_index: u32,
pub item_index: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct GraphemeClusterId {
pub source_run: u32,
pub start_byte_in_run: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub enum CursorAffinity {
Leading,
Trailing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct TextCursor {
pub cluster_id: GraphemeClusterId,
pub affinity: CursorAffinity,
}
impl_option!(
TextCursor,
OptionTextCursor,
[Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd]
);
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct SelectionRange {
pub start: TextCursor,
pub end: TextCursor,
}
impl_option!(
SelectionRange,
OptionSelectionRange,
[Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd]
);
impl_vec!(
SelectionRange,
SelectionRangeVec,
SelectionRangeVecDestructor,
SelectionRangeVecDestructorType,
SelectionRangeVecSlice,
OptionSelectionRange
);
impl_vec_debug!(SelectionRange, SelectionRangeVec);
impl_vec_clone!(
SelectionRange,
SelectionRangeVec,
SelectionRangeVecDestructor
);
impl_vec_partialeq!(SelectionRange, SelectionRangeVec);
impl_vec_partialord!(SelectionRange, SelectionRangeVec);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum Selection {
Cursor(TextCursor),
Range(SelectionRange),
}
impl_option!(
Selection,
OptionSelection,
[Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord]
);
impl_vec!(
Selection,
SelectionVec,
SelectionVecDestructor,
SelectionVecDestructorType,
SelectionVecSlice,
OptionSelection
);
impl_vec_debug!(Selection, SelectionVec);
impl_vec_clone!(Selection, SelectionVec, SelectionVecDestructor);
impl_vec_partialeq!(Selection, SelectionVec);
impl_vec_partialord!(Selection, SelectionVec);
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct SelectionState {
pub selections: SelectionVec,
pub node_id: DomNodeId,
}
impl SelectionState {
pub fn add(&mut self, new_selection: Selection) {
let mut selections: Vec<Selection> = self.selections.as_ref().to_vec();
selections.push(new_selection);
selections.sort_unstable();
selections.dedup(); self.selections = selections.into();
}
}
impl_option!(
SelectionState,
OptionSelectionState,
copy = false,
clone = false,
[Debug, Clone, PartialEq]
);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct SelectionId {
pub inner: u64,
}
impl SelectionId {
pub fn new() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self {
inner: COUNTER.fetch_add(1, Ordering::Relaxed),
}
}
}
impl Default for SelectionId {
fn default() -> Self {
Self::new()
}
}
impl_option!(
SelectionId,
OptionSelectionId,
[Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord]
);
impl_vec!(
SelectionId,
SelectionIdVec,
SelectionIdVecDestructor,
SelectionIdVecDestructorType,
SelectionIdVecSlice,
OptionSelectionId
);
impl_vec_debug!(SelectionId, SelectionIdVec);
impl_vec_clone!(SelectionId, SelectionIdVec, SelectionIdVecDestructor);
impl_vec_partialeq!(SelectionId, SelectionIdVec);
impl_vec_partialord!(SelectionId, SelectionIdVec);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct IdentifiedSelection {
pub id: SelectionId,
pub selection: Selection,
pub owner: SelectionOwner,
}
impl_option!(
IdentifiedSelection,
OptionIdentifiedSelection,
[Debug, Clone, Copy, PartialEq, Eq, Hash]
);
impl_vec!(
IdentifiedSelection,
IdentifiedSelectionVec,
IdentifiedSelectionVecDestructor,
IdentifiedSelectionVecDestructorType,
IdentifiedSelectionVecSlice,
OptionIdentifiedSelection
);
impl_vec_debug!(IdentifiedSelection, IdentifiedSelectionVec);
impl_vec_clone!(
IdentifiedSelection,
IdentifiedSelectionVec,
IdentifiedSelectionVecDestructor
);
impl_vec_partialeq!(IdentifiedSelection, IdentifiedSelectionVec);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub struct SelectionOwner {
pub high: u64,
pub low: u64,
}
impl SelectionOwner {
pub const LOCAL: Self = Self { high: 0, low: 0 };
#[must_use]
pub const fn new(high: u64, low: u64) -> Self {
Self { high, low }
}
#[must_use]
pub const fn is_local(self) -> bool {
self.high == 0 && self.low == 0
}
pub const SEAT_HIGH: u64 = 0x5EA7_0000_0000_0001;
#[must_use]
pub const fn seat(seat_id: u64) -> Self {
if seat_id == 0 {
return Self::LOCAL;
}
Self {
high: Self::SEAT_HIGH,
low: seat_id,
}
}
#[must_use]
pub const fn is_seat(self) -> bool {
self.high == Self::SEAT_HIGH
}
#[must_use]
pub const fn seat_id(self) -> Option<u64> {
if self.is_seat() {
Some(self.low)
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MultiCursorState {
pub selections: Vec<IdentifiedSelection>,
pub primary_id: SelectionId,
pub node_id: DomNodeId,
pub contenteditable_key: u64,
}
impl MultiCursorState {
#[must_use]
pub fn new_with_cursor(
cursor: TextCursor,
node_id: DomNodeId,
contenteditable_key: u64,
) -> Self {
let id = SelectionId::new();
Self {
selections: vec![IdentifiedSelection {
id,
selection: Selection::Cursor(cursor),
owner: SelectionOwner::LOCAL,
}],
primary_id: id,
node_id,
contenteditable_key,
}
}
#[must_use]
pub fn add_cursor(&mut self, cursor: TextCursor) -> SelectionId {
let id = SelectionId::new();
self.selections.push(IdentifiedSelection {
id,
selection: Selection::Cursor(cursor),
owner: SelectionOwner::LOCAL,
});
self.primary_id = id;
self.merge_overlapping();
id
}
#[must_use]
pub fn add_selection(&mut self, range: SelectionRange) -> SelectionId {
let id = SelectionId::new();
self.selections.push(IdentifiedSelection {
id,
selection: Selection::Range(range),
owner: SelectionOwner::LOCAL,
});
self.primary_id = id;
self.merge_overlapping();
id
}
#[must_use]
pub fn remove_selection(&mut self, id: SelectionId) -> bool {
let len_before = self.selections.len();
self.selections.retain(|s| s.id != id);
let removed = self.selections.len() < len_before;
if removed {
self.ensure_primary_valid();
}
removed
}
pub fn local_selections(&self) -> impl Iterator<Item = &IdentifiedSelection> {
self.selections.iter().filter(|s| s.owner.is_local())
}
pub fn local_selections_mut(&mut self) -> impl Iterator<Item = &mut IdentifiedSelection> {
self.selections.iter_mut().filter(|s| s.owner.is_local())
}
#[must_use]
pub fn local_len(&self) -> usize {
self.local_selections().count()
}
#[must_use]
pub fn get_primary(&self) -> Option<&IdentifiedSelection> {
let pid = self.primary_id;
self.selections
.iter()
.find(|s| s.id == pid && s.owner.is_local())
.or_else(|| self.local_selections().last())
}
pub fn get_primary_mut(&mut self) -> Option<&mut IdentifiedSelection> {
let pid = self.primary_id;
if let Some(pos) = self
.selections
.iter()
.position(|s| s.id == pid && s.owner.is_local())
{
return self.selections.get_mut(pos);
}
self.local_selections_mut().last()
}
fn ensure_primary_valid(&mut self) {
let pid = self.primary_id;
if !self.selections.iter().any(|s| s.id == pid && s.owner.is_local()) {
if let Some(last) = self.local_selections().last() {
self.primary_id = last.id;
}
}
}
#[must_use]
pub fn get_primary_cursor(&self) -> Option<TextCursor> {
self.get_primary().map(|s| match &s.selection {
Selection::Cursor(c) => *c,
Selection::Range(r) => r.end,
})
}
#[must_use]
pub fn to_selections(&self) -> Vec<Selection> {
self.local_selections().map(|s| s.selection).collect()
}
pub fn update_from_edit_result(&mut self, new_selections: &[Selection]) {
let old_ids: Vec<SelectionId> = self.local_selections().map(|s| s.id).collect();
let peers: Vec<IdentifiedSelection> = self
.selections
.iter()
.filter(|s| !s.owner.is_local())
.copied()
.collect();
self.selections.clear();
for (i, sel) in new_selections.iter().enumerate() {
let id = old_ids.get(i).copied().unwrap_or_else(SelectionId::new);
self.selections.push(IdentifiedSelection {
id,
selection: *sel,
owner: SelectionOwner::LOCAL,
});
}
self.selections.extend(peers);
self.ensure_primary_valid();
}
pub fn shift_peers_across(&mut self, changes: &[RunTextChange]) {
self.shift_across(changes, false);
}
pub fn shift_all_across(&mut self, changes: &[RunTextChange]) {
self.shift_across(changes, true);
}
pub fn shift_peers_across_diff(&mut self, diff: &RunTextDiff) {
self.shift_across_diff(diff, false);
}
pub fn shift_all_across_diff(&mut self, diff: &RunTextDiff) {
self.shift_across_diff(diff, true);
}
fn shift_across_diff(&mut self, diff: &RunTextDiff, include_local: bool) {
if diff.is_empty() {
return;
}
for sel in self
.selections
.iter_mut()
.filter(|s| include_local || !s.owner.is_local())
{
sel.selection = match sel.selection {
Selection::Cursor(c) => Selection::Cursor(diff.map_cursor(c)),
Selection::Range(r) => {
let start = diff.map_cursor(r.start);
let end = diff.map_cursor(r.end);
if start == end {
Selection::Cursor(start)
} else {
Selection::Range(SelectionRange { start, end })
}
}
};
}
}
fn shift_across(&mut self, changes: &[RunTextChange], include_local: bool) {
if changes.is_empty() {
return;
}
let shift = |mut c: TextCursor| -> TextCursor {
for change in changes {
if change.run == c.cluster_id.source_run {
c.cluster_id.start_byte_in_run =
change.transform(c.cluster_id.start_byte_in_run);
}
}
c
};
for sel in self
.selections
.iter_mut()
.filter(|s| include_local || !s.owner.is_local())
{
sel.selection = match sel.selection {
Selection::Cursor(c) => Selection::Cursor(shift(c)),
Selection::Range(r) => Selection::Range(SelectionRange {
start: shift(r.start),
end: shift(r.end),
}),
};
}
}
fn surviving_local_id(&self) -> SelectionId {
self.get_primary().map_or_else(SelectionId::new, |primary| primary.id)
}
pub fn set_single_cursor(&mut self, cursor: TextCursor) {
let id = self.surviving_local_id();
self.selections.retain(|s| !s.owner.is_local());
self.selections.insert(
0,
IdentifiedSelection {
id,
selection: Selection::Cursor(cursor),
owner: SelectionOwner::LOCAL,
},
);
self.primary_id = id;
}
pub fn set_single_range(&mut self, range: SelectionRange) {
let id = self.surviving_local_id();
self.selections.retain(|s| !s.owner.is_local());
self.selections.insert(
0,
IdentifiedSelection {
id,
selection: Selection::Range(range),
owner: SelectionOwner::LOCAL,
},
);
self.primary_id = id;
}
#[must_use]
pub const fn len(&self) -> usize {
self.selections.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.selections.is_empty()
}
pub fn merge_overlapping(&mut self) {
if self.selections.len() <= 1 {
return;
}
let primary = self.primary_id;
let mut new_primary = primary;
self.selections.sort_by(|a, b| {
a.owner.cmp(&b.owner).then_with(|| {
let pos_a = selection_start_pos(&a.selection);
let pos_b = selection_start_pos(&b.selection);
pos_a.cmp(&pos_b)
})
});
let mut merged: Vec<IdentifiedSelection> = Vec::with_capacity(self.selections.len());
for sel in self.selections.drain(..) {
if let Some(last) = merged.last_mut() {
let last_end = selection_end_pos(&last.selection);
let cur_start = selection_start_pos(&sel.selection);
if last.owner == sel.owner && cur_start <= last_end {
let new_start = selection_start_pos(&last.selection);
let cur_end = selection_end_pos(&sel.selection);
let new_end = if cur_end > last_end {
cur_end
} else {
last_end
};
if new_start == new_end {
last.selection = Selection::Cursor(new_start);
} else {
last.selection = Selection::Range(SelectionRange {
start: new_start,
end: new_end,
});
}
let inherits_primary =
last.id == primary || sel.id == primary || last.id == new_primary;
last.id = sel.id;
if inherits_primary {
new_primary = sel.id;
}
continue;
}
}
merged.push(sel);
}
self.selections = merged;
self.primary_id = new_primary;
self.ensure_primary_valid();
}
pub fn set_owner_selections(
&mut self,
owner: SelectionOwner,
selections: &[Selection],
) -> bool {
if owner.is_local() {
return false;
}
self.selections.retain(|s| s.owner != owner);
for selection in selections {
self.selections.push(IdentifiedSelection {
id: SelectionId::new(),
selection: *selection,
owner,
});
}
self.ensure_primary_valid();
true
}
pub fn remove_owner(&mut self, owner: SelectionOwner) -> usize {
if owner.is_local() {
return 0;
}
let before = self.selections.len();
self.selections.retain(|s| s.owner != owner);
self.ensure_primary_valid();
before - self.selections.len()
}
#[must_use]
pub fn owners(&self) -> Vec<SelectionOwner> {
let mut out: Vec<SelectionOwner> = self.selections.iter().map(|s| s.owner).collect();
out.sort_unstable();
out.dedup();
out
}
pub fn move_all_cursors(
&mut self,
extend_selection: bool,
move_fn: impl Fn(&TextCursor) -> TextCursor,
) {
self.move_all_cursors_with(extend_selection, true, move_fn);
}
pub fn move_all_cursors_with(
&mut self,
extend_selection: bool,
collapse_range_to_boundary: bool,
move_fn: impl Fn(&TextCursor) -> TextCursor,
) {
for sel in self.selections.iter_mut().filter(|s| s.owner.is_local()) {
match &sel.selection {
Selection::Cursor(c) => {
let new_cursor = move_fn(c);
if extend_selection {
if *c != new_cursor {
sel.selection = Selection::Range(SelectionRange {
start: *c,
end: new_cursor,
});
}
} else {
sel.selection = Selection::Cursor(new_cursor);
}
}
Selection::Range(r) => {
if extend_selection {
let new_end = move_fn(&r.end);
if r.start == new_end {
sel.selection = Selection::Cursor(r.start);
} else {
sel.selection = Selection::Range(SelectionRange {
start: r.start,
end: new_end,
});
}
} else if collapse_range_to_boundary {
let (lo, hi) = if r.start <= r.end {
(r.start, r.end)
} else {
(r.end, r.start)
};
let probe = move_fn(&r.end);
let collapsed = if probe >= r.end { hi } else { lo };
sel.selection = Selection::Cursor(collapsed);
} else {
sel.selection = Selection::Cursor(move_fn(&r.end));
}
}
}
}
self.merge_overlapping();
}
pub fn remap_node_ids(&mut self, dom_id: DomId, node_id_map: &BTreeMap<NodeId, NodeId>) {
if self.node_id.dom != dom_id {
return;
}
if let Some(old_node_id) = self.node_id.node.into_crate_internal() {
if let Some(&new_node_id) = node_id_map.get(&old_node_id) {
self.node_id.node =
crate::styled_dom::NodeHierarchyItemId::from_crate_internal(Some(new_node_id));
} else {
self.selections.clear();
}
}
}
}
fn selection_start_pos(sel: &Selection) -> TextCursor {
match sel {
Selection::Cursor(c) => *c,
Selection::Range(r) => {
if r.start <= r.end {
r.start
} else {
r.end
}
}
}
}
fn selection_end_pos(sel: &Selection) -> TextCursor {
match sel {
Selection::Cursor(c) => *c,
Selection::Range(r) => {
if r.end >= r.start {
r.end
} else {
r.start
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SelectionAnchor {
pub ifc_root_node_id: NodeId,
pub cursor: TextCursor,
pub char_bounds: LogicalRect,
pub mouse_position: LogicalPosition,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SelectionFocus {
pub ifc_root_node_id: NodeId,
pub cursor: TextCursor,
pub mouse_position: LogicalPosition,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextSelection {
pub dom_id: DomId,
pub anchor: SelectionAnchor,
pub focus: SelectionFocus,
pub affected_nodes: BTreeMap<NodeId, Vec<SelectionRange>>,
pub remote_ranges: BTreeMap<NodeId, Vec<(SelectionOwner, SelectionRange)>>,
pub is_forward: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RunRemap {
pub first: u32,
pub old_lens: Vec<u32>,
pub new_lens: Vec<u32>,
pub middle: Option<RunTextChange>,
pub prev_len: Option<u32>,
}
impl RunRemap {
#[must_use]
pub fn map_cursor(&self, c: TextCursor) -> TextCursor {
let run = c.cluster_id.source_run;
let first = self.first;
let old_end = first + self.old_lens.len() as u32;
let new_end = first + self.new_lens.len() as u32;
if run < first {
return c;
}
if run >= old_end {
return TextCursor {
cluster_id: GraphemeClusterId {
source_run: run - old_end + new_end,
start_byte_in_run: c.cluster_id.start_byte_in_run,
},
affinity: c.affinity,
};
}
let mut global: u32 = self.old_lens[..(run - first) as usize].iter().sum();
global = global.saturating_add(c.cluster_id.start_byte_in_run);
if let Some(m) = &self.middle {
global = m.transform(global);
}
let total_new: u32 = self.new_lens.iter().sum();
global = global.min(total_new);
if self.new_lens.is_empty() {
return match (first.checked_sub(1), self.prev_len) {
(Some(prev), Some(len)) => TextCursor {
cluster_id: GraphemeClusterId {
source_run: prev,
start_byte_in_run: len,
},
affinity: c.affinity,
},
_ => TextCursor {
cluster_id: GraphemeClusterId {
source_run: first,
start_byte_in_run: 0,
},
affinity: c.affinity,
},
};
}
let mut offset = global;
let mut target = first;
for (i, len) in self.new_lens.iter().enumerate() {
let last = i + 1 == self.new_lens.len();
if offset <= *len || last {
target = first + i as u32;
break;
}
offset -= len;
}
TextCursor {
cluster_id: GraphemeClusterId {
source_run: target,
start_byte_in_run: offset,
},
affinity: c.affinity,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct RunTextDiff {
pub remap: Option<RunRemap>,
pub changes: Vec<RunTextChange>,
}
impl RunTextDiff {
#[must_use]
pub fn is_empty(&self) -> bool {
self.remap.is_none() && self.changes.is_empty()
}
#[must_use]
pub fn map_cursor(&self, c: TextCursor) -> TextCursor {
let mut c = match &self.remap {
Some(remap) => remap.map_cursor(c),
None => c,
};
for change in &self.changes {
if change.run == c.cluster_id.source_run {
c.cluster_id.start_byte_in_run = change.transform(c.cluster_id.start_byte_in_run);
}
}
c
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RunTextChange {
pub run: u32,
pub start: u32,
pub end: u32,
pub inserted: u32,
}
impl RunTextChange {
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn between(run: u32, old: &str, new: &str) -> Option<Self> {
if old == new {
return None;
}
let ob = old.as_bytes();
let nb = new.as_bytes();
let shorter = ob.len().min(nb.len());
let mut prefix = 0;
while prefix < shorter && ob[prefix] == nb[prefix] {
prefix += 1;
}
while prefix > 0 && !(old.is_char_boundary(prefix) && new.is_char_boundary(prefix)) {
prefix -= 1;
}
let mut suffix = 0;
while suffix < shorter - prefix && ob[ob.len() - 1 - suffix] == nb[nb.len() - 1 - suffix] {
suffix += 1;
}
while suffix > 0
&& !(old.is_char_boundary(ob.len() - suffix) && new.is_char_boundary(nb.len() - suffix))
{
suffix -= 1;
}
Some(Self {
run,
start: prefix as u32,
end: (ob.len() - suffix) as u32,
inserted: (nb.len() - prefix - suffix) as u32,
})
}
#[must_use]
pub fn transform(&self, byte: u32) -> u32 {
if byte < self.start {
byte
} else if byte >= self.end {
byte - (self.end - self.start) + self.inserted
} else {
self.start
}
}
}
impl TextSelection {
#[must_use]
pub fn new_collapsed(
dom_id: DomId,
ifc_root_node_id: NodeId,
cursor: TextCursor,
char_bounds: LogicalRect,
mouse_position: LogicalPosition,
) -> Self {
let anchor = SelectionAnchor {
ifc_root_node_id,
cursor,
char_bounds,
mouse_position,
};
let focus = SelectionFocus {
ifc_root_node_id,
cursor,
mouse_position,
};
let mut affected_nodes = BTreeMap::new();
affected_nodes.insert(
ifc_root_node_id,
vec![SelectionRange {
start: cursor,
end: cursor,
}],
);
Self {
remote_ranges: BTreeMap::new(),
dom_id,
anchor,
focus,
affected_nodes,
is_forward: true, }
}
#[must_use]
pub fn is_collapsed(&self) -> bool {
self.anchor.ifc_root_node_id == self.focus.ifc_root_node_id
&& self.anchor.cursor == self.focus.cursor
}
#[must_use]
pub fn get_range_for_node(&self, ifc_root_node_id: &NodeId) -> Option<&SelectionRange> {
self.affected_nodes
.get(ifc_root_node_id)
.and_then(|r| r.first())
}
#[must_use]
pub fn ranges_for_node(&self, ifc_root_node_id: &NodeId) -> &[SelectionRange] {
self.affected_nodes
.get(ifc_root_node_id)
.map_or(&[], Vec::as_slice)
}
}
impl_option!(
TextSelection,
OptionTextSelection,
copy = false,
clone = false,
[Debug, Clone, PartialEq, Eq]
);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct DocumentPosition {
pub node: DomNodeId,
pub text_byte: u32,
}
impl_option!(
DocumentPosition,
OptionDocumentPosition,
[Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord]
);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct DocumentSelectionSpan {
pub node: DomNodeId,
pub start_byte: u32,
pub end_byte: u32,
}
impl_option!(
DocumentSelectionSpan,
OptionDocumentSelectionSpan,
[Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord]
);
impl_vec!(
DocumentSelectionSpan,
DocumentSelectionSpanVec,
DocumentSelectionSpanVecDestructor,
DocumentSelectionSpanVecDestructorType,
DocumentSelectionSpanVecSlice,
OptionDocumentSelectionSpan
);
impl_vec_debug!(DocumentSelectionSpan, DocumentSelectionSpanVec);
impl_vec_clone!(
DocumentSelectionSpan,
DocumentSelectionSpanVec,
DocumentSelectionSpanVecDestructor
);
impl_vec_partialeq!(DocumentSelectionSpan, DocumentSelectionSpanVec);
impl_vec_partialord!(DocumentSelectionSpan, DocumentSelectionSpanVec);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct DocumentTextEdit {
pub node: DomNodeId,
pub text: azul_css::corety::AzString,
pub revision: u64,
}
impl_option!(
DocumentTextEdit,
OptionDocumentTextEdit,
copy = false,
[Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord]
);
impl_vec!(
DocumentTextEdit,
DocumentTextEditVec,
DocumentTextEditVecDestructor,
DocumentTextEditVecDestructorType,
DocumentTextEditVecSlice,
OptionDocumentTextEdit
);
impl_vec_debug!(DocumentTextEdit, DocumentTextEditVec);
impl_vec_clone!(DocumentTextEdit, DocumentTextEditVec, DocumentTextEditVecDestructor);
impl_vec_partialeq!(DocumentTextEdit, DocumentTextEditVec);
impl_vec_partialord!(DocumentTextEdit, DocumentTextEditVec);
#[cfg(test)]
#[path = "selection_test.rs"]
mod selection_test;