Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions lib/rouge/lexers/swift.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class Swift < RegexLexer
actor any associatedtype borrowing class consuming deinit distributed dynamic enum convenience extension fileprivate final func import indirect init internal lazy let macro nonisolated open optional package private protocol public required some static struct subscript typealias var
)

# Swift Testing macros
testing_macros = Set.new %w(
expect require
)

constants = Set.new %w(
true false nil
)
Expand All @@ -35,9 +40,13 @@ class Swift < RegexLexer
@re_delim = "" # multi-line regex delimiter
end

# beginning of line
# beginning of line

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: whitespace added

state :bol do
rule %r/#(?![#"\/]).*/, Comment::Preproc
# Handle Swift compiler directives

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what other directives there are that we should look for. For example #ifdef, #warning, #error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've intentionally tokenized #if and its relatives as preprocessor, but traditionally they're called as compiler directive in Swift land.

The rest like #warning, #error et al will be treated as Keyword as they'll not match to any rule.

rule %r/#if\b.*/, Comment::Preproc
rule %r/#elseif\b.*/, Comment::Preproc
rule %r/#else\b.*/, Comment::Preproc
rule %r/#endif\b.*/, Comment::Preproc

mixin :inline_whitespace

Expand Down Expand Up @@ -86,7 +95,13 @@ class Swift < RegexLexer
rule %r{[\d]+(?:_\d+)*}, Num::Integer

rule %r/@#{id}/, Keyword::Declaration
rule %r/##{id}/, Keyword
rule %r/#(#{id})/ do |m|
if testing_macros.include?(m[1])
token Name::Builtin
else
token Keyword
end
end

rule %r/(private|internal)(\([ ]*)(\w+)([ ]*\))/ do |m|
if m[3] == 'set'
Expand Down
36 changes: 36 additions & 0 deletions spec/lexers/swift_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,41 @@
['Text', "\n\n"],
['Comment.Multiline', separate_comment]
end

it 'lexes Swift Testing macros as built-ins' do
# Test #expect macro
expect_code = '#expect(result == 42)'
assert_tokens_equal expect_code,
['Name.Builtin', '#expect'],
['Punctuation', '('],
['Name', 'result'],
['Text', ' '],
['Operator', '=='],
['Text', ' '],
['Literal.Number.Integer', '42'],
['Punctuation', ')']

# Test #require macro
require_code = '#require(value != nil)'
assert_tokens_equal require_code,
['Name.Builtin', '#require'],
['Punctuation', '('],
['Name', 'value'],
['Text', ' '],
['Operator', '!='],
['Text', ' '],
['Keyword.Constant', 'nil'],
['Punctuation', ')']
end

it 'lexes other # prefixed identifiers as keywords' do
# Test that non-testing macros are still lexed as keywords
other_macro = '#selector(buttonTapped)'
assert_tokens_equal other_macro,
['Keyword', '#selector'],
['Punctuation', '('],
['Name', 'buttonTapped'],
['Punctuation', ')']
end
end
end