Skip to content

Commit ad64d71

Browse files
authored
Improve swift attribute formatting (#618)
Render Swift decl attributes on their own line. **Examples** Before: ``` @discardableResult func foo() -> Bool ``` After: ``` @discardableResult func foo() -> Bool ```` Before: ``` @objc(foo) func bar() ``` After: ``` @objc(foo) func bar() ``` Resolves: rdar://108412440
1 parent 7f11ed9 commit ad64d71

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

src/components/DocumentationTopic/PrimaryContent/DeclarationSource.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ export default {
127127
}
128128
}
129129
130+
// check if this is a text token following an attribute token
131+
// so we can insert a newline here and split each attribute onto its
132+
// own line
133+
//
134+
// we want to avoid doing this when the attribute is encountered
135+
// in a param clause for attributes like `@escaping`
136+
if (token.kind === TokenKind.text && i > 0
137+
&& tokens[i - 1].kind === TokenKind.attribute
138+
&& numUnclosedParens === 0) {
139+
newToken.text = `${token.text.trimEnd()}\n`;
140+
}
141+
130142
// if we find some text ending with ", " and the next token is the start
131143
// of a new param, update this token text to replace the space with a
132144
// newline followed by 4 spaces

tests/unit/components/DocumentationTopic/PrimaryContent/DeclarationSource.spec.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,4 +686,107 @@ describe('Swift function/initializer formatting', () => {
686686

687687
themeSettingsState.theme = originalTheme;
688688
});
689+
690+
it('breaks attributes onto their own lines', () => {
691+
// Before:
692+
// @discardableResult @objc(baz) func foobarbaz() -> Int
693+
//
694+
// After:
695+
// @discardableResult
696+
// @objc(baz)
697+
// func foobarbaz() -> Int
698+
const tokens = [
699+
{
700+
kind: 'attribute',
701+
text: '@discardableResult',
702+
},
703+
{
704+
kind: 'text',
705+
text: ' ',
706+
},
707+
{
708+
kind: 'attribute',
709+
text: '@objc',
710+
},
711+
{
712+
kind: 'text',
713+
text: '(baz) ',
714+
},
715+
{
716+
kind: 'keyword',
717+
text: 'func',
718+
},
719+
{
720+
kind: 'text',
721+
text: ' ',
722+
},
723+
{
724+
kind: 'identifier',
725+
text: 'foobarbaz',
726+
},
727+
{
728+
kind: 'text',
729+
text: '() -> ',
730+
},
731+
{
732+
kind: 'typeIdentifier',
733+
text: 'Int',
734+
preciseIdentifier: 's:Si',
735+
},
736+
];
737+
const wrapper = mountWithTokens(tokens);
738+
739+
const tokenComponents = wrapper.findAll(Token);
740+
expect(tokenComponents.length).toBe(tokens.length);
741+
expect(tokenComponents.at(1).props('text')).toBe('\n');
742+
expect(tokenComponents.at(3).props('text')).toBe('(baz)\n');
743+
});
744+
745+
it('does not add newlines to attributes within param clause', () => {
746+
// func foo(bar: @escaping () -> ())
747+
const tokens = [
748+
{
749+
kind: 'keyword',
750+
text: 'func',
751+
},
752+
{
753+
kind: 'text',
754+
text: ' ',
755+
},
756+
{
757+
kind: 'identifier',
758+
text: 'foo',
759+
},
760+
{
761+
kind: 'text',
762+
text: '(',
763+
},
764+
{
765+
kind: 'externalParam',
766+
text: 'bar',
767+
},
768+
{
769+
kind: 'text',
770+
text: ': ',
771+
},
772+
{
773+
kind: 'attribute',
774+
text: '@escaping',
775+
},
776+
{
777+
kind: 'text',
778+
text: ' () -> ()',
779+
},
780+
{
781+
kind: 'text',
782+
text: ')',
783+
},
784+
];
785+
const wrapper = mountWithTokens(tokens);
786+
787+
const tokenComponents = wrapper.findAll(Token);
788+
expect(tokenComponents.length).toBe(tokens.length);
789+
expect(tokenComponents.at(6).props('text')).toBe(tokens[6].text);
790+
expect(tokenComponents.at(7).props('text')).toBe(tokens[7].text);
791+
});
689792
});

0 commit comments

Comments
 (0)