Skip to content

Commit 0562110

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[scopes] Format the code in FunctionCode
If the code is minified and not already from a source map, make it pretty with the formatter. Bug: 462135240 Change-Id: I8fef927929b106e71033407b205741e766e28e39 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7205506 Reviewed-by: Paul Irish <paulirish@chromium.org> Commit-Queue: Connor Clark <cjamcl@chromium.org> Auto-Submit: Connor Clark <cjamcl@chromium.org>
1 parent 10522a4 commit 0562110

3 files changed

Lines changed: 89 additions & 21 deletions

File tree

front_end/models/source_map_scopes/FunctionCodeResolver.snapshot.txt

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
Title: FunctionCodeResolver getFunctionCodeFromLocation [no source maps] lookup named function
22
Content:
3-
use strict";function fibonacci<FUNCTION_START>(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}<FUNCTION_END>const btn=document.querySelect
3+
se strict";
4+
function fibonacci<FUNCTION_START>(e) {
5+
return e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)
6+
}
7+
<FUNCTION_END>const btn = document.querySele
48
=== end content
59

610
Title: FunctionCodeResolver getFunctionCodeFromLocation [no source maps] lookup anonymous function
711
Content:
8-
;btn.addEventListener("click",<FUNCTION_START>()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}<FUNCTION_END>);const input=document.querySe
12+
btn.addEventListener("click", <FUNCTION_START>() => {
13+
console.log(fibonacci(Number(params.get("x")))),
14+
btn.style.backgroundColor = "red"
15+
}
16+
<FUNCTION_END>);
17+
const input = document.quer
918
=== end content
1019

1120
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps] lookup named function with generated location
@@ -32,12 +41,20 @@ function <FUNCTION_START>fibonacci(num) {
3241

3342
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps, no source contents] lookup named function with generated location
3443
Content:
35-
use strict";function fibonacci<FUNCTION_START>(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}<FUNCTION_END>const btn=document.querySelect
44+
se strict";
45+
function fibonacci<FUNCTION_START>(e) {
46+
return e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)
47+
}
48+
<FUNCTION_END>const btn = document.querySele
3649
=== end content
3750

3851
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps, no source contents] lookup named function with original location
3952
Content:
40-
use strict";function fibonacci<FUNCTION_START>(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}<FUNCTION_END>const btn=document.querySelect
53+
se strict";
54+
function fibonacci<FUNCTION_START>(e) {
55+
return e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)
56+
}
57+
<FUNCTION_END>const btn = document.querySele
4158
=== end content
4259

4360
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps] lookup anonymous function with generated location
@@ -62,10 +79,20 @@ const input = document.que
6279

6380
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps, no source contents] lookup anonymous function with generated location
6481
Content:
65-
;btn.addEventListener("click",<FUNCTION_START>()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}<FUNCTION_END>);const input=document.querySe
82+
btn.addEventListener("click", <FUNCTION_START>() => {
83+
console.log(fibonacci(Number(params.get("x")))),
84+
btn.style.backgroundColor = "red"
85+
}
86+
<FUNCTION_END>);
87+
const input = document.quer
6688
=== end content
6789

6890
Title: FunctionCodeResolver getFunctionCodeFromLocation [source maps, no source contents] lookup anonymous function with original location
6991
Content:
70-
;btn.addEventListener("click",<FUNCTION_START>()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}<FUNCTION_END>);const input=document.querySe
92+
btn.addEventListener("click", <FUNCTION_START>() => {
93+
console.log(fibonacci(Number(params.get("x")))),
94+
btn.style.backgroundColor = "red"
95+
}
96+
<FUNCTION_END>);
97+
const input = document.quer
7198
=== end content

