Skip to content

Commit

Permalink
Add support for absolute and relative glob patterns (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S committed Jan 22, 2020
1 parent cbe103b commit 662d719
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 16 deletions.
3 changes: 2 additions & 1 deletion packages/cspell/cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"ignorePaths": [
"dictionaries/**",
"migrated_dictionaries/**",
"node_modules/**",
"**/node_modules/**",
"**/*.d.ts",
"coverage/**",
".git/**",
"dist/**",
Expand Down
52 changes: 52 additions & 0 deletions packages/cspell/src/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ jest.mock('get-stdin', () => {
return jest.fn(() => Promise.resolve(getStdinResult.value));
});

import * as path from 'path';
import {expect} from 'chai';
import * as App from './application';

Expand Down Expand Up @@ -89,6 +90,53 @@ describe('Validate the Application', () => {
});
});

describe('Validate createInit', () => {
test('createInit', async () => {
async function worked() {
try {
await App.createInit({});
} catch (e) {
return false;
}
return true;
}
expect(await worked()).to.equal(false);
});
});

describe('Validate internal functions', () => {
test('_globP empty pattern', async () => {
expect(await App._testing_._globP('', {})).to.be.empty;
});

test('normalizePattern relative', () => {
const root = process.cwd();
const r = App._testing_.normalizePattern('../../packages/**/*.ts', root);
expect(r.root).to.eq(path.dirname(path.dirname(root)));
expect(r.pattern).to.eq('packages/**/*.ts');
});

test('normalizePattern relative absolute', () => {
const root = process.cwd();
const p = '/packages/**/*.ts';
const r = App._testing_.normalizePattern(p, root);
expect(r.root).to.eq(root);
expect(r.pattern).to.eq(p);
});

test('normalizePattern absolute', () => {
const root = process.cwd();
const p = path.join(__dirname, '**', '*.ts');
const r = App._testing_.normalizePattern(p, root);
expect(r.root).to.eq(path.sep);
expect(r.pattern).to.eq(p);
});

test('findFiles', async () => {

});
});

