Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix detection when string has equal amount of tabs and spaces #24

Merged
merged 3 commits into from Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions fixture/space-tab-last.js
@@ -0,0 +1,5 @@
4 spaces
4 spaces
1 tab
1 tab
1 tab
95 changes: 53 additions & 42 deletions index.js
Expand Up @@ -19,7 +19,7 @@ function getMostUsed(indents) {
if (u > maxUsed || (u === maxUsed && w > maxWeight)) {
maxUsed = u;
maxWeight = w;
result = Number(key);
result = key;
}
}

Expand All @@ -31,85 +31,96 @@ module.exports = str => {
throw new TypeError('Expected a string');
}

// Used to see if tabs or spaces are the most used
let tabs = 0;
let spaces = 0;

// Remember the size of previous line's indentation
let prev = 0;
let indentTypePrev;
// Indents key (ident type + size of the indents/unindents)
let key;

// Remember how many indents/unindents as occurred for a given size and how much lines follow a given indentation
// The key is a concatenation of the indentation type (s = space and t = tab) and the size of the indents/unindents
//
// indents = {
// 3: [1, 0],
// 4: [1, 5],
// 5: [1, 0],
// 12: [1, 0],
// t3: [1, 0],
// t4: [1, 5],
// s5: [1, 0],
// s12: [1, 0],
// }
const indents = new Map();

// Pointer to the array of last used indent
let current;

// Whether the last action was an indent (opposed to an unindent)
let isIndent;

for (const line of str.split(/\n/g)) {
if (!line) {
// Ignore empty lines
continue;
}

let indent;
let indentType;
let weight;
let entry;
const matches = line.match(INDENT_RE);

if (matches) {
if (!matches) {
prev = 0;
indentTypePrev = '';
} else {
indent = matches[0].length;

if (matches[1]) {
spaces++;
indentType = 's';
} else {
tabs++;
indentType = 't';
}
} else {
indent = 0;
}

const diff = indent - prev;
prev = indent;
if (indentType !== indentTypePrev) {
prev = 0;
}
indentTypePrev = indentType;

if (diff) {
// An indent or unindent has been detected
weight = 0;

isIndent = diff > 0;
const diff = indent - prev;
prev = indent;

current = indents.get(isIndent ? diff : -diff);
// Previous line have same indent?
if (diff === 0) {
weight++;
// We use the key from previous loop
} else {
key = indentType + String(diff > 0 ? diff : -diff);
}

// Update the stats
entry = indents.get(key);

if (current) {
current[0]++;
if (entry === undefined) {
entry = [1, 0]; // Init
} else {
current = [1, 0];
indents.set(diff, current);
entry = [++entry[0], entry[1] + weight];
}
} else if (current) {
// If the last action was an indent, increment the weight
current[1] += Number(isIndent);
}
indents.set(key, entry);
}
}

const amount = getMostUsed(indents);
const result = getMostUsed(indents);

let amount;
let type;
let indent;
if (!amount) {
if (!result) {
amount = 0;
type = null;
indent = '';
} else if (spaces >= tabs) {
type = 'space';
indent = ' '.repeat(amount);
} else {
type = 'tab';
indent = '\t'.repeat(amount);
amount = Number(result.substring(1));

if (result[0] === 's') {
type = 'space';
indent = ' '.repeat(amount);
} else {
type = 'tab';
indent = '\t'.repeat(amount);
}
}

return {
Expand Down
17 changes: 13 additions & 4 deletions test.js
Expand Up @@ -103,11 +103,20 @@ test('return indentation stats for fifty-fifty indented files with spaces first'
});
});

test.failing('return indentation stats for fifty-fifty indented files with tabs first', t => {
test('return indentation stats for fifty-fifty indented files with tabs first', t => {
const stats = m(getFile('fixture/fifty-fifty-tab-first.js'));
t.deepEqual(stats, {
amount: 4,
indent: ' ',
type: 'space'
amount: 1,
indent: ' ',
type: 'tab'
});
});

test('return indentation stats for indented files with spaces and tabs last', t => {
const stats = m(getFile('fixture/space-tab-last.js'));
t.deepEqual(stats, {
amount: 1,
indent: ' ',
type: 'tab'
});
});