Skip to content

Commit

Permalink
Create the concept of an "unsupported" file
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyagr committed Mar 10, 2024
1 parent e09fd35 commit 2f935ff
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 110 deletions.
45 changes: 31 additions & 14 deletions backend-local-server/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};

use walkdir::{DirEntry, WalkDir};

use crate::{DataInterface, DataReadError, DataSaveError, EntriesToCompare};
use crate::{DataInterface, DataReadError, DataSaveError, EntriesToCompare, FileEntry};

fn scan(root: &Path) -> impl Iterator<Item = Result<(DirEntry, String), DataReadError>> {
// As an alternative to WalkDir, see
Expand Down Expand Up @@ -120,23 +120,40 @@ pub fn fake_data() -> EntriesToCompare {
(
"edited_file",
[
Some("First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two"),
Some("First\nSecond\nThird\nFifth\nSixth\n----\none\n"),
FileEntry::Text(
"First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two"
.to_string(),
),
FileEntry::Text("First\nSecond\nThird\nFifth\nSixth\n----\none\n".to_string()),
],
),
(
"deleted_file",
[FileEntry::Text("deleted".to_string()), FileEntry::Missing],
),
(
"added file",
[FileEntry::Missing, FileEntry::Text("added".to_string())],
),
(
"unsupported-left",
[
FileEntry::Unsupported("demo of an unsupported file".to_string()),
FileEntry::Text("text".to_string()),
],
),
(
"unsupported-right",
[
FileEntry::Text("text".to_string()),
FileEntry::Unsupported("demo of an unsupported file".to_string()),
],
),
("deleted_file", [Some("deleted"), None]),
("added file", [None, Some("added")]),
];
let optstr = |opt: Option<&str>| opt.map(|s| s.to_string());
EntriesToCompare(
two_sides_map
.into_iter()
.map(|(key, [left, right])| {
(
PathBuf::from(key),
[optstr(left), optstr(right), optstr(right)],
)
})
.map(|(key, [left, right])| (PathBuf::from(key), [left, right.clone(), right]))
.collect(),
)
}
Expand All @@ -160,9 +177,9 @@ fn scan_several(roots: [&PathBuf; 3]) -> Result<EntriesToCompare, DataReadError>
)
}),
))
.or_insert(Default::default())
.or_insert([FileEntry::Missing, FileEntry::Missing, FileEntry::Missing])
.as_mut();
value[i] = Some(contents);
value[i] = FileEntry::Text(contents);
}
}
Ok(result)
Expand Down
13 changes: 11 additions & 2 deletions backend-local-server/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ use std::path::PathBuf;

use thiserror::Error;

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum FileEntry {
Missing,
// TODO: Track executable bit, other metadata perhaps
Text(String),
Unsupported(String),
}

/// TODO: Clean this up to make things more readable
const OUTPUT_INDEX: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
//struct EntriesToCompare<P, const N: usize>(std::collections::BTreeMap<P,
// [Option<String>; N]>);
pub struct EntriesToCompare(pub std::collections::BTreeMap<PathBuf, [Option<String>; 3]>);
pub struct EntriesToCompare(pub std::collections::BTreeMap<PathBuf, [FileEntry; 3]>);

#[derive(Error, Debug)]
pub enum DataSaveError {
Expand Down Expand Up @@ -114,7 +123,7 @@ impl DataInterface for EntriesToCompare {
self.0
.get_mut(&PathBuf::from(path))
.expect("At this point, `save()` should have verified that the path is valid")
[OUTPUT_INDEX] = Some(new_value);
[OUTPUT_INDEX] = FileEntry::Text(new_value);
}
Ok(())
}
Expand Down

Large diffs are not rendered by default.

20 changes: 16 additions & 4 deletions webapp/src/backend_interactions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { InvokeArgs, invoke as tauriInvoke } from "@tauri-apps/api/tauri";
import { exit as tauriExit } from "@tauri-apps/api/process";

type SingleFileMergeInput = {
left: string | null;
right: string | null;
edit: string | null;
export type FileEntry =
| { type: "Missing" }
| { type: "Text"; value: string }
| { type: "Unsupported"; value: string };
export type SingleFileMergeInput = {
left: FileEntry;
right: FileEntry;
edit: FileEntry;
};
export type MergeInput = Record<string, SingleFileMergeInput>;

export function to_text(file_entry: FileEntry): string | null {
if (file_entry.type == "Text") {
return file_entry.value;
} else {
return null;
}
}

// Tauri interop

// https://github.com/tauri-apps/tauri/discussions/6119
Expand Down
97 changes: 64 additions & 33 deletions webapp/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
exit_success,
TAURI_BACKEND,
exit_user_abandoned_merge,
SingleFileMergeInput,
to_text,
} from "./backend_interactions";