describe('Application, Validate Samples', () => {
sampleTests().map(sample =>
test(`Test file: "${sample.file}"`, async () => {
Expand All @@ -111,11 +159,15 @@ interface SampleTest {
function sampleTests(): SampleTest[] {
// cspell:disable
return [
{ file: path.resolve(path.join(__dirname, '../samples/src/drives.ps1')), issues: ['Woude', 'Woude'] },
{ file: path.resolve(path.join(__dirname, '../../cspell-lib/samples/src/drives.ps1')), issues: ['Woude', 'Woude'] },
{ file: 'samples/src/drives.ps1', issues: ['Woude', 'Woude'] },
{ file: 'samples/src/sample.c', issues: [] },
{ file: 'samples/src/sample.go', issues: ['garbbage'] },
{ file: 'samples/src/sample.py', issues: ['garbbage'] },
{ file: 'samples/src/sample.tex', issues: ['includegraphics', 'Zotero'] },
{ file: path.resolve(path.join(__dirname, '../samples/src/drives.ps1')), issues: ['Woude', 'Woude'] },
{ file: path.resolve(path.join(__dirname, '../../cspell-lib/samples/src/drives.ps1')), issues: ['Woude', 'Woude'] },
];
// cspell:enable
}
Expand Down
129 changes: 114 additions & 15 deletions packages/cspell/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ export async function checkText(filename: string, options: BaseOptions): Promise
}

export function createInit(_: CSpellApplicationOptions): Promise<void> {
return Promise.resolve();
return Promise.reject();
}

const defaultExcludeGlobs = [
Expand Down Expand Up @@ -366,9 +366,9 @@ function readFile(filename: string, encoding: string = UTF8): Promise<string> {
async function findFiles(globPatterns: string[], options: GlobOptions): Promise<string[]> {
const globPats = globPatterns.filter(filename => filename !== STDIN);
const stdin = globPats.length < globPatterns.length ? [ STDIN ] : [];
const globs = globPats.length ? (await globP(globPats, options)) : [];
const globResults = globPats.length ? (await globP(globPats, options)) : [];
const cwd = options.cwd || process.cwd();
return stdin.concat(globs.map(filename => path.resolve(cwd, filename)));
return stdin.concat(globResults.map(filename => path.resolve(cwd, filename)));
}


Expand Down Expand Up @@ -417,13 +417,109 @@ function yesNo(value: boolean) {
return value ? 'Yes' : 'No';
}

function globP(pattern: string | string[], options?: GlobOptions): Promise<string[]> {
const globPattern = typeof pattern === 'string'
? pattern
: pattern.length > 1
? `{${pattern.join(',')}}`
: (pattern[0] || '');
if (!globPattern) {
interface PatternRoot {
pattern: string;
root: string;
}

function findBaseDir(pat: string) {
const globChars = /[*@()?|\[\]{},]/;
while (globChars.test(pat)) {
pat = path.dirname(pat);
}
return pat;
}


function exists(filename: string): boolean {
try {
fsp.accessSync(filename);
} catch (e) {
return false;
}

return true;
}

/**
* Attempt to normalize a pattern based upon the root.
* If the pattern is absolute, check to see if it exists and adjust the root, otherwise it is assumed to be based upon the current root.
* If the pattern starts with a relative path, adjust the root to match.
* The challenge is with the patterns that begin with `/`. Is is an absolute path or relative pattern?
* @param pat glob pattern
* @param root absolute path | empty
* @returns the adjusted root and pattern.
*/
function normalizePattern(pat: string, root: string): PatternRoot {
// Absolute pattern
if (path.isAbsolute(pat)) {
const dir = findBaseDir(pat);
if (dir.length > 1 && exists(dir)) {
// Assume it is an absolute path
return {
pattern: pat,
root: path.sep,
};
}
}
// normal pattern
if (!/^\.\./.test(pat)) {
return {
pattern: pat,
root,
};
}
// relative pattern
pat = (path.sep === '\\') ? pat.replace(/\\/g, '/') : pat;
const patParts = pat.split('/');
const rootParts = root.split(path.sep);
let i = 0;
for (; i < patParts.length && patParts[i] === '..'; ++i) {
rootParts.pop();
}
return {
pattern: patParts.slice(i).join('/'),
root: rootParts.join(path.sep),
};
}

function groupPatterns(patterns: PatternRoot[]): PatternRoot[] {
const groups = new Map<string, string[]>();
patterns.forEach(pat => {
const pats = groups.get(pat.root) || [];
pats.push(pat.pattern);
groups.set(pat.root, pats);
});
const resultPatterns: PatternRoot[] = [];
for (const [root, patterns] of groups) {
resultPatterns.push({
root,
pattern: patterns.join(','),
});
}
return resultPatterns;
}

async function globP(pattern: string | string[], options?: GlobOptions): Promise<string[]> {
const root = options?.root || process.cwd();
const opts = options || {};
const rawPatterns = typeof pattern === 'string' ? [ pattern ] : pattern;
const normPatterns = groupPatterns(rawPatterns.map(pat => normalizePattern(pat, root)));
const globResults = normPatterns.map(async pat => {
const opt: GlobOptions = {...opts, root: pat.root, cwd: pat.root};
console.log(pat);
const absolutePaths = (await _globP(pat.pattern, opt))
.map(filename => path.resolve(pat.root, filename));
const relativeToRoot = absolutePaths.map(absFilename => path.relative(root, absFilename));
return relativeToRoot;
});
const results = (await Promise.all(globResults)).reduce((prev, next) => prev.concat(next), []);
console.log(results);
return results;
}

function _globP(pattern: string, options: GlobOptions): Promise<string[]> {
if (!pattern) {
return Promise.resolve([]);
}
return new Promise<string[]>((resolve, reject) => {
Expand All @@ -433,11 +529,14 @@ function globP(pattern: string | string[], options?: GlobOptions): Promise<strin
}
resolve(matches);
};
if (options) {
glob(globPattern, options, cb);
} else {
glob(globPattern, cb);
}
glob(pattern, options, cb);
});
}


export const _testing_ = {
_globP,
findFiles,
groupPatterns,
normalizePattern,
};

0 comments on commit 662d719

Please sign in to comment.