Skip to content

Visualize scope tests in docs #3016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 96 commits into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
5f05193
Started working on scope visualizer for docs page
AndreasArvidsson Jul 4, 2025
404e9ce
Create separate code component
AndreasArvidsson Jul 4, 2025
13aae13
Update example
AndreasArvidsson Jul 4, 2025
b293f03
Working on scripts to prepare assets
AndreasArvidsson Jul 4, 2025
c612f3b
Working on script
AndreasArvidsson Jul 4, 2025
280941c
Script updated
AndreasArvidsson Jul 4, 2025
6d1a3cd
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 4, 2025
ca336f4
More styling
AndreasArvidsson Jul 5, 2025
a04835b
Use bundled theme
AndreasArvidsson Jul 5, 2025
d9ff419
render whitespace
AndreasArvidsson Jul 5, 2025
09ca29a
Restore build command
AndreasArvidsson Jul 5, 2025
4382cfd
Remove unused import
AndreasArvidsson Jul 5, 2025
714cc29
mata updater
AndreasArvidsson Jul 5, 2025
3b99137
fix lint issues
AndreasArvidsson Jul 5, 2025
7e20eec
Add new line at the end of scope tests json
AndreasArvidsson Jul 5, 2025
dc559b8
Better rendering off whitespace
AndreasArvidsson Jul 5, 2025
e463bc1
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 5, 2025
8440a37
Handle overlapping ranges
AndreasArvidsson Jul 5, 2025
d0803ac
Handle multiple targets per scope
AndreasArvidsson Jul 5, 2025
bafdca7
Better handling of conflict ranges
AndreasArvidsson Jul 5, 2025
1bb2a54
another range fix
AndreasArvidsson Jul 5, 2025
afc1a9c
Support imported languages
AndreasArvidsson Jul 5, 2025
4e35d71
Render fixtures grouped by scope and facet
AndreasArvidsson Jul 5, 2025
edad3e9
General cleanup
AndreasArvidsson Jul 5, 2025
5f7477a
Fix import problems
AndreasArvidsson Jul 5, 2025
5f16e72
lint fixes
AndreasArvidsson Jul 5, 2025
71b94f8
Fix imports
AndreasArvidsson Jul 5, 2025
31c6ce1
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 5, 2025
ed2f5d4
Remove range label
AndreasArvidsson Jul 5, 2025
a4ca516
Remove insertion delimiter
AndreasArvidsson Jul 5, 2025
a1f6526
Added link buttons
AndreasArvidsson Jul 5, 2025
0d7905b
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 5, 2025
0d29277
Added plain text language
AndreasArvidsson Jul 5, 2025
43eb317
Add changelog
AndreasArvidsson Jul 5, 2025
eaab731
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 5, 2025
47036d1
Merge with main
AndreasArvidsson Jul 5, 2025
9a8acc9
Rename range selection
AndreasArvidsson Jul 5, 2025
a610bd6
Use scope tests plugin
AndreasArvidsson Jul 6, 2025
178d905
Remove old scope support
AndreasArvidsson Jul 6, 2025
5713f2f
Show all facets on missing language copes
AndreasArvidsson Jul 6, 2025
6a1090f
Update plugin types
AndreasArvidsson Jul 6, 2025
a217520
Update scope link icon
AndreasArvidsson Jul 6, 2025
9c23e86
Added dynamic table of content
AndreasArvidsson Jul 6, 2025
3f4b417
Fix scrolling to anchor offset
AndreasArvidsson Jul 6, 2025
2525896
Clean up dynamic table of content
AndreasArvidsson Jul 6, 2025
f79e922
Made tos level optional
AndreasArvidsson Jul 6, 2025
772e447
Created header components
AndreasArvidsson Jul 6, 2025
bc1a8b1
Clean up
AndreasArvidsson Jul 6, 2025
5ba5c34
Made facet name a header
AndreasArvidsson Jul 6, 2025
a1d19d3
Added leading numbers to facets
AndreasArvidsson Jul 6, 2025
a3a7b62
Support replacing existing table of content
AndreasArvidsson Jul 6, 2025
40e071a
separate internal scopes
AndreasArvidsson Jul 6, 2025
ee69491
Refactor utilities
AndreasArvidsson Jul 6, 2025
beb38fb
Sort ranges before calculating highlights
AndreasArvidsson Jul 6, 2025
6b8a2ea
Extract calculate highlights into separate file
AndreasArvidsson Jul 6, 2025
aac06b1
Added link to scope fixture on GitHub
AndreasArvidsson Jul 6, 2025
a580d78
Added context menu to scope visualizer sidebar to open in browser
AndreasArvidsson Jul 6, 2025
a6d5ddd
Open Cursorless org webpage as default if no url is provided
AndreasArvidsson Jul 6, 2025
c5070f2
Fix test
AndreasArvidsson Jul 6, 2025
5fd68e6
Added tests
AndreasArvidsson Jul 6, 2025
9d30802
Remove trailing/from tests
AndreasArvidsson Jul 6, 2025
f7f3cb5
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 6, 2025
873054f
Added contribution scopes page to compare scopes between languages
AndreasArvidsson Jul 6, 2025
2ec36b9
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 7, 2025
ab1fa1e
Removed private prefixed scope files
AndreasArvidsson Jul 7, 2025
9a69fff
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 7, 2025
c1e570e
Merge branch 'main' into scopeVisualizerDocs
AndreasArvidsson Jul 7, 2025
08efde7
Renamed header value attribute to id
AndreasArvidsson Jul 7, 2025
5a760b5
Rename field
AndreasArvidsson Jul 7, 2025
399b618
Refactor and reuse code from vscode scope visualizer
AndreasArvidsson Jul 8, 2025
a2774e6
Conflict debugging
AndreasArvidsson Jul 8, 2025
5799a8e
Blend colors
AndreasArvidsson Jul 8, 2025
cc91fe7
Clean up
AndreasArvidsson Jul 8, 2025
8a4f085
combined background color
AndreasArvidsson Jul 8, 2025
62282b5
Border radius
AndreasArvidsson Jul 8, 2025
1f007b3
Lint cleanup
AndreasArvidsson Jul 8, 2025
dc528fb
Some more clean up
AndreasArvidsson Jul 8, 2025
3c2723a
Patch shiki core
AndreasArvidsson Jul 8, 2025
ab70df3
Better patch
AndreasArvidsson Jul 8, 2025
827eac7
Refactor
AndreasArvidsson Jul 8, 2025
8a79159
More clean up
AndreasArvidsson Jul 8, 2025
008a5a5
Update imports
AndreasArvidsson Jul 9, 2025
7b423d1
Added flatten highlights test
AndreasArvidsson Jul 9, 2025
7ce775f
rename
AndreasArvidsson Jul 9, 2025
255754f
Better filtering of highlights
AndreasArvidsson Jul 9, 2025
53653db
Update comment
AndreasArvidsson Jul 9, 2025
b6de450
Fix unused argument
AndreasArvidsson Jul 9, 2025
e1f2128
Update test
AndreasArvidsson Jul 9, 2025
3397085
Update test
AndreasArvidsson Jul 9, 2025
73fa84f
Made scroll to hash more robust
AndreasArvidsson Jul 9, 2025
33673b8
Rename hash component
AndreasArvidsson Jul 9, 2025
f9b04d4
Remove separate lists were domain and nested ranges are equal
AndreasArvidsson Jul 9, 2025
2980d43
Improved handling of empty ranges
AndreasArvidsson Jul 9, 2025
e435c42
Simplify calculate highlights
AndreasArvidsson Jul 9, 2025
63877b3
more clean up
AndreasArvidsson Jul 9, 2025
0a5f73a
More improvements to empty ranges
AndreasArvidsson Jul 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/2025-07-scopeDocumentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
tags: [documentation]
pullRequest: 3016
---

