Selaa lähdekoodia

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 vuotta sitten
vanhempi
commit
e04a58db9c
23 muutettua tiedostoa jossa 184 lisäystä ja 146 poistoa
  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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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 Näytä tiedosto

@ -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,
);

Ladataan…
Peruuta
Tallenna