@@ -549,6 +549,11 @@ extension Formatter {
549549 return false
550550 }
551551
552+ // If the code is in a string, then it could be inside a string interpolation
553+ if tokens [ startOfScopeIndex] == . startOfScope( " \" " ) || tokens [ startOfScopeIndex] == . startOfScope( " \" \" \" " ) {
554+ return false
555+ }
556+
552557 // If this is a function scope, but not the body of the function itself,
553558 // then this is some nested function.
554559 if lastSignificantKeyword ( at: startOfScopeIndex, excluding: [ " where " ] ) == " func " ,
@@ -1688,6 +1693,146 @@ extension Formatter {
16881693 return startIndex ... endOfExpression
16891694 }
16901695
1696+ /// Parses the expression ending at the given index.
1697+ ///
1698+ /// This is the reverse counterpart to `parseExpressionRange(startingAt:)`.
1699+ /// It works backwards from an ending position to find where the expression starts.
1700+ func parseExpressionRange(
1701+ endingAt endIndex: Int
1702+ ) -> ClosedRange < Int > ? {
1703+ let token = tokens [ endIndex]
1704+
1705+ // First, check what comes BEFORE this token to see if we're part of a larger expression
1706+ if let prevIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: endIndex) {
1707+ let prevToken = tokens [ prevIndex]
1708+
1709+ // Check for dot notation (foo.bar)
1710+ if prevToken == . operator( " . " , . infix) || prevToken == . delimiter( " . " ) {
1711+ guard let beforeDotIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: prevIndex) ,
1712+ let previousExpression = parseExpressionRange ( endingAt: beforeDotIndex)
1713+ else { return nil }
1714+ return previousExpression. lowerBound ... endIndex
1715+ }
1716+
1717+ // Check for infix operators (foo + bar, but not foo = bar)
1718+ if prevToken. isOperator ( ofType: . infix) , prevToken. string != " = " {
1719+ guard let beforeOperatorIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: prevIndex) ,
1720+ let previousExpression = parseExpressionRange ( endingAt: beforeOperatorIndex)
1721+ else { return nil }
1722+ return previousExpression. lowerBound ... endIndex
1723+ }
1724+
1725+ // Prefix operators have to be the start of a subexpression,
1726+ // but can come after infix operators like `foo == !bar`.
1727+ if prevToken. isOperator ( ofType: . prefix) ,
1728+ let tokenBeforeOperator = index ( of: . nonSpaceOrComment, before: prevIndex) ,
1729+ tokens [ tokenBeforeOperator] . isOperator ( ofType: . infix) ,
1730+ let previousExpression = parseExpressionRange ( endingAt: prevIndex)
1731+ {
1732+ return previousExpression. lowerBound ... endIndex
1733+ }
1734+
1735+ // Check for type casting keywords (as, is)
1736+ if prevToken == . keyword( " as " ) || prevToken == . keyword( " is " ) {
1737+ guard let beforeKeywordIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: prevIndex) ,
1738+ let previousExpression = parseExpressionRange ( endingAt: beforeKeywordIndex)
1739+ else { return nil }
1740+ return previousExpression. lowerBound ... endIndex
1741+ }
1742+
1743+ // Check for as?, as! (unwrap operator after "as")
1744+ if prevToken. isUnwrapOperator,
1745+ let asIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: prevIndex) ,
1746+ tokens [ asIndex] == . keyword( " as " )
1747+ {
1748+ guard let beforeAsIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: asIndex) ,
1749+ let previousExpression = parseExpressionRange ( endingAt: beforeAsIndex)
1750+ else { return nil }
1751+ return previousExpression. lowerBound ... endIndex
1752+ }
1753+
1754+ // Check for prefix operators or keywords (!, try, await)
1755+ let prefixKeywords = [ " await " , " try " , " repeat " , " each " ]
1756+ if prevToken. isOperator ( ofType: . prefix) || prefixKeywords. contains ( prevToken. string) {
1757+ return prevIndex ... endIndex
1758+ }
1759+
1760+ // Check for operators after prefix keywords (try!, try?, await!)
1761+ if prevToken. isUnwrapOperator || prevToken == . operator( " ? " , . postfix) {
1762+ if let beforeOperatorIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: prevIndex) ,
1763+ prefixKeywords. contains ( tokens [ beforeOperatorIndex] . string)
1764+ {
1765+ return beforeOperatorIndex ... endIndex
1766+ }
1767+ }
1768+ }
1769+
1770+ // Handle postfix and infix operators (!, ?, +) that can be preceded by other parts of the expression
1771+ if token. isOperator ( ofType: . postfix) || token. isOperator ( ofType: . infix) {
1772+ guard let prevIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: endIndex) ,
1773+ let previousExpression = parseExpressionRange ( endingAt: prevIndex)
1774+ else { return nil }
1775+ return previousExpression. lowerBound ... endIndex
1776+ }
1777+
1778+ // Handle end of scope tokens ), ], }, "
1779+ if case . endOfScope = token {
1780+ guard let startOfScope = index ( of: . startOfScope, before: endIndex) else { return nil }
1781+
1782+ // Check if there's something before the scope (method call, subscript)
1783+ if let prevIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: startOfScope) ,
1784+ let previousExpression = parseExpressionRange ( endingAt: prevIndex)
1785+ {
1786+ return previousExpression. lowerBound ... endIndex
1787+ }
1788+
1789+ // The scope itself is the expression (array literal, closure, etc)
1790+ return startOfScope ... endIndex
1791+ }
1792+
1793+ // Base cases: identifiers, numbers, literals, etc.
1794+ if token. isIdentifier || token. isNumber {
1795+ return endIndex ... endIndex
1796+ }
1797+
1798+ return nil
1799+ }
1800+
1801+ /// Parses the expression that contains the token at the given index.
1802+ func parseExpressionRange(
1803+ containing index: Int
1804+ ) -> ClosedRange < Int > ? {
1805+ // To find the complete expression, parse forwards from the input index to the end of the expression,
1806+ // and then parse backwards from the end of that expression to the start of the complete expression.
1807+ var forwardRange = parseExpressionRange ( startingAt: index)
1808+
1809+ // If this is an operator that can't ever be the start of an expression, parse from some previous token
1810+ if forwardRange == nil ,
1811+ tokens [ index] . isOperator ( ofType: . postfix) || tokens [ index] . isOperator ( ofType: . infix)
1812+ {
1813+ var parseToken = index
1814+ while let previousToken = self . index ( of: . nonSpaceOrCommentOrLinebreak, before: parseToken) {
1815+ // Check if this previous token is the start of a subexpression that contains the given index
1816+ if let forwardRangeFromPreviousToken = parseExpressionRange ( startingAt: parseToken) ,
1817+ forwardRangeFromPreviousToken. contains ( index)
1818+ {
1819+ forwardRange = forwardRangeFromPreviousToken
1820+ break
1821+ }
1822+
1823+ parseToken = previousToken
1824+ }
1825+ forwardRange = parseExpressionRange ( startingAt: parseToken)
1826+ }
1827+
1828+ guard let forwardRange,
1829+ let backwardRange = parseExpressionRange ( endingAt: forwardRange. upperBound) ,
1830+ backwardRange. contains ( index)
1831+ else { return nil }
1832+
1833+ return backwardRange
1834+ }
1835+
16911836 /// Parses all of the declarations in the source file.
16921837 func parseDeclarations( ) -> [ Declaration ] {
16931838 parseDeclarations ( in: tokens. indices)
0 commit comments