Apply styling to RA predicate names

This commit is contained in:
Asger F
2024-11-13 14:45:43 +01:00
parent 70ec5704c8
commit 8268d6812f

View File

@@ -1,4 +1,5 @@
import { Fragment } from "react"; import { Fragment } from "react";
import { styled } from "styled-components";
/** /**
* A set of names, for generating unambiguous abbreviations. * A set of names, for generating unambiguous abbreviations.
@@ -12,7 +13,7 @@ class NameSet {
qnames qnames
.map((qname) => builder.visitQName(qname)) .map((qname) => builder.visitQName(qname))
.forEach((r, index) => { .forEach((r, index) => {
this.abbreviations.set(names[index], r.abbreviate()); this.abbreviations.set(names[index], r.abbreviate(true));
}); });
} }
@@ -86,7 +87,7 @@ class TrieNode {
interface VisitResult { interface VisitResult {
node: TrieNode; node: TrieNode;
abbreviate: () => React.ReactNode; abbreviate: (isRoot?: boolean) => React.ReactNode;
} }
class TrieBuilder { class TrieBuilder {
@@ -118,13 +119,21 @@ class TrieBuilder {
} }
return { return {
node: trieNode, node: trieNode,
abbreviate: () => { abbreviate: (isRoot = false) => {
const result: React.ReactNode[] = []; const result: React.ReactNode[] = [];
if (prefix != null) { if (prefix != null) {
result.push(prefix.abbreviate()); result.push(prefix.abbreviate());
result.push("::"); result.push("::");
} }
result.push(qname.name); const { name } = qname;
const hash = name.indexOf("#");
if (hash !== -1 && isRoot) {
const shortName = name.substring(0, hash);
result.push(<IdentifierSpan>{shortName}</IdentifierSpan>);
result.push(name.substring(hash));
} else {
result.push(isRoot ? <IdentifierSpan>{name}</IdentifierSpan> : name);
}
if (args != null) { if (args != null) {
result.push("<"); result.push("<");
if (trieNodeBeforeArgs.children.size === 1) { if (trieNodeBeforeArgs.children.size === 1) {
@@ -148,33 +157,73 @@ class TrieBuilder {
} }
} }
const nameTokenRegex = /\b[^ ]+::[^ (]+\b/g; /**
* Span enclosing an entire qualified name.
*
* Can be used to gray out uninteresting parts of the name, though this looks worse than expected.
*/
const QNameSpan = styled.span`
/* color: var(--vscode-disabledForeground); */
`;
/** Span enclosing the innermost identifier, e.g. the `foo` in `A::B<X>::foo#abc` */
const IdentifierSpan = styled.span`
font-weight: 600;
`;
/** Span enclosing keywords such as `JOIN` and `WITH`. */
const KeywordSpan = styled.span`
font-weight: 500;
`;
const nameTokenRegex = /\b[^ (]+\b/g;
function traverseMatches(
text: string,
regex: RegExp,
callbacks: {
onMatch: (match: RegExpMatchArray) => void;
onText: (text: string) => void;
},
) {
const matches = Array.from(text.matchAll(regex));
let lastIndex = 0;
for (const match of matches) {
const before = text.substring(lastIndex, match.index);
if (before !== "") {
callbacks.onText(before);
}
callbacks.onMatch(match);
lastIndex = match.index + match[0].length;
}
const after = text.substring(lastIndex);
if (after !== "") {
callbacks.onText(after);
}
}
export function abbreviateRASteps(steps: string[]): React.ReactNode[] { export function abbreviateRASteps(steps: string[]): React.ReactNode[] {
const nameTokens = steps.flatMap((step) => const nameTokens = steps.flatMap((step) =>
Array.from(step.matchAll(nameTokenRegex)).map((tok) => tok[0]), Array.from(step.matchAll(nameTokenRegex)).map((tok) => tok[0]),
); );
const nameSet = new NameSet(nameTokens); const nameSet = new NameSet(nameTokens.filter((name) => name.includes("::")));
return steps.map((step, index) => { return steps.map((step, index) => {
const matches = Array.from(step.matchAll(nameTokenRegex));
const result: React.ReactNode[] = []; const result: React.ReactNode[] = [];
for (let i = 0; i < matches.length; i++) { traverseMatches(step, nameTokenRegex, {
const match = matches[i]; onMatch(match) {
const before = step.slice( const text = match[0];
i === 0 ? 0 : matches[i - 1].index + matches[i - 1][0].length, if (text.includes("::")) {
match.index, result.push(<QNameSpan>{nameSet.getAbbreviation(text)}</QNameSpan>);
); } else if (/[A-Z]+/.test(text)) {
result.push(before); result.push(<KeywordSpan>{text}</KeywordSpan>);
result.push(nameSet.getAbbreviation(match[0])); } else {
} result.push(match[0]);
result.push( }
matches.length === 0 },
? step onText(text) {
: step.slice( result.push(text);
matches[matches.length - 1].index + },
matches[matches.length - 1][0].length, });
),
);
return <Fragment key={index}>{result}</Fragment>; return <Fragment key={index}>{result}</Fragment>;
}); });
} }