How to use regex quantifiers * and + for SwiftLint custom rule - swift

I'm trying to write a custom rule for SwiftLint. Following the directions in the readme, I've added the following to .swiftlint.yml:
custom_rules:
multi_clause_guard:
regex: 'guard .*,'
However, this regex is not matching any lines in my project, despite there being plenty of lines where it should match, for example:
guard let x = Int(s), let y = Int(t) else { return }
I've tried various other values for the regex, and it works until you introduce a quantifier.
✅ 'guard .,' will match the line guard a,
✅ 'guard ..,' will match the line guard _a,
❌ 'guard .*,' will not match the line guard a,
❌ 'guard .+,' will not match the line guard a,
Is there a way I can use * and + in a SwiftLint custom rule?

It seems that quantifiers can be applied to character sets that you define explicitly. In this case, it was enough for me to replace . with [\h\S] (which includes horizontal whitespace characters and any other character that's not a whitespace character).
custom_rules:
multi_clause_guard:
regex: 'guard [\h\S]*,'
If anyone knows how to make quantifiers work with ., I'm still interested to know!

Related

Check string matches exact format swift 5

I want to check if a string matches an exact regex pattern;
Currently, even though the string being compared is not an exact match, my function is returning true.
Pattern String: "([0-9],[0-9])"
For example,
(1,1) is valid
(5,4) is valid
Only strings entered in this format are valid I.E. Bracket Number Comma Number Bracket (Without spaces)
I.E.
[5,5] is not valid
{5,5] is not valid.
5,5 is not valid
Code I am using to check:
let stringToCheck = "[5,5]"
return stringToCheck.range(of: "([0-9],[0-9])", options: .regularExpression, range: nil, locale: nil) != nil
Can anyone help me with how to adjust this to check for exact matches in line with my pattern?
Thanks!
You need two things:
Escape parentheses
Add anchors because in the current code, the regex can match a part of a string.
You can thus use
stringToCheck.range(of: #"^\([0-9],[0-9]\)\z"#, options: .regularExpression, range: nil, locale: nil) != nil
Note the # chars on both ends, they allow escaping with single backslashes.
Details:
^ - start of string
\( - a ( char
[0-9] - a single ASCII digit (add + after ] to match one or more digits)
, - a comma
[0-9] - a single ASCII digit (add + after ] to match one or more digits)
\) - a ) char
\z - the very end of string (if linebreaks cannot be present in the string, $ is enough).

How can I individuate the text that encloses some other text in a pattern and edit it with regex

I'm learning about regex and I'm trying to create a program where a certain pattern is substituted.
Given the following string:
###hello#!
I want to recognise "###" and "#!" and substitute them with "*** and "*^". What's between these characters must remain as it is.
Now, I tried something like:
text.replacingOccurrences(of: #"(###)"#, with: "***", options: .regularExpression)
text.replacingOccurrences(of: #"(#!)"#, with: "*^", options: .regularExpression)
but if my string is:
"###hello#! ###hello###"
my output becomes:
"**hello^ hello"
while the desired one should be:
"**hello^ ###hello###"
In fact I only want the characters to be substituted when they follow the pattern:
### some text #!
I created a regex with the following pattern:
#"(###)(?:\\.*?)(#!)"#
but I'm not able to get the text and substitute it.
How can I individuate the text that encloses some other text in a pattern and edit it?
You can use
text = text.replacingOccurrences(of: #"(?s)###(.*?)#!"#, with: "***$1*^", options: .regularExpression)
See the regex demo. Details:
(?s) - an inline "singleline" flag that makes . match any char
### - left-hand delimiter
(.*?) - Capturing group 1 ($1 refers to this value): any zero or more chars as few as possible
#! - right-hand delimiter.
Swift test:
let text = "###hello#! ###hello###"
print(text.replacingOccurrences(of: #"(?s)###(.*?)#!"#, with: "***$1*^", options: .regularExpression))
// -> ***hello*^ ###hello###

Count leading tabs in Swift string

I need to count the number of leading tabs in a Swift string. I know there are fairly simple solutions (e.g. looping over the string until a non-tab character is encountered) but I am looking for a more elegant solution.
I have attempted to use a regex such as ^\\t* along with the .numberOfMatches method but this detects all the tab characters as one match. For example, if the string has three leading tabs then that method just returns 1. Is there a way to write a regex that treats each individual tab character as a single match?
Also open to other ways of approaching this without using a regex.
Here is a non-regex solution
let count = someString.prefix(while: {$0 == "\t"}).count
You may use
\G\t
See the regex demo.
Here,
\G - matches a string start position or end of the previous match position, and
\t - matches a single tab.
Swift test:
let string = "\t\t123"
let regex = try! NSRegularExpression(pattern: "\\G\t", options: [])
let numberOfOccurrences = regex.numberOfMatches(in: string, range: NSRange(string.startIndex..., in: string))
print(numberOfOccurrences) // => 2

Extracting range of unpadded string

I'd like to extract the Range<String.Index> of a sentence within its whitespace padding. For example,
let padded = " El águila (🦅). "
let sentenceRangeInPadded = ???
assert(padded[sentenceRangeInPadded] == "El águila (🦅).") // The test!
Here's some regex that I started with, but looks like variable length lookbehinds aren't supported.
let sentenceRangeInPadded = padded.range(of: #"(?<=^\s*).*?(?=\s*$)"#, options: .regularExpression)!
I'm not looking to extract the sentence (could just use trimmingCharacters(in:) for that), just the Range.
Thanks for reading!
You may use
#"(?s)\S(?:.*\S)?"#
See the regex demo.
Details
(?s) - a DOTALL modifier making . match any char, including line break chars
\S - the first non-whitespace char
(?:.*\S)? - an optional non-capturing group matching
.* - any 0+ chars as many as possible
\S - up to the last non-whitespace char.

Match "com.project.name" but not when it contains something else

I have the following code:
var i = "test"
and
var i = "com.project.name.test"
print("something else")
fatalError("some error")
I have a regex:
"((?!com\.project\.name).)*"
to match any string that does NOT contain "com.project.name".
However, I want to modify it to still have the above condition but not if the line contains print\(.*?\) and fatalError\(.*?\).
Why do I want to do this? Because I can only use regex for SwiftLint custom rules and right now my regex is greedy and matches every single string in the project that the developers forgot to localize..
What I've tried:
"((?!com\\.project\\.name).)*(?!print)(?!fatalError)"
but it does not work and instead matches the same as the original expression.
You may use this regex with a negative lookahead assertions:
^(?!.*(?:com\.project\.name|print\(|fatalError\()).*
RegEx Demo
This negative lookahead assertion uses alternations to fail the match on 3 different matches anywhere in the input:
com\.project\.name
print\(
fatalError\(