Skip to content

Commit

Permalink
Floem editor (#296)
Browse files Browse the repository at this point in the history
* Initial floem-editor move

* Fix handling of shift-movement keybinds

* Reexport floem editor core

* Put serde under a feature flag

* Simplify creation of Editor

* Stop using `Rc` in `RwSignal<Rc<Editor>>`

* Remove tracing remnant

* Cargo fmt

* Rename old lapce references

* Use View/Widget split

* Move editor/ into views/

* Add SimpleStylingBuilder

* Add text_editor view

* Add TextEditor::update listener

* Remove manual impl of view id

* Have TextEditor use structures for data

* TextDocument into own file; TextEditor into views/

* Add more TextEditor fns; fix read only

* Use shorthand 'doc' instead of 'document'

* Add Document::edit for applying edits from outside

* Rename editor.rs -> mod.rs

* Use a range for Document::exec_motion_mode

* Add editor to default features
  • Loading branch information
MinusGix authored Feb 7, 2024
1 parent 4a3636e commit d9a3fb8
Show file tree
Hide file tree
Showing 42 changed files with 18,624 additions and 2 deletions.
21 changes: 19 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["renderer", "vger", "tiny_skia", "reactive", "examples/*"]
members = ["renderer", "vger", "tiny_skia", "reactive", "editor-core", "examples/*"]

[workspace.package]
license = "MIT"
Expand All @@ -17,6 +17,12 @@ edition = "2021"
rust-version = "1.75"
license.workspace = true

[workspace.dependencies]
serde = "1.0"
lapce-xi-rope = { version = "0.3.2", features = ["serde"] }
strum = "0.21.0"
strum_macros = "0.21.1"

[dependencies]
sha2 = "0.10.6"
bitflags = "2.2.1"
Expand All @@ -36,14 +42,25 @@ crossbeam-channel = "0.5.6"
once_cell = "1.17.1"
im = "15.1.0"
im-rc = "15.1.0"
serde = { workspace = true, optional = true }
lapce-xi-rope = { workspace = true, optional = true }
strum = { workspace = true, optional = true }
strum_macros = { workspace = true, optional = true }
# TODO: once https://github.com/rust-lang/rust/issues/65991 is stabilized we don't need this
downcast-rs = { version = "1.2.0", optional = true }
parking_lot = { version = "0.12.1" }
floem_renderer = { path = "renderer", version = "0.1.0" }
floem_vger_renderer = { path = "vger", version = "0.1.0" }
floem_tiny_skia_renderer = { path = "tiny_skia", version = "0.1.0" }
floem_reactive = { path = "reactive", version = "0.1.0" }
floem-winit = { version = "0.29.4", features = ["rwh_05"] }
floem-editor-core = { path = "editor-core", version = "0.1.0", optional = true }
image = { version = "0.24", features = ["jpeg", "png"] }
copypasta = { version = "0.10.0", default-features = false, features = ["wayland", "x11"] }

[features]
serde = ["floem-winit/serde"]
default = ["editor"]
# TODO: this is only winit and the editor serde, there are other dependencies that still depend on
# serde
serde = ["floem-winit/serde", "dep:serde"]
editor = ["floem-editor-core", "dep:lapce-xi-rope", "dep:strum", "dep:strum_macros", "dep:downcast-rs"]
19 changes: 19 additions & 0 deletions editor-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "floem-editor-core"
version.workspace = true
edition = "2021"
repository = "https://github.com/lapce/floem"
license.workspace = true

[dependencies]
serde = { workspace = true, optional = true }
strum.workspace = true
strum_macros.workspace = true

lapce-xi-rope.workspace = true

itertools = "0.10.1"
bitflags = "1.3.2"

[features]
serde = ["dep:serde"]
242 changes: 242 additions & 0 deletions editor-core/src/buffer/diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
use std::{
borrow::Cow,
ops::Range,
sync::{
atomic::{self, AtomicU64},
Arc,
},
};

use lapce_xi_rope::Rope;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DiffResult<T> {
Left(T),
Both(T, T),
Right(T),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DiffBothInfo {
pub left: Range<usize>,
pub right: Range<usize>,
pub skip: Option<Range<usize>>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DiffLines {
Left(Range<usize>),
Both(DiffBothInfo),
Right(Range<usize>),
}

pub enum DiffExpand {
Up(usize),
Down(usize),
All,
}

pub fn expand_diff_lines(
diff_lines: &mut [DiffLines],
line: usize,
expand: DiffExpand,
is_right: bool,
) {
for diff_line in diff_lines.iter_mut() {
if let DiffLines::Both(info) = diff_line {
if (is_right && info.right.start == line) || (!is_right && info.left.start == line) {
match expand {
DiffExpand::All => {
info.skip = None;
}
DiffExpand::Up(n) => {
if let Some(skip) = &mut info.skip {
if n >= skip.len() {
info.skip = None;
} else {
skip.start += n;
}
}
}
DiffExpand::Down(n) => {
if let Some(skip) = &mut info.skip {
if n >= skip.len() {
info.skip = None;
} else {
skip.end -= n;
}
}
}
}
break;
}
}
}
}

pub fn rope_diff(
left_rope: Rope,
right_rope: Rope,
rev: u64,
atomic_rev: Arc<AtomicU64>,
context_lines: Option<usize>,
) -> Option<Vec<DiffLines>> {
let left_lines = left_rope.lines(..).collect::<Vec<Cow<str>>>();
let right_lines = right_rope.lines(..).collect::<Vec<Cow<str>>>();

let left_count = left_lines.len();
let right_count = right_lines.len();
let min_count = std::cmp::min(left_count, right_count);

let leading_equals = left_lines
.iter()
.zip(right_lines.iter())
.take_while(|p| p.0 == p.1)
.count();
let trailing_equals = left_lines
.iter()
.rev()
.zip(right_lines.iter().rev())
.take(min_count - leading_equals)
.take_while(|p| p.0 == p.1)
.count();

let left_diff_size = left_count - leading_equals - trailing_equals;
let right_diff_size = right_count - leading_equals - trailing_equals;

let table: Vec<Vec<u32>> = {
let mut table = vec![vec![0; right_diff_size + 1]; left_diff_size + 1];
let left_skip = left_lines.iter().skip(leading_equals).take(left_diff_size);
let right_skip = right_lines
.iter()
.skip(leading_equals)
.take(right_diff_size);

for (i, l) in left_skip.enumerate() {
for (j, r) in right_skip.clone().enumerate() {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return None;
}
table[i + 1][j + 1] = if l == r {
table[i][j] + 1
} else {
std::cmp::max(table[i][j + 1], table[i + 1][j])
};
}
}

table
};

let diff = {
let mut diff = Vec::with_capacity(left_diff_size + right_diff_size);
let mut i = left_diff_size;
let mut j = right_diff_size;
let mut li = left_lines.iter().rev().skip(trailing_equals);
let mut ri = right_lines.iter().skip(trailing_equals);

loop {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return None;
}
if j > 0 && (i == 0 || table[i][j] == table[i][j - 1]) {
j -= 1;
diff.push(DiffResult::Right(ri.next().unwrap()));
} else if i > 0 && (j == 0 || table[i][j] == table[i - 1][j]) {
i -= 1;
diff.push(DiffResult::Left(li.next().unwrap()));
} else if i > 0 && j > 0 {
i -= 1;
j -= 1;
diff.push(DiffResult::Both(li.next().unwrap(), ri.next().unwrap()));
} else {
break;
}
}

diff
};

let mut changes = Vec::new();
let mut left_line = 0;
let mut right_line = 0;
if leading_equals > 0 {
changes.push(DiffLines::Both(DiffBothInfo {
left: 0..leading_equals,
right: 0..leading_equals,
skip: None,
}))
}
left_line += leading_equals;
right_line += leading_equals;

for diff in diff.iter().rev() {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return None;
}
match diff {
DiffResult::Left(_) => {
match changes.last_mut() {
Some(DiffLines::Left(r)) => r.end = left_line + 1,
_ => changes.push(DiffLines::Left(left_line..left_line + 1)),
}
left_line += 1;
}
DiffResult::Both(_, _) => {
match changes.last_mut() {
Some(DiffLines::Both(info)) => {
info.left.end = left_line + 1;
info.right.end = right_line + 1;
}
_ => changes.push(DiffLines::Both(DiffBothInfo {
left: left_line..left_line + 1,
right: right_line..right_line + 1,
skip: None,
})),
}
left_line += 1;
right_line += 1;
}
DiffResult::Right(_) => {
match changes.last_mut() {
Some(DiffLines::Right(r)) => r.end = right_line + 1,
_ => changes.push(DiffLines::Right(right_line..right_line + 1)),
}
right_line += 1;
}
}
}

if trailing_equals > 0 {
changes.push(DiffLines::Both(DiffBothInfo {
left: left_count - trailing_equals..left_count,
right: right_count - trailing_equals..right_count,
skip: None,
}));
}
if let Some(context_lines) = context_lines {
if !changes.is_empty() {
let changes_last = changes.len() - 1;
for (i, change) in changes.iter_mut().enumerate() {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return None;
}
if let DiffLines::Both(info) = change {
if i == 0 || i == changes_last {
if info.right.len() > context_lines {
if i == 0 {
info.skip = Some(0..info.right.len() - context_lines);
} else {
info.skip = Some(context_lines..info.right.len());
}
}
} else if info.right.len() > context_lines * 2 {
info.skip = Some(context_lines..info.right.len() - context_lines);
}
}
}
}
}

Some(changes)
}
Loading

0 comments on commit d9a3fb8

Please sign in to comment.