瀏覽代碼

Adds `using` support & removes classes for parsers

main
Eric Amodio 1 年之前
父節點
當前提交
e6b3c96738
共有 20 個檔案被更改,包括 1272 行新增1326 行删除
  1. +1
    -1
      src/annotations/gutterBlameAnnotationProvider.ts
  2. +1
    -1
      src/annotations/gutterChangesAnnotationProvider.ts
  3. +1
    -1
      src/annotations/gutterHeatmapBlameAnnotationProvider.ts
  4. +11
    -11
      src/env/node/git/git.ts
  5. +46
    -44
      src/env/node/git/localGitProvider.ts
  6. +2
    -2
      src/git/models/diff.ts
  7. +188
    -193
      src/git/parsers/blameParser.ts
  8. +59
    -60
      src/git/parsers/branchParser.ts
  9. +16
    -18
      src/git/parsers/diffParser.ts
  10. +473
    -506
      src/git/parsers/logParser.ts
  11. +109
    -110
      src/git/parsers/reflogParser.ts
  12. +60
    -61
      src/git/parsers/remoteParser.ts
  13. +117
    -117
      src/git/parsers/statusParser.ts
  14. +50
    -51
      src/git/parsers/tagParser.ts
  15. +33
    -34
      src/git/parsers/treeParser.ts
  16. +74
    -75
      src/git/parsers/worktreeParser.ts
  17. +28
    -38
      src/system/stopwatch.ts
  18. +1
    -1
      src/webviews/apps/tsconfig.json
  19. +1
    -1
      tsconfig.base.json
  20. +1
    -1
      tsconfig.browser.json

+ 1
- 1
src/annotations/gutterBlameAnnotationProvider.ts 查看文件

@ -48,7 +48,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
const blame = await this.getBlame();
if (blame == null) return false;
const sw = maybeStopWatch(scope);
using sw = maybeStopWatch(scope);
const cfg = configuration.get('blame');

+ 1
- 1
src/annotations/gutterChangesAnnotationProvider.ts 查看文件

@ -156,7 +156,7 @@ export class GutterChangesAnnotationProvider extends AnnotationProviderBase
).filter(<T>(d?: T): d is T => Boolean(d));
if (!diffs?.length) return false;
const sw = maybeStopWatch(scope);
using sw = maybeStopWatch(scope);
const decorationsMap = new Map<
string,

+ 1
- 1
src/annotations/gutterHeatmapBlameAnnotationProvider.ts 查看文件

@ -26,7 +26,7 @@ export class GutterHeatmapBlameAnnotationProvider extends BlameAnnotationProvide
const blame = await this.getBlame();
if (blame == null) return false;
const sw = maybeStopWatch(scope);
using sw = maybeStopWatch(scope);
const decorationsMap = new Map<
string,

+ 11
- 11
src/env/node/git/git.ts 查看文件

