Skip to content

Commit d9a0156

Browse files
committed
wip on new predictor versions
1 parent 8cd433a commit d9a0156

File tree

9 files changed

+161
-204
lines changed

9 files changed

+161
-204
lines changed

src/adapter/breakpointPredictor.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export class BreakpointsPredictor {
106106
type DiscoveredMetadata = ISourceMapMetadata & { sourceUrl: string; resolvedPath: string };
107107
type MetadataMap = Map<string, Set<DiscoveredMetadata>>;
108108

109+
const defaultFileMappings = ['**/*.js', '!node_modules/**'];
110+
109111
class DirectoryScanner {
110112
private _predictor: BreakpointsPredictor;
111113
private _sourcePathToCompiled: Promise<MetadataMap>;
@@ -135,7 +137,8 @@ class DirectoryScanner {
135137
};
136138

137139
const start = Date.now();
138-
const todo = Object.values(await this.repo.findAllChildren(absolutePath)).map(
140+
await this.repo.streamAllChildren(
141+
defaultFileMappings.map(m => `${absolutePath}/${m}`),
139142
async metadata => {
140143
const baseUrl = metadata.sourceMapUrl.startsWith('data:')
141144
? metadata.compiledPath
@@ -175,7 +178,6 @@ class DirectoryScanner {
175178
},
176179
);
177180

178-
await Promise.all(todo);
179181
console.log('runtime using', this.repo.constructor.name, Date.now() - start);
180182
return sourcePathToCompiled;
181183
}

src/adapter/debugAdapter.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import { Cdp } from '../cdp/api';
1515
import { ISourcePathResolver } from '../common/sourcePathResolver';
1616
import { AnyLaunchConfiguration } from '../configuration';
1717
import { RawTelemetryReporter } from '../telemetry/telemetryReporter';
18-
import { RipGrepSourceMapRepository } from '../common/sourceMaps/ripGrepSourceMapRepository';
19-
import { NodeSourceMapRepository } from '../common/sourceMaps/nodeSourceMapRepository';
18+
import { CodeSearchSourceMapRepository } from '../common/sourceMaps/codeSearchSourceMapRepository';
2019
import { BreakpointsPredictor, BreakpointPredictionCache } from './breakpointPredictor';
2120
import { CorrelatedCache } from '../common/sourceMaps/mtimeCorrelatedCache';
2221
import { join } from 'path';
@@ -72,9 +71,7 @@ export class DebugAdapter {
7271
})),
7372
);
7473

75-
const sourceMapRepo = launchConfig.__ripGrepStoragePath
76-
? RipGrepSourceMapRepository.create(launchConfig.__ripGrepStoragePath)
77-
: new NodeSourceMapRepository();
74+
const sourceMapRepo = CodeSearchSourceMapRepository.createOrFallback();
7875
const bpCache: BreakpointPredictionCache | undefined = launchConfig.__workspaceCachePath
7976
? new CorrelatedCache(join(launchConfig.__workspaceCachePath, 'bp-predict.json'))
8077
: undefined;

