Skip to content

Commit 7b21c46

Browse files
potetojbrown215
andauthored
[forgive] Refactor inferred deps (#32999)
Refactor. Co-authored-by: Jordan Brown <[email protected]> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32999). * #33002 * #33001 * #33000 * __->__ #32999 Co-authored-by: Jordan Brown <[email protected]>
1 parent e25e8c7 commit 7b21c46

File tree

10 files changed

+196
-66
lines changed

10 files changed

+196
-66
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export type LoggerEvent =
183183
| CompileSkipEvent
184184
| PipelineErrorEvent
185185
| TimingEvent
186-
| AutoDepsDecorations;
186+
| AutoDepsDecorationsEvent;
187187

188188
export type CompileErrorEvent = {
189189
kind: 'CompileError';
@@ -220,7 +220,7 @@ export type TimingEvent = {
220220
kind: 'Timing';
221221
measurement: PerformanceMeasure;
222222
};
223-
export type AutoDepsDecorations = {
223+
export type AutoDepsDecorationsEvent = {
224224
kind: 'AutoDepsDecorations';
225225
useEffectCallExpr: t.SourceLocation;
226226
decorations: Array<t.SourceLocation>;

compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,10 @@ function collectDepUsages(
380380

381381
for (const [, block] of fnExpr.loweredFunc.func.body.blocks) {
382382
for (const instr of block.instructions) {
383-
if (instr.value.kind === 'LoadLocal') {
383+
if (
384+
instr.value.kind === 'LoadLocal' &&
385+
identifiers.has(instr.value.place.identifier.id)
386+
) {
384387
loadedDeps.add(instr.lvalue.identifier.id);
385388
}
386389
for (const place of eachInstructionOperand(instr)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
type RGB = [number, number, number];
2+
3+
const int = Math.floor;
4+
5+
export class Color {
6+
constructor(
7+
private r: number,
8+
private g: number,
9+
private b: number,
10+
) {}
11+
12+
toAlphaString(a: number) {
13+
return this.toCssString(a);
14+
}
15+
toString() {
16+
return this.toCssString(1);
17+
}
18+
19+
/**
20+
* Adjust the color by a multiplier to lighten (`> 1.0`) or darken (`< 1.0`) the color. Returns a new
21+
* instance.
22+
*/
23+
adjusted(mult: number) {
24+
const adjusted = Color.redistribute([
25+
this.r * mult,
26+
this.g * mult,
27+
this.b * mult,
28+
]);
29+
return new Color(...adjusted);
30+
}
31+
32+
private toCssString(a: number) {
33+
return `rgba(${this.r},${this.g},${this.b},${a})`;
34+
}
35+
/**
36+
* Redistributes rgb, maintaing hue until its clamped.
37+
* https://stackoverflow.com/a/141943
38+
*/
39+
private static redistribute([r, g, b]: RGB): RGB {
40+
const threshold = 255.999;
41+
const max = Math.max(r, g, b);
42+
if (max <= threshold) {
43+
return [int(r), int(g), int(b)];
44+
}
45+
const total = r + g + b;
46+
if (total >= 3 * threshold) {
47+
return [int(threshold), int(threshold), int(threshold)];
48+
}
49+
const x = (3 * threshold - total) / (3 * max - total);
50+
const gray = threshold - x * max;
51+
return [int(gray + x * r), int(gray + x * g), int(gray + x * b)];
52+
}
53+
}
54+
55+
export const BLACK = new Color(0, 0, 0);
56+
export const WHITE = new Color(255, 255, 255);
57+
58+
const COLOR_POOL = [
59+
new Color(249, 65, 68),
60+
new Color(243, 114, 44),
61+
new Color(248, 150, 30),
62+
new Color(249, 132, 74),
63+
new Color(249, 199, 79),
64+
new Color(144, 190, 109),
65+
new Color(67, 170, 139),
66+
new Color(77, 144, 142),
67+
new Color(87, 117, 144),
68+
new Color(39, 125, 161),
69+
];
70+
71+
export function getColorFor(index: number): Color {
72+
return COLOR_POOL[Math.abs(index) % COLOR_POOL.length]!;
73+
}

compiler/packages/react-forgive/client/src/extension.ts

+43-17
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,30 @@ import * as vscode from 'vscode';
44
import {
55
LanguageClient,
66
LanguageClientOptions,
7-
Position,
7+
type Position,
8+
RequestType,
89
ServerOptions,
910
TransportKind,
1011
} from 'vscode-languageclient/node';
12+
import {WHITE} from './colors';
1113

1214
let client: LanguageClient;
15+
const inferredEffectDepDecoration =
16+
vscode.window.createTextEditorDecorationType({
17+
backgroundColor: WHITE.toAlphaString(0.3),
18+
});
19+
20+
type Range = [Position, Position];
21+
interface AutoDepsDecorationsParams {
22+
position: Position;
23+
}
24+
namespace AutoDepsDecorationsRequest {
25+
export const type = new RequestType<
26+
AutoDepsDecorationsParams,
27+
Array<Range> | null,
28+
void
29+
>('react/autodeps_decorations');
30+
}
1331

1432
export function activate(context: vscode.ExtensionContext) {
1533
const serverModule = context.asAbsolutePath(path.join('dist', 'server.js'));
@@ -54,23 +72,24 @@ export function activate(context: vscode.ExtensionContext) {
5472
vscode.languages.registerHoverProvider(documentSelector, {
5573
provideHover(_document, position, _token) {
5674
client
57-
.sendRequest('react/autodepsdecorations', position)
58-
.then((decorations: Array<[Position, Position]>) => {
59-
for (const [start, end] of decorations) {
60-
const range = new vscode.Range(
61-
new vscode.Position(start.line, start.character),
62-
new vscode.Position(end.line, end.character),
75+
.sendRequest(AutoDepsDecorationsRequest.type, {position})
76+
.then(decorations => {
77+
if (Array.isArray(decorations)) {
78+
const decorationOptions = decorations.map(([start, end]) => {
79+
return {
80+
range: new vscode.Range(
81+
new vscode.Position(start.line, start.character),
82+
new vscode.Position(end.line, end.character),
83+
),
84+
hoverMessage: 'Inferred as an effect dependency',
85+
};
86+
});
87+
vscode.window.activeTextEditor?.setDecorations(
88+
inferredEffectDepDecoration,
89+
decorationOptions,
6390
);
64-
const vscodeDecoration =
65-
vscode.window.createTextEditorDecorationType({
66-
backgroundColor: 'red',
67-
});
68-
vscode.window.activeTextEditor?.setDecorations(vscodeDecoration, [
69-
{
70-
range,
71-
hoverMessage: 'hehe',
72-
},
73-
]);
91+
} else {
92+
clearDecorations(inferredEffectDepDecoration);
7493
}
7594
});
7695
return null;
@@ -85,4 +104,11 @@ export function deactivate(): Thenable<void> | undefined {
85104
if (client !== undefined) {
86105
return client.stop();
87106
}
107+
return;
108+
}
109+
110+
export function clearDecorations(
111+
decorationType: vscode.TextEditorDecorationType,
112+
) {
113+
vscode.window.activeTextEditor?.setDecorations(decorationType, []);
88114
}

compiler/packages/react-forgive/server/src/custom-requests/autodepsdecorations.ts

-18
This file was deleted.

compiler/packages/react-forgive/server/src/index.ts

+11-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {Position, TextDocument} from 'vscode-languageserver-textdocument';
8+
import {TextDocument} from 'vscode-languageserver-textdocument';
99
import {
1010
CodeLens,
1111
createConnection,
@@ -19,15 +19,17 @@ import {compile, lastResult} from './compiler';
1919
import {type PluginOptions} from 'babel-plugin-react-compiler/src';
2020
import {resolveReactConfig} from './compiler/options';
2121
import {
22-
CompileSuccessEvent,
22+
type CompileSuccessEvent,
23+
type LoggerEvent,
2324
defaultOptions,
24-
LoggerEvent,
2525
} from 'babel-plugin-react-compiler/src/Entrypoint/Options';
2626
import {babelLocationToRange, getRangeFirstCharacter} from './compiler/compat';
2727
import {
28-
AutoDepsDecorationsLSPEvent,
28+
type AutoDepsDecorationsLSPEvent,
29+
AutoDepsDecorationsRequest,
2930
mapCompilerEventToLSPEvent,
30-
} from './custom-requests/autodepsdecorations';
31+
} from './requests/autodepsdecorations';
32+
import {isPositionWithinRange} from './utils/range';
3133

3234
const SUPPORTED_LANGUAGE_IDS = new Set([
3335
'javascript',
@@ -155,19 +157,11 @@ connection.onCodeLensResolve(lens => {
155157
return lens;
156158
});
157159

158-
connection.onRequest('react/autodepsdecorations', (position: Position) => {
159-
connection.console.log('Client hovering on: ' + JSON.stringify(position));
160-
connection.console.log(JSON.stringify(autoDepsDecorations, null, 2));
161-
160+
connection.onRequest(AutoDepsDecorationsRequest.type, async params => {
161+
const position = params.position;
162+
connection.console.debug('Client hovering on: ' + JSON.stringify(position));
162163
for (const dec of autoDepsDecorations) {
163-
// TODO: extract to helper
164-
if (
165-
position.line >= dec.useEffectCallExpr[0].line &&
166-
position.line <= dec.useEffectCallExpr[1].line
167-
) {
168-
connection.console.log(
169-
'found decoration: ' + JSON.stringify(dec.decorations),
170-
);
164+
if (isPositionWithinRange(position, dec.useEffectCallExpr)) {
171165
return dec.decorations;
172166
}
173167
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {type AutoDepsDecorationsEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
2+
import {type Position} from 'vscode-languageserver-textdocument';
3+
import {RequestType} from 'vscode-languageserver/node';
4+
import {type Range, sourceLocationToRange} from '../utils/range';
5+
6+
export type AutoDepsDecorationsLSPEvent = {
7+
useEffectCallExpr: Range;
8+
decorations: Array<Range>;
9+
};
10+
export interface AutoDepsDecorationsParams {
11+
position: Position;
12+
}
13+
export namespace AutoDepsDecorationsRequest {
14+
export const type = new RequestType<
15+
AutoDepsDecorationsParams,
16+
Array<Range> | null,
17+
void
18+
>('react/autodeps_decorations');
19+
}
20+
21+
export function mapCompilerEventToLSPEvent(
22+
event: AutoDepsDecorationsEvent,
23+
): AutoDepsDecorationsLSPEvent {
24+
return {
25+
useEffectCallExpr: sourceLocationToRange(event.useEffectCallExpr),
26+
decorations: event.decorations.map(sourceLocationToRange),
27+
};
28+
}

compiler/packages/react-forgive/server/src/utils/lsp-adapter.ts

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as t from '@babel/types';
2+
import {type Position} from 'vscode-languageserver/node';
3+
4+
export type Range = [Position, Position];
5+
export function isPositionWithinRange(
6+
position: Position,
7+
[start, end]: Range,
8+
): boolean {
9+
return position.line >= start.line && position.line <= end.line;
10+
}
11+
12+
export function sourceLocationToRange(
13+
loc: t.SourceLocation,
14+
): [Position, Position] {
15+
return [
16+
{line: loc.start.line - 1, character: loc.start.column},
17+
{line: loc.end.line - 1, character: loc.end.column},
18+
];
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "@tsconfig/strictest/tsconfig.json",
3+
"compilerOptions": {
4+
"module": "Node16",
5+
"moduleResolution": "Node16",
6+
"rootDir": "../",
7+
"noEmit": true,
8+
"jsx": "react-jsxdev",
9+
"lib": ["ES2022"],
10+
11+
"target": "ES2022",
12+
"importsNotUsedAsValues": "remove",
13+
},
14+
"exclude": ["node_modules"],
15+
"include": ["server/src/**/*.ts", "client/src/**/*.ts"],
16+
}

0 commit comments

Comments
 (0)