@ -24,11 +24,11 @@ import type { GitDir } from '../../../git/gitProvider';
import type { GitDiffFilter } from '../../../git/models/diff';
import { isUncommitted, isUncommittedStaged, shortenRevision } from '../../../git/models/reference';
import type { GitUser } from '../../../git/models/user';
import { GitBranchParser } from '../../../git/parsers/branchParser';
import { GitLogParser } from '../../../git/parsers/logParser';
import { GitReflogParser } from '../../../git/parsers/reflogParser';
import { parseGitBranchesDefaultFormat } from '../../../git/parsers/branchParser';
import { parseGitLogAllFormat, parseGitLogDefaultFormat } from '../../../git/parsers/logParser';
import { parseGitRefLogDefaultFormat } from '../../../git/parsers/reflogParser';
import { parseGitRemoteUrl } from '../../../git/parsers/remoteParser';
import { GitTagParser } from '../../../git/parsers/tagParser';
import { parseGitTagsDefaultFormat } from '../../../git/parsers/tagParser';
import { splitAt } from '../../../system/array';
import { configuration } from '../../../system/configuration';
import { log } from '../../../system/decorators/log';
@ -1011,7 +1011,7 @@ export class Git {
}
for_each_ref__branch(repoPath: string, options: { all: boolean } = { all: false }) {
const params = ['for-each-ref', `--format=${GitBranchParser.defaultFormat}`, 'refs/heads'];
const params = ['for-each-ref', `--format=${parseGitBranchesDefaultFormat}`, 'refs/heads'];
if (options.all) {
params.push('refs/remotes');
}
@ -1045,7 +1045,7 @@ export class Git {
},
) {
if (argsOrFormat == null) {
argsOrFormat = ['--name-status', `--format=${all ? GitLogParser.allFormat : GitLogParser.defaultFormat}`];
argsOrFormat = ['--name-status', `--format=${all ? parseGitLogAllFormat : parseGitLogDefaultFormat}`];
}
if (typeof argsOrFormat === 'string') {
@ -1242,7 +1242,7 @@ export class Git {
const [file, root] = splitPath(fileName, repoPath, true);
if (argsOrFormat == null) {
argsOrFormat = [`--format=${all ? GitLogParser.allFormat : GitLogParser.defaultFormat}`];
argsOrFormat = [`--format=${all ? parseGitLogAllFormat : parseGitLogDefaultFormat}`];
}
if (typeof argsOrFormat === 'string') {
@ -1438,7 +1438,7 @@ export class Git {
'show',
'--stdin',
'--name-status',
`--format=${GitLogParser.defaultFormat}`,
`--format=${parseGitLogDefaultFormat}`,
'--use-mailmap',
);
}
@ -1451,7 +1451,7 @@ export class Git {
'log',
...(options?.stdin ? ['--stdin'] : emptyArray),
'--name-status',
`--format=${GitLogParser.defaultFormat}`,
`--format=${parseGitLogDefaultFormat}`,
'--use-mailmap',
...search,
...(options?.ordering ? [`--${options.ordering}-order`] : emptyArray),
@ -1542,7 +1542,7 @@ export class Git {
skip?: number;
} = {},
): Promise<string> {
const params = ['log', '--walk-reflogs', `--format=${GitReflogParser.defaultFormat}`, '--date=iso8601'];
const params = ['log', '--walk-reflogs', `--format=${parseGitRefLogDefaultFormat}`, '--date=iso8601'];
if (ordering) {
params.push(`--${ordering}-order`);
@ -2139,7 +2139,7 @@ export class Git {
}
tag(repoPath: string) {
return this.git<string>({ cwd: repoPath }, 'tag', '-l', `--format=${GitTagParser.defaultFormat}`);
return this.git<string>({ cwd: repoPath }, 'tag', '-l', `--format=${parseGitTagsDefaultFormat}`);
}
worktree__add(

+ 46
- 44
src/env/node/git/localGitProvider.ts 查看文件

@ -108,9 +108,9 @@ import type { GitTreeEntry } from '../../../git/models/tree';
import type { GitUser } from '../../../git/models/user';
import { isUserMatch } from '../../../git/models/user';
import type { GitWorktree } from '../../../git/models/worktree';
import { GitBlameParser } from '../../../git/parsers/blameParser';
import { GitBranchParser } from '../../../git/parsers/branchParser';
import { parseDiffNameStatusFiles, parseDiffShortStat, parseFileDiff } from '../../../git/parsers/diffParser';
import { parseGitBlame } from '../../../git/parsers/blameParser';
import { parseGitBranches } from '../../../git/parsers/branchParser';
import { parseGitDiffNameStatusFiles, parseGitDiffShortStat, parseGitFileDiff } from '../../../git/parsers/diffParser';
import {
createLogParserSingle,
createLogParserWithFiles,
@ -119,15 +119,20 @@ import {
getGraphStatsParser,
getRefAndDateParser,
getRefParser,
GitLogParser,
LogType,
parseGitLog,
parseGitLogAllFormat,
parseGitLogDefaultFormat,
parseGitLogSimple,
parseGitLogSimpleFormat,
parseGitLogSimpleRenamed,
} from '../../../git/parsers/logParser';
import { GitReflogParser } from '../../../git/parsers/reflogParser';
import { GitRemoteParser } from '../../../git/parsers/remoteParser';
import { GitStatusParser } from '../../../git/parsers/statusParser';
import { GitTagParser } from '../../../git/parsers/tagParser';
import { GitTreeParser } from '../../../git/parsers/treeParser';
import { GitWorktreeParser } from '../../../git/parsers/worktreeParser';
import { parseGitRefLog } from '../../../git/parsers/reflogParser';
import { parseGitRemotes } from '../../../git/parsers/remoteParser';
import { parseGitStatus } from '../../../git/parsers/statusParser';
import { parseGitTags } from '../../../git/parsers/tagParser';
import { parseGitTree } from '../../../git/parsers/treeParser';
import { parseGitWorktrees } from '../../../git/parsers/worktreeParser';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../../git/remotes/remoteProviders';
import type { GitSearch, GitSearchResultData, GitSearchResults, SearchQuery } from '../../../git/search';
import { getGitArgsFromSearchQuery, getSearchQueryComparisonKey } from '../../../git/search';
@ -954,7 +959,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// Now check if that commit had any renames
data = await this.git.log__file(repoPath, '.', ref, {
argsOrFormat: GitLogParser.simpleFormat,
argsOrFormat: parseGitLogSimpleFormat,
fileMode: 'simple',
filters: ['R', 'C', 'D'],
limit: 1,
@ -962,7 +967,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
});
if (data == null || data.length === 0) break;
const [foundRef, foundFile, foundStatus] = GitLogParser.parseSimpleRenamed(data, relativePath);
const [foundRef, foundFile, foundStatus] = parseGitLogSimpleRenamed(data, relativePath);
if (foundStatus === 'D' && foundFile != null) return undefined;
if (foundRef == null || foundFile == null) break;
@ -1455,7 +1460,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
args: configuration.get('advanced.blame.customArguments'),
ignoreWhitespace: configuration.get('blame.ignoreWhitespace'),
});
const blame = GitBlameParser.parse(this.container, data, root, await this.getCurrentUser(root));
const blame = parseGitBlame(this.container, data, root, await this.getCurrentUser(root));
return blame;
} catch (ex) {
// Trap and cache expected blame errors
@ -1536,7 +1541,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
correlationKey: `:${key}`,
ignoreWhitespace: configuration.get('blame.ignoreWhitespace'),
});
const blame = GitBlameParser.parse(this.container, data, root, await this.getCurrentUser(root));
const blame = parseGitBlame(this.container, data, root, await this.getCurrentUser(root));
return blame;
} catch (ex) {
// Trap and cache expected blame errors
@ -1601,7 +1606,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
startLine: lineToBlame,
endLine: lineToBlame,
});
const blame = GitBlameParser.parse(this.container, data, root, await this.getCurrentUser(root));
const blame = parseGitBlame(this.container, data, root, await this.getCurrentUser(root));
if (blame == null) return undefined;
return {
@ -1652,7 +1657,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
startLine: lineToBlame,
endLine: lineToBlame,
});
const blame = GitBlameParser.parse(this.container, data, root, await this.getCurrentUser(root));
const blame = parseGitBlame(this.container, data, root, await this.getCurrentUser(root));
if (blame == null) return undefined;
return {
@ -1819,7 +1824,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
return current != null ? { values: [current] } : emptyPagedResult;
}
return { values: GitBranchParser.parse(this.container, data, repoPath!) };
return { values: parseGitBranches(this.container, data, repoPath!) };
} catch (ex) {
this._branchesCache.delete(repoPath!);
@ -1856,7 +1861,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const data = await this.git.diff__shortstat(repoPath, ref);
if (!data) return undefined;
return parseDiffShortStat(data);
return parseGitDiffShortStat(data);
}
@log()
@ -2752,7 +2757,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
similarityThreshold: configuration.get('advanced.similarityThreshold'),
});
const diff = parseFileDiff(data);
const diff = parseGitFileDiff(data);
return diff;
} catch (ex) {
// Trap and cache expected diff errors
@ -2839,7 +2844,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
similarityThreshold: configuration.get('advanced.similarityThreshold'),
});
const diff = parseFileDiff(data);
const diff = parseGitFileDiff(data);
return diff;
} catch (ex) {
// Trap and cache expected diff errors
@ -2895,7 +2900,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
});
if (!data) return undefined;
const files = parseDiffNameStatusFiles(data, repoPath);
const files = parseGitDiffNameStatusFiles(data, repoPath);
return files == null || files.length === 0 ? undefined : files;
} catch (ex) {
return undefined;
@ -2911,7 +2916,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const data = await this.git.show__name_status(root, relativePath, ref);
if (!data) return undefined;
const files = parseDiffNameStatusFiles(data, repoPath);
const files = parseGitDiffNameStatusFiles(data, repoPath);
if (files == null || files.length === 0) return undefined;
return files[0];
@ -2986,7 +2991,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const similarityThreshold = configuration.get('advanced.similarityThreshold');
const args = [
`--format=${options?.all ? GitLogParser.allFormat : GitLogParser.defaultFormat}`,
`--format=${options?.all ? parseGitLogAllFormat : parseGitLogDefaultFormat}`,
`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,
'-m',
];
@ -3076,7 +3081,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// );
// }
const log = GitLogParser.parse(
const log = parseGitLog(
this.container,
data,
LogType.Log,
@ -3461,7 +3466,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
startLine: range == null ? undefined : range.start.line + 1,
endLine: range == null ? undefined : range.end.line + 1,
});
const log = GitLogParser.parse(
const log = parseGitLog(
this.container,
data,
// If this is the log of a folder, parse it as a normal log rather than a file log
@ -3815,7 +3820,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const relativePath = this.getRelativePath(uri, repoPath);
let data = await this.git.log__file(repoPath, relativePath, ref, {
argsOrFormat: GitLogParser.simpleFormat,
argsOrFormat: parseGitLogSimpleFormat,
fileMode: 'simple',
filters: filters,
limit: skip + 1,
@ -3825,11 +3830,11 @@ export class LocalGitProvider implements GitProvider, Disposable {
});
if (data == null || data.length === 0) return undefined;
const [nextRef, file, status] = GitLogParser.parseSimple(data, skip);
const [nextRef, file, status] = parseGitLogSimple(data, skip);
// If the file was deleted, check for a possible rename
if (status === 'D') {
data = await this.git.log__file(repoPath, '.', nextRef, {
argsOrFormat: GitLogParser.simpleFormat,
argsOrFormat: parseGitLogSimpleFormat,
fileMode: 'simple',
filters: ['R', 'C'],
limit: 1,
@ -3840,7 +3845,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
return GitUri.fromFile(file ?? relativePath, repoPath, nextRef);
}
const [nextRenamedRef, renamedFile] = GitLogParser.parseSimpleRenamed(data, file ?? relativePath);
const [nextRenamedRef, renamedFile] = parseGitLogSimpleRenamed(data, file ?? relativePath);
return GitUri.fromFile(
renamedFile ?? file ?? relativePath,
repoPath,
@ -4085,7 +4090,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
let data;
try {
data = await this.git.log__file(repoPath, relativePath, ref, {
argsOrFormat: GitLogParser.simpleFormat,
argsOrFormat: parseGitLogSimpleFormat,
fileMode: 'simple',
limit: skip + 2,
ordering: configuration.get('advanced.commitOrdering'),
@ -4113,7 +4118,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
if (data == null || data.length === 0) return undefined;
const [previousRef, file] = GitLogParser.parseSimple(data, skip, ref);
const [previousRef, file] = parseGitLogSimple(data, skip, ref);
// If the previous ref matches the ref we asked for assume we are at the end of the history
if (ref != null && ref === previousRef) return undefined;
@ -4143,7 +4148,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
});
if (data == null) return undefined;
const reflog = GitReflogParser.parse(data, repoPath, reflogCommands, limit, limit * 100);
const reflog = parseGitRefLog(data, repoPath, reflogCommands, limit, limit * 100);
if (reflog?.hasMore) {
reflog.more = this.getReflogMoreFn(reflog, options);
}
@ -4209,13 +4214,11 @@ export class LocalGitProvider implements GitProvider, Disposable {
try {
const data = await this.git.remote(repoPath!);
const remotes = GitRemoteParser.parse(
const remotes = parseGitRemotes(
data,
repoPath!,
getRemoteProviderMatcher(this.container, providers),
);
if (remotes == null) return [];
return remotes;
} catch (ex) {
this._remotesCache.delete(repoPath!);
@ -4341,7 +4344,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
similarityThreshold: configuration.get('advanced.similarityThreshold'),
});
const status = GitStatusParser.parse(data, root, porcelainVersion);
const status = parseGitStatus(data, root, porcelainVersion);
return status?.files?.[0];
}
@ -4355,7 +4358,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
similarityThreshold: configuration.get('advanced.similarityThreshold'),
});
const status = GitStatusParser.parse(data, root, porcelainVersion);
const status = parseGitStatus(data, root, porcelainVersion);
return status?.files ?? [];
}
@ -4368,7 +4371,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const data = await this.git.status(repoPath, porcelainVersion, {
similarityThreshold: configuration.get('advanced.similarityThreshold'),
});
const status = GitStatusParser.parse(data, repoPath, porcelainVersion);
const status = parseGitStatus(data, repoPath, porcelainVersion);
if (status?.detached) {
const rebaseStatus = await this.getRebaseStatus(repoPath);
@ -4399,7 +4402,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
async function load(this: LocalGitProvider): Promise<PagedResult<GitTag>> {
try {
const data = await this.git.tag(repoPath!);
return { values: GitTagParser.parse(data, repoPath!) ?? [] };
return { values: parseGitTags(data, repoPath!) };
} catch (ex) {
this._tagsCache.delete(repoPath!);
@ -4436,8 +4439,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const [relativePath, root] = splitPath(path, repoPath);
const data = await this.git.ls_tree(root, ref, relativePath);
const trees = GitTreeParser.parse(data);
return trees?.length ? trees[0] : undefined;
return parseGitTree(data)[0];
}
@log()
@ -4445,7 +4447,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (repoPath == null) return [];
const data = await this.git.ls_tree(repoPath, ref);
return GitTreeParser.parse(data) ?? [];
return parseGitTree(data);
}
@log({ args: { 1: false } })
@ -4811,7 +4813,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
shas: shas,
stdin: stdin,
});
const log = GitLogParser.parse(
const log = parseGitLog(
this.container,
data,
LogType.Log,
@ -5212,7 +5214,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
);
const data = await this.git.worktree__list(repoPath);
return GitWorktreeParser.parse(data, repoPath);
return parseGitWorktrees(data, repoPath);
}
// eslint-disable-next-line @typescript-eslint/require-await

+ 2
- 2
src/git/models/diff.ts 查看文件

@ -1,4 +1,4 @@
import { parseDiffHunk } from '../parsers/diffParser';
import { parseGitDiffHunk } from '../parsers/diffParser';
export interface GitDiffLine {
line: string;
@ -35,7 +35,7 @@ export class GitDiffHunk {
private parsedHunk: { lines: GitDiffHunkLine[]; state: 'added' | 'changed' | 'removed' } | undefined;
private parseHunk() {
if (this.parsedHunk == null) {
this.parsedHunk = parseDiffHunk(this);
this.parsedHunk = parseGitDiffHunk(this);
}
return this.parsedHunk;
}

+ 188
- 193
src/git/parsers/blameParser.ts 查看文件

@ -1,5 +1,5 @@
import type { Container } from '../../container';
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import { getLines } from '../../system/string';
import type { GitBlame, GitBlameAuthor } from '../models/blame';
import type { GitCommitLine } from '../models/commit';
@ -34,228 +34,223 @@ interface BlameEntry {
summary?: string;
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitBlameParser {
@debug({ args: false, singleLine: true })
static parse(
container: Container,
data: string,
repoPath: string,
currentUser: GitUser | undefined,
): GitBlame | undefined {
if (!data) return undefined;
const authors = new Map<string, GitBlameAuthor>();
const commits = new Map<string, GitCommit>();
const lines: GitCommitLine[] = [];
let entry: BlameEntry | undefined = undefined;
let key: string;
let line: string;
let lineParts: string[];
for (line of getLines(data)) {
lineParts = line.split(' ');
if (lineParts.length < 2) continue;
[key] = lineParts;
if (entry == null) {
entry = {
sha: key,
originalLine: parseInt(lineParts[1], 10),
line: parseInt(lineParts[2], 10),
lineCount: parseInt(lineParts[3], 10),
} as unknown as BlameEntry;
continue;
}
export function parseGitBlame(
container: Container,
data: string,
repoPath: string,
currentUser: GitUser | undefined,
): GitBlame | undefined {
using sw = maybeStopWatch(`Git.parseBlame(${repoPath})`, { log: false, logLevel: 'debug' });
if (!data) return undefined;
const authors = new Map<string, GitBlameAuthor>();
const commits = new Map<string, GitCommit>();
const lines: GitCommitLine[] = [];
let entry: BlameEntry | undefined = undefined;
let key: string;
let line: string;
let lineParts: string[];
for (line of getLines(data)) {
lineParts = line.split(' ');
if (lineParts.length < 2) continue;
[key] = lineParts;
if (entry == null) {
entry = {
sha: key,
originalLine: parseInt(lineParts[1], 10),
line: parseInt(lineParts[2], 10),
lineCount: parseInt(lineParts[3], 10),
} as unknown as BlameEntry;
continue;
}
switch (key) {
case 'author':
if (entry.sha === uncommitted) {
entry.author = 'You';
} else {
entry.author = line.slice(key.length + 1).trim();
}
break;
switch (key) {
case 'author':
if (entry.sha === uncommitted) {
entry.author = 'You';
} else {
entry.author = line.slice(key.length + 1).trim();
}
break;
case 'author-mail': {
if (entry.sha === uncommitted) {
entry.authorEmail = currentUser?.email;
continue;
}
case 'author-mail': {
if (entry.sha === uncommitted) {
entry.authorEmail = currentUser?.email;
continue;
}
entry.authorEmail = line.slice(key.length + 1).trim();
const start = entry.authorEmail.indexOf('<');
if (start >= 0) {
const end = entry.authorEmail.indexOf('>', start);
if (end > start) {
entry.authorEmail = entry.authorEmail.substring(start + 1, end);
} else {
entry.authorEmail = entry.authorEmail.substring(start + 1);
}
entry.authorEmail = line.slice(key.length + 1).trim();
const start = entry.authorEmail.indexOf('<');
if (start >= 0) {
const end = entry.authorEmail.indexOf('>', start);
if (end > start) {
entry.authorEmail = entry.authorEmail.substring(start + 1, end);
} else {
entry.authorEmail = entry.authorEmail.substring(start + 1);
}
}
break;
break;
}
case 'author-time':
entry.authorDate = lineParts[1];
break;
case 'author-tz':
entry.authorTimeZone = lineParts[1];
break;
case 'committer':
if (isUncommitted(entry.sha)) {
entry.committer = 'You';
} else {
entry.committer = line.slice(key.length + 1).trim();
}
case 'author-time':
entry.authorDate = lineParts[1];
break;
break;
case 'author-tz':
entry.authorTimeZone = lineParts[1];
break;
case 'committer-mail': {
if (isUncommitted(entry.sha)) {
entry.committerEmail = currentUser?.email;
continue;
}
case 'committer':
if (isUncommitted(entry.sha)) {
entry.committer = 'You';
entry.committerEmail = line.slice(key.length + 1).trim();
const start = entry.committerEmail.indexOf('<');
if (start >= 0) {
const end = entry.committerEmail.indexOf('>', start);
if (end > start) {
entry.committerEmail = entry.committerEmail.substring(start + 1, end);
} else {
entry.committer = line.slice(key.length + 1).trim();
}
break;
case 'committer-mail': {
if (isUncommitted(entry.sha)) {
entry.committerEmail = currentUser?.email;
continue;
entry.committerEmail = entry.committerEmail.substring(start + 1);
}
entry.committerEmail = line.slice(key.length + 1).trim();
const start = entry.committerEmail.indexOf('<');
if (start >= 0) {
const end = entry.committerEmail.indexOf('>', start);
if (end > start) {
entry.committerEmail = entry.committerEmail.substring(start + 1, end);
} else {
entry.committerEmail = entry.committerEmail.substring(start + 1);
}
}
break;
}
case 'committer-time':
entry.committerDate = lineParts[1];
break;
case 'committer-tz':
entry.committerTimeZone = lineParts[1];
break;
break;
}
case 'committer-time':
entry.committerDate = lineParts[1];
break;
case 'committer-tz':
entry.committerTimeZone = lineParts[1];
break;
case 'summary':
entry.summary = line.slice(key.length + 1).trim();
break;
case 'summary':
entry.summary = line.slice(key.length + 1).trim();
break;
case 'previous':
entry.previousSha = lineParts[1];
entry.previousPath = lineParts.slice(2).join(' ');
break;
case 'previous':
entry.previousSha = lineParts[1];
entry.previousPath = lineParts.slice(2).join(' ');
break;
case 'filename':
// Don't trim to allow spaces in the filename
entry.path = line.slice(key.length + 1);
case 'filename':
// Don't trim to allow spaces in the filename
entry.path = line.slice(key.length + 1);
// Since the filename marks the end of a commit, parse the entry and clear it for the next
GitBlameParser.parseEntry(container, entry, repoPath, commits, authors, lines, currentUser);
// Since the filename marks the end of a commit, parse the entry and clear it for the next
parseBlameEntry(container, entry, repoPath, commits, authors, lines, currentUser);
entry = undefined;
break;
entry = undefined;
break;
default:
break;
}
default:
break;
}
}
for (const [, c] of commits) {
if (!c.author.name) continue;
for (const [, c] of commits) {
if (!c.author.name) continue;
const author = authors.get(c.author.name);
if (author == undefined) return undefined;
const author = authors.get(c.author.name);
if (author == undefined) return undefined;
author.lineCount += c.lines.length;
}
author.lineCount += c.lines.length;
}
const sortedAuthors = new Map([...authors.entries()].sort((a, b) => b[1].lineCount - a[1].lineCount));
const sortedAuthors = new Map([...authors.entries()].sort((a, b) => b[1].lineCount - a[1].lineCount));
const blame: GitBlame = {
repoPath: repoPath,
authors: sortedAuthors,
commits: commits,
lines: lines,
};
return blame;
}
sw?.stop({ suffix: ` parsed ${lines.length} lines, ${commits.size} commits` });
private static parseEntry(
container: Container,
entry: BlameEntry,
repoPath: string,
commits: Map<string, GitCommit>,
authors: Map<string, GitBlameAuthor>,
lines: GitCommitLine[],
currentUser: { name?: string; email?: string } | undefined,
) {
let commit = commits.get(entry.sha);
if (commit == null) {
if (entry.author != null) {
if (
currentUser != null &&
// Name or e-mail is configured
(currentUser.name != null || currentUser.email != null) &&
// Match on name if configured
(currentUser.name == null || currentUser.name === entry.author) &&
// Match on email if configured
(currentUser.email == null || currentUser.email === entry.authorEmail)
) {
entry.author = 'You';
}
const blame: GitBlame = {
repoPath: repoPath,
authors: sortedAuthors,
commits: commits,
lines: lines,
};
return blame;
}
let author = authors.get(entry.author);
if (author == null) {
author = {
name: entry.author,
lineCount: 0,
};
authors.set(entry.author, author);
}
function parseBlameEntry(
container: Container,
entry: BlameEntry,
repoPath: string,
commits: Map<string, GitCommit>,
authors: Map<string, GitBlameAuthor>,
lines: GitCommitLine[],
currentUser: { name?: string; email?: string } | undefined,
) {
let commit = commits.get(entry.sha);
if (commit == null) {
if (entry.author != null) {
if (
currentUser != null &&
// Name or e-mail is configured
(currentUser.name != null || currentUser.email != null) &&
// Match on name if configured
(currentUser.name == null || currentUser.name === entry.author) &&
// Match on email if configured
(currentUser.email == null || currentUser.email === entry.authorEmail)
) {
entry.author = 'You';
}
commit = new GitCommit(
container,
repoPath,
entry.sha,
new GitCommitIdentity(entry.author, entry.authorEmail, new Date((entry.authorDate as any) * 1000)),
new GitCommitIdentity(
entry.committer,
entry.committerEmail,
new Date((entry.committerDate as any) * 1000),
),
entry.summary!,
[],
undefined,
new GitFileChange(
repoPath,
entry.path,
GitFileIndexStatus.Modified,
entry.previousPath && entry.previousPath !== entry.path ? entry.previousPath : undefined,
entry.previousSha,
),
undefined,
[],
);
commits.set(entry.sha, commit);
let author = authors.get(entry.author);
if (author == null) {
author = {
name: entry.author,
lineCount: 0,
};
authors.set(entry.author, author);
}
}
for (let i = 0, len = entry.lineCount; i < len; i++) {
const line: GitCommitLine = {
sha: entry.sha,
previousSha: commit.file!.previousSha,
originalLine: entry.originalLine + i,
line: entry.line + i,
};
commit = new GitCommit(
container,
repoPath,
entry.sha,
new GitCommitIdentity(entry.author, entry.authorEmail, new Date((entry.authorDate as any) * 1000)),
new GitCommitIdentity(entry.committer, entry.committerEmail, new Date((entry.committerDate as any) * 1000)),
entry.summary!,
[],
undefined,
new GitFileChange(
repoPath,
entry.path,
GitFileIndexStatus.Modified,
entry.previousPath && entry.previousPath !== entry.path ? entry.previousPath : undefined,
entry.previousSha,
),
undefined,
[],
);
commits.set(entry.sha, commit);
}
commit.lines.push(line);
lines[line.line - 1] = line;
}
for (let i = 0, len = entry.lineCount; i < len; i++) {
const line: GitCommitLine = {
sha: entry.sha,
previousSha: commit.file!.previousSha,
originalLine: entry.originalLine + i,
line: entry.line + i,
};
commit.lines.push(line);
lines[line.line - 1] = line;
}
}

+ 59
- 60
src/git/parsers/branchParser.ts 查看文件

@ -1,5 +1,5 @@
import type { Container } from '../../container';
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import { GitBranch } from '../models/branch';
const branchWithTrackingRegex =
@ -9,73 +9,72 @@ const branchWithTrackingRegex =
const lb = '%3c'; // `%${'<'.charCodeAt(0).toString(16)}`;
const rb = '%3e'; // `%${'>'.charCodeAt(0).toString(16)}`;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitBranchParser {
static defaultFormat = [
`${lb}h${rb}%(HEAD)`, // HEAD indicator
`${lb}n${rb}%(refname)`, // branch name
`${lb}u${rb}%(upstream:short)`, // branch upstream
`${lb}t${rb}%(upstream:track)`, // branch upstream tracking state
`${lb}r${rb}%(objectname)`, // ref
`${lb}d${rb}%(committerdate:iso8601)`, // committer date
].join('');
export const parseGitBranchesDefaultFormat = [
`${lb}h${rb}%(HEAD)`, // HEAD indicator
`${lb}n${rb}%(refname)`, // branch name
`${lb}u${rb}%(upstream:short)`, // branch upstream
`${lb}t${rb}%(upstream:track)`, // branch upstream tracking state
`${lb}r${rb}%(objectname)`, // ref
`${lb}d${rb}%(committerdate:iso8601)`, // committer date
].join('');
@debug({ args: false, singleLine: true })
static parse(container: Container, data: string, repoPath: string): GitBranch[] {
const branches: GitBranch[] = [];
export function parseGitBranches(container: Container, data: string, repoPath: string): GitBranch[] {
using sw = maybeStopWatch(`Git.parseBranches(${repoPath})`, { log: false, logLevel: 'debug' });
if (!data) return branches;
const branches: GitBranch[] = [];
if (!data) return branches;
let current;
let name;
let upstream;
let ahead;
let behind;
let missing;
let ref;
let date;
let current;
let name;
let upstream;
let ahead;
let behind;
let missing;
let ref;
let date;
let remote;
let remote;
let match;
do {
match = branchWithTrackingRegex.exec(data);
if (match == null) break;
let match;
do {
match = branchWithTrackingRegex.exec(data);
if (match == null) break;
[, current, name, upstream, ahead, behind, missing, ref, date] = match;
[, current, name, upstream, ahead, behind, missing, ref, date] = match;
if (name.startsWith('refs/remotes/')) {
// Strip off refs/remotes/
name = name.substr(13);
if (name.endsWith('/HEAD')) continue;
if (name.startsWith('refs/remotes/')) {
// Strip off refs/remotes/
name = name.substr(13);
if (name.endsWith('/HEAD')) continue;
remote = true;
} else {
// Strip off refs/heads/
name = name.substr(11);
remote = false;
}
remote = true;
} else {
// Strip off refs/heads/
name = name.substr(11);
remote = false;
}
branches.push(
new GitBranch(
container,
repoPath,
name,
remote,
current.charCodeAt(0) === 42, // '*',
date ? new Date(date) : undefined,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
ref == null || ref.length === 0 ? undefined : ` ${ref}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
upstream == null || upstream.length === 0
? undefined
: { name: ` ${upstream}`.substr(1), missing: Boolean(missing) },
Number(ahead) || 0,
Number(behind) || 0,
),
);
} while (true);
branches.push(
new GitBranch(
container,
repoPath,
name,
remote,
current.charCodeAt(0) === 42, // '*',
date ? new Date(date) : undefined,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
ref == null || ref.length === 0 ? undefined : ` ${ref}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
upstream == null || upstream.length === 0
? undefined
: { name: ` ${upstream}`.substr(1), missing: Boolean(missing) },
Number(ahead) || 0,
Number(behind) || 0,
),
);
} while (true);
return branches;
}
sw?.stop({ suffix: ` parsed ${branches.length} branches` });
return branches;
}

+ 16
- 18
src/git/parsers/diffParser.ts 查看文件

@ -1,5 +1,5 @@
import { maybeStopWatch } from '../../system/stopwatch';
import { getLines, pluralize } from '../../system/string';
import { getLines } from '../../system/string';
import type { GitDiffFile, GitDiffHunkLine, GitDiffLine, GitDiffShortStat } from '../models/diff';
import { GitDiffHunk } from '../models/diff';
import type { GitFile, GitFileStatus } from '../models/file';
@ -7,11 +7,10 @@ import type { GitFile, GitFileStatus } from '../models/file';
const shortStatDiffRegex = /(\d+)\s+files? changed(?:,\s+(\d+)\s+insertions?\(\+\))?(?:,\s+(\d+)\s+deletions?\(-\))?/;
const unifiedDiffRegex = /^@@ -([\d]+)(?:,([\d]+))? \+([\d]+)(?:,([\d]+))? @@(?:.*?)\n([\s\S]*?)(?=^@@)/gm;
export function parseFileDiff(data: string, includeContents: boolean = false): GitDiffFile | undefined {
export function parseGitFileDiff(data: string, includeContents: boolean = false): GitDiffFile | undefined {
using sw = maybeStopWatch('Git.parseFileDiff', { log: false, logLevel: 'debug' });
if (!data) return undefined;
const sw = maybeStopWatch('parseFileDiff', { log: false, logLevel: 'debug' });
const hunks: GitDiffHunk[] = [];
let previousStart;
@ -54,7 +53,7 @@ export function parseFileDiff(data: string, includeContents: boolean = false): G
);
} while (true);
sw?.stop({ suffix: ` parsed ${pluralize('hunk', hunks.length)}` });
sw?.stop({ suffix: ` parsed ${hunks.length} hunks` });
if (!hunks.length) return undefined;
@ -65,8 +64,11 @@ export function parseFileDiff(data: string, includeContents: boolean = false): G
return diff;
}
export function parseDiffHunk(hunk: GitDiffHunk): { lines: GitDiffHunkLine[]; state: 'added' | 'changed' | 'removed' } {
const sw = maybeStopWatch('parseDiffHunk', { log: false, logLevel: 'debug' });
export function parseGitDiffHunk(hunk: GitDiffHunk): {
lines: GitDiffHunkLine[];
state: 'added' | 'changed' | 'removed';
} {
using sw = maybeStopWatch('Git.parseDiffHunk', { log: false, logLevel: 'debug' });
const currentStart = hunk.current.position.start;
const previousStart = hunk.previous.position.start;
@ -140,7 +142,7 @@ export function parseDiffHunk(hunk: GitDiffHunk): { lines: GitDiffHunkLine[]; st
});
}
sw?.stop({ suffix: ` parsed ${pluralize('line', hunkLines.length)}` });
sw?.stop({ suffix: ` parsed ${hunkLines.length} hunk lines` });
return {
lines: hunkLines,
@ -148,11 +150,10 @@ export function parseDiffHunk(hunk: GitDiffHunk): { lines: GitDiffHunkLine[]; st
};
}
export function parseDiffNameStatusFiles(data: string, repoPath: string): GitFile[] | undefined {
export function parseGitDiffNameStatusFiles(data: string, repoPath: string): GitFile[] | undefined {
using sw = maybeStopWatch('Git.parseDiffNameStatusFiles', { log: false, logLevel: 'debug' });
if (!data) return undefined;
const sw = maybeStopWatch('parseDiffNameStatusFiles', { log: false, logLevel: 'debug' });
const files: GitFile[] = [];
let status;
@ -172,16 +173,15 @@ export function parseDiffNameStatusFiles(data: string, repoPath: string): GitFil
});
}
sw?.stop({ suffix: ` parsed ${pluralize('file', files.length)}` });
sw?.stop({ suffix: ` parsed ${files.length} files` });
return files;
}
export function parseDiffShortStat(data: string): GitDiffShortStat | undefined {
export function parseGitDiffShortStat(data: string): GitDiffShortStat | undefined {
using sw = maybeStopWatch('Git.parseDiffShortStat', { log: false, logLevel: 'debug' });
if (!data) return undefined;
const sw = maybeStopWatch('parseDiffShortStat', { log: false, logLevel: 'debug' });
const match = shortStatDiffRegex.exec(data);
if (match == null) return undefined;
@ -194,9 +194,7 @@ export function parseDiffShortStat(data: string): GitDiffShortStat | undefined {
};
sw?.stop({
suffix: ` parsed ${pluralize('file', diffShortStat.changedFiles)}, +${diffShortStat.additions} -${
diffShortStat.deletions
}`,
suffix: ` parsed ${diffShortStat.changedFiles} files, +${diffShortStat.additions} -${diffShortStat.deletions}`,
});
return diffShortStat;

+ 473
- 506
src/git/parsers/logParser.ts
文件差異過大導致無法顯示
查看文件


+ 109
- 110
src/git/parsers/reflogParser.ts 查看文件

@ -1,4 +1,4 @@
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import type { GitReflog } from '../models/reflog';
import { GitReflogRecord } from '../models/reflog';
@ -10,119 +10,118 @@ const reflogHEADRegex = /.*?\/?HEAD$/;
const lb = '%x3c'; // `%x${'<'.charCodeAt(0).toString(16)}`;
const rb = '%x3e'; // `%x${'>'.charCodeAt(0).toString(16)}`;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitReflogParser {
static defaultFormat = [
`${lb}r${rb}%H`, // ref
`${lb}d${rb}%gD`, // reflog selector (with iso8601 timestamp)
`${lb}s${rb}%gs`, // reflog subject
// `${lb}n${rb}%D` // ref names
].join('');
@debug({ args: false })
static parse(
data: string,
repoPath: string,
commands: string[],
limit: number,
totalLimit: number,
): GitReflog | undefined {
if (!data) return undefined;
const records: GitReflogRecord[] = [];
let sha;
let selector;
let date;
let command;
let commandArgs;
let details;
let head;
let headDate;
let headSha;
let count = 0;
let total = 0;
let recordDate;
let record: GitReflogRecord | undefined;
let match;
do {
match = reflogRegex.exec(data);
if (match == null) break;
[, sha, selector, date, command, commandArgs, details] = match;
total++;
if (record !== undefined) {
// If the next record has the same sha as the previous, use it if it is not pointing to just HEAD and the previous is
export const parseGitRefLogDefaultFormat = [
`${lb}r${rb}%H`, // ref
`${lb}d${rb}%gD`, // reflog selector (with iso8601 timestamp)
`${lb}s${rb}%gs`, // reflog subject
// `${lb}n${rb}%D` // ref names
].join('');
export function parseGitRefLog(
data: string,
repoPath: string,
commands: string[],
limit: number,
totalLimit: number,
): GitReflog | undefined {
using sw = maybeStopWatch(`Git.parseRefLog(${repoPath})`, { log: false, logLevel: 'debug' });
if (!data) return undefined;
const records: GitReflogRecord[] = [];
let sha;
let selector;
let date;
let command;
let commandArgs;
let details;
let head;
let headDate;
let headSha;
let count = 0;
let total = 0;
let recordDate;
let record: GitReflogRecord | undefined;
let match;
do {
match = reflogRegex.exec(data);
if (match == null) break;
[, sha, selector, date, command, commandArgs, details] = match;
total++;
if (record !== undefined) {
// If the next record has the same sha as the previous, use it if it is not pointing to just HEAD and the previous is
if (
sha === record.sha &&
(date !== recordDate || !reflogHEADRegex.test(record.selector) || reflogHEADRegex.test(selector))
) {
continue;
}
if (sha !== record.sha) {
if (
sha === record.sha &&
(date !== recordDate || !reflogHEADRegex.test(record.selector) || reflogHEADRegex.test(selector))
head != null &&
headDate === recordDate &&
headSha == record.sha &&
reflogHEADRegex.test(record.selector)
) {
continue;
record.update(sha, head);
} else {
record.update(sha);
}
if (sha !== record.sha) {
if (
head != null &&
headDate === recordDate &&
headSha == record.sha &&
reflogHEADRegex.test(record.selector)
) {
record.update(sha, head);
} else {
record.update(sha);
}
records.push(record);
record = undefined;
recordDate = undefined;
count++;
if (limit !== 0 && count >= limit) break;
}
}
if (command === 'HEAD') {
head = selector;
headDate = date;
headSha = sha;
continue;
}
records.push(record);
record = undefined;
recordDate = undefined;
if (commands.includes(command)) {
record = new GitReflogRecord(
repoPath,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${sha}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${selector}`.substr(1),
new Date(date),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${command}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
commandArgs == null || commandArgs.length === 0 ? undefined : commandArgs.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
details == null || details.length === 0 ? undefined : details.substr(1),
);
recordDate = date;
count++;
if (limit !== 0 && count >= limit) break;
}
} while (true);
// Ensure the regex state is reset
reflogRegex.lastIndex = 0;
return {
repoPath: repoPath,
records: records,
count: count,
total: total,
limit: limit,
hasMore: (limit !== 0 && count >= limit) || (totalLimit !== 0 && total >= totalLimit),
};
}
}
if (command === 'HEAD') {
head = selector;
headDate = date;
headSha = sha;
continue;
}
if (commands.includes(command)) {
record = new GitReflogRecord(
repoPath,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${sha}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${selector}`.substr(1),
new Date(date),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${command}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
commandArgs == null || commandArgs.length === 0 ? undefined : commandArgs.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
details == null || details.length === 0 ? undefined : details.substr(1),
);
recordDate = date;
}
} while (true);
// Ensure the regex state is reset
reflogRegex.lastIndex = 0;
sw?.stop({ suffix: ` parsed ${records.length} records` });
return {
repoPath: repoPath,
records: records,
count: count,
total: total,
limit: limit,
hasMore: (limit !== 0 && count >= limit) || (totalLimit !== 0 && total >= totalLimit),
};
}

+ 60
- 61
src/git/parsers/remoteParser.ts 查看文件

@ -1,4 +1,4 @@
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import type { GitRemoteType } from '../models/remote';
import { GitRemote } from '../models/remote';
import type { getRemoteProviderMatcher } from '../remotes/remoteProviders';
@ -7,68 +7,67 @@ const emptyStr = '';
const remoteRegex = /^(.*)\t(.*)\s\((.*)\)$/gm;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitRemoteParser {
@debug({ args: false, singleLine: true })
static parse(
data: string,
repoPath: string,
remoteProviderMatcher: ReturnType<typeof getRemoteProviderMatcher>,
): GitRemote[] | undefined {
if (!data) return undefined;
const remotes = new Map<string, GitRemote>();
let name;
let url;
let type;
let scheme;
let domain;
let path;
let remote: GitRemote | undefined;
let match;
do {
match = remoteRegex.exec(data);
if (match == null) break;
[, name, url, type] = match;
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
name = ` ${name}`.substr(1);
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
url = ` ${url}`.substr(1);
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
type = ` ${type}`.substr(1);
[scheme, domain, path] = parseGitRemoteUrl(url);
remote = remotes.get(name);
if (remote == null) {
remote = new GitRemote(repoPath, name, scheme, domain, path, remoteProviderMatcher(url, domain, path), [
{ url: url, type: type as GitRemoteType },
]);
remotes.set(name, remote);
} else {
remote.urls.push({ url: url, type: type as GitRemoteType });
if (remote.provider != null && type !== 'push') continue;
if (remote.provider?.hasRichIntegration()) {
remote.provider.dispose();
}
const provider = remoteProviderMatcher(url, domain, path);
if (provider == null) continue;
remote = new GitRemote(repoPath, name, scheme, domain, path, provider, remote.urls);
remotes.set(name, remote);
export function parseGitRemotes(
data: string,
repoPath: string,
remoteProviderMatcher: ReturnType<typeof getRemoteProviderMatcher>,
): GitRemote[] {
using sw = maybeStopWatch(`Git.parseRemotes(${repoPath})`, { log: false, logLevel: 'debug' });
if (!data) return [];
const remotes = new Map<string, GitRemote>();
let name;
let url;
let type;
let scheme;
let domain;
let path;
let remote: GitRemote | undefined;
let match;
do {
match = remoteRegex.exec(data);
if (match == null) break;
[, name, url, type] = match;
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
name = ` ${name}`.substr(1);
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
url = ` ${url}`.substr(1);
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
type = ` ${type}`.substr(1);
[scheme, domain, path] = parseGitRemoteUrl(url);
remote = remotes.get(name);
if (remote == null) {
remote = new GitRemote(repoPath, name, scheme, domain, path, remoteProviderMatcher(url, domain, path), [
{ url: url, type: type as GitRemoteType },
]);
remotes.set(name, remote);
} else {
remote.urls.push({ url: url, type: type as GitRemoteType });
if (remote.provider != null && type !== 'push') continue;
if (remote.provider?.hasRichIntegration()) {
remote.provider.dispose();
}
} while (true);
return [...remotes.values()];
}
const provider = remoteProviderMatcher(url, domain, path);
if (provider == null) continue;
remote = new GitRemote(repoPath, name, scheme, domain, path, provider, remote.urls);
remotes.set(name, remote);
}
} while (true);
sw?.stop({ suffix: ` parsed ${remotes.size} remotes` });
return [...remotes.values()];
}
// Test git urls

+ 117
- 117
src/git/parsers/statusParser.ts 查看文件

@ -1,5 +1,5 @@
import { debug } from '../../system/decorators/log';
import { normalizePath } from '../../system/path';
import { maybeStopWatch } from '../../system/stopwatch';
import { GitStatus, GitStatusFile } from '../models/status';
const emptyStr = '';
@ -7,137 +7,137 @@ const emptyStr = '';
const aheadStatusV1Regex = /(?:ahead ([0-9]+))/;
const behindStatusV1Regex = /(?:behind ([0-9]+))/;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitStatusParser {
@debug({ args: false, singleLine: true })
static parse(data: string, repoPath: string, porcelainVersion: number): GitStatus | undefined {
if (!data) return undefined;
export function parseGitStatus(data: string, repoPath: string, porcelainVersion: number): GitStatus | undefined {
using sw = maybeStopWatch(`Git.parseStatus(${repoPath}, v=${porcelainVersion})`, {
log: false,
logLevel: 'debug',
});
if (!data) return undefined;
const lines = data.split('\n').filter(<T>(i?: T): i is T => Boolean(i));
if (lines.length === 0) return undefined;
const lines = data.split('\n').filter(<T>(i?: T): i is T => Boolean(i));
if (lines.length === 0) return undefined;
if (porcelainVersion < 2) return this.parseV1(lines, repoPath);
const status = porcelainVersion < 2 ? parseStatusV1(lines, repoPath) : parseStatusV2(lines, repoPath);
return this.parseV2(lines, repoPath);
}
sw?.stop({ suffix: ` parsed ${status.files.length} files` });
@debug({ args: false, singleLine: true })
private static parseV1(lines: string[], repoPath: string): GitStatus {
let branch: string | undefined;
const files = [];
const state = {
ahead: 0,
behind: 0,
};
let upstream;
let position = -1;
while (++position < lines.length) {
const line = lines[position];
// Header
if (line.startsWith('##')) {
const lineParts = line.split(' ');
[branch, upstream] = lineParts[1].split('...');
if (lineParts.length > 2) {
const upstreamStatus = lineParts.slice(2).join(' ');
const aheadStatus = aheadStatusV1Regex.exec(upstreamStatus);
state.ahead = aheadStatus == null ? 0 : Number(aheadStatus[1]) || 0;
const behindStatus = behindStatusV1Regex.exec(upstreamStatus);
state.behind = behindStatus == null ? 0 : Number(behindStatus[1]) || 0;
}
return status;
}
function parseStatusV1(lines: string[], repoPath: string): GitStatus {
let branch: string | undefined;
const files = [];
const state = {
ahead: 0,
behind: 0,
};
let upstream;
let position = -1;
while (++position < lines.length) {
const line = lines[position];
// Header
if (line.startsWith('##')) {
const lineParts = line.split(' ');
[branch, upstream] = lineParts[1].split('...');
if (lineParts.length > 2) {
const upstreamStatus = lineParts.slice(2).join(' ');
const aheadStatus = aheadStatusV1Regex.exec(upstreamStatus);
state.ahead = aheadStatus == null ? 0 : Number(aheadStatus[1]) || 0;
const behindStatus = behindStatusV1Regex.exec(upstreamStatus);
state.behind = behindStatus == null ? 0 : Number(behindStatus[1]) || 0;
}
} else {
const rawStatus = line.substring(0, 2);
const fileName = line.substring(3);
if (rawStatus.startsWith('R') || rawStatus.startsWith('C')) {
const [file1, file2] = fileName.replace(/"/g, emptyStr).split('->');
files.push(parseStatusFile(repoPath, rawStatus, file2.trim(), file1.trim()));
} else {
const rawStatus = line.substring(0, 2);
const fileName = line.substring(3);
if (rawStatus.startsWith('R') || rawStatus.startsWith('C')) {
const [file1, file2] = fileName.replace(/"/g, emptyStr).split('->');
files.push(this.parseStatusFile(repoPath, rawStatus, file2.trim(), file1.trim()));
} else {
files.push(this.parseStatusFile(repoPath, rawStatus, fileName));
}
files.push(parseStatusFile(repoPath, rawStatus, fileName));
}
}
return new GitStatus(normalizePath(repoPath), branch ?? emptyStr, emptyStr, files, state, upstream);
}
@debug({ args: false, singleLine: true })
private static parseV2(lines: string[], repoPath: string): GitStatus {
let branch: string | undefined;
const files = [];
let sha: string | undefined;
const state = {
ahead: 0,
behind: 0,
};
let upstream;
let position = -1;
while (++position < lines.length) {
const line = lines[position];
// Headers
if (line.startsWith('#')) {
const lineParts = line.split(' ');
switch (lineParts[1]) {
case 'branch.oid':
sha = lineParts[2];
break;
case 'branch.head':
branch = lineParts[2];
break;
case 'branch.upstream':
upstream = lineParts[2];
break;
case 'branch.ab':
state.ahead = Number(lineParts[2].substring(1));
state.behind = Number(lineParts[3].substring(1));
break;
}
} else {
const lineParts = line.split(' ');
switch (lineParts[0][0]) {
case '1': // normal
files.push(this.parseStatusFile(repoPath, lineParts[1], lineParts.slice(8).join(' ')));
break;
case '2': {
// rename
const file = lineParts.slice(9).join(' ').split('\t');
files.push(this.parseStatusFile(repoPath, lineParts[1], file[0], file[1]));
break;
}
case 'u': // unmerged
files.push(this.parseStatusFile(repoPath, lineParts[1], lineParts.slice(10).join(' ')));
break;
case '?': // untracked
files.push(this.parseStatusFile(repoPath, '??', lineParts.slice(1).join(' ')));
break;
return new GitStatus(normalizePath(repoPath), branch ?? emptyStr, emptyStr, files, state, upstream);
}
function parseStatusV2(lines: string[], repoPath: string): GitStatus {
let branch: string | undefined;
const files = [];
let sha: string | undefined;
const state = {
ahead: 0,
behind: 0,
};
let upstream;
let position = -1;
while (++position < lines.length) {
const line = lines[position];
// Headers
if (line.startsWith('#')) {
const lineParts = line.split(' ');
switch (lineParts[1]) {
case 'branch.oid':
sha = lineParts[2];
break;
case 'branch.head':
branch = lineParts[2];
break;
case 'branch.upstream':
upstream = lineParts[2];
break;
case 'branch.ab':
state.ahead = Number(lineParts[2].substring(1));
state.behind = Number(lineParts[3].substring(1));
break;
}
} else {
const lineParts = line.split(' ');
switch (lineParts[0][0]) {
case '1': // normal
files.push(parseStatusFile(repoPath, lineParts[1], lineParts.slice(8).join(' ')));
break;
case '2': {
// rename
const file = lineParts.slice(9).join(' ').split('\t');
files.push(parseStatusFile(repoPath, lineParts[1], file[0], file[1]));
break;
}
case 'u': // unmerged
files.push(parseStatusFile(repoPath, lineParts[1], lineParts.slice(10).join(' ')));
break;
case '?': // untracked
files.push(parseStatusFile(repoPath, '??', lineParts.slice(1).join(' ')));
break;
}
}
return new GitStatus(normalizePath(repoPath), branch ?? emptyStr, sha ?? emptyStr, files, state, upstream);
}
static parseStatusFile(
repoPath: string,
rawStatus: string,
fileName: string,
originalFileName?: string,
): GitStatusFile {
let x = !rawStatus.startsWith('.') ? rawStatus[0].trim() : undefined;
if (x == null || x.length === 0) {
x = undefined;
}
return new GitStatus(normalizePath(repoPath), branch ?? emptyStr, sha ?? emptyStr, files, state, upstream);
}
let y = undefined;
if (rawStatus.length > 1) {
y = rawStatus[1] !== '.' ? rawStatus[1].trim() : undefined;
if (y == null || y.length === 0) {
y = undefined;
}
}
function parseStatusFile(
repoPath: string,
rawStatus: string,
fileName: string,
originalFileName?: string,
): GitStatusFile {
let x = !rawStatus.startsWith('.') ? rawStatus[0].trim() : undefined;
if (x == null || x.length === 0) {
x = undefined;
}
return new GitStatusFile(repoPath, x, y, fileName, originalFileName);
let y = undefined;
if (rawStatus.length > 1) {
y = rawStatus[1] !== '.' ? rawStatus[1].trim() : undefined;
if (y == null || y.length === 0) {
y = undefined;
}
}
return new GitStatusFile(repoPath, x, y, fileName, originalFileName);
}

+ 50
- 51
src/git/parsers/tagParser.ts 查看文件

@ -1,4 +1,4 @@
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import { GitTag } from '../models/tag';
const tagRegex = /^<n>(.+)<\*r>(.*)<r>(.*)<d>(.*)<ad>(.*)<s>(.*)$/gm;
@ -7,54 +7,53 @@ const tagRegex = /^(.+)<\*r>(.*)(.*)(.*)(.*)(.*)$/gm;
const lb = '%3c'; // `%${'<'.charCodeAt(0).toString(16)}`;
const rb = '%3e'; // `%${'>'.charCodeAt(0).toString(16)}`;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitTagParser {
static defaultFormat = [
`${lb}n${rb}%(refname)`, // tag name
`${lb}*r${rb}%(*objectname)`, // ref
`${lb}r${rb}%(objectname)`, // ref
`${lb}d${rb}%(creatordate:iso8601)`, // created date
`${lb}ad${rb}%(authordate:iso8601)`, // author date
`${lb}s${rb}%(subject)`, // message
].join('');
@debug({ args: false, singleLine: true })
static parse(data: string, repoPath: string): GitTag[] | undefined {
if (!data) return undefined;
const tags: GitTag[] = [];
let name;
let ref1;
let ref2;
let date;
let commitDate;
let message;
let match;
do {
match = tagRegex.exec(data);
if (match == null) break;
[, name, ref1, ref2, date, commitDate, message] = match;
// Strip off refs/tags/
name = name.substr(10);
tags.push(
new GitTag(
repoPath,
name,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${ref1 || ref2}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${message}`.substr(1),
date ? new Date(date) : undefined,
commitDate == null || commitDate.length === 0 ? undefined : new Date(commitDate),
),
);
} while (true);
return tags;
}
export const parseGitTagsDefaultFormat = [
`${lb}n${rb}%(refname)`, // tag name
`${lb}*r${rb}%(*objectname)`, // ref
`${lb}r${rb}%(objectname)`, // ref
`${lb}d${rb}%(creatordate:iso8601)`, // created date
`${lb}ad${rb}%(authordate:iso8601)`, // author date
`${lb}s${rb}%(subject)`, // message
].join('');
export function parseGitTags(data: string, repoPath: string): GitTag[] {
using sw = maybeStopWatch(`Git.parseTags(${repoPath})`, { log: false, logLevel: 'debug' });
const tags: GitTag[] = [];
if (!data) return tags;
let name;
let ref1;
let ref2;
let date;
let commitDate;
let message;
let match;
do {
match = tagRegex.exec(data);
if (match == null) break;
[, name, ref1, ref2, date, commitDate, message] = match;
// Strip off refs/tags/
name = name.substr(10);
tags.push(
new GitTag(
repoPath,
name,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${ref1 || ref2}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
` ${message}`.substr(1),
date ? new Date(date) : undefined,
commitDate == null || commitDate.length === 0 ? undefined : new Date(commitDate),
),
);
} while (true);
sw?.stop({ suffix: ` parsed ${tags.length} tags` });
return tags;
}

