Last active
October 7, 2016 17:27
-
-
Save Reflejo/cb5f79d7ad6dc7a291965a5ca10616a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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