Skip to content

Commit

Permalink
feat: Use .appmapignore to ignore certain directories
Browse files Browse the repository at this point in the history
  • Loading branch information
ahtrotta committed Jan 26, 2024
1 parent 019feb1 commit 9a31784
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .appmapignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file instructs the AppMap extension & tool to ignore certain directores

# Ignore all directories including 'test'

test
24 changes: 14 additions & 10 deletions src/lib/findFiles.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import * as vscode from 'vscode';

/* Find files taking files.watcherExclude into account. */
export function findFiles(include: vscode.GlobPattern): Thenable<vscode.Uri[]> {
return vscode.workspace.findFiles(include, getExcludes());
export function findFiles(
include: vscode.GlobPattern,
excludes: vscode.GlobPattern[] = []
): Thenable<vscode.Uri[]> {
return vscode.workspace.findFiles(include, getExcludes(excludes));
}

function getExcludes(): vscode.GlobPattern | undefined {
const excludes = vscode.workspace
.getConfiguration('files')
.get<Record<string, boolean>>('watcherExclude');
if (!excludes) return;
const paths = Object.entries(excludes)
function getExcludes(excludes: vscode.GlobPattern[]): vscode.GlobPattern | undefined {
const editorExcludes =
vscode.workspace.getConfiguration('files').get<Record<string, boolean>>('watcherExclude') || [];

if (editorExcludes.length === 0 && excludes.length === 0) return;

const editorExcludePaths = Object.entries(editorExcludes)
.filter(([, enabled]) => enabled)
.map(([path]) => path);

const result = `{${paths.join(',')}}`;
return result;
const workspaceExcludePaths = excludes.map((exclude) => exclude.toString());
return `{${editorExcludePaths.concat(workspaceExcludePaths).join(',')}}`;
}
73 changes: 64 additions & 9 deletions src/services/appmapConfigManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { readFile, mkdir, writeFile } from 'fs/promises';
import { readFileSync } from 'fs';
import { dump, load } from 'js-yaml';
import { dirname, join } from 'path';
import assert from 'node:assert';
Expand All @@ -22,23 +23,76 @@ type WorkspaceConfig = {
fileProvider: ConfigFileProviderImpl;
};

export class AppmapignoreManager {
private _exclusions: vscode.GlobPattern[] = [];

constructor(private folder: vscode.WorkspaceFolder) {}

public getExclusions(): vscode.GlobPattern[] {
if (this._exclusions.length === 0) {
this._exclusions = this.readExclusions() || [];
}
return this._exclusions;
}

private readExclusions(): vscode.GlobPattern[] | undefined {
const filePath = join(this.folder.uri.fsPath, '.appmapignore');
let fileContent: string;
try {
fileContent = readFileSync(filePath, 'utf-8');
} catch (e) {
return;
}

if (!fileContent) return;

return fileContent
.split('\n')
.map((line: string) => this.convertToGlob(line.trim()))
.filter((pattern: string) => pattern.length > 0);
}

private convertToGlob(pattern: string): string {
let result = pattern;

if (pattern.length === 0 || pattern.startsWith('#')) {
return '';
} else if (pattern.startsWith('/')) {
result = pattern.slice(1);
} else {
result = '**/' + pattern;
}

if (pattern.endsWith('/')) {
result = result.slice(0, -1);
}

return result;
}
}

class ConfigFileProviderImpl implements ConfigFileProvider {
private _files?: vscode.Uri[] = undefined;

public constructor(private pattern: vscode.RelativePattern) {}
public constructor(
private pattern: vscode.RelativePattern,
private exclusions: vscode.GlobPattern[] = []
) {}

public async files(): Promise<vscode.Uri[]> {
if (this._files) {
return this._files;
}

this._files = await findFiles(this.pattern);
if (this._files) return this._files;
this._files = await findFiles(this.pattern, this.exclusions);
return this._files;
}

public reset() {
this._files = undefined;
}

public dispose() {
this.reset();
this.exclusions = [];
}
}

export class AppmapConfigManagerInstance implements WorkspaceServiceInstance {
Expand All @@ -48,13 +102,14 @@ export class AppmapConfigManagerInstance implements WorkspaceServiceInstance {
private _configFileProvider: ConfigFileProviderImpl;
private _hasConfigFile = false;
private _usingDefault = false;
private _watcherConfigured = false;
private _onConfigChanged = new vscode.EventEmitter<void>();
public readonly onConfigChanged = this._onConfigChanged.event;

constructor(public folder: vscode.WorkspaceFolder) {
const appmapignoreManager = new AppmapignoreManager(folder);
const exclusions = appmapignoreManager.getExclusions();
const configPattern = new vscode.RelativePattern(folder, this.CONFIG_PATTERN);
this._configFileProvider = new ConfigFileProviderImpl(configPattern);
this._configFileProvider = new ConfigFileProviderImpl(configPattern, exclusions);
}

public async initialize(): Promise<AppmapConfigManagerInstance> {
Expand Down Expand Up @@ -198,7 +253,7 @@ export class AppmapConfigManagerInstance implements WorkspaceServiceInstance {

public dispose(): void {
this._onConfigChanged.dispose();
this._configFileProvider.reset();
this._configFileProvider.dispose();
this._configs = [];
}
}
Expand Down
58 changes: 58 additions & 0 deletions test/unit/services/appmapignoreManager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import '../mock/vscode';

import assert from 'assert';
import { SinonSandbox, createSandbox } from 'sinon';

import { AppmapignoreManager } from '../../../src/services/appmapConfigManager';

import { randomUUID } from 'crypto';
import { mkdirSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import path from 'path';

describe('AppmapignoreManager', () => {
let sinon: SinonSandbox;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let mockWorkspaceFolder: any;
let appmapignoreManager: AppmapignoreManager;
let tmpDir: string;

beforeEach(() => {
sinon = createSandbox();
tmpDir = path.join(tmpdir(), randomUUID());
mkdirSync(tmpDir);

mockWorkspaceFolder = {
uri: {
fsPath: tmpDir,
},
};

appmapignoreManager = new AppmapignoreManager(mockWorkspaceFolder);
});

afterEach(() => {
sinon.restore();
rmSync(tmpDir, { recursive: true, force: true });
});

it('does not add exclusions when no .appmapignore file is present', () => {
assert.deepStrictEqual(appmapignoreManager.getExclusions(), []);
});

it('adds exclusions when a .appmapignore file is present', () => {
const appmapignoreFileLines = ['test', 'test2/', '/test3', '/test4/'];
writeFileSync(path.join(tmpDir, '.appmapignore'), appmapignoreFileLines.join('\n'));

const expectedExclusions = ['**/test', '**/test2', 'test3', 'test4'];
assert.deepStrictEqual(appmapignoreManager.getExclusions(), expectedExclusions);
});

it('ignores empty lines and comments in .appmapignore file', () => {
const appmapignoreFileLines = ['test', '', '# test2', '/test3', '/test4/'];
writeFileSync(path.join(tmpDir, '.appmapignore'), appmapignoreFileLines.join('\n'));

const expectedExclusions = ['**/test', 'test3', 'test4'];
assert.deepStrictEqual(appmapignoreManager.getExclusions(), expectedExclusions);
});
});

0 comments on commit 9a31784

Please sign in to comment.