Merged origin/master into keyboard-shortcuts

This commit is contained in:
estib 2024-09-12 09:13:39 +02:00
commit 5352eb1717
7 changed files with 74 additions and 25 deletions

View File

@ -1,7 +1,9 @@
<script lang="ts"> <script lang="ts">
/* eslint svelte/valid-compile: "off" */ /* eslint svelte/valid-compile: "off" */
/* - Required because spreading in prop destructuring still throws eslint errors */
import { renderers } from '$lib/utils/markdownRenderers'; import { renderers } from '$lib/utils/markdownRenderers';
import type { Tokens, Token } from 'marked'; import type { Tokens, Token } from 'marked';
import type { Component } from 'svelte';
type Props = type Props =
| { type: 'init'; tokens: Token[] } | { type: 'init'; tokens: Token[] }
@ -16,34 +18,33 @@
| Tokens.ListItem | Tokens.ListItem
| Tokens.List; | Tokens.List;
let { type, ...rest }: Props = $props(); const { type, ...rest }: Props = $props();
// @ts-expect-error indexing on string union is having trouble const CurrentComponent = renderers[type] as Component<Props>;
const CurrentComponent = renderers[type as Props['type']];
</script> </script>
{#if type && CurrentComponent} {#if (!type || type === 'init') && 'tokens' in rest && rest.tokens}
{#each rest.tokens as token}
<svelte:self {...token} />
{/each}
{:else if renderers[type]}
{#if type === 'list'} {#if type === 'list'}
{@const listItems = (rest as Extract<Props, { type: typeof type }>).items} {@const listItems = (rest as Extract<Props, { type: 'list' }>).items}
<CurrentComponent {...rest}> <CurrentComponent {...rest}>
{#each listItems as item} {#each listItems as item}
{@const ChildComponent = renderers[item.type]} {@const ChildComponent = renderers[item.type]}
<ChildComponent {...item}> <ChildComponent {...item}>
<svelte:self tokens={item.tokens} {renderers} /> <svelte:self tokens={item.tokens} />
</ChildComponent> </ChildComponent>
{/each} {/each}
</CurrentComponent> </CurrentComponent>
{:else} {:else}
<CurrentComponent {...rest}> <CurrentComponent this={renderers[type]} {...rest}>
{#if 'tokens' in rest} {#if 'tokens' in rest && rest.tokens}
<svelte:self tokens={rest.tokens} /> <svelte:self tokens={rest.tokens} />
{:else if 'raw' in rest}
{rest.raw}
{/if} {/if}
</CurrentComponent> </CurrentComponent>
{/if} {/if}
{:else if 'tokens' in rest && rest.tokens}
{#each rest.tokens as token}
<svelte:self {...token} />
{/each}
{:else if 'raw' in rest}
{@html rest.raw?.replaceAll('\n', '') ?? ''}
{/if} {/if}

View File

@ -0,0 +1,9 @@
<script lang="ts">
interface Props {
raw: string;
}
const { raw }: Props = $props();
</script>
{@html raw}

View File

@ -1,11 +1,13 @@
<script lang="ts"> <script lang="ts">
import { type Snippet } from 'svelte';
interface Props { interface Props {
text: string; children: Snippet;
} }
const { text }: Props = $props(); const { children }: Props = $props();
</script> </script>
<span> <span>
{text} {@render children()}
</span> </span>

View File

@ -2,6 +2,7 @@ import Blockquote from '$lib/components/markdownRenderers/Blockquote.svelte';
import Code from '$lib/components/markdownRenderers/Code.svelte'; import Code from '$lib/components/markdownRenderers/Code.svelte';
import Codespan from '$lib/components/markdownRenderers/Codespan.svelte'; import Codespan from '$lib/components/markdownRenderers/Codespan.svelte';
import Heading from '$lib/components/markdownRenderers/Heading.svelte'; import Heading from '$lib/components/markdownRenderers/Heading.svelte';
import Html from '$lib/components/markdownRenderers/Html.svelte';
import Image from '$lib/components/markdownRenderers/Image.svelte'; import Image from '$lib/components/markdownRenderers/Image.svelte';
import List from '$lib/components/markdownRenderers/List.svelte'; import List from '$lib/components/markdownRenderers/List.svelte';
import ListItem from '$lib/components/markdownRenderers/ListItem.svelte'; import ListItem from '$lib/components/markdownRenderers/ListItem.svelte';
@ -16,10 +17,12 @@ export const renderers = {
code: Code, code: Code,
codespan: Codespan, codespan: Codespan,
text: Text, text: Text,
html: Html,
list: List, list: List,
list_item: ListItem, list_item: ListItem,
heading: Heading, heading: Heading,
paragraph: Paragraph paragraph: Paragraph,
init: null
}; };
export const options = { export const options = {

View File

@ -159,7 +159,7 @@ pub fn unapply_ownership(
}, },
)?; )?;
let final_tree_oid = gitbutler_diff::write::hunks_onto_tree(ctx, &final_tree, diff)?; let final_tree_oid = gitbutler_diff::write::hunks_onto_tree(ctx, &final_tree, diff, true)?;
let final_tree = repo let final_tree = repo
.find_tree(final_tree_oid) .find_tree(final_tree_oid)
.context("failed to find tree")?; .context("failed to find tree")?;

View File

@ -78,7 +78,7 @@ impl GitHunk {
new_lines: 0, new_lines: 0,
diff_lines: Default::default(), diff_lines: Default::default(),
binary: false, binary: false,
change_type: ChangeType::Modified, change_type: ChangeType::Added,
} }
} }
} }
@ -387,6 +387,11 @@ fn reverse_patch(patch: &BStr) -> Option<BString> {
// returns `None` if the reversal failed // returns `None` if the reversal failed
pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> { pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
let new_change_type = match hunk.change_type {
ChangeType::Added => ChangeType::Deleted,
ChangeType::Deleted => ChangeType::Added,
ChangeType::Modified => ChangeType::Modified,
};
if hunk.binary { if hunk.binary {
None None
} else { } else {
@ -397,7 +402,7 @@ pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
new_lines: hunk.old_lines, new_lines: hunk.old_lines,
diff_lines: diff.into(), diff_lines: diff.into(),
binary: hunk.binary, binary: hunk.binary,
change_type: hunk.change_type, change_type: new_change_type,
}) })
} }
} }

