浏览代码

Combines blame file vs contents

Providers can now choose how to handle editor contents blaming
Simplies "commit" line model to just have line & originalLine
  - Not really accurate for log parsing, but close enough
Adds forces line blame to improve performance
main
Eric Amodio 2 年前
父节点
当前提交
e04a58db9c
共有 23 个文件被更改,包括 184 次插入146 次删除
  1. +3
    -5
      src/annotations/blameAnnotationProvider.ts
  2. +2
    -4
      src/annotations/gutterBlameAnnotationProvider.ts
  3. +1
    -6
      src/annotations/gutterChangesAnnotationProvider.ts
  4. +1
    -1
      src/annotations/gutterHeatmapBlameAnnotationProvider.ts
  5. +3
    -7
      src/codelens/codeLensProvider.ts
  6. +1
    -7
      src/commands/copyMessageToClipboard.ts
  7. +1
    -3
      src/commands/copyShaToClipboard.ts
  8. +2
    -4
      src/commands/diffLineWithWorking.ts
  9. +1
    -3
      src/commands/openCommitOnRemote.ts
  10. +20
    -14
      src/env/node/git/localGitProvider.ts
  11. +6
    -3
      src/git/gitProvider.ts
  12. +10
    -6
      src/git/gitProviderService.ts
  13. +2
    -8
      src/git/models/commit.ts
  14. +3
    -3
      src/git/parsers/blameParser.ts
  15. +4
    -8
      src/git/parsers/logParser.ts
  16. +2
    -2
      src/hovers/hovers.ts
  17. +2
    -2
      src/hovers/lineHoverController.ts
  18. +105
    -41
      src/premium/github/githubGitProvider.ts
  19. +1
    -1
      src/statusbar/statusBarController.ts
  20. +9
    -13
      src/trackers/gitLineTracker.ts
  21. +1
    -1
      src/views/nodes/commitFileNode.ts
  22. +1
    -1
      src/views/nodes/fileRevisionAsCommitNode.ts
  23. +3
    -3
      src/views/nodes/lineHistoryNode.ts

+ 3
- 5
src/annotations/blameAnnotationProvider.ts 查看文件

