From d5880b1a5948b3bffd40f932dfb63ec8a4be58f9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 17 Oct 2017 17:19:24 +0200 Subject: [PATCH] deco - decorate ignored files --- extensions/git/src/decorationProvider.ts | 56 ++++++++++++++++++- extensions/git/src/repository.ts | 36 ++++++++++++ .../decorations/browser/decorationsService.ts | 7 ++- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index ed4bad8aef0..7adbb51003d 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -8,6 +8,57 @@ import { window, Uri, Disposable, Event, EventEmitter, DecorationData, DecorationProvider } from 'vscode'; import { Repository, GitResourceGroup } from './repository'; import { Model } from './model'; +import { debounce } from './decorators'; + +class GitIgnoreDecorationProvider implements DecorationProvider { + + private readonly _onDidChangeDecorations = new EventEmitter(); + readonly onDidChangeDecorations: Event = this._onDidChangeDecorations.event; + + private checkIgnoreQueue = new Map void, reject: (err: any) => void }>(); + private disposables: Disposable[] = []; + + constructor(private repository: Repository) { + this.disposables.push( + window.registerDecorationProvider(this, '.gitignore') + //todo@joh -> events when the ignore status actually changes, not when the file changes + ); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + this.checkIgnoreQueue.clear(); + } + + provideDecoration(uri: Uri): Promise { + return new Promise((resolve, reject) => { + this.checkIgnoreQueue.set(uri.fsPath, { resolve, reject }); + this.checkIgnoreSoon(); + }).then(ignored => { + if (ignored) { + return { + priority: 3, + opacity: 0.75 + }; + } + }); + } + + @debounce(500) + private checkIgnoreSoon(): void { + const queue = new Map(this.checkIgnoreQueue.entries()); + this.checkIgnoreQueue.clear(); + this.repository.checkIgnore([...queue.keys()]).then(ignoreSet => { + for (const [key, value] of queue.entries()) { + value.resolve(ignoreSet.has(key)); + } + }, err => { + for (const [, value] of queue.entries()) { + value.reject(err); + } + }); + } +} class GitDecorationProvider implements DecorationProvider { @@ -65,7 +116,7 @@ class GitDecorationProvider implements DecorationProvider { export class GitDecorations { private disposables: Disposable[] = []; - private providers = new Map(); + private providers = new Map(); constructor(private model: Model) { this.disposables.push( @@ -77,7 +128,8 @@ export class GitDecorations { private onDidOpenRepository(repository: Repository): void { const provider = new GitDecorationProvider(repository); - this.providers.set(repository, provider); + const ignoreProvider = new GitIgnoreDecorationProvider(repository); + this.providers.set(repository, Disposable.from(provider, ignoreProvider)); } private onDidCloseRepository(repository: Repository): void { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 319edc3a07e..74cd6c48bfd 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -646,6 +646,42 @@ export class Repository implements Disposable { }); } + checkIgnore(filePaths: string[]): Promise> { + return this.run(Operation.Ignore, () => { + return new Promise>((resolve, reject) => { + + const child = this.repository.stream(['check-ignore', ...filePaths]); + + const onExit = exitCode => { + if (exitCode === 1) { + // nothing ignored + resolve(new Set()); + } else if (exitCode === 0) { + // each line is something ignored + resolve(new Set(data.split('\n'))); + } else { + reject(); + } + }; + + let data = ''; + const onStdoutData = (raw: string) => { + data += raw; + }; + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', onStdoutData); + + // const stderrData: string[] = []; + // child.stderr.setEncoding('utf8'); + // child.stderr.on('data', raw => stderrData.push(raw as string)); + + child.on('error', reject); + child.on('exit', onExit); + }); + }); + } + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { if (this.state !== RepositoryState.Idle) { throw new Error('Repository not initialized'); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index ddbf12c6133..a6320b0fe65 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -249,15 +249,16 @@ class DecorationProviderWrapper { return; } - if (item === undefined && !includeChildren) { - // unknown, a leaf node -> trigger request + if (item === undefined) { + // unknown -> trigger request item = this._fetchData(uri); } if (item) { - // leaf node + // found something callback(item, false); } + if (includeChildren) { // (resolved) children const childTree = this.data.findSuperstr(key); -- GitLab