View File

@ -1,6 +1,6 @@
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
use std::os::unix::prelude::PermissionsExt; use std::os::unix::prelude::PermissionsExt;
use std::{borrow::Borrow, path::PathBuf}; use std::{borrow::Borrow, fs, path::PathBuf};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use bstr::{BString, ByteSlice, ByteVec}; use bstr::{BString, ByteSlice, ByteVec};
@ -38,13 +38,14 @@ where
let head_commit = git_repository.find_commit(commit_oid)?; let head_commit = git_repository.find_commit(commit_oid)?;
let base_tree = head_commit.tree()?; let base_tree = head_commit.tree()?;
hunks_onto_tree(ctx, &base_tree, files) hunks_onto_tree(ctx, &base_tree, files, false)
} }
pub fn hunks_onto_tree<T>( pub fn hunks_onto_tree<T>(
ctx: &CommandContext, ctx: &CommandContext,
base_tree: &git2::Tree, base_tree: &git2::Tree,
files: impl IntoIterator<Item = (impl Borrow<PathBuf>, impl Borrow<Vec<T>>)>, files: impl IntoIterator<Item = (impl Borrow<PathBuf>, impl Borrow<Vec<T>>)>,
allow_new_file: bool,
) -> Result<git2::Oid> ) -> Result<git2::Oid>
where where
T: Into<GitHunk> + Clone, T: Into<GitHunk> + Clone,
@ -62,7 +63,21 @@ where
&& hunks[0].diff_lines.contains_str(b"Subproject commit"); && hunks[0].diff_lines.contains_str(b"Subproject commit");
// if file exists // if file exists
if full_path.exists() { let full_path_exists = full_path.exists();
let discard_hunk = (hunks.len() == 1).then(|| &hunks[0]);
if full_path_exists || allow_new_file {
if discard_hunk.map_or(false, |hunk| hunk.change_type == crate::ChangeType::Deleted) {
// File was created but now that hunk is being discarded with an inversed hunk
builder.remove(rel_path);
fs::remove_file(full_path.clone()).or_else(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
Ok(())
} else {
Err(err)
}
})?;
continue;
}
// if file is executable, use 755, otherwise 644 // if file is executable, use 755, otherwise 644
let mut filemode = git2::FileMode::Blob; let mut filemode = git2::FileMode::Blob;
// check if full_path file is executable // check if full_path file is executable
@ -115,7 +130,7 @@ where
)?; )?;
builder.upsert(rel_path, blob_oid, filemode); builder.upsert(rel_path, blob_oid, filemode);
} else if let Ok(tree_entry) = base_tree.get_path(rel_path) { } else if let Ok(tree_entry) = base_tree.get_path(rel_path) {
if hunks.len() == 1 && hunks[0].binary { if discard_hunk.map_or(false, |hunk| hunk.binary) {
let new_blob_oid = &hunks[0].diff_lines; let new_blob_oid = &hunks[0].diff_lines;
// convert string to Oid // convert string to Oid
let new_blob_oid = new_blob_oid let new_blob_oid = new_blob_oid
@ -178,6 +193,20 @@ where
let new_blob_oid = git_repository.blob(blob_contents.as_bytes())?; let new_blob_oid = git_repository.blob(blob_contents.as_bytes())?;
// upsert into the builder // upsert into the builder
builder.upsert(rel_path, new_blob_oid, filemode); builder.upsert(rel_path, new_blob_oid, filemode);
} else if !full_path_exists
&& discard_hunk.map_or(false, |hunk| hunk.change_type == crate::ChangeType::Added)
{
// File was deleted but now that hunk is being discarded with an inversed hunk
let mut all_diffs = BString::default();
for hunk in hunks {
all_diffs.push_str(&hunk.diff_lines);
}
let patch = Patch::from_bytes(&all_diffs)?;
let blob_contents =
apply([], &patch).context(format!("failed to apply {}", all_diffs))?;
let new_blob_oid = git_repository.blob(&blob_contents)?;
builder.upsert(rel_path, new_blob_oid, filemode);
} else { } else {
// create a git blob from a file on disk // create a git blob from a file on disk
let blob_oid = git_repository let blob_oid = git_repository