src/baseConfigurationProvider.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ export abstract class BaseConfigurationProvider<T extends AnyLaunchConfiguration
9696
*/
9797
protected commonResolution(config: T): T {
9898
config.trace = fulfillLoggerOptions(config.trace, this.extensionContext.logPath);
99-
config.__ripGrepStoragePath = this.extensionContext.globalStoragePath;
10099
config.__workspaceCachePath = this.extensionContext.storagePath;
101100

102101
return config;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import { ISourceMapMetadata } from './sourceMap';
6+
import { ISourceMapRepository, createMetadataForFile } from './sourceMapRepository';
7+
import { NodeSourceMapRepository } from './nodeSourceMapRepository';
8+
import { logger } from '../logging/logger';
9+
import { LogTag } from '../logging';
10+
import { forceForwardSlashes } from '../pathUtils';
11+
12+
type FindTextFn = (typeof import('vscode'))['workspace']['findTextInFiles'];
13+
14+
/**
15+
* A source map repository that uses VS Code's proposed search API to
16+
* look for candidate files.
17+
*/
18+
export class CodeSearchSourceMapRepository implements ISourceMapRepository {
19+
constructor(private readonly findFn: FindTextFn) {}
20+
21+
public static createOrFallback() {
22+
try {
23+
const code: typeof import('vscode') = require('vscode');
24+
if (code.workspace.findTextInFiles) {
25+
return new CodeSearchSourceMapRepository(
26+
code.workspace.findTextInFiles.bind(code.workspace),
27+
);
28+
}
29+
} catch {
30+
// ignored -- VS won't have vscode as a viable import, fall back to the memory/node.js version
31+
}
32+
33+
return new NodeSourceMapRepository();
34+
}
35+
36+
/**
37+
* Returns the sourcemaps in the directory, given as an absolute path..
38+
*/
39+
public async findDirectChildren(
40+
_absolutePath: string,
41+
): Promise<{ [path: string]: Required<ISourceMapMetadata> }> {
42+
throw new Error('not implemented');
43+
}
44+
45+
/**
46+
* Recursively finds all children of the given direcotry.
47+
*/
48+
public async streamAllChildren<T>(
49+
patterns: ReadonlyArray<string>,
50+
onChild: (child: Required<ISourceMapMetadata>) => Promise<T>,
51+
): Promise<T[]> {
52+
const todo: Promise<T | void>[] = [];
53+
54+
await this.findFn(
55+
{ pattern: 'sourceMappingURL', isCaseSensitive: true },
56+
{
57+
// todo(connor4312): is this correct way to join globs for search?
58+
include: forceForwardSlashes(patterns.filter(p => !p.startsWith('!')).join(', ')),
59+
exclude:
60+
patterns
61+
.filter(p => p.startsWith('!'))
62+
.map(p => forceForwardSlashes(p.slice(1)))
63+
.join(','),
64+
useIgnoreFiles: false,
65+
useGlobalIgnoreFiles: false,
66+
followSymlinks: true,
67+
previewOptions: { charsPerLine: Number.MAX_SAFE_INTEGER, matchLines: 1 },
68+
beforeContext: 0,
69+
afterContext: 0,
70+
},
71+
result => {
72+
const text = 'text' in result ? result.text : result.preview.text;
73+
todo.push(
74+
createMetadataForFile(result.uri.fsPath, text)
75+
.then(parsed => parsed && onChild(parsed))
76+
.catch(error =>
77+
logger.warn(LogTag.SourceMapParsing, 'Error parsing source map', {
78+
error,
79+
file: result.uri.fsPath,
80+
}),
81+
),
82+
);
83+
},
84+
);
85+
86+
return (await Promise.all(todo)).filter((t): t is T => t !== undefined);
87+
}
88+
}

src/common/sourceMaps/ripGrepSourceMapRepository.ts

Lines changed: 0 additions & 149 deletions
This file was deleted.

src/common/sourceMaps/sourceMapRepository.ts

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,53 @@
33
*--------------------------------------------------------*/
44

55
import { ISourceMapMetadata } from './sourceMap';
6+
import { parseSourceMappingUrl } from '../sourceUtils';
7+
import { completeUrl, absolutePathToFileUrl } from '../urlUtils';
8+
import { stat, readfile } from '../fsUtils';
69

710
export interface ISourceMapRepository {
811
/**
9-
* Returns the sourcemaps in the directory, given as an absolute path.
12+
* Recursively finds all children matching the given glob, calling `onChild`
13+
* when children are found and returning a promise that resolves once all
14+
* children have been discovered.
1015
*/
11-
findDirectChildren(absolutePath: string): Promise<{ [path: string]: Required<ISourceMapMetadata> }>;
12-
13-
/**
14-
* Recursively finds all children of the given direcotry.
15-
*/
16-
findAllChildren(absolutePath: string): Promise<{ [key: string]: Required<ISourceMapMetadata> }>;
16+
streamAllChildren<T>(
17+
patterns: ReadonlyArray<string>,
18+
onChild: (child: Required<ISourceMapMetadata>) => Promise<T>,
19+
): Promise<T[]>;
1720
}
21+
22+
/**
23+
* Generates source map metadata from a path on disk and file contents.
24+
* @param compiledPath -- Absolute path of the .js file on disk
25+
* @param fileContents -- Read contents of the file
26+
*/
27+
export const createMetadataForFile = async (compiledPath: string, fileContents?: string) => {
28+
if (typeof fileContents === 'undefined') {
29+
fileContents = await readfile(compiledPath);
30+
}
31+
32+
let sourceMapUrl = parseSourceMappingUrl(fileContents);
33+
if (!sourceMapUrl) {
34+
return;
35+
}
36+
37+
sourceMapUrl = completeUrl(absolutePathToFileUrl(compiledPath), sourceMapUrl);
38+
if (!sourceMapUrl) {
39+
return;
40+
}
41+
42+
if (!sourceMapUrl.startsWith('data:') && !sourceMapUrl.startsWith('file://')) {
43+
return;
44+
}
45+
46+
const stats = await stat(compiledPath);
47+
if (!stats) {
48+
return;
49+
}
50+
return {
51+
compiledPath,
52+
sourceMapUrl: sourceMapUrl!,
53+
mtime: stats && stats.mtimeMs,
54+
};
55+
};

src/configuration.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,6 @@ export interface IBaseConfiguration extends IMandatedConfiguration, Dap.LaunchPa
131131
*/
132132
__workspaceFolder: string;
133133

134-
/**
135-
* If set, ripgrep will be used for discovering local sourcemaps and will
136-
* be stored in this folder after downloading. If this is omitted, we'll
137-
* use a plain Node.js strategy for grepping instead.
138-
*/
139-
__ripGrepStoragePath?: string;
140-
141134
/**
142135
* Cache directory for workspace-related configuration.
143136
*/

0 commit comments

Comments
 (0)