front_end/models/source_map_scopes/FunctionCodeResolver.test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,16 @@ describeWithMockConnection('FunctionCodeResolver', function() {
6565
line: 0,
6666
column: 35,
6767
sourceMap: null,
68-
expectedCode: `(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}`
68+
expectedCode: `(e) {\n\treturn e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)\n}\n`
6969
},
7070
{
7171
name: '[no source maps] lookup anonymous function',
7272
url: URL,
7373
line: 0,
7474
column: 201,
7575
sourceMap: null,
76-
expectedCode: `()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}`
76+
expectedCode:
77+
`() => {\n\tconsole.log(fibonacci(Number(params.get(\"x\")))),\n\tbtn.style.backgroundColor = \"red\"\n}\n`
7778
},
7879

7980
{
@@ -102,15 +103,15 @@ describeWithMockConnection('FunctionCodeResolver', function() {
102103
column: 35,
103104
sourceMap: {url: sourceMapUrl, content: sourceMapContentButNoSources},
104105
// TODO: createFromAst does not include function identifiers in the created scope start position.
105-
expectedCode: `(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}`
106+
expectedCode: `(e) {\n\treturn e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)\n}\n`
106107
},
107108
{
108109
name: '[source maps, no source contents] lookup named function with original location',
109110
url: urlString`file:///tmp/index.js`,
110111
line: 1,
111112
column: 5,
112113
sourceMap: {url: sourceMapUrl, content: sourceMapContentButNoSources},
113-
expectedCode: `(e){return e<=1?1:fibonacci(e-1)+fibonacci(e-2)}`
114+
expectedCode: `(e) {\n\treturn e <= 1 ? 1 : fibonacci(e - 1) + fibonacci(e - 2)\n}\n`
114115
},
115116

116117
{
@@ -138,15 +139,17 @@ describeWithMockConnection('FunctionCodeResolver', function() {
138139
line: 0,
139140
column: 201,
140141
sourceMap: {url: sourceMapUrl, content: sourceMapContentButNoSources},
141-
expectedCode: `()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}`
142+
expectedCode:
143+
`() => {\n\tconsole.log(fibonacci(Number(params.get(\"x\")))),\n\tbtn.style.backgroundColor = \"red\"\n}\n`
142144
},
143145
{
144146
name: '[source maps, no source contents] lookup anonymous function with original location',
145147
url: urlString`file:///tmp/index.js`,
146148
line: 10,
147149
column: 3,
148150
sourceMap: {url: sourceMapUrl, content: sourceMapContentButNoSources},
149-
expectedCode: `()=>{console.log(fibonacci(Number(params.get("x")))),btn.style.backgroundColor="red"}`
151+
expectedCode:
152+
`() => {\n\tconsole.log(fibonacci(Number(params.get(\"x\")))),\n\tbtn.style.backgroundColor = \"red\"\n}\n`
150153
},
151154
];
152155

front_end/models/source_map_scopes/FunctionCodeResolver.ts

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import type * as Platform from '../../core/platform/platform.js';
66
import * as SDK from '../../core/sdk/sdk.js';
77
import * as Bindings from '../bindings/bindings.js';
8+
import * as Formatter from '../formatter/formatter.js';
89
import * as TextUtils from '../text_utils/text_utils.js';
910
import * as Workspace from '../workspace/workspace.js';
1011

@@ -13,27 +14,42 @@ export interface FunctionCode {
1314
functionBounds: Workspace.UISourceCode.UIFunctionBounds;
1415
/** The text of `uiSourceCode`. */
1516
text: TextUtils.Text.Text;
16-
/** The scope/function text. */
17+
/** The function text. */
1718
code: string;
1819
/** The range of `code` within `text`. */
1920
range: TextUtils.TextRange.TextRange;
20-
/** The scope/function text, plus some additional context before and after. The actual function scope is wrapped in <FUNCTION_START>...<FUNCTION_END> */
21+
/** The function text, plus some additional context before and after. The actual function is wrapped in <FUNCTION_START>...<FUNCTION_END> */
2122
codeWithContext: string;
2223
/** The range of `codeWithContext` within `text`. */
2324
rangeWithContext: TextUtils.TextRange.TextRange;
2425
}
2526

2627
export interface CreateFunctionCodeOptions {
27-
/** Number of characters to include before and after the function scope. Stacks with `contextLineLength`. */
28+
/** Number of characters to include before and after the function. Stacks with `contextLineLength`. */
2829
contextLength?: number;
29-
/** Number of lines to include before and after the function scope. Stacks with `contextLength`. */
30+
/** Number of lines to include before and after the function. Stacks with `contextLength`. */
3031
contextLineLength?: number;
3132
}
3233

3334
function createFunctionCode(
34-
text: TextUtils.Text.Text, functionBounds: Workspace.UISourceCode.UIFunctionBounds,
35-
options?: CreateFunctionCodeOptions): FunctionCode {
36-
const {startLine, startColumn, endLine, endColumn} = functionBounds.range;
35+
uiSourceCodeContent: string, formattedContent: Formatter.ScriptFormatter.FormattedContent|null,
36+
functionBounds: Workspace.UISourceCode.UIFunctionBounds, options?: CreateFunctionCodeOptions): FunctionCode {
37+
let {startLine, startColumn, endLine, endColumn} = functionBounds.range;
38+
let text;
39+
if (formattedContent) {
40+
text = new TextUtils.Text.Text(formattedContent.formattedContent);
41+
42+
const startMapped = formattedContent.formattedMapping.originalToFormatted(startLine, startColumn);
43+
startLine = startMapped[0];
44+
startColumn = startMapped[1];
45+
46+
const endMapped = formattedContent.formattedMapping.originalToFormatted(endLine, endColumn);
47+
endLine = endMapped[0];
48+
endColumn = endMapped[1];
49+
} else {
50+
text = new TextUtils.Text.Text(uiSourceCodeContent);
51+
}
52+
3753
const content = text.value();
3854

3955
// Define two ranges - the first is just the function bounds, the second includes
@@ -121,6 +137,28 @@ export async function getFunctionCodeFromLocation(
121137
return await getFunctionCodeFromRawLocation(rawLocation, options);
122138
}
123139

140+
const formatCache =
141+
new WeakMap<Workspace.UISourceCode.UISourceCode, Promise<Formatter.ScriptFormatter.FormattedContent|null>>();
142+
143+
async function formatAndCache(uiSourceCode: Workspace.UISourceCode.UISourceCode, content: string):
144+
Promise<Formatter.ScriptFormatter.FormattedContent|null> {
145+
let cachedPromise = formatCache.get(uiSourceCode);
146+
if (cachedPromise) {
147+
return await cachedPromise;
148+
}
149+
150+
const contentType = uiSourceCode.contentType();
151+
const shouldFormat = !contentType.isFromSourceMap() && (contentType.isDocument() || contentType.isScript()) &&
152+
TextUtils.TextUtils.isMinified(content);
153+
if (!shouldFormat) {
154+
return null;
155+
}
156+
157+
cachedPromise = Formatter.ScriptFormatter.formatScriptContent(contentType.canonicalMimeType(), content, '\t');
158+
formatCache.set(uiSourceCode, cachedPromise);
159+
return await cachedPromise;
160+
}
161+
124162
/**
125163
* Returns a {@link FunctionCode} for the given raw location.
126164
*/
@@ -138,6 +176,6 @@ export async function getFunctionCodeFromRawLocation(
138176
return null;
139177
}
140178

141-
const text = new TextUtils.Text.Text(content);
142-
return createFunctionCode(text, functionBounds, options);
179+
const formattedContent = await formatAndCache(functionBounds.uiSourceCode, content);
180+
return createFunctionCode(content, formattedContent, functionBounds, options);
143181
}

0 commit comments

Comments
 (0)