Skip to content

Instantly share code, notes, and snippets.

@Reflejo
Last active October 7, 2016 17:27
Show Gist options
  • Save Reflejo/cb5f79d7ad6dc7a291965a5ca10616a3 to your computer and use it in GitHub Desktop.
Save Reflejo/cb5f79d7ad6dc7a291965a5ca10616a3 to your computer and use it in GitHub Desktop.
import argparse
import fnmatch
import json
import os
import re
import sys
from collections import namedtuple
REGEX = re.compile("""
fileprivate
\s+
(?:var|func|let)
\s+
(.+?)
\s*
[:(]
""", re.VERBOSE)
USE_REGEX = """
(?:^|\W) # Should be prepended by a non-alphanumeric char or SOL
{variable} # The variable itself (replaced further down)
(?:$|\W) # Should be followed by a non-alphanumeric char or EOL
"""
def find_scopes(content, max_depth=1):
queue = []
for i, letter in enumerate(content):
if letter == "{":
queue.append((i, "{"))
elif letter == "}":
depth = len(queue)
start, _ = queue.pop()
if max_depth >= depth:
yield depth, start, i + 1
def convert(content):
"""
>>> content = '''
... private let global: () -> Void = { print("heh") }
... struct Test {
... fileprivate let a: Int = 1
... fileprivate let b: Int = 1
... @IBOutlet fileprivate var c: String
... let d: Int = 1
... fileprivate func test() {}
... }
...
... extension Test {
... fileprivate var something: Int {
... Test().b
... print(a)
... return self.d
... }
... }
... '''
>>> expected = '''
... private let global: () -> Void = { print("heh") }
... struct Test {
... fileprivate let a: Int = 1
... fileprivate let b: Int = 1
... @IBOutlet private var c: String
... let d: Int = 1
... private func test() {}
... }
...
... extension Test {
... private var something: Int {
... Test().b
... print(a)
... return self.d
... }
... }
... '''
>>> convert(content) == expected
True
"""
replaces = []
for _, start, end in find_scopes(content):
scope = content[start:end]
rest = content[:start] + content[end:]
for match in REGEX.finditer(scope):
variable = match.group(1)
mstart, mend = match.start(), match.start() + len(match.group())
regex = re.compile(USE_REGEX.format(variable=variable), re.VERBOSE)
if regex.findall(rest):
continue
replaces.append(start + mstart)
for start in replaces[::-1]:
end = start + len("fileprivate")
content = content[:start] + "private" + content[end:]
return content
def main(args):
files = [os.path.join(path, filepath)
for path, dirs, files in os.walk(args.directory)
for filepath in fnmatch.filter(files, "*.swift")]
for filepath in files or [args.file]:
content = open(filepath).read()
fixed = convert(content)
open(filepath, "w").write(fixed)
def test():
import doctest
doctest.testmod()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Migrates fileprivate insanity on a local scope"
)
parser.add_argument('-t', help="Run tests", action='store_const',
const=True, dest="test")
parser.add_argument('--file', help="Swift file to fix")
parser.add_argument('--directory', help="Swift directory to fix")
args = parser.parse_args()
if not args.file and not args.directory and not args.test:
parser.print_help()
sys.exit(1)
if args.test:
test()
else:
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment