From 3711b35c93baf3ff4f3c982c6a25a3084a35579c Mon Sep 17 00:00:00 2001 From: Matus Ferech Date: Thu, 19 Mar 2020 00:10:33 +0100 Subject: [PATCH] feat(extension): ignore patterns from global gitignore - add function for expanding paths starting with `~` - add method for getting path to global gitignore from git config --- src/gitignore-hider.ts | 54 +++++++++++++++++++++++++++++++++------- src/gitignore-reader.ts | 4 +-- src/gitignore.model.ts | 1 + src/pattern-converter.ts | 6 ++--- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/gitignore-hider.ts b/src/gitignore-hider.ts index 9a97fa4..36658cb 100644 --- a/src/gitignore-hider.ts +++ b/src/gitignore-hider.ts @@ -1,15 +1,26 @@ -import { commands, ExtensionContext, workspace } from 'vscode'; +import { spawn } from "child_process"; +import { join } from "path"; +import { homedir } from "os"; +import { commands, ExtensionContext, workspace, Uri } from 'vscode'; import { GitignoreReader } from './gitignore-reader'; import { PatternConverter } from './pattern-converter'; import { SettingsAccessor } from './settings-accessor'; +import { Pattern } from "./pattern.model"; + +function expandHome(path: string): string { + if (!path) return path; + if (path == '~') return homedir(); + if (path.slice(0, 2) != '~/') return path; + return join(homedir(), path.slice(2)); +} export class GitignoreHider { constructor( private _reader: GitignoreReader, private _converter: PatternConverter, private _settings: SettingsAccessor, - ) {} + ) { } public registerCommands(context: ExtensionContext): void { const hideDisposable = commands.registerCommand('extension.hideGitignored', () => { @@ -23,25 +34,50 @@ export class GitignoreHider { context.subscriptions.push(showDisposable); } + private async getGlobalGitignore(): Promise { + return new Promise((resolve, reject) => { + const commandExecuter = spawn('git', ['config', '--global', 'core.excludesfile']); + let stdOutData = ''; + let stderrData = ''; + + commandExecuter.stdout.on('data', (data) => stdOutData += data); + commandExecuter.stderr.on('data', (data) => stderrData += data); + commandExecuter.on('close', (code) => { + code != 0 ? reject(stderrData.toString()) : resolve(stdOutData.toString().trim()) + }) + }) + } + + private async getGlobalPatterns(): Promise { + try { + const globalGitIgnore = expandHome(await this.getGlobalGitignore()); + const doc = await workspace.openTextDocument(Uri.file(globalGitIgnore)); + return this._converter.convert(this._reader.read(doc, true)); + } catch (error) { + console.log('Unable to get global gitignore', error); + return []; + } + } + public async run(show = false): Promise { const files = await workspace.findFiles('**/.gitignore'); + const globalPatterns = await this.getGlobalPatterns(); - if (files.length < 1) { + if (files.length == 0 && globalPatterns.length == 0) { return; } - const handlers = files.map((file) => workspace.openTextDocument(file)); + const handlers = files.map(file => workspace.openTextDocument(file)); const docs = await Promise.all(handlers); - const patterns = docs - .map((doc) => this._reader.read(doc)) - .map((gitignore) => this._converter.convert(gitignore)) + .map(doc => this._reader.read(doc)) + .map(gitignore => this._converter.convert(gitignore)) .reduce((prev, cur) => cur.concat(prev), []); if (show) { - await this._settings.show(patterns); + await this._settings.show(patterns.concat(globalPatterns)); } else { - await this._settings.hide(patterns); + await this._settings.hide(patterns.concat(globalPatterns)); } } } diff --git a/src/gitignore-reader.ts b/src/gitignore-reader.ts index 42296ec..af6cbc2 100644 --- a/src/gitignore-reader.ts +++ b/src/gitignore-reader.ts @@ -17,13 +17,13 @@ export class GitignoreReader { * @returns {Gitignore} * @memberof GitignoreReader */ - public read(document: TextDocument): Gitignore { + public read(document: TextDocument, isGlobal: boolean = false): Gitignore { const lineCount = document.lineCount; const lines: string[] = []; for (let index = 0; index < lineCount; index++) { lines.push(document.lineAt(index).text); } const path = dirname(workspace.asRelativePath(document.fileName)); - return { lines, path }; + return { lines, path, isGlobal }; } } diff --git a/src/gitignore.model.ts b/src/gitignore.model.ts index a4dc91e..ee28661 100644 --- a/src/gitignore.model.ts +++ b/src/gitignore.model.ts @@ -1,4 +1,5 @@ export interface Gitignore { lines: string[]; path: string; + isGlobal: boolean; } diff --git a/src/pattern-converter.ts b/src/pattern-converter.ts index 29b73a0..1f517a0 100644 --- a/src/pattern-converter.ts +++ b/src/pattern-converter.ts @@ -17,7 +17,7 @@ export class PatternConverter { */ public convert(gitignore: Gitignore): Pattern[] { return gitignore.lines - .map((line) => this._convertToPattern(line, gitignore.path)) + .map((line) => this._convertToPattern(line, gitignore.path, gitignore.isGlobal)) .filter((line) => line !== void 0) as Pattern[]; } @@ -30,7 +30,7 @@ export class PatternConverter { * @returns {(Pattern | void)} * @memberof PatternConverter */ - private _convertToPattern(line: string, path: string): Pattern | void { + private _convertToPattern(line: string, path: string, isGlobalGitignore: boolean): Pattern | void { if (this._canBeIgnored(line)) { return; } @@ -54,7 +54,7 @@ export class PatternConverter { } // prefix with path - glob = path !== '.' ? `${path}/${glob}` : glob; + glob = path === '.' || isGlobalGitignore ? glob : `${path}/${glob}`; return { glob, hide, line }; }