-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #60 from TheBestTvarynka/diff-checker
Diff checker: Initial implementation
- Loading branch information
Showing
11 changed files
with
426 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
.diff-changes-container { | ||
display: flex; | ||
flex-direction: row; | ||
gap: 0; | ||
justify-content: flex-start; | ||
align-items: center; | ||
width: 100%; | ||
flex-wrap: wrap; | ||
//background-color: #edd5ce; | ||
//border-radius: 0.2em; | ||
//padding: 0.5em; | ||
} | ||
|
||
.diff-insert { | ||
white-space: pre-wrap; | ||
background-color: #97e0bd; | ||
color: #016939; | ||
} | ||
|
||
.diff-remove { | ||
white-space: pre-wrap; | ||
background-color: #d38693; | ||
color: #9d0620; | ||
} | ||
|
||
.diff-new-line { | ||
height: 0; | ||
flex-basis: 100%; | ||
} | ||
|
||
.diff-line-number { | ||
background-color: #edd5ce; | ||
padding-right: 0.3em; | ||
padding-left: 0.3em; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
mod diff_algo; | ||
mod diff_viewer; | ||
|
||
use similar::{capture_diff_slices, Algorithm, DiffOp, TextDiff}; | ||
use web_sys::{HtmlInputElement, KeyboardEvent}; | ||
use yew::html::onchange::Event; | ||
use yew::virtual_dom::VNode; | ||
use yew::{classes, function_component, html, use_effect_with_deps, use_state, Callback, Html, TargetCast}; | ||
use yew_hooks::use_local_storage; | ||
|
||
use self::diff_algo::DiffAlgo; | ||
use self::diff_viewer::DiffViewer; | ||
|
||
const DEFAULT_ORIGINAL: &str = "TheBestTvarynka | ||
TheBestTvarynka | ||
TheBestTvarynka"; | ||
const DEFAULT_CHANGED: &str = "thebesttravynka | ||
thebesttravynka | ||
thebesttravynka | ||
"; | ||
const DEFAULT_ALGORITHM: DiffAlgo = DiffAlgo(Algorithm::Myers); | ||
|
||
const LOCAL_STORAGE_ORIGINAL: &str = "ORIGINAL_DATA"; | ||
const LOCAL_STORAGE_ALGORITHM: &str = "ALGORITHM"; | ||
const LOCAL_STORAGE_CHANGED: &str = "CHANGED_DATA"; | ||
|
||
const ALL_ALGORITHMS: &[DiffAlgo] = &[ | ||
DiffAlgo(Algorithm::Myers), | ||
DiffAlgo(Algorithm::Lcs), | ||
DiffAlgo(Algorithm::Patience), | ||
]; | ||
|
||
#[derive(Debug, Clone, PartialEq)] | ||
struct DiffData { | ||
pub original: Vec<char>, | ||
pub changed: Vec<char>, | ||
pub changes: Vec<DiffOp>, | ||
} | ||
|
||
impl DiffData { | ||
pub fn empty() -> Self { | ||
Self { | ||
original: Vec::new(), | ||
changed: Vec::new(), | ||
changes: Vec::new(), | ||
} | ||
} | ||
} | ||
|
||
fn render_algorithm_options(current_algorithm: DiffAlgo) -> Vec<VNode> { | ||
ALL_ALGORITHMS | ||
.iter() | ||
.map(|algo| { | ||
html! { | ||
<option selected={current_algorithm == *algo} value={algo.to_string()}>{algo}</option> | ||
} | ||
}) | ||
.collect() | ||
} | ||
|
||
#[function_component(DiffPage)] | ||
pub fn diff_page() -> Html { | ||
let original = use_state(|| DEFAULT_ORIGINAL.to_owned()); | ||
let changed = use_state(|| DEFAULT_CHANGED.to_owned()); | ||
let algorithm = use_state(|| DEFAULT_ALGORITHM); | ||
let diffs = use_state(|| { | ||
let original = DEFAULT_ORIGINAL.chars().collect::<Vec<_>>(); | ||
let changed = DEFAULT_CHANGED.chars().collect::<Vec<_>>(); | ||
let changes = TextDiff::configure() | ||
.algorithm(DEFAULT_ALGORITHM.into()) | ||
.newline_terminated(true) | ||
.diff_chars(DEFAULT_ORIGINAL, DEFAULT_CHANGED); | ||
|
||
DiffData { | ||
original, | ||
changed, | ||
changes: changes.ops().to_owned(), | ||
} | ||
}); | ||
|
||
let original_data = original.chars().collect::<Vec<_>>(); | ||
let changed_data = changed.chars().collect::<Vec<_>>(); | ||
let diffs_setter = diffs.setter(); | ||
let algo = *algorithm; | ||
let compute_diff = Callback::from(move |_: ()| { | ||
let changes = capture_diff_slices(algo.into(), &original_data, &changed_data); | ||
|
||
diffs_setter.set(DiffData { | ||
original: original_data.clone(), | ||
changed: changed_data.clone(), | ||
changes, | ||
}); | ||
}); | ||
|
||
let original_local_storage = use_local_storage::<String>(LOCAL_STORAGE_ORIGINAL.to_owned()); | ||
let original_setter = original.setter(); | ||
let changed_local_storage = use_local_storage::<String>(LOCAL_STORAGE_CHANGED.to_owned()); | ||
let changed_setter = changed.setter(); | ||
let algorithm_local_storage = use_local_storage::<String>(LOCAL_STORAGE_ALGORITHM.to_owned()); | ||
let algorithm_setter = algorithm.setter(); | ||
let diffs_setter = diffs.setter(); | ||
use_effect_with_deps( | ||
move |_: &[(); 0]| { | ||
let mut flag = false; | ||
|
||
if let Some(original) = (*original_local_storage).as_ref() { | ||
original_setter.set(original.to_string()); | ||
flag = true; | ||
} | ||
if let Some(changed) = (*changed_local_storage).as_ref() { | ||
changed_setter.set(changed.to_string()); | ||
flag = true; | ||
} | ||
if let Some(raw_algorithm) = (*algorithm_local_storage).as_ref() { | ||
if let Ok(algorithm) = raw_algorithm.as_str().try_into() { | ||
algorithm_setter.set(algorithm); | ||
flag = true; | ||
} | ||
} | ||
|
||
if flag { | ||
diffs_setter.set(DiffData::empty()); | ||
} | ||
}, | ||
[], | ||
); | ||
|
||
let local_storage = use_local_storage::<String>(LOCAL_STORAGE_ORIGINAL.to_owned()); | ||
use_effect_with_deps( | ||
move |[original]| { | ||
local_storage.set((*original).to_string()); | ||
}, | ||
[original.clone()], | ||
); | ||
|
||
let local_storage = use_local_storage::<String>(LOCAL_STORAGE_CHANGED.to_owned()); | ||
use_effect_with_deps( | ||
move |[changed]| { | ||
local_storage.set((*changed).to_string()); | ||
}, | ||
[changed.clone()], | ||
); | ||
|
||
let local_storage = use_local_storage::<String>(LOCAL_STORAGE_ALGORITHM.to_owned()); | ||
use_effect_with_deps( | ||
move |[algorithm]| { | ||
local_storage.set((*algorithm).to_string()); | ||
}, | ||
[algorithm.clone()], | ||
); | ||
|
||
let original_setter = original.setter(); | ||
let on_original_input = Callback::from(move |event: html::oninput::Event| { | ||
let input: HtmlInputElement = event.target_unchecked_into(); | ||
original_setter.set(input.value()); | ||
}); | ||
|
||
let changed_setter = changed.setter(); | ||
let on_changed_input = Callback::from(move |event: html::oninput::Event| { | ||
let input: HtmlInputElement = event.target_unchecked_into(); | ||
changed_setter.set(input.value()); | ||
}); | ||
|
||
let diff = compute_diff.clone(); | ||
let onclick = Callback::from(move |_| { | ||
diff.emit(()); | ||
}); | ||
|
||
let algorithm_setter = algorithm.setter(); | ||
let on_algorithm_change = Callback::from(move |event: Event| { | ||
let input: HtmlInputElement = event.target_unchecked_into(); | ||
if let Ok(algorithm) = input.value().as_str().try_into() { | ||
algorithm_setter.set(algorithm); | ||
} | ||
}); | ||
|
||
let onkeydown = Callback::from(move |event: KeyboardEvent| { | ||
if event.ctrl_key() && event.code() == "Enter" { | ||
compute_diff.emit(()); | ||
} | ||
}); | ||
|
||
html! { | ||
<div class={classes!("vertical", "asn1-page")} {onkeydown}> | ||
<div class="horizontal"> | ||
<span>{"Diff algorithm:"}</span> | ||
<div> | ||
<select class="base-input" onchange={on_algorithm_change}> | ||
{render_algorithm_options(*algorithm)} | ||
</select> | ||
</div> | ||
</div> | ||
<div class="horizontal"> | ||
<textarea | ||
rows="8" | ||
placeholder={"original"} | ||
class="base-input" | ||
value={(*original).clone()} | ||
oninput={on_original_input} | ||
/> | ||
<textarea | ||
rows="8" | ||
placeholder={"changed"} | ||
class="base-input" | ||
value={(*changed).clone()} | ||
oninput={on_changed_input} | ||
/> | ||
</div> | ||
<div class="horizontal"> | ||
<button class="action-button" onclick={onclick}>{"Diff"}</button> | ||
<span class="total">{"(ctrl+enter)"}</span> | ||
</div> | ||
<DiffViewer diff={(*diffs).clone()} /> | ||
</div> | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use std::fmt::{Display, Formatter}; | ||
use std::ops::Deref; | ||
|
||
use similar::Algorithm; | ||
|
||
const MYERS: &str = "Myers"; | ||
const PATIENCE: &str = "Patience"; | ||
const LCS: &str = "Lcs"; | ||
|
||
#[derive(Clone, Copy, Eq, PartialEq)] | ||
pub struct DiffAlgo(pub Algorithm); | ||
|
||
impl From<DiffAlgo> for Algorithm { | ||
fn from(value: DiffAlgo) -> Self { | ||
value.0 | ||
} | ||
} | ||
|
||
impl From<Algorithm> for DiffAlgo { | ||
fn from(value: Algorithm) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
impl Display for DiffAlgo { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
f.write_str(match self.0 { | ||
Algorithm::Myers => MYERS, | ||
Algorithm::Patience => PATIENCE, | ||
Algorithm::Lcs => LCS, | ||
}) | ||
} | ||
} | ||
|
||
impl Deref for DiffAlgo { | ||
type Target = Algorithm; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl TryFrom<&str> for DiffAlgo { | ||
type Error = String; | ||
|
||
fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
Ok(match value { | ||
MYERS => Algorithm::Myers.into(), | ||
PATIENCE => Algorithm::Patience.into(), | ||
LCS => Algorithm::Lcs.into(), | ||
_ => return Err(format!("Unsupported diff algorithm: {}.", value)), | ||
}) | ||
} | ||
} |
Oops, something went wrong.