- Visualize scope tests in docs. Visualizes scope fixtures on [cursorless.org/docs/user/languages](https://www.cursorless.org/docs/user/languages).
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
},
"pnpm": {
"patchedDependencies": {
"@shikijs/core": "patches/@shikijs__core.patch",
"@types/[email protected]": "patches/@[email protected]",
"[email protected]": "patches/[email protected]"
}
Expand Down
3 changes: 3 additions & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@
"watch": "pnpm run --filter @cursorless/common --parallel '/^watch:.*/'"
},
"dependencies": {
"itertools": "2.4.1",
"lodash-es": "4.17.21",
"tinycolor2": "1.6.0",
"vscode-uri": "3.1.0"
},
"devDependencies": {
"@types/js-yaml": "4.0.9",
"@types/lodash-es": "4.17.12",
"@types/mocha": "10.0.10",
"@types/tinycolor2": "1.4.6",
"cross-spawn": "7.0.6",
"fast-check": "4.1.1",
"js-yaml": "4.1.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/cursorlessCommandIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const cursorlessCommandIds = [
"cursorless.toggleDecorations",
"cursorless.showScopeVisualizer",
"cursorless.hideScopeVisualizer",
"cursorless.scopeVisualizer.openUrl",
"cursorless.tutorial.start",
"cursorless.tutorial.next",
"cursorless.tutorial.previous",
Expand Down Expand Up @@ -100,6 +101,7 @@ export const cursorlessCommandDescriptions: Record<
["cursorless.hideScopeVisualizer"]: new VisibleCommand(
"Hide the scope visualizer",
),
["cursorless.scopeVisualizer.openUrl"]: new VisibleCommand("Open in browser"),
["cursorless.analyzeCommandHistory"]: new VisibleCommand(
"Analyze collected command history",
),
Expand Down
6 changes: 5 additions & 1 deletion packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export * from "./scopeSupportFacets/languageScopeSupport";
export * from "./scopeSupportFacets/PlaintextScopeSupportFacetInfos";
export * from "./scopeSupportFacets/scopeSupportFacetInfos";
export * from "./scopeSupportFacets/scopeSupportFacets.types";
export * from "./scopeVisualizerUtil/decorationStyle.types";
export * from "./scopeVisualizerUtil/decorationUtil";
export * from "./scopeVisualizerUtil/generateDecorationsForCharacterRange";
export * from "./scopeVisualizerUtil/generateDecorationsForLineRange";
export * from "./StoredTargetKey";
export * from "./testUtil/asyncSafety";
export * from "./testUtil/extractTargetedMarks";
Expand Down Expand Up @@ -93,6 +97,7 @@ export * from "./types/Token";
export * from "./types/TreeSitter";
export * from "./types/tutorial.types";
export * from "./util";
export * from "./util/blendColors";
export * from "./util/clientSupportsFallback";
export * from "./util/CompositeKeyDefaultMap";
export * from "./util/CompositeKeyMap";
Expand All @@ -105,7 +110,6 @@ export * from "./util/Notifier";
export * from "./util/object";
export * from "./util/omitByDeep";
export * from "./util/prettifyLanguageName";
export * from "./util/range";
export * from "./util/regex";
export * from "./util/selectionsEqual";
export * from "./util/serializedMarksToTokenHats";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Range } from "../types/Range";

