<commit_before>import * as assert from 'assert'
import * as path from 'path'
import * as fs from 'fs'

import { TreeDataProvider, TreeItem, TreeItemCollapsibleState,
	     Uri, Command, Disposable, EventEmitter, Event, TextDocumentShowOptions,
		 QuickPickItem, ProgressLocation, Memento,
	     workspace, commands, window } from 'vscode'
import { NAMESPACE } from './constants'
import { Repository, Ref, RefType } from './git/git'
import { anyEvent, filterEvent } from './git/util'
import { toGitUri } from './git/uri'
import { getDefaultBranch, getMergeBase, getHeadModificationDate,
	     diffIndex, IDiffStatus, StatusCode } from './gitHelper'
import { debounce, throttle } from './git/decorators'

class FileElement implements IDiffStatus {
	constructor(public absPath: string, public status: StatusCode) {}
}

class FolderElement {
	constructor(public absPath: string, public useFilesOutsideTreeRoot?: boolean, public collapsed?: boolean) {}
}

class RootElement {
	// TODO without a member the type checker throws up otherwise further down
	foo;
}

class RefElement {
	constructor(public refName: string, public hasChildren: boolean) {}
}

type Element = FileElement | FolderElement | RootElement | RefElement
type FileSystemElement = FileElement | FolderElement

class RefItem implements QuickPickItem {
	label: string
	description: string
	constructor(public ref: Ref) {
		this.label = ref.name!;
		this.description = (ref.commit || '').substr(0, 8);
	}
}

export class GitTreeCompareProvider implements TreeDataProvider<Element>, Disposable {

	private _onDidChangeTreeData: EventEmitter<Element | undefined> = new EventEmitter<Element | undefined>();
	readonly onDidChangeTreeData: Event<Element | undefined> = this._onDidChangeTreeData.event;

	private openChangesOnSelect: boolean;
	private autoRefresh: boolean;

	private treeRoot: string;
	private readonly repoRoot: string;

	private filesInsideTreeRoot: Map<string, IDiffStatus[]>;
	private filesOutsideTreeRoot: Map<string, IDiffStatus[]>;
	private includeFilesOutsideWorkspaceRoot: boolean;

	private headLastChecked: Date;
	private baseRef: string;
	private mergeBase: string;

	private readonly disposables: Disposable[] = [];