+ 33
- 34
src/git/parsers/treeParser.ts 查看文件

@ -1,40 +1,39 @@
import { debug } from '../../system/decorators/log';
import { maybeStopWatch } from '../../system/stopwatch';
import type { GitTreeEntry } from '../models/tree';
const emptyStr = '';
const treeRegex = /(?:.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+)/gm;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitTreeParser {
@debug({ args: false, singleLine: true })
static parse(data: string | undefined): GitTreeEntry[] | undefined {
if (!data) return undefined;
const trees: GitTreeEntry[] = [];
let type;
let sha;
let size;
let filePath;
let match;
do {
match = treeRegex.exec(data);
if (match == null) break;
[, type, sha, size, filePath] = match;
trees.push({
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
commitSha: sha == null || sha.length === 0 ? emptyStr : ` ${sha}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
path: filePath == null || filePath.length === 0 ? emptyStr : ` ${filePath}`.substr(1),
size: Number(size) || 0,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
type: (type == null || type.length === 0 ? emptyStr : ` ${type}`.substr(1)) as 'blob' | 'tree',
});
} while (true);
return trees;
}
export function parseGitTree(data: string | undefined): GitTreeEntry[] {
using sw = maybeStopWatch(`Git.parseTree`, { log: false, logLevel: 'debug' });
const trees: GitTreeEntry[] = [];
if (!data) return trees;
let type;
let sha;
let size;
let filePath;
let match;
do {
match = treeRegex.exec(data);
if (match == null) break;
[, type, sha, size, filePath] = match;
trees.push({
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
commitSha: sha == null || sha.length === 0 ? emptyStr : ` ${sha}`.substr(1),
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
path: filePath == null || filePath.length === 0 ? emptyStr : ` ${filePath}`.substr(1),
size: Number(size) || 0,
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
type: (type == null || type.length === 0 ? emptyStr : ` ${type}`.substr(1)) as 'blob' | 'tree',
});
} while (true);
sw?.stop({ suffix: ` parsed ${trees.length} trees` });
return trees;
}

+ 74
- 75
src/git/parsers/worktreeParser.ts 查看文件

@ -1,6 +1,6 @@
import { Uri } from 'vscode';
import { debug } from '../../system/decorators/log';
import { normalizePath } from '../../system/path';
import { maybeStopWatch } from '../../system/stopwatch';
import { getLines } from '../../system/string';
import { GitWorktree } from '../models/worktree';
@ -14,88 +14,87 @@ interface WorktreeEntry {
prunable?: boolean | string;
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class GitWorktreeParser {
@debug({ args: false, singleLine: true })
static parse(data: string, repoPath: string): GitWorktree[] {
if (!data) return [];
export function parseGitWorktrees(data: string, repoPath: string): GitWorktree[] {
using sw = maybeStopWatch(`Git.parseWorktrees(${repoPath})`, { log: false, logLevel: 'debug' });
if (repoPath != null) {
repoPath = normalizePath(repoPath);
}
const worktrees: GitWorktree[] = [];
const worktrees: GitWorktree[] = [];
if (!data) return worktrees;
let entry: Partial<WorktreeEntry> | undefined = undefined;
let line: string;
let index: number;
let key: string;
let value: string;
let locked: string;
let prunable: string;
let main = true; // the first worktree is the main worktree
if (repoPath != null) {
repoPath = normalizePath(repoPath);
}
for (line of getLines(data)) {
index = line.indexOf(' ');
if (index === -1) {
key = line;
value = '';
} else {
key = line.substring(0, index);
value = line.substring(index + 1);
}
let entry: Partial<WorktreeEntry> | undefined = undefined;
let line: string;
let index: number;
let key: string;
let value: string;
let locked: string;
let prunable: string;
let main = true; // the first worktree is the main worktree
if (key.length === 0 && entry != null) {
worktrees.push(
new GitWorktree(
main,
entry.bare ? 'bare' : entry.detached ? 'detached' : 'branch',
repoPath,
Uri.file(entry.path!),
entry.locked ?? false,
entry.prunable ?? false,
entry.sha,
entry.branch,
),
);
for (line of getLines(data)) {
index = line.indexOf(' ');
if (index === -1) {
key = line;
value = '';
} else {
key = line.substring(0, index);
value = line.substring(index + 1);
}
entry = undefined;
main = false;
continue;
}
if (key.length === 0 && entry != null) {
worktrees.push(
new GitWorktree(
main,
entry.bare ? 'bare' : entry.detached ? 'detached' : 'branch',
repoPath,
Uri.file(entry.path!),
entry.locked ?? false,
entry.prunable ?? false,
entry.sha,
entry.branch,
),
);
if (entry == null) {
entry = {};
}
entry = undefined;
main = false;
continue;
}
switch (key) {
case 'worktree':
entry.path = value;
break;
case 'bare':
entry.bare = true;
break;
case 'HEAD':
entry.sha = value;
break;
case 'branch':
// Strip off refs/heads/
entry.branch = value.substr(11);
break;
case 'detached':
entry.detached = true;
break;
case 'locked':
[, locked] = value.split(' ', 2);
entry.locked = locked?.trim() || true;
break;
case 'prunable':
[, prunable] = value.split(' ', 2);
entry.prunable = prunable?.trim() || true;
break;
}
if (entry == null) {
entry = {};
}
return worktrees;
switch (key) {
case 'worktree':
entry.path = value;
break;
case 'bare':
entry.bare = true;
break;
case 'HEAD':
entry.sha = value;
break;
case 'branch':
// Strip off refs/heads/
entry.branch = value.substr(11);
break;
case 'detached':
entry.detached = true;
break;
case 'locked':
[, locked] = value.split(' ', 2);
entry.locked = locked?.trim() || true;
break;
case 'prunable':
[, prunable] = value.split(' ', 2);
entry.prunable = prunable?.trim() || true;
break;
}
}
sw?.stop({ suffix: ` parsed ${worktrees.length} worktrees` });
return worktrees;
}

+ 28
- 38
src/system/stopwatch.ts 查看文件

@ -3,7 +3,10 @@ import type { LogProvider } from './logger';
import { defaultLogProvider } from './logger';
import type { LogLevel } from './logger.constants';
import type { LogScope } from './logger.scope';
import { getNextLogScopeId } from './logger.scope';
import { getNewLogScope } from './logger.scope';
(Symbol as any).dispose ??= Symbol('Symbol.dispose');
(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose');
type StopwatchLogOptions = { message?: string; suffix?: string };
type StopwatchOptions = {
@ -13,8 +16,8 @@ type StopwatchOptions = {
};
type StopwatchLogLevel = Exclude<LogLevel, 'off'>;
export class Stopwatch {
private readonly instance = `[${String(getNextLogScopeId()).padStart(5)}] `;
export class Stopwatch implements Disposable {
private readonly logScope: LogScope;
private readonly logLevel: StopwatchLogLevel;
private readonly logProvider: LogProvider;
@ -23,17 +26,10 @@ export class Stopwatch {
return this._time;
}
constructor(
private readonly scope: string | LogScope | undefined,
options?: StopwatchOptions,
...params: any[]
) {
let logScope;
if (typeof scope !== 'string') {
logScope = scope;
scope = '';
this.instance = '';
}
private _stopped = false;
constructor(scope: string | LogScope | undefined, options?: StopwatchOptions, ...params: any[]) {
this.logScope = scope != null && typeof scope !== 'string' ? scope : getNewLogScope(scope ?? '');
let logOptions: StopwatchLogOptions | undefined;
if (typeof options?.log === 'boolean') {
@ -52,57 +48,51 @@ export class Stopwatch {
if (params.length) {
this.logProvider.log(
this.logLevel,
logScope,
`${this.instance}${scope}${logOptions.message ?? ''}${logOptions.suffix ?? ''}`,
this.logScope,
`${logOptions.message ?? ''}${logOptions.suffix ?? ''}`,
...params,
);
} else {
this.logProvider.log(
this.logLevel,
logScope,
`${this.instance}${scope}${logOptions.message ?? ''}${logOptions.suffix ?? ''}`,
this.logScope,
`${logOptions.message ?? ''}${logOptions.suffix ?? ''}`,
);
}
}
}
[Symbol.dispose](): void {
this.stop();
}
elapsed(): number {
const [secs, nanosecs] = hrtime(this._time);
return secs * 1000 + Math.floor(nanosecs / 1000000);
}
log(options?: StopwatchLogOptions): void {
this.logCore(this.scope, options, false);
this.logCore(options, false);
}
restart(options?: StopwatchLogOptions): void {
this.logCore(this.scope, options, true);
this.logCore(options, true);
this._time = hrtime();
this._stopped = false;
}
stop(options?: StopwatchLogOptions): void {
if (this._stopped) return;
this.restart(options);
this._stopped = true;
}
private logCore(
scope: string | LogScope | undefined,
options: StopwatchLogOptions | undefined,
logTotalElapsed: boolean,
): void {
private logCore(options: StopwatchLogOptions | undefined, logTotalElapsed: boolean): void {
if (!this.logProvider.enabled(this.logLevel)) return;
let logScope;
if (typeof scope !== 'string') {
logScope = scope;
scope = '';
}
if (!logTotalElapsed) {
this.logProvider.log(
this.logLevel,
logScope,
`${this.instance}${scope}${options?.message ?? ''}${options?.suffix ?? ''}`,
);
this.logProvider.log(this.logLevel, this.logScope, `${options?.message ?? ''}${options?.suffix ?? ''}`);
return;
}
@ -110,10 +100,10 @@ export class Stopwatch {
const [secs, nanosecs] = hrtime(this._time);
const ms = secs * 1000 + Math.floor(nanosecs / 1000000);
const prefix = `${this.instance}${scope}${options?.message ?? ''}`;
const prefix = options?.message ?? '';
this.logProvider.log(
ms > 250 ? 'warn' : this.logLevel,
logScope,
this.logScope,
`${prefix ? `${prefix} ` : ''}[${ms}ms]${options?.suffix ?? ''}`,
);
}

+ 1
- 1
src/webviews/apps/tsconfig.json 查看文件

@ -2,7 +2,7 @@
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react",
"lib": ["dom", "dom.iterable", "es2022"],
"lib": ["dom", "dom.iterable", "es2022", "esnext.disposable"],
"outDir": "../../",
"paths": {
"@env/*": ["src/env/browser/*"]

+ 1
- 1
tsconfig.base.json 查看文件

@ -6,7 +6,7 @@
"forceConsistentCasingInFileNames": true,
"incremental": true,
"isolatedModules": true,
"lib": ["es2022"],
"lib": ["es2022", "esnext.disposable"],
"module": "esnext",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,

+ 1
- 1
tsconfig.browser.json 查看文件

@ -1,7 +1,7 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "es2022"],
"lib": ["dom", "dom.iterable", "es2022", "esnext.disposable"],
"paths": {
"@env/*": ["src/env/browser/*"],
"path": ["node_modules/path-browserify"]

Loading…
取消
儲存