Files
azuredatastudio/extensions/git/src/contentProvider.ts
Karl Burtram dafb780987 Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches

* A few more merges

* Post npm install

* Fix batch of build breaks

* Fix more build breaks

* Fix more build errors

* Fix more build breaks

* Runtime fixes 1

* Get connection dialog working with some todos

* Fix a few packaging issues

* Copy several node_modules to package build to fix loader issues

* Fix breaks from master

* A few more fixes

* Make tests pass

* First pass of license header updates

* Second pass of license header updates

* Fix restore dialog issues

* Remove add additional themes menu items

* fix select box issues where the list doesn't show up

* formatting

* Fix editor dispose issue

* Copy over node modules to correct location on all platforms
2018-04-04 15:27:51 -07:00

146 lines
3.9 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode';
import { debounce, throttle } from './decorators';
import { fromGitUri, toGitUri } from './uri';
import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model';
import { filterEvent, eventToPromise, isDescendant } from './util';
interface CacheRow {
uri: Uri;
timestamp: number;
}
interface Cache {
[uri: string]: CacheRow;
}
const THREE_MINUTES = 1000 * 60 * 3;
const FIVE_MINUTES = 1000 * 60 * 5;
export class GitContentProvider {
private _onDidChange = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this._onDidChange.event; }
private changedRepositoryRoots = new Set<string>();
private cache: Cache = Object.create(null);
private disposables: Disposable[] = [];
constructor(private model: Model) {
this.disposables.push(
model.onDidChangeRepository(this.onDidChangeRepository, this),
model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this),
workspace.registerTextDocumentContentProvider('git', this)
);
setInterval(() => this.cleanup(), FIVE_MINUTES);
}
private onDidChangeRepository({ repository }: ModelChangeEvent): void {
this.changedRepositoryRoots.add(repository.root);
this.eventuallyFireChangeEvents();
}
private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void {
if (uri.scheme !== 'file') {
return;
}
this._onDidChange.fire(toGitUri(uri, '', { replaceFileExtension: true }));
}
@debounce(1100)
private eventuallyFireChangeEvents(): void {
this.fireChangeEvents();
}
@throttle
private async fireChangeEvents(): Promise<void> {
if (!window.state.focused) {
const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused);
await eventToPromise(onDidFocusWindow);
}
Object.keys(this.cache).forEach(key => {
const uri = this.cache[key].uri;
const fsPath = uri.fsPath;
for (const root of this.changedRepositoryRoots) {
if (isDescendant(root, fsPath)) {
this._onDidChange.fire(uri);
return;
}
}
});
this.changedRepositoryRoots.clear();
}
async provideTextDocumentContent(uri: Uri): Promise<string> {
let { path, ref, submoduleOf } = fromGitUri(uri);
if (submoduleOf) {
const repository = this.model.getRepository(submoduleOf);
if (!repository) {
return '';
}
return await repository.diff(path, { cached: ref === 'index' });
}
const repository = this.model.getRepository(uri);
if (!repository) {
return '';
}
const cacheKey = uri.toString();
const timestamp = new Date().getTime();
const cacheValue: CacheRow = { uri, timestamp };
this.cache[cacheKey] = cacheValue;
if (ref === '~') {
const fileUri = Uri.file(path);
const uriString = fileUri.toString();
const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString);
ref = indexStatus ? '' : 'HEAD';
}
try {
return await repository.show(ref, path);
} catch (err) {
return '';
}
}
private cleanup(): void {
const now = new Date().getTime();
const cache = Object.create(null);
Object.keys(this.cache).forEach(key => {
const row = this.cache[key];
const { path } = fromGitUri(row.uri);
const isOpen = workspace.textDocuments
.filter(d => d.uri.scheme === 'file')
.some(d => d.uri.fsPath === path);
if (isOpen || now - row.timestamp < THREE_MINUTES) {
cache[row.uri.toString()] = row;
}
});
this.cache = cache;
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
}
}