@ -21,9 +21,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
) {
super(annotationType, editor, trackedDocument);
this.blame = editor.document.isDirty
? this.container.git.getBlameForFileContents(this.trackedDocument.uri, editor.document.getText())
: this.container.git.getBlameForFile(this.trackedDocument.uri);
this.blame = this.container.git.getBlame(this.trackedDocument.uri, editor.document);
if (editor.document.isDirty) {
trackedDocument.setForceDirtyStateChangeOnNextDocumentChange();
@ -174,8 +172,8 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
private async getDetailsHoverMessage(commit: GitCommit, document: TextDocument) {
let editorLine = this.editor.selection.active.line;
const line = editorLine + 1;
const commitLine = commit.lines.find(l => l.to.line === line) ?? commit.lines[0];
editorLine = commitLine.from.line - 1;
const commitLine = commit.lines.find(l => l.line === line) ?? commit.lines[0];
editorLine = commitLine.originalLine - 1;
return Hovers.detailsMessage(
commit,

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

@ -89,7 +89,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
for (const l of blame.lines) {
// editor lines are 0-based
const editorLine = l.to.line - 1;
const editorLine = l.line - 1;
if (previousSha === l.sha) {
if (gutter == null) continue;
@ -202,9 +202,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
const highlightDecorationRanges = Arrays.filterMap(blame.lines, l =>
l.sha === sha
? // editor lines are 0-based
this.editor.document.validateRange(
new Range(l.to.line - 1, 0, l.to.line - 1, Number.MAX_SAFE_INTEGER),
)
this.editor.document.validateRange(new Range(l.line - 1, 0, l.line - 1, Number.MAX_SAFE_INTEGER))
: undefined,
);

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

@ -166,12 +166,7 @@ export class GutterChangesAnnotationProvider extends AnnotationProviderBase
// If we want to only show changes from the specified sha, get the blame so we can compare with "visible" shas
const blame =
context?.sha != null && context?.only
? this.editor?.document.isDirty
? await this.container.git.getBlameForFileContents(
this.trackedDocument.uri,
this.editor.document.getText(),
)
: await this.container.git.getBlameForFile(this.trackedDocument.uri)
? await this.container.git.getBlame(this.trackedDocument.uri, this.editor?.document)
: undefined;
let selection: Selection | undefined;

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

@ -36,7 +36,7 @@ export class GutterHeatmapBlameAnnotationProvider extends BlameAnnotationProvide
let commit: GitCommit | undefined;
for (const l of blame.lines) {
// editor lines are 0-based
const editorLine = l.to.line - 1;
const editorLine = l.line - 1;
commit = blame.commits.get(l.sha);
if (commit == null) continue;

+ 3
- 7
src/codelens/codeLensProvider.ts 查看文件

@ -154,14 +154,10 @@ export class GitCodeLensProvider implements CodeLensProvider {
if (token.isCancellationRequested) return lenses;
if (languageScope.scopes.length === 1 && languageScope.scopes.includes(CodeLensScopes.Document)) {
blame = document.isDirty
? await this.container.git.getBlameForFileContents(gitUri, document.getText())
: await this.container.git.getBlameForFile(gitUri);
blame = await this.container.git.getBlame(gitUri, document);
} else {
[blame, symbols] = await Promise.all([
document.isDirty
? this.container.git.getBlameForFileContents(gitUri, document.getText())
: this.container.git.getBlameForFile(gitUri),
this.container.git.getBlame(gitUri, document),
commands.executeCommand<SymbolInformation[]>(
BuiltInCommands.ExecuteDocumentSymbolProvider,
document.uri,
@ -169,7 +165,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
]);
}
if (blame === undefined || blame.lines.length === 0) return lenses;
if (blame == null || blame?.lines.length === 0) return lenses;
} else if (languageScope.scopes.length !== 1 || !languageScope.scopes.includes(CodeLensScopes.Document)) {
symbols = await commands.executeCommand<SymbolInformation[]>(
BuiltInCommands.ExecuteDocumentSymbolProvider,

+ 1
- 7
src/commands/copyMessageToClipboard.ts 查看文件

@ -73,13 +73,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
if (blameline < 0) return;
try {
const blame = editor?.document.isDirty
? await this.container.git.getBlameForLineContents(
gitUri,
blameline,
editor.document.getText(),
)
: await this.container.git.getBlameForLine(gitUri, blameline);
const blame = await this.container.git.getBlameForLine(gitUri, blameline, editor?.document);
if (blame == null || blame.commit.isUncommitted) return;
void (await GitActions.Commit.copyMessageToClipboard(blame.commit));

+ 1
- 3
src/commands/copyShaToClipboard.ts 查看文件

@ -65,9 +65,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
try {
const gitUri = await GitUri.fromUri(uri);
const blame = editor?.document.isDirty
? await this.container.git.getBlameForLineContents(gitUri, blameline, editor.document.getText())
: await this.container.git.getBlameForLine(gitUri, blameline);
const blame = await this.container.git.getBlameForLine(gitUri, blameline, editor?.document);
if (blame == null) return;
args.sha = blame.commit.sha;

+ 2
- 4
src/commands/diffLineWithWorking.ts 查看文件

@ -39,9 +39,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
if (blameline < 0) return;
try {
const blame = editor?.document.isDirty
? await this.container.git.getBlameForLineContents(gitUri, blameline, editor.document.getText())
: await this.container.git.getBlameForLine(gitUri, blameline);
const blame = await this.container.git.getBlameForLine(gitUri, blameline, editor?.document);
if (blame == null) {
void Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
@ -68,7 +66,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
lhsUri = args.commit.file!.uri;
}
// editor lines are 0-based
args.line = blame.line.to.line - 1;
args.line = blame.line.line - 1;
} catch (ex) {
Logger.error(ex, 'DiffLineWithWorkingCommand', `getBlameForLine(${blameline})`);
void Messages.showGenericErrorMessage('Unable to open compare');

+ 1
- 3
src/commands/openCommitOnRemote.ts 查看文件

@ -71,9 +71,7 @@ export class OpenCommitOnRemoteCommand extends ActiveEditorCommand {
const blameline = editor == null ? 0 : editor.selection.active.line;
if (blameline < 0) return;
const blame = editor?.document.isDirty
? await this.container.git.getBlameForLineContents(gitUri, blameline, editor.document.getText())
: await this.container.git.getBlameForLine(gitUri, blameline);
const blame = await this.container.git.getBlameForLine(gitUri, blameline, editor?.document);
if (blame == null) {
void Messages.showFileNotUnderSourceControlWarningMessage(
'Unable to open commit on remote provider',

+ 20
- 14
src/env/node/git/localGitProvider.ts 查看文件

@ -9,6 +9,7 @@ import {
extensions,
FileType,
Range,
TextDocument,
Uri,
window,
workspace,
@ -905,9 +906,11 @@ export class LocalGitProvider implements GitProvider, Disposable {
@gate()
@log()
async getBlameForFile(uri: GitUri): Promise<GitBlame | undefined> {
async getBlame(uri: GitUri, document?: TextDocument | undefined): Promise<GitBlame | undefined> {
const cc = Logger.getCorrelationContext();
if (document?.isDirty) return this.getBlameContents(uri, document.getText());
let key = 'blame';
if (uri.sha != null) {
key += `:${uri.sha}`;
@ -930,7 +933,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
const promise = this.getBlameForFileCore(uri, doc, key, cc);
const promise = this.getBlameCore(uri, doc, key, cc);
if (doc.state != null) {
Logger.debug(cc, `Cache add: '${key}'`);
@ -944,7 +947,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
return promise;
}
private async getBlameForFileCore(
private async getBlameCore(
uri: GitUri,
document: TrackedDocument<GitDocumentState>,
key: string,
@ -986,8 +989,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
@log<LocalGitProvider['getBlameForFileContents']>({ args: { 1: '<contents>' } })
async getBlameForFileContents(uri: GitUri, contents: string): Promise<GitBlame | undefined> {
@log<LocalGitProvider['getBlameContents']>({ args: { 1: '<contents>' } })
async getBlameContents(uri: GitUri, contents: string): Promise<GitBlame | undefined> {
const cc = Logger.getCorrelationContext();
const key = `blame:${md5(contents)}`;
@ -1009,7 +1012,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
const promise = this.getBlameForFileContentsCore(uri, contents, doc, key, cc);
const promise = this.getBlameContentsCore(uri, contents, doc, key, cc);
if (doc.state != null) {
Logger.debug(cc, `Cache add: '${key}'`);
@ -1023,7 +1026,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
return promise;
}
private async getBlameForFileContentsCore(
private async getBlameContentsCore(
uri: GitUri,
contents: string,
document: TrackedDocument<GitDocumentState>,
@ -1071,10 +1074,13 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getBlameForLine(
uri: GitUri,
editorLine: number,
document?: TextDocument | undefined,
options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined> {
if (document?.isDirty) return this.getBlameForLineContents(uri, editorLine, document.getText(), options);
if (!options?.forceSingleLine && this.useCaching) {
const blame = await this.getBlameForFile(uri);
const blame = await this.getBlame(uri);
if (blame == null) return undefined;
let blameLine = blame.lines[editorLine];
@ -1125,7 +1131,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined> {
if (!options?.forceSingleLine && this.useCaching) {
const blame = await this.getBlameForFileContents(uri, contents);
const blame = await this.getBlameContents(uri, contents);
if (blame == null) return undefined;
let blameLine = blame.lines[editorLine];
@ -1170,7 +1176,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log()
async getBlameForRange(uri: GitUri, range: Range): Promise<GitBlameLines | undefined> {
const blame = await this.getBlameForFile(uri);
const blame = await this.getBlame(uri);
if (blame == null) return undefined;
return this.getBlameRange(blame, uri, range);
@ -1178,7 +1184,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log<LocalGitProvider['getBlameForRangeContents']>({ args: { 2: '<contents>' } })
async getBlameForRangeContents(uri: GitUri, range: Range, contents: string): Promise<GitBlameLines | undefined> {
const blame = await this.getBlameForFileContents(uri, contents);
const blame = await this.getBlameContents(uri, contents);
if (blame == null) return undefined;
return this.getBlameRange(blame, uri, range);
@ -1205,7 +1211,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (!shas.has(c.sha)) continue;
const commit = c.with({
lines: c.lines.filter(l => l.to.line >= startLine && l.to.line <= endLine),
lines: c.lines.filter(l => l.line >= startLine && l.line <= endLine),
});
commits.set(c.sha, commit);
@ -2915,7 +2921,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref = blameLine.commit.sha;
relativePath = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? relativePath;
uri = this.getAbsoluteUri(relativePath, repoPath);
editorLine = blameLine.line.from.line - 1;
editorLine = blameLine.line.originalLine - 1;
if (skip === 0 && blameLine.commit.file?.previousSha) {
previous = GitUri.fromFile(relativePath, repoPath, blameLine.commit.file.previousSha);
@ -2944,7 +2950,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref = blameLine.commit.sha;
relativePath = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? relativePath;
uri = this.getAbsoluteUri(relativePath, repoPath);
editorLine = blameLine.line.from.line - 1;
editorLine = blameLine.line.originalLine - 1;
if (skip === 0 && blameLine.commit.file?.previousSha) {
previous = GitUri.fromFile(relativePath, repoPath, blameLine.commit.file.previousSha);

+ 6
- 3
src/git/gitProvider.ts 查看文件

@ -1,4 +1,4 @@
import { Disposable, Event, Range, Uri, WorkspaceFolder } from 'vscode';
import { Disposable, Event, Range, TextDocument, Uri, WorkspaceFolder } from 'vscode';
import { Commit, InputBox } from '../@types/vscode.git';
import { GitUri } from './gitUri';
import {
@ -129,23 +129,26 @@ export interface GitProvider extends Disposable {
/**
* Returns the blame of a file
* @param uri Uri of the file to blame
* @param document Optional TextDocument to blame the contents of if dirty
*/
getBlameForFile(uri: GitUri): Promise<GitBlame | undefined>;
getBlame(uri: GitUri, document?: TextDocument | undefined): Promise<GitBlame | undefined>;
/**
* Returns the blame of a file, using the editor contents (for dirty editors)
* @param uri Uri of the file to blame
* @param contents Contents from the editor to use
*/
getBlameForFileContents(uri: GitUri, contents: string): Promise<GitBlame | undefined>;
getBlameContents(uri: GitUri, contents: string): Promise<GitBlame | undefined>;
/**
* Returns the blame of a single line
* @param uri Uri of the file to blame
* @param editorLine Editor line number (0-based) to blame (Git is 1-based)
* @param document Optional TextDocument to blame the contents of if dirty
* @param options.forceSingleLine Forces blame to be for the single line (rather than the whole file)
*/
getBlameForLine(
uri: GitUri,
editorLine: number,
document?: TextDocument | undefined,
options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined>;
/**

+ 10
- 6
src/git/gitProviderService.ts 查看文件

@ -6,6 +6,7 @@ import {
EventEmitter,
ProgressLocation,
Range,
TextDocument,
TextEditor,
Uri,
window,
@ -849,21 +850,22 @@ export class GitProviderService implements Disposable {
/**
* Returns the blame of a file
* @param uri Uri of the file to blame
* @param document Optional TextDocument to blame the contents of if dirty
*/
async getBlameForFile(uri: GitUri): Promise<GitBlame | undefined> {
async getBlame(uri: GitUri, document?: TextDocument | undefined): Promise<GitBlame | undefined> {
const { provider } = this.getProvider(uri);
return provider.getBlameForFile(uri);
return provider.getBlame(uri, document);
}
@log<GitProviderService['getBlameForFileContents']>({ args: { 1: '<contents>' } })
@log<GitProviderService['getBlameContents']>({ args: { 1: '<contents>' } })
/**
* Returns the blame of a file, using the editor contents (for dirty editors)
* @param uri Uri of the file to blame
* @param contents Contents from the editor to use
*/
async getBlameForFileContents(uri: GitUri, contents: string): Promise<GitBlame | undefined> {
async getBlameContents(uri: GitUri, contents: string): Promise<GitBlame | undefined> {
const { provider } = this.getProvider(uri);
return provider.getBlameForFileContents(uri, contents);
return provider.getBlameContents(uri, contents);
}
@log()
@ -871,15 +873,17 @@ export class GitProviderService implements Disposable {
* Returns the blame of a single line
* @param uri Uri of the file to blame
* @param editorLine Editor line number (0-based) to blame (Git is 1-based)
* @param document Optional TextDocument to blame the contents of if dirty
* @param options.forceSingleLine Forces blame to be for the single line (rather than the whole file)
*/
async getBlameForLine(
uri: GitUri,
editorLine: number,
document?: TextDocument | undefined,
options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined> {
const { provider } = this.getProvider(uri);
return provider.getBlameForLine(uri, editorLine, options);
return provider.getBlameForLine(uri, editorLine, document, options);
}
@log<GitProviderService['getBlameForLineContents']>({ args: { 2: '<contents>' } })

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

@ -494,14 +494,8 @@ export class GitCommitIdentity {
export interface GitCommitLine {
sha: string;
previousSha?: string | undefined;
from: {
line: number;
count: number;
};
to: {
line: number;
count: number;
};
originalLine: number;
line: number;
}
export interface GitCommitStats {

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

@ -254,12 +254,12 @@ export class GitBlameParser {
const line: GitCommitLine = {
sha: entry.sha,
previousSha: commit.file!.previousSha,
from: { line: entry.originalLine + i, count: 1 },
to: { line: entry.line + i, count: 1 },
originalLine: entry.originalLine + i,
line: entry.line + i,
};
commit.lines.push(line);
lines[line.to.line - 1] = line;
lines[line.line - 1] = line;
}
}
}

+ 4
- 8
src/git/parsers/logParser.ts 查看文件

@ -451,14 +451,10 @@ export class GitLogParser {
if (match !== null) {
entry.line = {
sha: entry.sha!,
from: {
line: parseInt(match[1], 10),
count: parseInt(match[2], 10),
},
to: {
line: parseInt(match[3], 10),
count: parseInt(match[4], 10),
},
originalLine: parseInt(match[1], 10),
// count: parseInt(match[2], 10),
line: parseInt(match[3], 10),
// count: parseInt(match[4], 10),
};
}

+ 2
- 2
src/hovers/hovers.ts 查看文件

@ -43,7 +43,7 @@ export namespace Hovers {
}
const line = editorLine + 1;
const commitLine = commit.lines.find(l => l.to.line === line) ?? commit.lines[0];
const commitLine = commit.lines.find(l => l.line === line) ?? commit.lines[0];
let originalPath = commit.file.originalPath;
if (originalPath == null) {
@ -52,7 +52,7 @@ export namespace Hovers {
}
}
editorLine = commitLine.to.line - 1;
editorLine = commitLine.line - 1;
// TODO: Doesn't work with dirty files -- pass in editor? or contents?
let hunkLine = await Container.instance.git.getDiffForLine(uri, editorLine, ref, ref2);

+ 2
- 2
src/hovers/lineHoverController.ts 查看文件

@ -135,8 +135,8 @@ export class LineHoverController implements Disposable {
let editorLine = position.line;
const line = editorLine + 1;
const commitLine = commit.lines.find(l => l.to.line === line) ?? commit.lines[0];
editorLine = commitLine.from.line - 1;
const commitLine = commit.lines.find(l => l.line === line) ?? commit.lines[0];
editorLine = commitLine.originalLine - 1;
const trackedDocument = await this.container.tracker.get(document);
if (trackedDocument == null) return undefined;

+ 105
- 41
src/premium/github/githubGitProvider.ts 查看文件

@ -7,6 +7,7 @@ import {
EventEmitter,
FileType,
Range,
TextDocument,
Uri,
window,
workspace,
@ -344,9 +345,12 @@ export class GitHubGitProvider implements GitProvider, Disposable {
@gate()
@log()
async getBlameForFile(uri: GitUri): Promise<GitBlame | undefined> {
async getBlame(uri: GitUri, document?: TextDocument | undefined): Promise<GitBlame | undefined> {
const cc = Logger.getCorrelationContext();
// TODO@eamodio we need to figure out when to do this, since dirty isn't enough, we need to know if there are any uncommitted changes
if (document?.isDirty) return this.getBlameContents(uri, document.getText());
let key = 'blame';
if (uri.sha != null) {
key += `:${uri.sha}`;
@ -367,7 +371,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
doc.state = new GitDocumentState(doc.key);
}
const promise = this.getBlameForFileCore(uri, doc, key, cc);
const promise = this.getBlameCore(uri, doc, key, cc);
if (doc.state != null) {
Logger.debug(cc, `Cache add: '${key}'`);
@ -381,7 +385,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return promise;
}
private async getBlameForFileCore(
private async getBlameCore(
uri: GitUri,
document: TrackedDocument<GitDocumentState>,
key: string,
@ -393,7 +397,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
const { metadata, github, remotehub, session } = context;
const root = remotehub.getVirtualUri(remotehub.getProviderRootUri(uri));
const file = this.getRelativePath(uri, root);
const relativePath = this.getRelativePath(uri, root);
// const sha = await this.resolveReferenceCore(uri.repoPath!, metadata, uri.sha);
// if (sha == null) return undefined;
@ -404,7 +408,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
metadata.repo.owner,
metadata.repo.name,
ref,
file,
relativePath,
);
const authors = new Map<string, GitBlameAuthor>();
@ -440,7 +444,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
c.message.split('\n', 1)[0],
c.parents.nodes[0]?.oid ? [c.parents.nodes[0]?.oid] : [],
c.message,
new GitFileChange(root.toString(), file, GitFileIndexStatus.Modified),
new GitFileChange(root.toString(), relativePath, GitFileIndexStatus.Modified),
{ changedFiles: c.changedFiles ?? 0, additions: c.additions ?? 0, deletions: c.deletions ?? 0 },
[],
);
@ -449,17 +453,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
}
for (let i = range.startingLine; i <= range.endingLine; i++) {
const line: GitCommitLine = {
sha: c.oid,
from: {
line: i,
count: 1,
},
to: {
line: i,
count: 1,
},
};
const line: GitCommitLine = { sha: c.oid, originalLine: i, line: i };
commit.lines.push(line);
lines[i - 1] = line;
@ -496,9 +490,10 @@ export class GitHubGitProvider implements GitProvider, Disposable {
}
}
@log<GitHubGitProvider['getBlameForFileContents']>({ args: { 1: '<contents>' } })
async getBlameForFileContents(uri: GitUri, _contents: string): Promise<GitBlame | undefined> {
return this.getBlameForFile(uri);
@log<GitHubGitProvider['getBlameContents']>({ args: { 1: '<contents>' } })
async getBlameContents(_uri: GitUri, _contents: string): Promise<GitBlame | undefined> {
// TODO@eamodio figure out how to actually generate a blame given the contents (need to generate a diff)
return undefined; //this.getBlame(uri);
}
@gate()
@ -506,41 +501,110 @@ export class GitHubGitProvider implements GitProvider, Disposable {
async getBlameForLine(
uri: GitUri,
editorLine: number,
_options?: { forceSingleLine?: boolean },
document?: TextDocument | undefined,
options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined> {
const blame = await this.getBlameForFile(uri);
if (blame == null) return undefined;
const cc = Logger.getCorrelationContext();
// TODO@eamodio we need to figure out when to do this, since dirty isn't enough, we need to know if there are any uncommitted changes
if (document?.isDirty) return this.getBlameForLineContents(uri, editorLine, document.getText(), options);
if (!options?.forceSingleLine) {
const blame = await this.getBlame(uri);
if (blame == null) return undefined;
let blameLine = blame.lines[editorLine];
if (blameLine == null) {
if (blame.lines.length !== editorLine) return undefined;
blameLine = blame.lines[editorLine - 1];
}
const commit = blame.commits.get(blameLine.sha);
if (commit == null) return undefined;
let blameLine = blame.lines[editorLine];
if (blameLine == null) {
if (blame.lines.length !== editorLine) return undefined;
blameLine = blame.lines[editorLine - 1];
const author = blame.authors.get(commit.author.name)!;
return {
author: { ...author, lineCount: commit.lines.length },
commit: commit,
line: blameLine,
};
}
const commit = blame.commits.get(blameLine.sha);
if (commit == null) return undefined;
try {
const context = await this.ensureRepositoryContext(uri.repoPath!);
if (context == null) return undefined;
const { metadata, github, remotehub, session } = context;
const author = blame.authors.get(commit.author.name)!;
return {
author: { ...author, lineCount: commit.lines.length },
commit: commit,
line: blameLine,
};
const root = remotehub.getVirtualUri(remotehub.getProviderRootUri(uri));
const relativePath = this.getRelativePath(uri, root);
const ref = uri.sha ?? (await metadata.getRevision()).revision;
const blame = await github.getBlame(
session?.accessToken,
metadata.repo.owner,
metadata.repo.name,
ref,
relativePath,
);
const range = blame.ranges.find(r => r.startingLine === editorLine);
if (range == null) return undefined;
const c = range.commit;
const { viewer = session.account.label } = blame;
const authorName = viewer != null && c.author.name === viewer ? 'You' : c.author.name;
const committerName = viewer != null && c.committer.name === viewer ? 'You' : c.committer.name;
const commit = new GitCommit(
this.container,
uri.repoPath!,
c.oid,
new GitCommitIdentity(authorName, c.author.email, new Date(c.author.date), c.author.avatarUrl),
new GitCommitIdentity(committerName, c.committer.email, new Date(c.author.date)),
c.message.split('\n', 1)[0],
c.parents.nodes[0]?.oid ? [c.parents.nodes[0]?.oid] : [],
c.message,
new GitFileChange(root.toString(), relativePath, GitFileIndexStatus.Modified),
{ changedFiles: c.changedFiles ?? 0, additions: c.additions ?? 0, deletions: c.deletions ?? 0 },
[],
);
for (let i = range.startingLine; i <= range.endingLine; i++) {
const line: GitCommitLine = { sha: c.oid, originalLine: i, line: i };
commit.lines.push(line);
}
return {
author: {
name: authorName,
lineCount: range.endingLine - range.startingLine + 1,
},
commit: commit,
line: { sha: c.oid, originalLine: range.startingLine, line: editorLine },
};
} catch (ex) {
debugger;
Logger.error(cc, ex);
return undefined;
}
}
@log<GitHubGitProvider['getBlameForLineContents']>({ args: { 2: '<contents>' } })
async getBlameForLineContents(
uri: GitUri,
editorLine: number,
_uri: GitUri,
_editorLine: number,
_contents: string,
_options?: { forceSingleLine?: boolean },
): Promise<GitBlameLine | undefined> {
return this.getBlameForLine(uri, editorLine);
// TODO@eamodio figure out how to actually generate a blame given the contents (need to generate a diff)
return undefined; //this.getBlameForLine(uri, editorLine);
}
@log()
async getBlameForRange(uri: GitUri, range: Range): Promise<GitBlameLines | undefined> {
const blame = await this.getBlameForFile(uri);
const blame = await this.getBlame(uri);
if (blame == null) return undefined;
return this.getBlameRange(blame, uri, range);
@ -548,7 +612,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
@log<GitHubGitProvider['getBlameForRangeContents']>({ args: { 2: '<contents>' } })
async getBlameForRangeContents(uri: GitUri, range: Range, contents: string): Promise<GitBlameLines | undefined> {
const blame = await this.getBlameForFileContents(uri, contents);
const blame = await this.getBlameContents(uri, contents);
if (blame == null) return undefined;
return this.getBlameRange(blame, uri, range);
@ -575,7 +639,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
if (!shas.has(c.sha)) continue;
const commit = c.with({
lines: c.lines.filter(l => l.to.line >= startLine && l.to.line <= endLine),
lines: c.lines.filter(l => l.line >= startLine && l.line <= endLine),
});
commits.set(c.sha, commit);

+ 1
- 1
src/statusbar/statusBarController.ts 查看文件

@ -371,7 +371,7 @@ export class StatusBarController implements Disposable {
const tooltip = await Hovers.detailsMessage(
commit,
commit.getGitUri(),
commit.lines[0].to.line,
commit.lines[0].line,
this.container.config.statusBar.tooltipFormat,
this.container.config.defaultDateFormat,
{

+ 9
- 13
src/trackers/gitLineTracker.ts 查看文件

@ -148,14 +148,12 @@ export class GitLineTracker extends LineTracker {
}
if (selections.length === 1) {
const blameLine = editor.document.isDirty
? await this.container.git.getBlameForLineContents(
trackedDocument.uri,
selections[0].active,
editor.document.getText(),
)
: await this.container.git.getBlameForLine(trackedDocument.uri, selections[0].active);
if (blameLine === undefined) {
const blameLine = await this.container.git.getBlameForLine(
trackedDocument.uri,
selections[0].active,
editor?.document,
);
if (blameLine == null) {
if (cc != null) {
cc.exitDetails = ` ${GlyphChars.Dot} blame failed`;
}
@ -163,12 +161,10 @@ export class GitLineTracker extends LineTracker {
return false;
}
this.setState(blameLine.line.to.line - 1, new GitLineState(blameLine.commit));
this.setState(blameLine.line.line - 1, new GitLineState(blameLine.commit));
} else {
const blame = editor.document.isDirty
? await this.container.git.getBlameForFileContents(trackedDocument.uri, editor.document.getText())
: await this.container.git.getBlameForFile(trackedDocument.uri);
if (blame === undefined) {
const blame = await this.container.git.getBlame(trackedDocument.uri, editor.document);
if (blame == null) {
if (cc != null) {
cc.exitDetails = ` ${GlyphChars.Dot} blame failed`;
}

+ 1
- 1
src/views/nodes/commitFileNode.ts 查看文件

@ -139,7 +139,7 @@ export class CommitFileNode
override getCommand(): Command | undefined {
let line;
if (this.commit.lines.length) {
line = this.commit.lines[0].to.line - 1;
line = this.commit.lines[0].line - 1;
} else {
line = this._options.selection?.active.line ?? 0;
}

+ 1
- 1
src/views/nodes/fileRevisionAsCommitNode.ts 查看文件

@ -140,7 +140,7 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
override getCommand(): Command | undefined {
let line;
if (this.commit.lines.length) {
line = this.commit.lines[0].to.line - 1;
line = this.commit.lines[0].line - 1;
} else {
line = this._options.selection?.active.line ?? 0;
}

+ 3
- 3
src/views/nodes/lineHistoryNode.ts 查看文件

@ -94,11 +94,11 @@ export class LineHistoryNode
const lastLine = blame.lines[blame.lines.length - 1];
// Since there could be a change in the line numbers, update the selection
const firstActive = selection.active.line === firstLine.to.line - 1;
const firstActive = selection.active.line === firstLine.line - 1;
selection = new Selection(
(firstActive ? lastLine : firstLine).from.line - 1,
(firstActive ? lastLine : firstLine).originalLine - 1,
selection.anchor.character,
(firstActive ? firstLine : lastLine).from.line - 1,
(firstActive ? firstLine : lastLine).originalLine - 1,
selection.active.character,
);

正在加载...
取消
保存