From f1b7d895e207d3eda27500354c3abd1218b4be5b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 20 Sep 2017 15:31:17 -0400 Subject: [PATCH] Modify precommit script to clean up includes and forward declarations. // FREEBIE --- .../ConversationViewController.m | 10 +- SignalServiceKit/Utilities/precommit.py | 254 ++++++++++++++++++ .../src/Devices/OWSReadReceiptsProcessor.h | 2 +- 3 files changed, 260 insertions(+), 6 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 2ea8bc59a..28e5b7160 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2,18 +2,18 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "ConversationViewController.h" #import "AppDelegate.h" #import "AttachmentSharing.h" #import "BlockListUIUtils.h" #import "BlockListViewController.h" #import "ContactsViewHelper.h" +#import "ConversationViewController.h" #import "DebugUITableViewController.h" #import "Environment.h" #import "FingerprintViewController.h" #import "FullImageViewController.h" -#import "NSDate+millisecondTimeStamp.h" #import "NewGroupViewController.h" +#import "NSDate+millisecondTimeStamp.h" #import "OWSAudioAttachmentPlayer.h" #import "OWSCall.h" #import "OWSContactOffersCell.h" @@ -34,6 +34,7 @@ #import "OWSUnreadIndicatorCell.h" #import "Signal-Swift.h" #import "SignalKeyingStorage.h" +#import "ThreadUtil.h" #import "TSAttachmentPointer.h" #import "TSCall.h" #import "TSContactThread.h" @@ -46,15 +47,14 @@ #import "TSInfoMessage.h" #import "TSInvalidIdentityKeyErrorMessage.h" #import "TSUnreadIndicatorInteraction.h" -#import "ThreadUtil.h" #import "UIFont+OWS.h" #import "UIUtil.h" #import "UIViewController+CameraPermissions.h" #import "UIViewController+OWS.h" #import "ViewControllerUtils.h" -#import #import #import +#import #import #import #import @@ -79,12 +79,12 @@ #import #import #import +#import #import #import #import #import #import -#import #import @import Photos; diff --git a/SignalServiceKit/Utilities/precommit.py b/SignalServiceKit/Utilities/precommit.py index 7c72b84e5..f69953381 100755 --- a/SignalServiceKit/Utilities/precommit.py +++ b/SignalServiceKit/Utilities/precommit.py @@ -12,6 +12,256 @@ import commands git_repo_path = os.path.abspath(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip()) + +class include: + def __init__(self, isInclude, isQuote, body, comment): + self.isInclude = isInclude + self.isQuote = isQuote + self.body = body + self.comment = comment + + def format(self): + result = '%s %s%s%s' % ( + ('#include' if self.isInclude else '#import'), + ('"' if self.isQuote else '<'), + self.body.strip(), + ('"' if self.isQuote else '>'), + ) + if self.comment.strip(): + result += ' ' + self.comment.strip() + return result + + +def is_include_or_import(line): + line = line.strip() + if line.startswith('#include '): + return True + elif line.startswith('#import '): + return True + else: + return False + + +def parse_include(line): + remainder = line.strip() + + if remainder.startswith('#include '): + isInclude = True + remainder = remainder[len('#include '):] + elif remainder.startswith('#import '): + isInclude = False + remainder = remainder[len('#import '):] + elif remainder == '//': + return None + elif not remainder: + return None + else: + print ('Unexpected import or include: '+ line) + sys.exit(1) + + comment = None + if remainder.startswith('"'): + isQuote = True + endIndex = remainder.find('"', 1) + if endIndex < 0: + print ('Unexpected import or include: '+ line) + sys.exit(1) + body = remainder[1:endIndex] + comment = remainder[endIndex+1:] + elif remainder.startswith('<'): + isQuote = False + endIndex = remainder.find('>', 1) + if endIndex < 0: + print ('Unexpected import or include: '+ line) + sys.exit(1) + body = remainder[1:endIndex] + comment = remainder[endIndex+1:] + else: + print ('Unexpected import or include: '+ remainder) + sys.exit(1) + + return include(isInclude, isQuote, body, comment) + + +def parse_includes(text): + lines = text.split('\n') + + includes = [] + for line in lines: + include = parse_include(line) + if include: + includes.append(include) + + return includes + + +def sort_include_block(text, filepath, filename, file_extension): + lines = text.split('\n') + + includes = parse_includes(text) + + blocks = [] + + file_extension = file_extension.lower() + + for include in includes: + include.isInclude = False + + if file_extension in ('c', 'cpp', 'hpp'): + for include in includes: + include.isInclude = True + elif file_extension in ('m'): + for include in includes: + include.isInclude = False + + + def formatBlock(includes): + lines = [include.format() for include in includes] + lines = list(set(lines)) + def include_sorter(a, b): + return cmp(a.lower(), b.lower()) + lines.sort(include_sorter) + # print + # print 'filepath' + # for line in lines: + # print '\t', line + # print + return '\n'.join(lines) + + includeAngles = [include for include in includes if include.isInclude and not include.isQuote] + includeQuotes = [include for include in includes if include.isInclude and include.isQuote] + importAngles = [include for include in includes if (not include.isInclude) and not include.isQuote] + importQuotes = [include for include in includes if (not include.isInclude) and include.isQuote] + if includeQuotes: + blocks.append(formatBlock(includeQuotes)) + if includeAngles: + blocks.append(formatBlock(includeAngles)) + if importQuotes: + blocks.append(formatBlock(importQuotes)) + if importAngles: + blocks.append(formatBlock(importAngles)) + + return '\n'.join(blocks) + '\n' + + +def sort_class_statement_block(text, filepath, filename, file_extension): + lines = text.split('\n') + lines = [line.strip() for line in lines if line.strip()] + lines.sort() + return '\n' + '\n'.join(lines) + '\n' + + +def find_matching_section(text, match_test): + lines = text.split('\n') + first_matching_line_index = None + for index, line in enumerate(lines): + if match_test(line): + first_matching_line_index = index + break + + if first_matching_line_index is None: + return None + + # Absorb any leading empty lines. + while first_matching_line_index > 0: + prev_line = lines[first_matching_line_index - 1] + if prev_line.strip(): + break + first_matching_line_index = first_matching_line_index - 1 + + first_non_matching_line_index = None + for index, line in enumerate(lines[first_matching_line_index:]): + if not line.strip(): + # Absorb any trailing empty lines. + continue + if not match_test(line): + first_non_matching_line_index = index + first_matching_line_index + break + + text0 = '\n'.join(lines[:first_matching_line_index]) + if first_non_matching_line_index is None: + text1 = '\n'.join(lines[first_matching_line_index:]) + text2 = None + else: + text1 = '\n'.join(lines[first_matching_line_index:first_non_matching_line_index]) + text2 = '\n'.join(lines[first_non_matching_line_index:]) + + return text0, text1, text2 + + +def sort_matching_blocks(filepath, filename, file_extension, text, match_func, sort_func): + unprocessed = text + processed = None + while True: + section = find_matching_section(unprocessed, match_func) + # print '\t', 'sort_matching_blocks', section + if not section: + if processed: + processed = '\n'.join((processed, unprocessed,)) + else: + processed = unprocessed + break + + text0, text1, text2 = section + + if processed: + processed = '\n'.join((processed, text0,)) + else: + processed = text0 + + # print 'before:' + # temp_lines = text1.split('\n') + # for index, line in enumerate(temp_lines): + # if index < 3 or index + 3 >= len(temp_lines): + # print '\t', index, line + # # print text1 + # print + text1 = sort_func(text1, filepath, filename, file_extension) + # print 'after:' + # # print text1 + # temp_lines = text1.split('\n') + # for index, line in enumerate(temp_lines): + # if index < 3 or index + 3 >= len(temp_lines): + # print '\t', index, line + # print + processed = '\n'.join((processed, text1,)) + if text2: + unprocessed = text2 + else: + break + + return processed + + +def find_class_statement_section(text): + def is_class_statement(line): + return line.strip().startswith('@class ') + + return find_matching_section(text, is_class_statement) + + +def find_include_section(text): + def is_include_line(line): + return is_include_or_import(line) + # return is_include_or_import_or_empty(line) + + return find_matching_section(text, is_include_line) + + +def sort_includes(filepath, filename, file_extension, text): + print 'sort_includes', filepath + if file_extension not in ('.h', '.m', '.mm'): + return text + return sort_matching_blocks(filepath, filename, file_extension, text, find_include_section, sort_include_block) + + +def sort_class_statements(filepath, filename, file_extension, text): + print 'sort_class_statements', filepath + if file_extension not in ('.h', '.m', '.mm'): + return text + return sort_matching_blocks(filepath, filename, file_extension, text, find_class_statement_section, sort_class_statement_block) + + def splitall(path): allparts = [] while 1: @@ -52,7 +302,11 @@ def process(filepath): with open(filepath, 'rt') as f: text = f.read() + original_text = text + + text = sort_includes(filepath, filename, file_ext, text) + text = sort_class_statements(filepath, filename, file_ext, text) lines = text.split('\n') while lines and lines[0].startswith('//'): diff --git a/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.h b/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.h index 39b1663db..2af1075f7 100644 --- a/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.h +++ b/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.h @@ -4,8 +4,8 @@ NS_ASSUME_NONNULL_BEGIN -@class OWSSignalServiceProtosSyncMessageRead; @class OWSReadReceipt; +@class OWSSignalServiceProtosSyncMessageRead; @class TSIncomingMessage; @class TSStorageManager; @class YapDatabaseReadWriteTransaction;