Skip to content

Commit 0ea521a

Browse files
Added tutorial to docs (#3341)
1 parent acec040 commit 0ea521a

363 files changed

Lines changed: 235575 additions & 535 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.oxfmtrc.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
},
99

1010
"ignorePatterns": [
11-
"/packages/app-web-docs/src/docs/contributing/private-scopes/*.mdx",
1211
"/packages/app-web-docs/src/docs/user/actions/*.mdx",
13-
"/packages/app-web-docs/src/docs/user/languages/*.mdx",
1412
"/packages/app-web-docs/src/docs/user/modifiers/*.mdx",
1513
"/packages/app-web-docs/src/docs/user/scopes/*.mdx",
14+
"/packages/app-web-docs/src/docs/user/languages/*.mdx",
15+
"/packages/app-web-docs/src/docs/user/tutorial/*.mdx",
16+
"/packages/app-web-docs/src/docs/contributing/private-scopes/*.mdx",
17+
"/packages/app-web-docs/src/docs/**/fixtures/*.json",
1618
"/packages/app-vscode/src/keyboard/grammar/generated/",
1719
"/packages/lib-engine/src/customCommandGrammar/generated/",
1820
"/packages/lib-engine/src/snippets/vendor/",

‎packages/app-vscode/src/ide/vscode/VscodeEnabledHatStyleManager.ts‎

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export type ExtendedHatStyleMap = Partial<
1818
Record<VscodeHatStyleName, ExtendedHatStyleInfo>
1919
>;
2020

21+
interface Settings {
22+
shapeEnablement: Record<HatShape, boolean>;
23+
colorEnablement: Record<HatColor, boolean>;
24+
shapePenalties: Record<HatShape, number>;
25+
colorPenalties: Record<HatColor, number>;
26+
maxPenalty: number;
27+
}
28+
2129
/**
2230
* Keeps track of the available hat styles, along with their associated color
2331
* and shape identifiers, and penalties. Note that this class is not
@@ -52,32 +60,25 @@ export class VscodeEnabledHatStyleManager {
5260
}
5361

5462
private constructHatStyleMap() {
55-
const shapeEnablement = vscode.workspace
56-
.getConfiguration("cursorless.hatEnablement")
57-
.get<Record<HatShape, boolean>>("shapes")!;
58-
const colorEnablement = vscode.workspace
59-
.getConfiguration("cursorless.hatEnablement")
60-
.get<Record<HatColor, boolean>>("colors")!;
61-
const shapePenalties = vscode.workspace
62-
.getConfiguration("cursorless.hatPenalties")
63-
.get<Record<HatShape, number>>("shapes")!;
64-
const colorPenalties = vscode.workspace
65-
.getConfiguration("cursorless.hatPenalties")
66-
.get<Record<HatColor, number>>("colors")!;
67-
const maxPenalty = vscode.workspace
68-
.getConfiguration("cursorless")
69-
.get<number>("maximumHatStylePenalty")!;
63+
const {
64+
shapeEnablement,
65+
colorEnablement,
66+
shapePenalties,
67+
colorPenalties,
68+
maxPenalty,
69+
} =
70+
this.extensionContext.extensionMode === vscode.ExtensionMode.Test
71+
? this.getDefaultSettings()
72+
: this.getSettings();
7073

7174
shapeEnablement.default = true;
7275
colorEnablement.default = true;
7376
shapePenalties.default = 0;
7477
colorPenalties.default = 0;
7578

76-
// So that unit tests don't fail locally if you have some colors disabled
77-
const activeHatColors =
78-
this.extensionContext.extensionMode === vscode.ExtensionMode.Test
79-
? HAT_COLORS.filter((color) => !color.startsWith("user"))
80-
: HAT_COLORS.filter((color) => colorEnablement[color]);
79+
const activeHatColors = HAT_COLORS.filter(
80+
(color) => colorEnablement[color],
81+
);
8182
const activeNonDefaultHatShapes = HAT_NON_DEFAULT_SHAPES.filter(
8283
(shape) => shapeEnablement[shape],
8384
);
@@ -101,7 +102,7 @@ export class VscodeEnabledHatStyleManager {
101102
]),
102103
),
103104
),
104-
} as ExtendedHatStyleMap;
105+
};
105106

106107
if (maxPenalty > 0) {
107108
this.hatStyleMap = pickBy(
@@ -110,4 +111,85 @@ export class VscodeEnabledHatStyleManager {
110111
);
111112
}
112113
}
114+
115+
private getSettings(): Settings {
116+
const shapeEnablement = vscode.workspace
117+
.getConfiguration("cursorless.hatEnablement")
118+
.get<Record<HatShape, boolean>>("shapes")!;
119+
const colorEnablement = vscode.workspace
120+
.getConfiguration("cursorless.hatEnablement")
121+
.get<Record<HatColor, boolean>>("colors")!;
122+
const shapePenalties = vscode.workspace
123+
.getConfiguration("cursorless.hatPenalties")
124+
.get<Record<HatShape, number>>("shapes")!;
125+
const colorPenalties = vscode.workspace
126+
.getConfiguration("cursorless.hatPenalties")
127+
.get<Record<HatColor, number>>("colors")!;
128+
const maxPenalty = vscode.workspace
129+
.getConfiguration("cursorless")
130+
.get<number>("maximumHatStylePenalty")!;
131+
return {
132+
shapeEnablement,
133+
colorEnablement,
134+
shapePenalties,
135+
colorPenalties,
136+
maxPenalty,
137+
};
138+
}
139+
140+
private getDefaultSettings(): Settings {
141+
return {
142+
shapeEnablement: {
143+
default: true,
144+
bolt: false,
145+
curve: false,
146+
fox: false,
147+
frame: false,
148+
play: false,
149+
wing: false,
150+
hole: false,
151+
ex: false,
152+
crosshairs: false,
153+
eye: false,
154+
},
155+
colorEnablement: {
156+
default: true,
157+
blue: true,
158+
green: true,
159+
red: true,
160+
pink: true,
161+
yellow: true,
162+
userColor1: false,
163+
userColor2: false,
164+
userColor3: false,
165+
userColor4: false,
166+
},
167+
shapePenalties: {
168+
default: 1,
169+
bolt: 1,
170+
curve: 1,
171+
fox: 1,
172+
frame: 1,
173+
play: 1,
174+
wing: 1,
175+
hole: 1,
176+
ex: 1,
177+
crosshairs: 1,
178+
eye: 1,
179+
},
180+
colorPenalties: {
181+
default: 1,
182+
blue: 1,
183+
green: 1,
184+
red: 1,
185+
pink: 1,
186+
yellow: 1,
187+
userColor1: 1,
188+
userColor2: 1,
189+
userColor3: 1,
190+
userColor4: 1,
191+
},
192+
maxPenalty: 0,
193+
};
194+
}
113195
}

‎packages/app-web-docs/docusaurus.config.mts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,7 @@ const config: Config = {
165165
],
166166
],
167167

168-
plugins: [
169-
"docusaurus-plugin-sass",
170-
"./src/plugins/recorded-tests-plugin.ts",
171-
"./src/plugins/scope-tests-plugin.ts",
172-
],
168+
plugins: ["docusaurus-plugin-sass"],
173169

174170
themeConfig: {
175171
navbar: {

‎packages/app-web-docs/src/docs/components/Code.css‎

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,8 @@
161161
background-color: #e5c02c;
162162
}
163163

164-
/* Code hat referenced */
164+
/* Code mark referenced */
165165

166-
.code-hat-referenced::before {
167-
animation: code-hat-referenced-pulse 2s ease-in-out infinite;
168-
}
169-
170-
@keyframes code-hat-referenced-pulse {
171-
50% {
172-
background-color: var(--code-hat-referenced-color);
173-
}
174-
}
175-
176-
@media (prefers-reduced-motion: reduce) {
177-
.code-hat-referenced::before {
178-
background-color: var(--code-hat-referenced-color);
179-
animation: none;
180-
}
166+
.code-mark {
167+
background-color: var(--code-mark-color);
181168
}

‎packages/app-web-docs/src/docs/components/RecordedTestVisualizer.tsx‎

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { usePluginData } from "@docusaurus/useGlobalData";
21
import type { Dispatch, JSX, ReactNode, SetStateAction } from "react";
32
import { createContext, useContext, useMemo, useState } from "react";
43
import type { DecorationItem } from "shiki";
54
import type {
65
Position,
6+
Range,
77
Selection,
88
TestCaseSnapshot,
99
} from "@cursorless/lib-common";
@@ -25,14 +25,15 @@ const RecordedTestVisualizerContext = createContext<
2525
RecordedTestVisualizerContextValue | undefined
2626
>(undefined);
2727

28+
interface ProviderProps {
29+
children: ReactNode;
30+
recordedTests: RecordedTest[];
31+
}
32+
2833
export function RecordedTestVisualizerProvider({
2934
children,
30-
}: {
31-
children: ReactNode;
32-
}) {
33-
const recordedTests = usePluginData(
34-
"recorded-tests-plugin",
35-
) as RecordedTest[];
35+
recordedTests,
36+
}: ProviderProps): JSX.Element {
3637
const [renderWhitespace, setRenderWhitespace] = useState(true);
3738
const fixtures = useMemo(
3839
() =>
@@ -57,7 +58,7 @@ export function RecordedTestVisualizerProvider({
5758
);
5859
}
5960

60-
export function RecordedTestVisualizerOptions() {
61+
export function RecordedTestVisualizerOptions(): JSX.Element {
6162
const { renderWhitespace, setRenderWhitespace } = useRecordedTestVisualizer();
6263

6364
return (
@@ -99,7 +100,7 @@ export function RecordedTestVisualizer({
99100
// oxlint-disable-next-line react_perf/jsx-no-new-object-as-prop
100101
const link = {
101102
name: "GitHub",
102-
url: `https://github.com/cursorless-dev/cursorless/blob/main/resources/fixtures/recorded/docs/${path}`,
103+
url: `https://github.com/cursorless-dev/cursorless/blob/main/resources/fixtures/recorded/${path}`,
103104
};
104105

105106
const renderHats =
@@ -109,7 +110,7 @@ export function RecordedTestVisualizer({
109110
return (
110111
<div className="row">
111112
<div className="col">
112-
Input
113+
Before
113114
<CodeState
114115
renderWhitespace={renderWhitespace}
115116
renderHats={renderHats}
@@ -119,7 +120,7 @@ export function RecordedTestVisualizer({
119120
/>
120121
</div>
121122
<div className="col">
122-
Output
123+
After
123124
<CodeState
124125
renderWhitespace={renderWhitespace}
125126
renderHats={renderHats}
@@ -204,16 +205,21 @@ function toDecorations(
204205
plainObjectToRange(hatRange),
205206
)
206207
: [];
208+
const markRanges = renderHats
209+
? Object.values(state.marks ?? {}).map(plainObjectToRange)
210+
: [];
207211

208212
// Shiki rejects intersecting decorations. A zero-width cursor at the end of
209-
// a hat range intersects the hat decoration, so render both on one wrapper.
210-
// We only merge at the end because code-cursor-after recreates that exact
211-
// boundary without competing with the hat's ::before pseudo-element.
213+
// a hat or mark range intersects that decoration, so render both on one
214+
// wrapper. We only merge at the end because code-cursor-after recreates that
215+
// exact boundary without competing with the hat's ::before pseudo-element.
212216
const mergedCursorPositions = selections
213217
.filter(
214218
(selection) =>
215219
selection.isEmpty &&
216-
hatRanges.some(({ end }) => end.isEqual(selection.active)),
220+
[...hatRanges, ...markRanges].some(({ end }) =>
221+
end.isEqual(selection.active),
222+
),
217223
)
218224
.map(({ active }) => active);
219225

@@ -228,10 +234,46 @@ function toDecorations(
228234
),
229235
)
230236
.map(toDecoration),
237+
...toMarkDecorations(markRanges, hatRanges, mergedCursorPositions),
231238
...toHatDecorations(state, renderHats, mergedCursorPositions),
232239
];
233240
}
234241

242+
function toMarkDecorations(
243+
markRanges: readonly Range[],
244+
hatRanges: readonly Range[],
245+
mergedCursorPositions: readonly Position[],
246+
): DecorationItem[] {
247+
return markRanges
248+
.filter(
249+
(range, index) =>
250+
markRanges.findIndex((otherRange) => otherRange.isRangeEqual(range)) ===
251+
index,
252+
)
253+
.map((range) => {
254+
const className = ["code-mark"];
255+
256+
// Prefer placing the cursor on the smaller hat wrapper when both the hat
257+
// and its containing mark end at the cursor position.
258+
if (
259+
mergedCursorPositions.some((position) => position.isEqual(range.end)) &&
260+
!hatRanges.some(({ end }) => end.isEqual(range.end))
261+
) {
262+
className.push("code-cursor-after");
263+
}
264+
265+
return {
266+
start: range.start,
267+
end: range.end,
268+
alwaysWrap: true,
269+
properties: {
270+
className,
271+
style: `--code-mark-color: ${highlightColors.content.background};`,
272+
},
273+
};
274+
});
275+
}
276+
235277
function toHatDecorations(
236278
state: TestCaseSnapshot,
237279
renderHats: boolean,
@@ -241,23 +283,12 @@ function toHatDecorations(
241283
return [];
242284
}
243285

244-
const markRanges = Object.values(state.marks ?? {}).map(plainObjectToRange);
245-
246286
return state.hatTokenMap.map(({ hatStyle, hatRange }) => {
247287
const range = plainObjectToRange(hatRange);
248288
const properties: DecorationItem["properties"] = {
249289
className: ["code-hat", `code-hat-${hatStyle}`],
250290
};
251291

252-
const isReferenced = markRanges.some((markRange) =>
253-
markRange.contains(range),
254-
);
255-
256-
if (isReferenced) {
257-
properties.className?.push("code-hat-referenced");
258-
properties.style = `--code-hat-referenced-color: ${highlightColors.content.background};`;
259-
}
260-
261292
if (mergedCursorPositions.some((position) => position.isEqual(range.end))) {
262293
// The hat uses ::before and the cursor uses ::after, allowing both
263294
// visuals to share this wrapper without overlapping Shiki decorations.

0 commit comments

Comments
 (0)