export interface StyledRange {
style: DecorationStyle;
range: Range;
}

export interface DecorationStyle {
top: BorderStyle;
bottom: BorderStyle;
left: BorderStyle;
right: BorderStyle;
isWholeLine?: boolean;
}

export enum BorderStyle {
porous = "dashed",
solid = "solid",
none = "none",
}
48 changes: 48 additions & 0 deletions packages/common/src/scopeVisualizerUtil/decorationUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { BorderStyle } from "./decorationStyle.types";
import type { DecorationStyle } from "./decorationStyle.types";

export const BORDER_WIDTH = "1px";
export const BORDER_RADIUS = "2px";

export function getBorderStyle(borders: DecorationStyle): string {
return [borders.top, borders.right, borders.bottom, borders.left].join(" ");
}

export function getBorderColor(
solidColor: string,
porousColor: string,
borders: DecorationStyle,
): string {
return [
borders.top === BorderStyle.solid ? solidColor : porousColor,
borders.right === BorderStyle.solid ? solidColor : porousColor,
borders.bottom === BorderStyle.solid ? solidColor : porousColor,
borders.left === BorderStyle.solid ? solidColor : porousColor,
].join(" ");
}

export function getBorderRadius(borders: DecorationStyle): string {
return [
getSingleCornerBorderRadius(borders.top, borders.left),
getSingleCornerBorderRadius(borders.top, borders.right),
getSingleCornerBorderRadius(borders.bottom, borders.right),
getSingleCornerBorderRadius(borders.bottom, borders.left),
].join(" ");
}

