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

handle double-indent followed by double-outdent #89

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
140 changes: 140 additions & 0 deletions src/Formatter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,146 @@ describe('Formatter', () => {
end sub
`);
});

it('formats outdents', () => {
expect(formatter.format(undent`
temp = {
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
}
`, {
formatMultiLineObjectsAndArrays: false
})).to.equal(undent`
temp = {
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
}
`);

expect(formatter.format(undent`
function test()
temp = {
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function
}
}
end function
`, {
formatMultiLineObjectsAndArrays: false
})).to.equal(undent`
function test()
temp = {
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function
}
}
end function
`);

expect(formatter.format(undent`
function test()
temp = {
key_9: {
env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function
}
}
end function
`, {
formatMultiLineObjectsAndArrays: false
})).to.equal(undent`
function test()
temp = {
key_9: {
env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function
}
}
end function
`);

expect(formatter.format(undent`
namespace tests
class TestStuff
function _()
m.assertEqual(sanitize({
key_0: { env: [], themes: ["any"] }
key_1: { env: ["any"], themes: ["test"] }
key_2: { env: ["prod"], themes: ["test"] }
key_3: { env: ["prod"], themes: ["test"] }
key_4: { env: ["prod", "qa"], themes: ["test", "test"] }
key_5: { env: ["dev", "qa"], themes: ["test"] }
key_6: { env: ["dev", "qa"], themes: ["test", "test"] }
key_7: { env: ["dev", "qa"], themes: ["test"] }
key_8: { env: [], themes: [] }
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
key_10: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return false
end function }
key_11: { env: ["dev"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
key_12: { env: ["any"], themes: ["test"], runtimeCheck: function() as boolean
return true
end function }
key_13: { { env: ["any"], themes: ["test"], runtimeCheck: function() as boolean
return true
end function } }
}, "prod", "test"), {
enabled: ["key_1", "key_2", "key_4", "key_9"]
available: ["key_0", "key_1", "key_10", "key_11", "key_12", "key_2", "key_3", "key_4", "key_5", "key_6", "key_7", "key_8", "key_9"]
})
end function
end class
end namespace
`, {
formatMultiLineObjectsAndArrays: false
})).to.equal(undent`
namespace tests
class TestStuff
function _()
m.assertEqual(sanitize({
key_0: { env: [], themes: ["any"] }
key_1: { env: ["any"], themes: ["test"] }
key_2: { env: ["prod"], themes: ["test"] }
key_3: { env: ["prod"], themes: ["test"] }
key_4: { env: ["prod", "qa"], themes: ["test", "test"] }
key_5: { env: ["dev", "qa"], themes: ["test"] }
key_6: { env: ["dev", "qa"], themes: ["test", "test"] }
key_7: { env: ["dev", "qa"], themes: ["test"] }
key_8: { env: [], themes: [] }
key_9: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
key_10: { env: ["any"], themes: ["any"], runtimeCheck: function() as boolean
return false
end function }
key_11: { env: ["dev"], themes: ["any"], runtimeCheck: function() as boolean
return true
end function }
key_12: { env: ["any"], themes: ["test"], runtimeCheck: function() as boolean
return true
end function }
key_13: { { env: ["any"], themes: ["test"], runtimeCheck: function() as boolean
return true
end function } }
}, "prod", "test"), {
enabled: ["key_1", "key_2", "key_4", "key_9"]
available: ["key_0", "key_1", "key_10", "key_11", "key_12", "key_2", "key_3", "key_4", "key_5", "key_6", "key_7", "key_8", "key_9"]
})
end function
end class
end namespace
`);
});
});

describe('formatMultiLineObjectsAndArrays', () => {
Expand Down
59 changes: 59 additions & 0 deletions src/formatters/IndentFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class IndentFormatter {
let currentLineOffset = 0;
let nextLineOffset = 0;
let foundIndentorThisLine = false;
let outdentCount = 0;

for (let i = 0; i < lineTokens.length; i++) {
let token = lineTokens[i];
Expand Down Expand Up @@ -151,6 +152,10 @@ export class IndentFormatter {
}

nextLineOffset--;
if (OutdentSpacerTokenKinds.includes(token.kind)) {
outdentCount++;
}

if (foundIndentorThisLine === false) {
currentLineOffset--;
}
Expand Down Expand Up @@ -189,12 +194,66 @@ export class IndentFormatter {
// tabCount--;
// }
}

//check if next multiple indents are followed by multiple outdents and update indentation accordingly
if (nextLineOffset > 1) {
nextLineOffset = this.lookaheadSameLineMultiOutdents(tokens, lineTokens[lineTokens.length - 1], nextLineOffset, currentLineOffset);
} else if (outdentCount > 0) {
//if multiple outdents on same line then outdent only once
if (currentLineOffset < 0) {
currentLineOffset = -1;
}

//if multiple outdents on same line then outdent nextline only once
if (nextLineOffset < 0) {
nextLineOffset = -1;
}
}

return {
currentLineOffset: currentLineOffset,
nextLineOffset: nextLineOffset
};
}

/**
* Lookahead if next line with oudents are same as indents on current line
* @param tokens the array of tokens in a file
* @param curLineToken token of curent line
* @param nextLineOffset the number of tabs to indent the next line
* @param currentLineOffset the number of tabs to indent the current line
*/
private lookaheadSameLineMultiOutdents(tokens: Token[], curLineToken: Token, nextLineOffset: number, currentLineOffset: number): number {
let outdentCount = 0;
let tokenLineNum = 0;
let currentLineTokenIndex = tokens.indexOf(curLineToken);

for (let i = currentLineTokenIndex + 1; i < tokens.length; i++) {
let token = tokens[i];
if (token.kind !== TokenKind.Whitespace) {
//next line with outdents
if (OutdentSpacerTokenKinds.includes(token.kind)) {
if (tokenLineNum === 0) {
tokenLineNum = token.range.start.line;
}

if (token.range.start.line === tokenLineNum) {
outdentCount++;
}
}
if (tokenLineNum > 0 && token.range.start.line !== tokenLineNum) {
break;
}
}
}

//if outdents on next line with outdents = indents on current line then indent next line by one tab only
if (outdentCount > 0 && outdentCount === nextLineOffset) {
nextLineOffset = currentLineOffset + 1;
}
return nextLineOffset;
}

/**
* Ensure the list of tokens contains the correct number of tabs
* @param tokens the array of tokens to be modified in-place
Expand Down
Loading