	constructor(private repository: Repository, private workspaceState: Memento) {		
		this.repoRoot = path.normalize(repository.root);
		this.readConfig();

		this.disposables.push(workspace.onDidChangeConfiguration(this.handleConfigChange, this));

		const fsWatcher = workspace.createFileSystemWatcher('**');
		this.disposables.push(fsWatcher);

		const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
		const onNonGitChange = filterEvent(onWorkspaceChange, uri => !/\/\.git\//.test(uri.path) && !/\/\.git$/.test(uri.path));

		this.disposables.push(onNonGitChange(this.handleWorkspaceChange, this));
	}

	private readConfig() {
		const config = workspace.getConfiguration(NAMESPACE);
		if (config.get<string>('root') == 'repository') {
			this.treeRoot = this.repoRoot;
		} else {
			this.treeRoot = workspace.rootPath!;
		}
		this.includeFilesOutsideWorkspaceRoot = config.get<boolean>('includeFilesOutsideWorkspaceRoot', true);
		this.openChangesOnSelect = config.get<boolean>('openChanges', true);
		this.autoRefresh = config.get<boolean>('autoRefresh', true);
	}

	private getStoredBaseRef(): string | undefined {
		return this.workspaceState.get<string>('baseRef');
	}

	private updateStoredBaseRef(baseRef: string) {
		this.workspaceState.update('baseRef', baseRef);
	}

	getTreeItem(element: Element): TreeItem {
		return toTreeItem(element, this.openChangesOnSelect);
	}
<commit_msg>refresh if refs change (fixes #18)<commit_after>import * as assert from 'assert'
import * as path from 'path'
import * as fs from 'fs'

import { TreeDataProvider, TreeItem, TreeItemCollapsibleState,
	     Uri, Command, Disposable, EventEmitter, Event, TextDocumentShowOptions,
		 QuickPickItem, ProgressLocation, Memento,
	     workspace, commands, window } from 'vscode'
import { NAMESPACE } from './constants'
import { Repository, Ref, RefType } from './git/git'
import { anyEvent, filterEvent } from './git/util'
import { toGitUri } from './git/uri'
import { getDefaultBranch, getMergeBase, getHeadModificationDate,
	     diffIndex, IDiffStatus, StatusCode } from './gitHelper'
import { debounce, throttle } from './git/decorators'

class FileElement implements IDiffStatus {
	constructor(public absPath: string, public status: StatusCode) {}
}

class FolderElement {
	constructor(public absPath: string, public useFilesOutsideTreeRoot?: boolean, public collapsed?: boolean) {}
}

class RootElement {
	// TODO without a member the type checker throws up otherwise further down
	foo;
}

class RefElement {
	constructor(public refName: string, public hasChildren: boolean) {}
}

type Element = FileElement | FolderElement | RootElement | RefElement
type FileSystemElement = FileElement | FolderElement

class RefItem implements QuickPickItem {
	label: string
	description: string
	constructor(public ref: Ref) {
		this.label = ref.name!;
		this.description = (ref.commit || '').substr(0, 8);
	}
}

export class GitTreeCompareProvider implements TreeDataProvider<Element>, Disposable {

	private _onDidChangeTreeData: EventEmitter<Element | undefined> = new EventEmitter<Element | undefined>();
	readonly onDidChangeTreeData: Event<Element | undefined> = this._onDidChangeTreeData.event;

	private openChangesOnSelect: boolean;
	private autoRefresh: boolean;

	private treeRoot: string;
	private readonly repoRoot: string;

	private filesInsideTreeRoot: Map<string, IDiffStatus[]>;
	private filesOutsideTreeRoot: Map<string, IDiffStatus[]>;
	private includeFilesOutsideWorkspaceRoot: boolean;

	private headLastChecked: Date;
	private baseRef: string;
	private mergeBase: string;

	private readonly disposables: Disposable[] = [];

	constructor(private repository: Repository, private workspaceState: Memento) {		
		this.repoRoot = path.normalize(repository.root);
		this.readConfig();

		this.disposables.push(workspace.onDidChangeConfiguration(this.handleConfigChange, this));

		const fsWatcher = workspace.createFileSystemWatcher('**');
		this.disposables.push(fsWatcher);

		const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
		const onNonGitChange = filterEvent(onWorkspaceChange, uri => !/\/\.git\//.test(uri.path) && !/\/\.git$/.test(uri.path));
		const onGitRefsChange = filterEvent(onWorkspaceChange, uri => /\/\.git\/refs\//.test(uri.path));

		const onRelevantWorkspaceChange = anyEvent(onNonGitChange, onGitRefsChange);
		this.disposables.push(onRelevantWorkspaceChange(this.handleWorkspaceChange, this));
	}

	private readConfig() {
		const config = workspace.getConfiguration(NAMESPACE);
		if (config.get<string>('root') == 'repository') {
			this.treeRoot = this.repoRoot;
		} else {
			this.treeRoot = workspace.rootPath!;
		}
		this.includeFilesOutsideWorkspaceRoot = config.get<boolean>('includeFilesOutsideWorkspaceRoot', true);
		this.openChangesOnSelect = config.get<boolean>('openChanges', true);
		this.autoRefresh = config.get<boolean>('autoRefresh', true);
	}

	private getStoredBaseRef(): string | undefined {
		return this.workspaceState.get<string>('baseRef');
	}

	private updateStoredBaseRef(baseRef: string) {
		this.workspaceState.update('baseRef', baseRef);
	}

	getTreeItem(element: Element): TreeItem {
		return toTreeItem(element, this.openChangesOnSelect);
	}

{'fraction_non_alphanumeric': 0.11329063250600481, 'fraction_numerical': 0.0008006405124099279, 'mean_word_length': 5.752252252252252, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '615871', 'n_tokens_mistral': 2291, 'n_tokens_neox': 2183, 'n_words': 532}