export function useSingleCornerBorderRadius(
side1: BorderStyle,
side2: BorderStyle,
): boolean {
// We only round the corners if both sides are solid, as that makes them look
// more finished, whereas we want the dotted borders to look unfinished / cut
// off.
return side1 === BorderStyle.solid && side2 === BorderStyle.solid;
}

export function getSingleCornerBorderRadius(
side1: BorderStyle,
side2: BorderStyle,
) {
return useSingleCornerBorderRadius(side1, side2) ? BORDER_RADIUS : "0px";
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Range, TextEditor } from "@cursorless/common";
import { getLineRanges } from "@cursorless/common";
import type { Range } from "../../types/Range";
import type { StyledRange } from "../decorationStyle.types";
import { BorderStyle } from "../decorationStyle.types";
import { handleMultipleLines } from "./handleMultipleLines";
Expand All @@ -10,7 +9,7 @@ import { handleMultipleLines } from "./handleMultipleLines";
* that the range is visually distinct from adjacent ranges but looks continuous.
*/
export function* generateDecorationsForCharacterRange(
editor: TextEditor,
getLineRanges: (range: Range) => Range[],
range: Range,
): Iterable<StyledRange> {
if (range.isSingleLine) {
Expand All @@ -26,5 +25,15 @@ export function* generateDecorationsForCharacterRange(
return;
}

yield* handleMultipleLines(getLineRanges(editor, range));
// A list of ranges, one for each line in the given range, with the first and
// last ranges trimmed to the start and end of the given range.
const lineRanges = getLineRanges(range);

lineRanges[0] = lineRanges[0].with(range.start);
lineRanges[lineRanges.length - 1] = lineRanges[lineRanges.length - 1].with(
undefined,
range.end,
);

yield* handleMultipleLines(lineRanges);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Range } from "@cursorless/common";
import type { Range } from "../..";

/**
* Generates a line info for each line in the given range, which includes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import assert from "assert";
import { map } from "itertools";
import { Range } from "../..";
import { BorderStyle } from "../decorationStyle.types";
import { handleMultipleLines } from "./handleMultipleLines";
import { Range } from "@cursorless/common";
import { map } from "itertools";

const solid = BorderStyle.solid;
const porous = BorderStyle.porous;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Range } from "@cursorless/common";
import { flatmap } from "itertools";
import { Range } from "../..";
import type { DecorationStyle, StyledRange } from "../decorationStyle.types";
import { BorderStyle } from "../decorationStyle.types";
import { flatmap } from "itertools";
import type { LineInfo } from "./generateLineInfos";
import { generateLineInfos } from "./generateLineInfos";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Range } from "@cursorless/common";
import type { StyledRange } from "./decorationStyle.types";
import { BorderStyle } from "./decorationStyle.types";
import { Range } from "../types/Range";
import { BorderStyle, type StyledRange } from "./decorationStyle.types";

export function* generateDecorationsForLineRange(
startLine: number,
Expand Down
40 changes: 40 additions & 0 deletions packages/common/src/util/blendColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import tinycolor from "tinycolor2";

/**
* Blends two colors together according to their alpha channels, with the top
* color rendered on top of the base color.
*
* Basd on https://gist.github.com/JordanDelcros/518396da1c13f75ee057
*
* @param base The color to render underneath
* @param top The color to render on top
* @returns A color that is a blend of the two colors, with the top color
* rendered on top of the base color
*/
export function blendColors(base: string, top: string): string {
const baseRgba = tinycolor(base).toRgb();
const topRgba = tinycolor(top).toRgb();
const blendedAlpha = 1 - (1 - topRgba.a) * (1 - baseRgba.a);

function interpolateChannel(channel: "r" | "g" | "b"): number {
return Math.round(
(topRgba[channel] * topRgba.a) / blendedAlpha +
(baseRgba[channel] * baseRgba.a * (1 - topRgba.a)) / blendedAlpha,
);
}

return tinycolor({
r: interpolateChannel("r"),
g: interpolateChannel("g"),
b: interpolateChannel("b"),
a: blendedAlpha,
}).toHex8String();
}

export function blendMultipleColors(colors: string[]): string {
let color = colors[0];
for (let i = 1; i < colors.length; i++) {
color = blendColors(color, colors[i]);
}
return color;
}
22 changes: 0 additions & 22 deletions packages/common/src/util/range.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/cursorless-neovim/src/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export async function registerCommands(
// Scope visualizer
["cursorless.showScopeVisualizer"]: dummyCommandHandler,
["cursorless.hideScopeVisualizer"]: dummyCommandHandler,
["cursorless.scopeVisualizer.openUrl"]: dummyCommandHandler,

// Command history
["cursorless.analyzeCommandHistory"]: dummyCommandHandler,
Expand Down
5 changes: 4 additions & 1 deletion packages/cursorless-org-docs/docusaurus.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ const config: Config = {
},
],
],
plugins: ["./src/plugins/tailwind-plugin.ts"],
plugins: [
"./src/plugins/tailwind-plugin.ts",
"./src/plugins/scope-tests-plugin.ts",
],

