Procházet zdrojové kódy

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 před 2 roky
rodič
revize
e04a58db9c
23 změnil soubory, kde provedl 184 přidání a 146 odebrání
  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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

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

Načítá se…
Zrušit
Uložit