johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type { Chunk, FileView, Range } from '#lib/lexicons/dev/knotless/searchCode.ts';
import { repoHref, urlPath } from '#lib/format.ts';
import Avatar from '#lib/atoms/Avatar.svelte';
import { painted } from '#lib/highlight.ts';
/**
* Files a code search found, each in a box: whose repository and its
* path over the lines around what was found, each line's number linking
* to that line of the file, at the commit that was searched, and what
* was found marked, and its highlighting painted.
*/
let { files }: { files: FileView[] } = $props();
const encoder = new TextEncoder();
const decoder = new TextDecoder();
/** `line` in pieces, those `ranges` found marked: they count UTF-8 bytes. */
function pieces(line: string, ranges: Range[]): { text: string; found: boolean }[] {
const bytes = encoder.encode(line);
const out: { text: string; found: boolean }[] = [];
let at = 0;
for (const r of [...ranges].sort((a, b) => a.byteStart - b.byteStart)) {
const start = Math.max(at, r.byteStart);
if (start > at) out.push({ text: decoder.decode(bytes.slice(at, start)), found: false });
if (r.byteEnd > start)
out.push({ text: decoder.decode(bytes.slice(start, r.byteEnd)), found: true });
at = Math.max(at, r.byteEnd);
}
if (at < bytes.length) out.push({ text: decoder.decode(bytes.slice(at)), found: false });
return out;
}
/** A chunk's lines, by number, in pieces. */
const lines = (c: Chunk) =>
c.content.split('\n').map((text, i) => {
const n = c.startLine + i;
return {
n,
pieces: pieces(
text,
c.ranges.filter((r) => r.line === n)
)
};
});
/** The file `f`, at the commit searched, at line `n` if given. */
const href = (f: FileView, n?: number) =>
repoHref(
f.repo.owner?.handle ?? f.repo.did,
f.repo.name,
`/blob/${urlPath(f.commit ?? 'HEAD')}/${urlPath(f.path)}${n ? `#L${n}` : ''}`
);
</script>
<div class="code-results">
{#each files as f (f.repo.did + '/' + f.repo.name + '/' + f.path)}
{@const owner = f.repo.owner?.handle ?? f.repo.did}
<section class="box">
<header class="boxhead">
<Avatar name={owner} did={f.repo.did} src={f.repo.owner?.avatar} />
<span class="code-result-name"
><a class="mute" href={repoHref(owner, f.repo.name)}>{owner}/{f.repo.name}</a>
<span class="faint">·</span>
<a href={href(f)}
><b
>{#each f.path.split('/') as part, k (k)}{#if k}/<wbr />{/if}{part}{/each}</b
></a
></span
>
{#if f.language}<span class="code-result-lang faint">{f.language}</span>{/if}
</header>
{#if !f.chunks.length}<p class="code-result-path mute">Its path matches.</p>{/if}
{#each f.chunks as c, i (i)}
{@const shown = lines(c)}
<div class="code code-result">
<pre class="ln" aria-hidden="true">{#each shown as l (l.n)}<a
href={href(f, l.n)}
tabindex="-1">{l.n}</a
>{'\n'}{/each}</pre>
<pre class="src"><code use:painted={c.highlights}
>{#each shown as l (l.n)}{#each l.pieces as p, j (j)}{#if p.found}<mark>{p.text}</mark
>{:else}{p.text}{/if}{/each}{'\n'}{/each}</code
></pre>
</div>
{/each}
</section>
{/each}
</div>