class MergeState {
Expand All @@ -24,6 +26,7 @@ class MergeState {
values(): Record<string, string> {
let result: Record<string, string> = {};
for (let k in this.merge_views) {
// TODO: Treat deleted values properly
result[k] = this.merge_views[k].editor().getValue();
}
return result;
Expand All @@ -39,37 +42,62 @@ class MergeState {
function render_input(unique_id: string, merge_input: MergeInput) {
let templates = [];
let k_uid = (k: string) => `${k}_${unique_id}`;
let to_error = (input: SingleFileMergeInput) => {
let unsupported_value = Array.from([
{ file: input.left, side: "left" },
{ file: input.right, side: "right" },
{ file: input.edit, side: "middle" },
]).find((v) => v.file.type == "Unsupported");
if (unsupported_value == null) {
return null;
} else if (unsupported_value.file.type != "Unsupported") {
throw new Error("this statement is unreachable; this check exists to make TS happy");
}
return html`<b>error</b>: ${unsupported_value.file.value} (occurred on the
${unsupported_value.side} side)`;
};

for (let k in merge_input) {
templates.push(html`
<details open id="details_${k_uid(k)}">
<!-- We will close this with javascript shortly. See below. -->
<summary>
<code>${k}</code>
<button id="collapse_${k_uid(k)}" hidden>
(Un)Collapse (Doesn't work)
</button>
<button
id="prevChange_${k_uid(k)}"
alt="Previous Change"
title="Previous Change"
>
⇧ Previous Change
</button>
<button
id="nextChange_${k_uid(k)}"
alt="Next Change"
title="Next Change"
>
⇩ Next Change
</button>
<button id="linewrap_${k_uid(k)}" hidden>
<!--Buggy with collapseIdentical, see comment below -->
(Un)Wrap Lines
</button>
</summary>
<div id="cm_${k_uid(k)}"></div>
</details>
`);
let error = to_error(merge_input[k]);
if (error != null) {
templates.push(html` <details id="details_${k_uid(k)}">
<summary><code>${k}</code>: ${error}</summary>
<!-- TODO: Allow inserting error details here, perhaps grey out the triangle
-- if there are no details.
-->
</details>`);
} else {
templates.push(html`
<details open id="details_${k_uid(k)}">
<!-- We will close this with javascript shortly. See below. -->
<summary>
<code>${k}</code>
<button id="collapse_${k_uid(k)}" hidden>
(Un)Collapse (Doesn't work)
</button>
<button
id="prevChange_${k_uid(k)}"
alt="Previous Change"
title="Previous Change"
>
⇧ Previous Change
</button>
<button
id="nextChange_${k_uid(k)}"
alt="Next Change"
title="Next Change"
>
⇩ Next Change
</button>
<button id="linewrap_${k_uid(k)}" hidden>
<!--Buggy with collapseIdentical, see comment below -->
(Un)Wrap Lines
</button>
</summary>
<div id="cm_${k_uid(k)}"></div>
</details>
`);
}
}

let target_element = document.getElementById(unique_id)!;
Expand All @@ -78,6 +106,9 @@ function render_input(unique_id: string, merge_input: MergeInput) {

let merge_views: Record<string, MergeView> = {};
for (let k in merge_input) {
if (to_error(merge_input[k]) != null) {
continue;
}
let collapseButtonEl = document.getElementById(`collapse_${k_uid(k)}`)!;
let linewrapButtonEl = document.getElementById(`linewrap_${k_uid(k)}`)!;
let prevChangeButtonEl = document.getElementById(`prevChange_${k_uid(k)}`)!;
Expand All @@ -88,9 +119,9 @@ function render_input(unique_id: string, merge_input: MergeInput) {
let cmEl = document.getElementById(`cm_${k_uid(k)}`)!;

let config = {
value: merge_input[k].edit ?? "",
origLeft: merge_input[k].left ?? "", // Set to null for 2 panes
orig: merge_input[k].right ?? "",
value: to_text(merge_input[k].edit) ?? "",
origLeft: to_text(merge_input[k].left) ?? "", // Set to null for 2 panes
orig: to_text(merge_input[k].right) ?? "",
lineNumbers: true,
/* TODO: Toggling line wrapping breaks `collapseIdentical`. Need a
settings system where the user can decide whether they want line wrapping,
Expand Down

0 comments on commit 2f935ff

Please sign in to comment.