@@ -1023,6 +1023,120 @@ public struct _FormatRules {
10231023 }
10241024 }
10251025
1026+ public let insertBlankLinesAtScope = FormatRule (
1027+ help: " Insert blank line at start/end of scope class, struct, extension "
1028+ ) { formatter in
1029+
1030+ var canInsertSpaceAtStart = false
1031+ var startToken : Token ?
1032+
1033+ formatter. forEachToken { i, token in
1034+ switch token {
1035+ case . keyword( " class " ) ,
1036+ . keyword( " struct " ) ,
1037+ . keyword( " extension " ) :
1038+ canInsertSpaceAtStart = true
1039+
1040+ case . keyword( " func " ) ,
1041+ . keyword( " var " ) ,
1042+ . keyword( " protocol " ) :
1043+ canInsertSpaceAtStart = false
1044+
1045+ case . startOfScope( " { " ) :
1046+ guard canInsertSpaceAtStart else { return }
1047+ startToken = token
1048+
1049+ guard let linebreakToken = formatter. token ( at: i + 2 ) ,
1050+ !linebreakToken. isLinebreak
1051+ else { return }
1052+
1053+ guard let endOfScopeToken = formatter. token ( at: i + 1 ) ,
1054+ endOfScopeToken != . endOfScope( " { " )
1055+ else { return }
1056+
1057+ formatter. insertLinebreak ( at: i + 1 )
1058+
1059+ case . endOfScope( " } " ) :
1060+ guard let startToken = startToken,
1061+ token. isEndOfScope ( startToken)
1062+ else { return }
1063+
1064+ guard let linebreakToken = formatter. token ( at: i - 2 ) ,
1065+ !linebreakToken. isLinebreak
1066+ else { return }
1067+
1068+ guard let startOfScopeToken = formatter. token ( at: i - 1 ) ,
1069+ startOfScopeToken != . startOfScope( " { " )
1070+ else { return }
1071+
1072+ formatter. insertLinebreak ( at: i - 1 )
1073+
1074+ default :
1075+ break
1076+ }
1077+ }
1078+
1079+ formatter. forEachToken { i, token in
1080+ switch token {
1081+ case . keyword( " class " ) ,
1082+ . keyword( " struct " ) ,
1083+ . keyword( " extension " ) :
1084+ guard let startOfScopeIndex = formatter. index ( of: . startOfScope( " { " ) , after: i) ,
1085+ !( formatter. token ( at: startOfScopeIndex + 2 ) ? . isLinebreak ?? true )
1086+ else { return }
1087+
1088+ if let wrongToken = formatter. lastToken ( before: startOfScopeIndex, where: {
1089+ $0 == . keyword( " func " ) || $0 == . keyword( " protocol " )
1090+ } ) ,
1091+ let wrongIndex = formatter. index ( of: token, before: startOfScopeIndex)
1092+ {
1093+ let startOfScopeLine = formatter. originalLine ( at: startOfScopeIndex)
1094+ let funcLine = formatter. originalLine ( at: startOfScopeIndex)
1095+ if startOfScopeLine == funcLine { return }
1096+ }
1097+
1098+ formatter. insertLinebreak ( at: startOfScopeIndex + 1 )
1099+
1100+ case . endOfScope( " } " ) :
1101+ guard let currentScopeToken = formatter. currentScope ( at: i) ,
1102+ let startOfScopeIndex = formatter. index ( of: currentScopeToken, before: i) ,
1103+ let token = formatter. lastToken ( before: startOfScopeIndex, where: {
1104+ $0 == . keyword( " class " ) ||
1105+ $0 == . keyword( " struct " ) ||
1106+ $0 == . keyword( " extension " )
1107+ } ) ,
1108+ let keywordIndex = formatter. index ( of: token, before: startOfScopeIndex)
1109+ else { return }
1110+
1111+ let startOfScopeLine = formatter. originalLine ( at: startOfScopeIndex)
1112+ let keywordLine = formatter. originalLine ( at: keywordIndex)
1113+
1114+ if let wrongToken = formatter. lastToken ( before: startOfScopeIndex, where: {
1115+ $0 == . keyword( " func " ) || $0 == . keyword( " protocol " )
1116+ } ) ,
1117+ let wrongIndex = formatter. index ( of: token, before: startOfScopeIndex)
1118+ {
1119+ let funcLine = formatter. originalLine ( at: wrongIndex)
1120+ if startOfScopeLine == funcLine { return }
1121+ }
1122+
1123+ guard startOfScopeLine == keywordLine else { return }
1124+ guard !( formatter. token ( at: i) ? . isLinebreak ?? true ) else { return }
1125+ guard let prevEndScopeIndex = formatter. index ( of: . endOfScope, before: i) else { return }
1126+
1127+ let isCloseBrace = formatter. token ( at: prevEndScopeIndex) == . endOfScope( " } " )
1128+ let isCorrectEndOfScope = isCloseBrace || !( formatter. token ( at: i - 3 ) ? . isLinebreak ?? true )
1129+
1130+ guard isCorrectEndOfScope, prevEndScopeIndex == i - 2 else { return }
1131+
1132+ formatter. insertLinebreak ( at: prevEndScopeIndex + 1 )
1133+
1134+ default :
1135+ break
1136+ }
1137+ }
1138+ }
1139+
10261140 /// Adds a blank line around MARK: comments
10271141 public let blankLinesAroundMark = FormatRule (
10281142 help: " Insert blank line before and after `MARK:` comments. " ,
0 commit comments