From 8268d6812f341cbbad4d0e64a500a2d34a3a5f96 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 13 Nov 2024 14:45:43 +0100 Subject: [PATCH] Apply styling to RA predicate names --- .../compare-performance/RAPrettyPrinter.tsx | 97 ++++++++++++++----- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/extensions/ql-vscode/src/view/compare-performance/RAPrettyPrinter.tsx b/extensions/ql-vscode/src/view/compare-performance/RAPrettyPrinter.tsx index d7fae2a32..010523194 100644 --- a/extensions/ql-vscode/src/view/compare-performance/RAPrettyPrinter.tsx +++ b/extensions/ql-vscode/src/view/compare-performance/RAPrettyPrinter.tsx @@ -1,4 +1,5 @@ import { Fragment } from "react"; +import { styled } from "styled-components"; /** * A set of names, for generating unambiguous abbreviations. @@ -12,7 +13,7 @@ class NameSet { qnames .map((qname) => builder.visitQName(qname)) .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 { node: TrieNode; - abbreviate: () => React.ReactNode; + abbreviate: (isRoot?: boolean) => React.ReactNode; } class TrieBuilder { @@ -118,13 +119,21 @@ class TrieBuilder { } return { node: trieNode, - abbreviate: () => { + abbreviate: (isRoot = false) => { const result: React.ReactNode[] = []; if (prefix != null) { result.push(prefix.abbreviate()); 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({shortName}); + result.push(name.substring(hash)); + } else { + result.push(isRoot ? {name} : name); + } if (args != null) { result.push("<"); 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::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[] { const nameTokens = steps.flatMap((step) => 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) => { - const matches = Array.from(step.matchAll(nameTokenRegex)); const result: React.ReactNode[] = []; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - const before = step.slice( - i === 0 ? 0 : matches[i - 1].index + matches[i - 1][0].length, - match.index, - ); - result.push(before); - result.push(nameSet.getAbbreviation(match[0])); - } - result.push( - matches.length === 0 - ? step - : step.slice( - matches[matches.length - 1].index + - matches[matches.length - 1][0].length, - ), - ); + traverseMatches(step, nameTokenRegex, { + onMatch(match) { + const text = match[0]; + if (text.includes("::")) { + result.push({nameSet.getAbbreviation(text)}); + } else if (/[A-Z]+/.test(text)) { + result.push({text}); + } else { + result.push(match[0]); + } + }, + onText(text) { + result.push(text); + }, + }); return {result}; }); }