themeConfig: {
navbar: {
Expand Down
2 changes: 2 additions & 0 deletions packages/cursorless-org-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@
"prism-react-renderer": "2.4.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"shiki": "3.7.0",
"unist-util-visit": "5.0.0"
},
"devDependencies": {
"@cursorless/node-common": "workspace:*",
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/types": "3.8.1",
"@tailwindcss/postcss": "4.1.10",
Expand Down
4 changes: 0 additions & 4 deletions packages/cursorless-org-docs/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,3 @@
.pointer {
cursor: pointer;
}

.facet-name {
font-weight: 600;
}
66 changes: 66 additions & 0 deletions packages/cursorless-org-docs/src/docs/components/Code.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.code-container {
position: relative;
width: 100%;
min-height: 50px;
counter-reset: line;
}

.code-container .shiki {
line-height: 1rem;
}

.code-container .line {
position: relative;
margin-left: 1.5rem;
}

.code-container .line::before {
content: counter(line);
counter-increment: line;
position: absolute;
left: -40px;
padding-left: 0.5em;
padding-right: 0.5em;
width: 1rem;
text-align: right;
height: 135%;
color: rgba(115, 138, 148, 0.4);
border-right: 1px solid rgba(115, 138, 148, 0.4);
}

.code-ws-symbol {
color: #666;
}

.code-copy-button,
.code-container > .code-link {
display: none;
position: absolute;
top: 0.5em;
background-color: rgb(216, 222, 233);
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 0.75rem;
height: 1.75rem;
}

.code-container:hover .code-copy-button,
.code-container:hover .code-link {
display: block;
}

.code-copy-button {
right: 7.5em;
}

.code-container > .code-link {
right: 0.5em;
}

.code-container > .code-link:before {
display: inline-block;
width: 1.25rem;
height: 0.75rem;
}
Loading
Loading