~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
65 lines · 2.4 KB Raw History
// A filter of pulls and issues, in the words knotless-serve reads
// (crates/knotless/src/query.rs): `label:kind/bug -label:area/web
// author:alice.test crash`. The pages change it a term at a time.

/** `q` split on spaces, except within double quotes, each term as written. */
export function terms(q: string): string[] {
	return q.match(/(?:[^\s"]+|"[^"]*"?)+/g) ?? [];
}

/** A term's key and its value, its quotes taken off. */
function parts(term: string): [string, string] {
	const at = term.indexOf(':');
	return at < 0 ? ['', term] : [term.slice(0, at), term.slice(at + 1).replaceAll('"', '')];
}

const stateTerm = /^(?:state|is):(?:open|closed|merged)$/;

/** Whether `q` asks for the closed (or merged) ones, else the open, `state` if it says neither. */
export function closed(q: string, state?: string | null): boolean {
	const said = terms(q).findLast((t) => stateTerm.test(t));
	return said ? !said.endsWith(':open') : state === 'closed';
}

/** `q` asking for the open ones or the closed. */
export function withState(q: string, state: 'open' | 'closed'): string {
	return [`state:${state}`, ...terms(q).filter((t) => !stateTerm.test(t))].join(' ');
}

/** Whether `q` has a `key:` term of `value`. */
export function has(q: string, key: string, value: string): boolean {
	return terms(q).some((t) => {
		const [k, v] = parts(t);
		return k === key && v === value;
	});
}

/** `q` with a `key:` term of `value`, as it is if it has one. */
export function withTerm(q: string, key: string, value: string): string {
	if (has(q, key, value)) return q;
	return [...terms(q), `${key}:${/\s/.test(value) ? `"${value}"` : value}`].join(' ');
}

/** `q` with its `key:` term of `value` taken off, or put on if it has none. */
export function toggled(q: string, key: string, value: string): string {
	if (!has(q, key, value)) return withTerm(q, key, value);
	return terms(q)
		.filter((t) => {
			const [k, v] = parts(t);
			return k !== key || v !== value;
		})
		.join(' ');
}

/** Whether `q` filters by more than state. */
export function filtered(q: string): boolean {
	return terms(q).some((t) => !stateTerm.test(t));
}

/** The address of the list `q` filters, from the list's own. */
export function filterHref(q: string, cursor?: string): string {
	const params = new URLSearchParams();
	if (q.trim()) params.set('q', q.trim());
	if (cursor) params.set('cursor', cursor);
	return `?${params}`;
}